New shared component `artifacts/tx-os/src/components/app-dock.tsx`: takes a `currentSlug` prop, pulls the apps list from the same `useListApps()` query Home uses, filters out the current app and any inactive ones, and renders a floating centered pill near the bottom with one circular tinted button per remaining app. Reuses Home's `resolveIcon(name)` pattern (Lucide string -> component, falls back to Grid2X2) and Home's openApp logic (internal -> setLocation, external _tab -> window.open, external_iframe -> /embedded/:id). Renders nothing when fewer than 1 other apps are available. Hidden in print, respects safe-area-inset-bottom, scrollable on narrow widths. Mounted on every app page except Home: - executive-meetings.tsx (currentSlug="executive-meetings", matches seed slug; hidden when isFullscreen is true so the schedule fills the screen unobstructed). - services.tsx (currentSlug="services") - notes.tsx (currentSlug="notes") - orders-incoming.tsx (currentSlug="orders-incoming" — not a built-in app slug, so all apps show, which is the right UX since incoming inbox is reached via the topbar, not the home grid). - admin.tsx (currentSlug="admin") Removed the two gear/back-arrow blocks added by #596 in executive-meetings.tsx (test ids em-president-settings, em-president-back-to-schedule) — the dock now provides cross-app navigation including Admin/Settings. The L902 force-schedule effect that snaps the president back to the schedule section outside of schedule|settings is left in place; it still gates the hidden Manage / Audit tabs. Verified `pnpm --filter @workspace/tx-os exec tsc --noEmit` clean. No backend, no new translations, Home untouched.
This commit is contained in:
@@ -33,11 +33,19 @@ export function AppDock({ currentSlug }: AppDockProps) {
|
||||
const lang = i18n.language;
|
||||
const { data: apps } = useListApps({ query: { queryKey: getListAppsQueryKey() } });
|
||||
|
||||
// Match Home's source-of-truth contract: same `useListApps` query
|
||||
// key and the same `isActive` filter Home applies in its
|
||||
// orderedApps effect (home.tsx L365). We don't subscribe to Home's
|
||||
// local drag-reorder state here because that state is intentionally
|
||||
// a Home-only UX surface; the dock just mirrors the API order, same
|
||||
// as Home's `orderedApps ?? apps` fallback on first render.
|
||||
const others = (apps ?? []).filter(
|
||||
(a) => a.isActive && a.slug !== currentSlug,
|
||||
);
|
||||
|
||||
if (others.length < 1) return null;
|
||||
// Skip the degenerate single-button dock: if the user only has one
|
||||
// other app to switch to, the dock adds no real value.
|
||||
if (others.length <= 1) return null;
|
||||
|
||||
const openApp = (app: App) => {
|
||||
logAppOpen(app.id).catch(() => {});
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { OrderServiceModal } from "@/components/order-service-modal";
|
||||
import { AppDock } from "@/components/app-dock";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -514,6 +515,7 @@ export default function MyOrdersPage() {
|
||||
resolveImage={resolveServiceImageUrl}
|
||||
initialNotes={reorderTarget?.notes ?? null}
|
||||
/>
|
||||
<AppDock currentSlug="my-orders" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
import { ArrowRight, ArrowLeft, Bell, CheckCheck } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { AppDock } from "@/components/app-dock";
|
||||
import { formatDateTime } from "@/lib/i18n-format";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user