docs: add client portal tablo parity design spec

This commit is contained in:
Arthur Belleville 2026-04-15 22:18:27 +02:00
parent ebf6d8a255
commit d69bfffd6f
No known key found for this signature in database

View file

@ -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