# CLAUDE.md — p2c-business (Business Client Portal)

## Project Overview

* Next.js app for **business clients** of Parcel2Courier, sitting alongside `p2c-website` (the personal-customer site, which lives in a sibling repo and has its own `/dashboard`).
* v1 scope: fully working email/password login and logout, and a complete navigation shell where every nav destination is a real route with TailAdmin-styled chrome — but only **Account Dashboard** has real content. Every other leaf route is a "coming soon" placeholder.
* Architecture, routing, and code conventions are reused from `p2c-website`. Visual system (layout, sidebar, header, cards, colors, typography) is ported from **TailAdmin v2.3** (`free-nextjs-admin-dashboard`, available locally at `../web_templates/tailadmin_v2`).
* Full context and rationale for every decision below lives in `/home/addy/.claude/plans/declarative-bubbling-kitten.md` — read it if a "why" isn't obvious here.

## Build and Development Commands

```bash
pnpm dev              # start dev server
pnpm build            # production build
pnpm lint             # ESLint via next lint
npx prisma db pull    # reconcile schema.prisma against the real business DB
npx prisma generate   # regenerate Prisma client after schema changes (output: src/generated/prisma)
```

## Environment Variables

Real values live in `.env.local` (not committed). Key ones actually used by this app today:

| Variable | Purpose |
|---|---|
| `DATABASE_URL` | MariaDB connection string (Prisma), points at the business `p2cbus5_dev2` DB — separate from p2c-website's personal-customer DB |
| `AUTH_SECRET` | NextAuth secret |
| `ALLOWED_IPS` | Comma-separated IP allowlist enforced in `src/proxy.ts` |
| `NEXTAUTH_URL` | NextAuth base URL |

`.env.local` also carries several vars inherited from p2c-website's environment (`API_SERVICE_URL`, `EWAY_*`, `PAYWAY_*`, `PAYPAL_*`, `RECAPTCHA_*`, etc.) that **nothing in this app reads yet** — they're there for when the deferred features (shipping, billing) get built against the external REST API. Don't assume a var is wired up just because it's present in `.env.local`; check for actual `process.env.X` usage first.

### Data source: MariaDB via Prisma — `users` table only (for now)

`prisma/schema.prisma` was generated via `npx prisma db pull` against the real business database, so it contains the **full production schema** (accounts, orders, shipments, invoices, addresses, etc. — dozens of models). This app currently only reads/writes the `User` model, for authentication. Treat the rest of the schema as read reference for future feature work, not as things already wired into the app.

The Prisma client is generated to `src/generated/prisma/` (import via `@/generated/prisma`, not `@prisma/client`) and is gitignored/lint-ignored as generated code. `src/lib/db.ts` exports a singleton `db` client (using `@prisma/adapter-mariadb`) plus one helper, `fetchUserBalance(userId)`.

### External REST API

Follows p2c-website's `lib/api/*` server-action pattern for the external `API_SERVICE_URL`: all external API calls are server actions, one file per endpoint prefix, never called directly from a client component.

| Endpoint prefix | File |
|---|---|
| `/biz/*` | `src/lib/api/biz.ts` |

`src/lib/api/http.ts` holds shared request helpers (`apiHeadersBasic()` — HTTP Basic auth from `API_SERVICE_USERNAME`/`API_SERVICE_PASSWORD`), and `src/lib/definitions.ts` holds the corresponding TypeScript types (e.g. `SubscriptionPlan`). Note this app's convention is `src/lib/api/...` (matching the existing `src/lib/db.ts`, `src/lib/routes.ts`), **not** p2c-website's `src/app/lib/api/...`.

So far this only covers `fetchSubscriptionPlans()` (`GET /biz/subscription-plans`), consumed by the `(public-marketing)` landing page. Shipments, invoices, addresses, etc. are still unbuilt — add new files under `src/lib/api/` per prefix as those land (e.g. an `/api/*` prefix would go in `src/lib/api/services.ts`, mirroring p2c-website).

### Authentication

NextAuth v5 (`next-auth@5.0.0-beta.31`), Credentials provider, JWT session (1-day maxAge). Mirrors p2c-website's split exactly:

