# 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
