Add domain-specific AGENTS.md files for collections, components, lib, utils, and bot. Add login-return-url case study documenting the return-URL flow design decisions and debugging lessons learned.
3.5 KiB
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 <Item> component for grid cells with slots: <Item.Slot name="icon">, <Item.Slot name="stats">, <Item.Menu>. Attachments render as stacked badges on the grid cell; popover shows full detail on hover.
SSE consumers
GameTickRealtimemounts in(frontend)/layout.tsxfor all authed users.ShipmentToastsmounts only for logistics-qualified users.- Both use
useGameTick()hook (customptf:game-tickevent 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)/<domain>/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 theuseGameTick()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