- `src/auth.config.ts` — `pages.signIn = ROUTES.login`, empty `providers` (edge-safe config used by proxy).
- `src/auth.ts` — Credentials provider; looks up `users` by email via Prisma, `bcrypt.compare`s the password, rejects if `!user.active`.
- `src/proxy.ts` (Next.js 16 replacement for `middleware.ts`) — guards everything under `ROUTES.dashboard` (`/dashboard`), redirecting unauthenticated requests to `/login`; also enforces the `ALLOWED_IPS` allowlist (checks `cf-connecting-ip` → `x-real-ip` → `x-forwarded-for` in that order) for **all** routes when `ALLOWED_IPS` is set.
- `src/components/header/actions.ts` — `signOutAction` server action, `signOut({ redirectTo: ROUTES.login })`.
- `src/lib/routes.ts` — `ROUTES` constant (`login`, `dashboard`). Use this instead of hardcoding path strings.

### Route and file conventions

```
src/app/
  layout.tsx                         — root layout: Outfit font, Providers (next-themes)
  globals.css                        — TailAdmin theme tokens (Tailwind v4 @theme block)
  providers.tsx                      — next-themes ThemeProvider (attribute="class")

  (public-marketing)/
    layout.tsx                       — public-page shell: pink→plum gradient background + `.marketing-theme` (re-maps brand-* to the website's pink for shared primitives)
    page.tsx                         — landing page ("/"): hero + live subscription pricing plans
    components/pricing-plans.tsx     — client component, fetches via fetchSubscriptionPlans()
    sample-quote/                    — public sample quote (/sample-quote)
    login/
      page.tsx                       — route is /login (not TailAdmin's /signin); centred card on the public gradient
      components/login-form.tsx      — website-style form (marketing-* palette), wired via useActionState
      lib/actions.ts                 — authenticate server action
    register/                        — /register: 4-step business-account wizard (page.tsx + components/), same public look
    forgot-password/  reset-password/[token]/  register-confirm/[token]/
                                     — account-recovery / email-confirmation pages, each an <AuthCard> around a form

  dashboard/
    layout.tsx                       — auth() guard + SidebarProvider + AppSidebar/AppHeader/Backdrop
                                     (root div carries `.marketing-theme-light`: pink brand palette + gradient wash, light theme only; sidebar/header stay white)
    page.tsx                         — Account Dashboard: real content (stat cards)
    components/
      dashboard-shell.tsx            — content wrapper (header slot + children)
      stat-card.tsx                  — dashboard metric card
      coming-soon.tsx                — shared placeholder component
    shipping/
      create/{single-domestic,bulk-domestic,international}/page.tsx
      saved-quotes/page.tsx  history/page.tsx  search/page.tsx
    settings/
      address-book/page.tsx  address-groups/page.tsx
      dispatch-locations/page.tsx  packaging/page.tsx
    billing/
      invoices/page.tsx  search-invoices/page.tsx  report/page.tsx
    profile/
      page.tsx  preferences/page.tsx  top-up/page.tsx  credit-card/page.tsx

  api/auth/[...nextauth]/route.ts     — NextAuth handler

src/
  auth.ts  auth.config.ts  proxy.ts
  lib/
    db.ts                            — Prisma client singleton + fetchUserBalance
    routes.ts                        — ROUTES constant
  layout/
    AppSidebar.tsx                   — nav tree (NavItem, recursive subItems), sidebar chrome
    AppHeader.tsx                    — top bar: sidebar toggle, dark-mode toggle, UserDropdown
    Backdrop.tsx                     — mobile sidebar overlay
  context/
    SidebarContext.tsx               — expanded/hover/mobile-open state
  components/
    common/
      ThemeToggleButton.tsx          — TailAdmin visuals, useTheme() from next-themes
      Wordmark.tsx                   — parcel2courier text logo for public pages
      public-header.tsx              — white top bar (wordmark + action slot) shared by the landing and sample-quote pages
      PageBreadcrumb.tsx
      auth-card.tsx                  — AuthCard: wordmark + centred white card shell for login / forgot / reset / confirm pages
    header/
      UserDropdown.tsx  actions.ts   — signOutAction
    form/                            — Label, Input (ported TailAdmin form primitives)
    ui/
      button/  dropdown/  badge/     — ported TailAdmin UI primitives
  icons/
    index.tsx                        — barrel import for the SVG icon set used across sidebar/header
```

