diff --git a/docs/superpowers/specs/2026-04-15-client-portal-tablo-parity-design.md b/docs/superpowers/specs/2026-04-15-client-portal-tablo-parity-design.md new file mode 100644 index 0000000..8cedbad --- /dev/null +++ b/docs/superpowers/specs/2026-04-15-client-portal-tablo-parity-design.md @@ -0,0 +1,216 @@ +# Client Portal Tablo Parity + +**Date**: 2026-04-15 +**Status**: Approved + +## Overview + +Make the client portal route in `apps/clients` match the visual design of the main app route `apps/main` `/tablos/:tabloId` as closely as possible. + +The target route in `apps/clients` remains `/tablo/:tabloId`, but it should present the same shell language as `TabloDetailsPage`: + +- same header structure +- same metadata bar +- same sticky tab navigation +- same overview card layout and styling +- same Tailwind/CSS visual system +- same translation strategy through i18n + +The client portal keeps its simpler local tab state and stays read-only except for discussion. + +## Goals + +- Match the current `apps/main` `TabloDetailsPage` look as closely as possible +- Prevent visual drift between `apps/main` and `apps/clients` +- Reuse the existing shared `@xtablo/tablo-views` section components +- Keep the client portal safe by removing admin and mutation affordances +- Use shared i18n keys wherever possible instead of hardcoded copy + +## Non-Goals + +- Rebuilding the client portal as a separate visual concept +- Copying the main app's `?section=` route model into `apps/clients` +- Enabling client file uploads, task completion, task creation, or layout editing +- Refactoring unrelated app-level layout or navigation outside the tablo detail view + +## Chosen Approach + +Use a shared presentational shell for the tablo detail page, backed by a shared stylesheet source. + +This shell should live close to the existing shared tablo view layer, likely in `packages/tablo-views`, because that package already contains the shared section components used by both apps. + +Each app remains responsible for its own routing, data hooks, and permissions: + +- `apps/main` keeps full `TabloDetailsPage` behavior +- `apps/clients` keeps local internal tab state and client-specific data loading + +The shared shell owns the route-level presentation so visual changes land in one place and are inherited by both apps. + +## Shared CSS Source + +Exact visual parity requires shared CSS, not just shared JSX. + +Today both apps import `@xtablo/ui/styles/globals.css`, but `apps/main/src/main.css` and `apps/clients/src/main.css` already diverge on important visual tokens such as: + +- navbar colors +- dark mode tokens +- chat surface colors +- other route-level color variables + +Because of that, `apps/clients` must not keep an independent competing style source for this page. + +### Requirement + +Extract the route-relevant visual styles currently coming from `apps/main/src/main.css` into a shared stylesheet layer that both apps import. + +This shared stylesheet should include whatever is required for the tablo detail route to render identically, including: + +- theme tokens needed by the page shell +- chat-related styling used in the discussion tab +- any utility styles relied on by the shared shell + +### Constraint + +Do not import `apps/main/src/main.css` directly from `apps/clients`. That would create brittle cross-app coupling and make ownership unclear. + +Instead: + +- move route-relevant shared styles into a shared import +- keep app-specific styles in each app-local `main.css` + +## Shared Shell Responsibilities + +The shared tablo detail shell should own: + +- the project header layout +- the icon/image block next to the project title +- header action placement +- the metadata bar layout and styling +- sticky tab navigation styling and behavior hooks +- the overview page card grid and visual treatment + +The shell should accept data and slots through props rather than owning app-specific mutations or routing decisions. + +Suggested inputs: + +- `tablo` +- status label and badge styling +- progress values +- role label +- created-at label +- tab definitions +- active tab +- tab change handler +- header action slot +- overview capability flags or overview card content +- per-section capability flags such as read-only settings + +## `apps/main` Responsibilities + +`apps/main` remains the owner of: + +- `?section=` URL state +- invite and share flows +- client invite management +- overview layout editing +- task creation and mutation +- file mutations +- admin-only controls + +The main app should adopt the shared shell without losing any existing behavior. + +## `apps/clients` Responsibilities + +`apps/clients` adopts the same visual shell, but keeps a restricted capability profile. + +### Navigation + +- Keep the current local tab state in React +- Do not add `?section=` routing +- Keep the same visible tab order, icons, and active styling as `apps/main` + +### Header + +- Keep the same header structure and spacing as `apps/main` +- Keep the discussion CTA styling and placement +- Remove the `Inviter` action entirely + +### Metadata Bar + +- Keep the same metadata structure and styling +- Use client-safe translated labels for role and status copy + +### Overview + +- Use the same card layout and card styling as the main app +- Remove layout edit controls +- Remove task creation controls +- Make task completion non-interactive +- Keep file previews informational only + +### Read-only Scope + +The client portal should be read-only except for discussion. + +That means: + +- `discussion`: interactive +- `tasks`: readable, no mutations +- `etapes`: readable, no mutations +- `events`: readable, no mutations +- `roadmap`: readable, no mutations +- `files`: readable, no upload, rename, move, create-folder, delete, or overflow action UI + +## i18n + +The shared shell must not introduce new hardcoded French strings. + +### Rules + +- Reuse existing translation keys from `apps/main` when the copy already matches the desired wording +- Add missing keys only where the shared shell needs copy that does not already exist +- Ensure both `apps/main` and `apps/clients` can resolve the keys used by the shared shell +- Prefer shared wording for labels like tab names, metadata labels, and overview headings + +This keeps parity at the copy level and avoids one app silently diverging from the other. + +## Testing And Verification + +Implementation should prove both parity and restrictions. + +### Automated + +- Add tests that verify the client shell renders the same key structure as the main shell +- Add tests that verify client mode hides admin and mutation controls +- Add tests that discussion remains interactive while other sections are read-only + +### Manual + +Perform side-by-side checks between: + +- `apps/main` `/tablos/:tabloId` +- `apps/clients` `/tablo/:tabloId` + +Compare at minimum: + +- header layout +- metadata bar +- sticky tabs +- overview cards +- tab content framing +- discussion styling + +## Risks + +- Shared CSS extraction may expose assumptions currently embedded in app-local stylesheets +- Some `TabloDetailsPage` copy is currently hardcoded and will need i18n cleanup before sharing +- If the shared shell grows to own business logic, parity will become harder to maintain + +## Success Criteria + +This work is successful when: + +- the client portal visually matches the main app tablo detail route +- the shared shell and shared style source make future parity maintainable +- the client portal remains read-only except for discussion +- the route continues to use simpler internal navigation rather than query-param section routing