Root cause of 'navbar mobile tak bisa pindah halaman': Next <Link> client-side navigation is dead app-wide. A React hydration mismatch (#418: 'server rendered text didn't match the client') is thrown by the SSR-seeded live feeds — relative times (formatRelativeTime(e.edited_at) / m.created_at) computed with Date.now() render slightly differently on server vs client, which breaks the Next client router (router.push is a no-op). The desktop NavRail worked only because it uses plain <a href> (hard navigation bypasses the broken router). Fixes: - mobile-nav.tsx: use plain <a href> (NOT Next <Link>), identical to the working sidebar NavRail, so mobile nav always navigates regardless of router state ('ikuti cara kerja sidebar'). - Add suppressHydrationWarning to the SSR-seeded relative-time spans so server/client drift no longer throws #418 (EditHistory, LiveModerationFeed, messages/results + detail rows, recordings, TermGlossary, ChannelCultureGlossary, CategoryDrilldown). Verified on non-prod :4024 @375px: Voice/Media/Search all navigate, no #418 in console. Plan: .hermes/plans/mobile-nav-hydration-fix.md
3.7 KiB
3.7 KiB
Bete Frontend — Project Overview
Next.js 16 (App Router), React 19, TypeScript strict, Tailwind v4, shadcn/ui, base-ui.
Key points:
- Server-side rendered (SSR) —
output: "standalone"in next.config.ts; pages are React Server Components that fetch initial data from the backend at render-time, then hydrate interactive client components (no blank-spinner-first-load). - Server data layer at
src/lib/api/server.ts— server-only fetchers that call the backend directly viaGMW_BACKEND_URL(defaulthttp://127.0.0.1:4001). Never import from a client component. - API client at
src/lib/api/client.ts— browser-side fetch for live ops, same-origin through the reverse proxy. - WebSocket at
src/lib/ws/— auto-reconnecting client with typed event subscriptions. Realtime state (voice, media, messages) stays client-side. - Shared realtime state is server-authoritative: the backend aggregates the
gateway's
voice_active_userdeltas into a live speaker snapshot (GET /api/voice/status→activeSpeakers, plus WSvoice_statesent on connect). Every browser converges on the same voice state;useSpeakersseeds from the server snapshot instead of accumulating per-tab. - No authentication: all endpoints are public
Data flow (match these — do not invent endpoints)
Discord → discord-gateway → Redis pub/sub → backend (Express :4001) ←→ Next.js SSR
↑ REST /api/* (server: GMW_BACKEND_URL
└ WS /ws (events + PCM binary) 127.0.0.1:4001)
↑ browser WS (same-origin /ws)
- Rendering:
gmw-proxynginx (:4009) proxies/→ Next standalone server (:4017,node .next/standalone/server.js), and/api+/ws→ backend :4001. Public host:imphnen.asepharyana.my.id(Caddy reverse proxy → :4009). - SSR seed pattern: each
page.tsxis a server component that fetches viasrc/lib/api/server.tsand passes typed data to aview.tsxclient component; the hooks takeinitialDataas SWRfallbackData. - Local dev overrides:
NEXT_PUBLIC_API_URLandNEXT_PUBLIC_WS_URLfor the browser;GMW_BACKEND_URLfor the server. - Never hardcode a host in api/ws clients — same-origin/env or GMW_BACKEND_URL only.
Backend response shapes that bite
GET /api/chat/history→{ history: ChatbotHistoryRow[], total }where each row is{ id, user_id, user_message, bot_response, context, created_at }. Map rows to display messages inchatbot-context.tsx.message_deletedWS payload →{ id, deleted_at }(an object, not a string).voice_recording_uploadedWS payload has noduration_bytes(REST rows do).- Dashboard endpoints:
/api/dashboard/stats|users|channels(+/:iddetails). - Channel/guild names live inside
message.metadataJSON (channel.channelName), not top-level. GET /api/voice/statusnow includesactiveSpeakers(authoritative shared snapshot fromsrc/modules/voice/live-speaker.tson the backend).
This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ (resolved from this file's directory; in monorepos the next package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by next dev — verify at node_modules/next/dist/server/lib/generate-agent-files.js. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.