# Onboarding — Messaging/Inbox/Support Web SDK Welcome. This is a **static demo** of a Slack-grade messaging + inbox + support product, shipped as a **React SDK** (`@lynkd/messaging-inbox-sdk`) consumed by a **Next.js demo app** (`apps/web`), styled to match the LynkedUp Pro dashboard. Read this first; deep dives are in [`../docs`](../docs). ## Run it ```bash pnpm install # Node >= 20, pnpm >= 8 pnpm dev # http://localhost:4300 ``` No DB, no env setup needed — mock data ships in code and every feature works. ## Mental model (read once, saves hours) 1. **UI is headless-first.** Product logic lives in SDK hooks (`useMessages`, `useInbox`, `useTickets`…). Components in `apps/web/components` are just presentation. 2. **The UI never calls a backend directly.** It calls `BffClient` → the **BFF** (`apps/web/app/api/bff/**`) → the **mock store** (`apps/web/lib/mock/store.ts`). This is the one seam you replace to go real. This is the **BFF pattern**. 3. **Config is resolved once on the server** (`apps/web/lib/config.ts` → `getServerConfig()`) from `NEXT_PUBLIC_*` env, then handed to the client provider as plain JSON. That's how feature flags + theme reach the browser without dynamic `process.env` access. 4. **Theme = CSS variables.** `theme.ts` writes `--c-*`, `--r-*`, `--font-*` onto ``. Tailwind colors map to those vars (`tailwind.config.ts`). Light/dark + brand swap are just different variable values. ## Where things are | I want to… | Go to | |---|---| | Add / change a domain type | `packages/messaging-inbox-sdk/src/types.ts` | | Add a feature flag | `packages/messaging-inbox-sdk/src/config.ts` (`FeatureFlags` + `defaultFlags`) | | Change default theme | `packages/messaging-inbox-sdk/src/config.ts` (`darkTokens`/`lightTokens`/`defaultTheme`) | | Add a data hook | `packages/messaging-inbox-sdk/src/hooks.ts` | | Add a BFF endpoint | `apps/web/app/api/bff//route.ts` + a method on `store` + a method on `BffClient` | | Change seed data | `apps/web/lib/mock/data.ts` | | Edit the sidebar/header | `apps/web/components/nav/*` | | Edit a message/composer/thread | `apps/web/components/message/*` | | Edit the Support console | `apps/web/components/support/SupportView.tsx` | | Add a page/route | `apps/web/app/(app)//page.tsx` | ## Add a feature end-to-end (worked example) Say you want a `/pins` page listing pinned messages: 1. **Flag** (optional): add `pinsPage: boolean` to `FeatureFlags` + `defaultFlags`. 2. **BFF**: add `GET /api/bff/pins` returning `store.messages.filter(m => m.isPinned)`. 3. **Client**: add `listPinned()` to `BffClient`. 4. **Hook**: add `usePinned()` in `hooks.ts`. 5. **UI**: `apps/web/app/(app)/pins/page.tsx` renders a list, gated by `useFeature("pinsPage")`. ## Conventions - **Client vs server components:** anything using SDK hooks/`useUi` needs `"use client"`. Pages are thin server components that render a client view component. - **Styling:** Tailwind utility classes + the semantic tokens (`bg-panel`, `text-muted`, `border-border`, `rounded-card`, `bg-accent`). Don't hard-code hex — use tokens so light/dark + theming keep working. - **Icons:** `lucide-react`. - **Time:** render relative/clock time with `lib/format.ts` (client-only, avoids SSR drift). ## Gotchas - The Next.js version is **pinned for the demo** and has a known CVE — bump before real use. - The mock store is **in-memory**: it resets on server restart (that's expected). - Message timestamps are seeded relative to server start, so "just now / 4m" stays sensible. ## Next - [`../docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md) — the full picture - [`../docs/IIOS_INTEGRATION.md`](../docs/IIOS_INTEGRATION.md) — make it real - [`./memory/`](./memory) — durable project decisions (also loaded by Claude Code)