Every leaf route under `dashboard/` other than `/dashboard` itself renders `<ComingSoon title="..." />` behind a `PageBreadcrumb` — this keeps every nav item clickable without pretending functionality exists. When adding real functionality to one of these, replace its placeholder body; don't restructure the route.

### Nav structure

The sidebar nav tree (`src/layout/AppSidebar.tsx`) supports **3 levels** of nesting (`NavItem.subItems[].subItems[]`), one level deeper than TailAdmin's stock 2-level component — needed for Shipping → Create Shipment → Single/Bulk/International. If you add a new nav item, keep it inside this same recursive `NavItem`/`renderMenuItems` structure rather than special-casing depth.

## Technology Stack & Constraints

* **Framework:** Next.js 16, App Router only.
* **Language:** TypeScript, `strict: true`.
* **Styling:** Tailwind CSS v4, TailAdmin's theme (see below) — **not** p2c-website's shadcn/HSL-variable system.
* **Components:** Default to React Server Components; add `"use client"` only for interactivity/state/hooks.
* **State Management:** URL state or native React hooks; no external global stores.
* **Path alias:** `@/*` → `./src/*` (TailAdmin's convention — **differs from p2c-website**, where `@/*` → `./*`). Keep this in mind when porting any further TailAdmin or p2c-website files verbatim.

## Core Architectural Rules

* **Imports:** group sequentially — 1) React, 2) third-party libraries, 3) `@/*` absolute paths.
* **Routing:** all routes live under `src/app/`.
* **Client directives:** `"use client"` only when the file adds interactive listeners or hooks.
* **Naming:** PascalCase for React components, camelCase for functions.
* **Prisma:** only extend `User`-related code paths for now; treat every other model in `schema.prisma` as reference until a feature actually needs it. Regenerate (`npx prisma generate`) after any schema edit, and prefer `npx prisma db pull` to re-sync from the real DB over hand-editing generated-from-DB models.

### Styling conventions (TailAdmin system — adopted wholesale, not p2c-website's)

- **Theme tokens**: `src/app/globals.css` defines TailAdmin's literal palette scale (`brand-500 #465fff`, `gray-*`, `success/error/warning`) and fixed type scale (`text-title-sm`, `text-theme-sm`, etc.) in a Tailwind v4 `@theme` block. Use these tokens/classes, not p2c-website's semantic HSL vars (`bg-background`, `text-foreground`) — the two systems are intentionally different here.
- **Brand color is pink, not TailAdmin blue**: `globals.css` re-maps the `brand-*` scale to the website's pink inside `.marketing-theme` (public pages) and `.marketing-theme-light` (dashboard, light theme only — dark keeps the blue). Keep using `brand-*` utilities in components; don't hardcode pink hexes. `blue-light-*` status colors are intentionally left blue.
- **Font**: Outfit (loaded in `src/app/layout.tsx`).
- **Dark mode**: `next-themes` with `attribute="class"` (p2c-website's mechanism, not TailAdmin's own `ThemeContext`) — one theme system, restyled TailAdmin visuals. Toggle lives in `AppHeader` via `ThemeToggleButton`.
- **Card pattern**: `rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03]` (stat cards, placeholder cards, etc.) — reuse this instead of inventing a new card treatment.
- **Icons**: TailAdmin's own `src/icons` SVG set (imported via the barrel in `src/icons/index.tsx`), not lucide-react — keep new sidebar/header icons consistent with this set rather than mixing icon libraries.

## Not yet built (deferred beyond v1)

Real functionality behind: Create Shipment (single/bulk/international), Saved Quotes, Shipment History/Search, Address Book/Groups, Dispatch Locations, Packaging, Billing (invoices/search/report), Preferences, Top-Up, Credit Card on File. These all currently render `<ComingSoon />` and are meant to be built out against the external REST API using p2c-website's `lib/api/*` server-action pattern once that integration starts.

## Common Gotchas & Fixes

* Never import server actions or database logic inside standalone client files.
* Don't confuse this app's `@/*` → `./src/*` alias with p2c-website's `@/*` → `./*` when copying code between the two repos.
* Don't style with p2c-website's shadcn/HSL tokens here — this app uses TailAdmin's literal palette/type-scale tokens instead.
* A var existing in `.env.local` doesn't mean it's used — grep for `process.env.<NAME>` before assuming an integration is live.
