Commit Graph

18 Commits

Author SHA1 Message Date
Sharang Parnerkar 582355a1f2 fix(portal): pass Next.js 16's React-strict lint rules in M10.2
ci / shared (pull_request) Successful in 13s
ci / test (pull_request) Failing after 5m3s
ci / e2e (pull_request) Has been skipped
ci / image (pull_request) Has been skipped
CI on PR #13 failed at `pnpm lint --max-warnings 0`. Four findings, all
new-in-N16 react-strict checks:

* ThemeToggle.tsx — "Calling setState synchronously within an effect"
  Rewrites the theme reader to use `useSyncExternalStore` with a
  `MutationObserver` on `<html data-theme>`. SSR snapshot stays "light"
  (matches the root layout); the head script and the toggle just write
  the attribute, the observer pushes the change into React. Drops the
  `mounted` flag because the icon now mirrors the DOM truthfully.
* WorkflowEditor.tsx — "Cannot access refs during render"
  `stateRef.current = { pan, zoom }` was a direct ref-mutation in the
  component body so the global mousemove handler could read the latest
  viewport without re-subscribing. Moves the mirror into a `useEffect`
  keyed on `[pan, zoom]` — same semantics, satisfies the rule.
* MockWorker.tsx — drops an unused `eslint-disable-next-line no-console`
  (the `no-console` rule isn't enabled).
* public/mockServiceWorker.js — auto-generated by `msw init`; adds it to
  the eslint flat-config `ignores` so the lint pass never crosses it.

Local: `pnpm lint` + `pnpm typecheck` both clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 17:30:24 +02:00
Sharang Parnerkar 0797f8f99c feat(portal): M10.2 — MSW handlers + ToastHost + InviteButton end-to-end
ci / test (pull_request) Failing after 4m54s
ci / shared (pull_request) Successful in 11s
ci / e2e (pull_request) Has been skipped
ci / image (pull_request) Has been skipped
Closes out the design pass with the missing piece: a real client-side
mock-API pipeline so the write-path CTAs the design shows (invite a
teammate, run a scan, kick off a workflow test, request reactivation)
actually do something visible without a backend.

* `public/mockServiceWorker.js` — generated by `pnpm exec msw init`.
* `src/mocks/handlers.ts` — POST handlers for `/api/team/invites`,
  `/api/scans`, `/api/workflows/:id/test`, `/api/billing/reactivate`.
  Each returns the design's mono status-code header
  (`201 · invite.created`, `202 · scan.queued`, etc.) so the toast
  surface reads identical to the handoff. A `x-bp-tenant-status` hint
  header lets the same handler respond 402 (frozen) or 410 (archived)
  without needing a real session.
* `src/mocks/browser.ts` — thin `setupWorker(...handlers)` wrapper,
  imported lazily so prod bundles don't pull MSW.
* `src/components/portal/MockWorker.tsx` — client component that boots
  the worker only when `window.__BP_MOCK_API__` is true (set by
  `[slug]/layout` when `BP_DEV_FIXTURE` is on the server). Real Auth.js
  builds skip the worker entirely.
* `src/components/portal/ToastHost.tsx` — global bottom-right toast
  queue, mounted in `[slug]/layout`. Emits via a custom event so any
  client component can call `toast({ msg, code })` without prop-drilling.
* `src/components/portal/InviteButton.tsx` — first live write affordance.
  Modal with email + role-segmented buttons, POSTs to `/api/team/invites`
  with the tenant-status hint header, surfaces 201/402/410 differently.
  Wired into the Team page.
* `src/middleware.ts` — added `mockServiceWorker.js` to the matcher
  exclusion list so the host-rewrite doesn't 404 the worker script.

Verified end-to-end via Playwright: SW registers at the root scope,
click Invite member → fill email → Send invitation → MSW intercepts →
toast "Invitation sent · 201 · invite.created" → modal closes.

This closes the last open M10.2 task. Branch is ready to review/merge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 16:11:18 +02:00
Sharang Parnerkar 26f41a8122 fix(portal): workflows layout — canvas collapsed to 0 width
ci / test (pull_request) Failing after 4m55s
ci / shared (pull_request) Successful in 11s
ci / e2e (pull_request) Has been skipped
ci / image (pull_request) Has been skipped
The workflows route layout used `display: flex` on the wrapper without
giving the `.flow` child `flex: 1`. With Flexbox's default
`flex: 0 1 auto`, the child shrunk to its non-flex content — palette
(234px) + inspector (286px) = 520px — leaving the canvas wrap at width 0.
Nodes, toolbar and zoom controls were positioned absolutely against a
0-wide parent so nothing painted.

Fix: drop `display: flex` on the wrapper. `.flow` already has
`display: flex; height: 100%` and as a block-level child it fills the
width naturally.

Verified via Playwright: canvas-wrap now 1168×746 and all 7 seed nodes
render at expected positions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 16:04:26 +02:00
Sharang Parnerkar a03aa0a4c4 feat(portal): M10.2 — workflows node-graph editor
ci / shared (pull_request) Successful in 11s
ci / test (pull_request) Failing after 4m56s
ci / e2e (pull_request) Has been skipped
ci / image (pull_request) Has been skipped
Builds the §3 workflows editor as a client component at
`/[slug]/workflows`. IT_ADMIN only. Full-bleed layout (own
`layout.tsx`) — palette (234px) + canvas (flex) + inspector (286px).

* `src/lib/flow-modules.ts` — TS port of the handoff `FLOW_MODULES`
  catalog: 18 modules across Triggers / Scanner / CERTifAI / Logic /
  Actions, each with kind-colored monogram, input/output ports, and a
  typed settings schema (select / text / num / area / toggle). Helpers
  for `nodeH`, `portX/Y`, `defConfig`, `wirePath` (bezier), `seedFlow`
  (7-node sample workflow), `modsByCat`. KIND_COLOR token map.
* `src/components/portal/workflows/WorkflowEditor.tsx` — client
  component with:
  - Palette: collapsible category tree, draggable items, kind-colored
    dots and monos.
  - Canvas: dotted grid that pans (drag background) and zooms (+/− with
    `Maximize2` reset, 0.5–1.6). Floating toolbar = workflow name input
    (running pulse on the dot during a test run) + node/link count +
    Validate / Save / **Test run** buttons. Save respects the frozen
    write-guard; Test run highlights nodes in BFS order from triggers
    with animated wires (`.wire.run` keyframes already in globals.css).
  - Nodes: 202px cards with kind-bordered monogram + title, first-config
    value or `desc` in the body, input ports on left, output ports on
    right (multi-output gates labeled PASS/FAIL, etc.). Drag to move,
    click to select. Delete/Backspace removes selection.
  - Wires: bezier paths via `wirePath`. Drag output port → input port
    creates an edge (replaces existing edges into that input). Click to
    select. Pending wire shows dashed.
  - Inspector: live form against `selNode.config` driven by the module's
    settings schema. Per-type fields (select / text / num / area /
    toggle). Empty state shows the kind legend; edge selection shows a
    delete-link affordance.
  - Toasts: inline bottom-right queue with mono status-code footer for
    the workflow actions (`workflow.valid`, `workflow.saved`,
    `workflow.tested`, `402 → reactivation.requested` when frozen).
* `src/app/[slug]/workflows/layout.tsx` — strips `.content-inner` and
  fills `position: absolute; inset: 0` so the editor's 3-column flex
  fills the entire content area.

The page returns 200 against `BP_DEV_FIXTURE=admin-acme` with every
flow-* class marker present.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 15:52:07 +02:00
Sharang Parnerkar 91a655b6df feat(portal): M10.2 — restyle Products + Org + Team + Billing + Audit + SSO
ci / image (pull_request) Has been skipped
ci / test (pull_request) Failing after 5m25s
ci / shared (pull_request) Successful in 27s
ci / e2e (pull_request) Has been skipped
Six existing customer-area shells under [slug]/* rebuilt against the
handoff design (sections §2/§4/§5/§6/§7/§8). Every screen reuses the
new Panel / Monogram / Sev primitives and the ledger-table token system
so the visual contract stays single-source-of-truth in globals.css.

* `[slug]/settings` (Organization, IT_ADMIN) — legal entity dl, primary
  contact card, plan & seats meter, products subscribed kv-list
  (ENTITLED green dot / TRIALING amber dot).
* `[slug]/settings/users` (Team, IT_ADMIN) — bracketed member ledger
  with role chips, last-active mono dim, active/invited dot status.
  Invite affordance present, modal wiring deferred.
* `[slug]/billing` (Billing, CXO + FINANCE + IT_ADMIN) — current plan
  card with monthly net + 19% VAT, seats + evidence-storage meters,
  payment method block that swaps to "Payment failed → Re-activate"
  when tenant.status is frozen, full invoices ledger with paid/due dot.
* `[slug]/audit` (Audit log, LEGAL + IT_ADMIN) — filter bar (search +
  event-type chip toggles + product select), ledger table with denied
  red dot, footer count + retention note.
* `[slug]/settings/integrations` (SSO, IT_ADMIN) — read-only OIDC
  summary pulling from KEYCLOAK_ISSUER / KEYCLOAK_CLIENT_ID, IdP-group→
  role mapping table.
* `[slug]/products` (Products index, USER+) — 2x2 product grid with
  live cards (entitled + trialing chips) and "Coming soon" dashed
  placeholders, plus a cross-product findings table with filter chips.

Plus a new `NotAllowed` 403 surface in the same ledger language that
replaces the inline "NotAuthorized" message used by the old shells, so
forbidden routes still look like the rest of the portal.

Every page goes through `getPortalSession()` so `BP_DEV_FIXTURE` still
swaps between admin / user / trial / frozen / archived without
Keycloak. Every screen returns 200 against
`BP_DEV_FIXTURE=admin-acme pnpm dev`.

Still to come on this branch:
* Workflows editor (palette + canvas + inspector + drag-wiring)
* ⌘K command palette + toasts
* Product launch detail (per-product page)
* Login redesign (mock SSO picker + violet gradient panel)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 13:35:25 +02:00
Sharang Parnerkar f3c95123fa feat(portal): M10.2 design system foundations — tokens, shell, Dashboard
Brings in the handoff design system from `Breakpilot Platform.zip`
(`breakpilot/design_handoff_customer_portal/`) as the base for restyling
every customer-area surface.

What's in:

* **Design tokens & layout primitives** — `src/app/globals.css` is the
  handoff `styles.css` in full (OKLCH paper + ink + brand-violet,
  --rule-* hairlines, --sev-* severity ramp, corner-tick bracket
  treatment, ledger table, 32–36px row density, dark mode via
  `[data-theme="dark"]`). Tailwind v4 layered on top via PostCSS for
  utility helpers; the design system itself stays in plain CSS.
* **Geist + Geist Mono** wired through `next/font/google` so the
  monospaced metadata/figures everywhere render at the intended weight.
* **Shell chrome** under `src/components/portal/`:
  `Brand` (Breakpilot. wordmark with the violet trailing dot),
  `Lifeline` (top full-width tenant rail — active / trial / frozen /
  demo variants; archived swaps in `ArchivedLockout`),
  `NavRail` (232px left rail with tenant switcher + workspace/admin/
  settings groups + user chip; locked routes show a lock icon and a
  "Requires X" tooltip rather than vanishing),
  `Topbar` (breadcrumb + ⌘K button placeholder + theme toggle),
  `ThemeToggle` (Sun/Moon, persists to `localStorage["bp.theme"]`,
  no-flash via a head script in the root layout).
* **Dashboard** at `/[slug]/dashboard` rebuilt per handoff §1:
  page-head with Export + Run scan (the latter wrapped in the frozen
  write-guard hovercard surfacing `HTTP 402 · payment required`),
  5-cell bracketed KPI rail (open findings + 14-day sparkbars + 7-day
  delta, critical with severity stack, controls passing with violet
  ring gauge + n/240, evidence area sparkline, last-scan cadence),
  12-col grid: 30-day findings flow + severity stack legend +
  top-5 open findings table on the left, product posture rows +
  scan-activity heatmap (5x7) + recent-activity feed on the right.
  Plain USER role drops the KPI rail and the org-wide panels per spec.
* **Charts** — minimal SVG primitives in `components/portal/charts/`:
  Sparkbars, Sparkline (area + line), Ring, StackBar, Heatmap +
  HeatLegend. All token-driven (`var(--sev-*)`, `var(--accent)`).
* **Fixtures** — `src/lib/fixtures.ts` is a TS port of the handoff's
  `data.js`. Deterministic mulberry32 generators give the same
  realistic DACH/EU compliance data every reload (~5 tenants × 30+ days
  activity / 4–13 findings per product / 9 months invoices / hash-
  chained audit). Source of truth for the design until tenant-registry
  is enriched to carry these fields end-to-end. RBAC table (`canAccess`,
  `landingFor`) ported alongside.
* **Dev session bypass** — `src/lib/get-session.ts` returns a synthetic
  `SessionWithExtras` from one of the 6 fixtures when
  `BP_DEV_FIXTURE=<id>` is set. Lets the portal render the design
  without Keycloak + tenant-registry up. Real Auth.js wiring untouched.

What's NOT in yet (next commits):

* Products / Product launch / Org / Team / Billing / Audit / SSO pages
* Workflows editor (palette + canvas + inspector + drag-wiring)
* Command palette + toast system
* MSW handlers for the tenant data shapes (today the page reads the
  fixture module directly server-side; MSW is for client-side calls)

Run locally:
  pnpm install
  BP_DEV_FIXTURE=admin-acme pnpm dev
  open http://acme.localhost:3000/acme/dashboard

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 13:29:29 +02:00
sharang e387b9a963 feat(portal): M10.1 — fill the 10 customer-area shells
ci / shared (push) Successful in 8s
ci / test (push) Successful in 25s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
Four real surfaces wired to tenant-registry (settings, settings/api-keys CRUD, audit pagination, products live entitlements), five forward-looking empty states with CTAs. 56 vitest tests + 10 Playwright canaries. lib/format.ts consolidates date helpers.

Refs: M10.1
2026-05-20 07:20:31 +00:00
sharang ecbe6ae74b feat(portal): M11.1 catalog flow + M12.1 self-serve trial
ci / shared (push) Successful in 5s
ci / test (push) Successful in 26s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
Closes the customer loop: /start signup → tenant + KC org + IT_ADMIN invite → portal dashboard with trial banner → /[slug]/catalog with Request + Start trial server actions wired to tenant-registry.

Refs: M11.1 + M12.1
2026-05-19 16:27:10 +00:00
sharang 8ab82c8b37 docs(dev): pin AUTH_URL to the tenant subdomain
ci / test (push) Successful in 28s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
ci / shared (push) Successful in 4s
Capture the redirect_uri gotcha from the live-stack smoke. .env.example pins AUTH_URL to acme.localhost:3000 with a long-form comment; README gets an 'AUTH_URL gotcha' callout.

Refs: M5.1 follow-up
2026-05-19 16:05:45 +00:00
sharang 3310a942f2 fix(test): assert real shipped behaviour for signed-out /products
ci / shared (push) Successful in 5s
ci / test (push) Successful in 33s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
Caught during live local-smoke run.

Refs: M4.2/M5.3
2026-05-19 15:09:01 +00:00
sharang 99fe3b55b2 feat(test): M5.3 — Playwright e2e harness for the dev stack
ci / shared (push) Successful in 4s
ci / test (push) Successful in 23s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
playwright.config.ts + tests/e2e/{apex,tenant,health}.spec.ts. make e2e for local. CI e2e job opt-in via RUN_E2E repo variable. OIDC click-through deferred to when stage is up.

Refs: M5.3
2026-05-19 14:53:18 +00:00
sharang fe139332ee feat(app): M5.2 — customer-area route shells + role-gated nav
ci / shared (push) Successful in 4s
ci / test (push) Successful in 29s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
10 route shells under /[slug]/, role-filtered Nav, backstage stub at /__backstage__, dashboard reads session.products to render tiles. src/lib/session.ts is the canonical role × surface matrix; canSee() is the only RBAC primitive in the portal (real enforcement remains at the API layer). 24 vitest tests; 100% src/lib coverage.

Refs: M5.2
2026-05-19 14:47:15 +00:00
sharang 2961f36cca fix(dev): point TENANT_REGISTRY_URL at :8090
ci / shared (push) Successful in 6s
ci / test (push) Successful in 29s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
Unblock local dev: Keycloak owns :8080, so tenant-registry shifts to :8090. Prod is functionally unchanged.

Refs: M5.1
2026-05-19 09:47:38 +00:00
sharang e7a1290246 feat(app): Next.js 16 + Auth.js v5 portal skeleton
ci / shared (push) Successful in 4s
ci / test (push) Successful in 26s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
Next.js 16 + Auth.js v5 skeleton: host→slug middleware, tenant-context layout, OIDC sign-in flow against breakpilot-dev realm. 100% coverage on src/lib. Bumps next to 16.2.6 to clear trivy CVEs in 15.0.3.
2026-05-19 09:35:05 +00:00
sharang 3c7409ee9e chore(domain): yourplatform.com → breakpilot.com
ci / shared (push) Successful in 5s
ci / test (push) Has been skipped
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
Apply platform-domain decision (2026-05-18). No services touched; docs/config only.

Refs: M1.1
2026-05-18 20:28:44 +00:00
sharang dd4764fbb2 ci: rework workflow for Gitea Actions (M0.2)
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
ci / shared (push) Successful in 5s
ci / test (push) Has been skipped
Switches commitlint to bash regex, gitleaks to inline binary, trivy to inline binary (v0.70.0). Per-stack jobs gated on hashFiles.

Refs: M0.2
2026-05-18 19:42:30 +00:00
sharang 21f6d70a32 chore: bootstrap repo scaffolding (M0.1)
ci / shared (push) Failing after 6s
ci / test (push) Failing after 3s
ci / e2e (push) Has been skipped
ci / image (push) Has been skipped
Bootstraps §1.2 scaffolding (README, CONTRIBUTING, CODEOWNERS, CHANGELOG, PR + issue templates, LICENSE, CI workflow, release workflow, commitlint, cliff, .editorconfig, .gitignore, .env.example) and ships a proprietary all-rights-reserved LICENSE naming both founders.

Refs: M0.1
2026-05-18 19:15:34 +00:00
sharang ae9f68d6b6 Initial commit 2026-05-18 19:05:35 +00:00