# AGENTS.md — Frontend Components > **Parent**: `../../AGENTS.md` — commands, auth flow, Next.js 16 rules, deployment. ## Overview 100+ client components organized by domain. The `(frontend)` layout renders guests `LandingPage` (no app shell); authed users get `AppSidebar` + `SiteHeader` + `CommandPalette`. ## Structure ``` components/frontend/ auth/ 2 files LoginForm, LoginLink (returnTo via usePathname) account/ 4 files Profile, password, preferences, Discord link banking/ 9 files Account cards, ledger, deposit/withdraw/transfer dialogs blocks/ 6 files AppSidebar, NavCore, NavUser, XPDisplay dashboard/ 6 files ProfileSummary, QuickStats, MissionBriefing, RecentEvents flappy/ 4 files Canvas game, leaderboard, sounds helpdesk/ 6 files Ticket list/detail, create dialog, timeline intelligence/ 13 files Mission cards/attendance/comms, campaign/faction cards locker/ 12 files Grid, equipment editor, loadouts, wardrobe logistics/ 8 files Shipment cards/actions, toast notifications market/ 10 files Listing cards, negotiation flow, NPC chat, patience meter notifications/ 2 files Bell (polling), inbox page realtime/ 1 file GameTickRealtime (SSE -> router.refresh) roster/ 1 file Org chart view storage/ 14 files Structure grid, storage dialogs, event ledger ``` ## Conventions ### Server → Client data flow Pages are server components that fetch via `getPayload()`, then pass data as props to client components. Client components receive typed props and never call Payload directly. ### Item compound component `storage/` uses a compound `` component for grid cells with slots: ``, ``, ``. Attachments render as stacked badges on the grid cell; popover shows full detail on hover. ### SSE consumers - `GameTickRealtime` mounts in `(frontend)/layout.tsx` for all authed users. - `ShipmentToasts` mounts only for logistics-qualified users. - Both use `useGameTick()` hook (custom `ptf:game-tick` event dispatch). ### Nested dialogs Market negotiation uses `MakeOfferDialog` nested inside `ListingCard` dialog. Loadout editor uses `EquipmentEditorDialog` nested inside locker grid. When a parent dialog unmounts (e.g., sold listing), the child stays visible for 3.5s minimum via `useNow()` hook for readability before refresh. ### Route anomalies `logistics/vehicles/VehiclesList.tsx` is a client component placed directly in the route directory (not under `components/frontend/`). This is the only route with an inline component file. ## Where to look | Task | Path | |------|------| | Add a new page | `src/app/(frontend)//page.tsx` + create client component here | | Add nav entry | `src/components/frontend/blocks/NavCore.tsx` | | Modify auth gate | `src/app/(frontend)/layout.tsx` (conditional renders `LandingPage` or shell) | | Add SSE consumer | `src/hooks/useGameTick.ts` + mount in layout | | NPC dialogue/chatter | `src/lib/market/npcDialogue.ts` (pure, client-safe import) | | Keyboard shortcuts | `src/components/command-palette/` | ## Anti-patterns - **NEVER** call `getPayload()` in client components — fetch in server page, pass as props - **NEVER** add `useRouter().refresh()` in SSE consumers without a guard — use the `useGameTick()` hook - **NEVER** use shadcn defaults for dark theme — the admin panel's theme handles styling - **NEVER** place server-side logic (hooks, Payload calls) in `"use client"` files