docs(portal): add dashboard redesign spec + implementation plan
Spec covers the brainstormed design: jobs-forward layout, V2 stepper with timestamps, EN Plating teal/gradient palette, 4 doc categories. Plan decomposes implementation into 4 independently-deployable phases (tokens+buttons -> dashboard -> jobs detail -> cosmetic sweep) with 27 tasks total. Also adds .gitignore so .superpowers/ brainstorm artifacts stay untracked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
fusion_plating/.gitignore
vendored
Normal file
10
fusion_plating/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Superpowers brainstorm session artifacts (mockups, HTML drafts).
|
||||
# The companion server saves files here; not project source.
|
||||
.superpowers/
|
||||
|
||||
# Local Odoo dev artifacts
|
||||
*.pyc
|
||||
__pycache__/
|
||||
*.egg-info/
|
||||
.idea/
|
||||
.vscode/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,278 @@
|
||||
# Customer Portal Dashboard Redesign
|
||||
|
||||
**Module**: `fusion_plating_portal`
|
||||
**Date**: 2026-05-17
|
||||
**Status**: Design locked, awaiting implementation plan
|
||||
**Surface**: `https://enplating.com/my/*`
|
||||
|
||||
## Problem
|
||||
|
||||
The existing `/my/home` is functional but visually bland and (until the 2026-05-17 hotfix) was not even reaching the browser because of a controller-inheritance bug. The customer's primary question on login — "where is my part right now?" — was buried inside one of six equal-weight cards. There is no per-job view of progress over time, no grouped document surface for QA managers, and no brand alignment with the marketing site at enplating.com.
|
||||
|
||||
This redesign reframes the portal around jobs, gives each job a customer-visible timeline with timestamps, and surfaces every job-related document in a single grouped panel. Branding follows enplating.com's teal palette.
|
||||
|
||||
## User stories
|
||||
|
||||
1. **As an aerospace QA manager**, when I log in I want to see the status of every active job without clicking into anything, so I can answer "where's PO-7783?" in under 5 seconds.
|
||||
2. **As a buyer**, when I open a job's detail page I want to see exactly when each stage happened (date + time + operator name) so I can verify our SLA terms are being met.
|
||||
3. **As a QA manager**, when I need a Certificate of Conformance for an audit I want every document for that job grouped by category (customer inputs, specifications, quality records, shipping) so I'm not hunting through a list.
|
||||
4. **As any customer**, when I land on the portal I want it to look and feel like enplating.com — teal accents, gradient CTAs, brand typography — so it feels like the same company.
|
||||
|
||||
## Locked design decisions (from brainstorming 2026-05-17)
|
||||
|
||||
| Decision | Choice | Why |
|
||||
|---|---|---|
|
||||
| Design language | Modern SaaS — generous whitespace, rounded corners (`14px` cards, `9px` buttons, `9999px` badges), soft shadows | User picked "Modern SaaS" over Corporate B2B and Industrial in the design-direction screen. |
|
||||
| Brand palette | EN Plating teal/green pulled live from enplating.com | User asked for "stay close to the site color schemes". |
|
||||
| Primary card design | Numbered circular stepper + horizontal connecting lines + date/time stamp under each step label | User picked V2 (numbered stepper) and asked for V3's timestamps merged in. |
|
||||
| Dashboard layout | Jobs-forward: welcome → 4-tile KPI strip → Active Work Orders hero (full V2 cards) → 3-col secondary panels (Certs / Packing Slips / Invoices) | User picked Layout B over the 6-equal-section grid. |
|
||||
| CTA buttons | Gradient `#2eaf93 → #1a6b59` linear-135deg with brand-tinted box-shadow; danger is `#ef4444 → #b91c1c` gradient; secondary is flat with subtle `#fff → #f3f7f6` gradient | User asked for "color gradients on the buttons to make it modern and professional". |
|
||||
| Stepper visuals | Completed circles get gradient fill; active circle uses brand teal stroke + `rgba(46,175,147,.2)` mint glow ring | Pulled from approved branded mockup. |
|
||||
| Status badges | Pill with `width:7px;height:7px` coloured dot + soft glow halo. Status-to-colour map: In Progress → mint; QC → amber; Ready / Done → green; Hold → red. | Pulled from approved branded mockup. |
|
||||
| Document categories | **From You** (customer-uploaded: PO, drawings) · **Specifications** (FP customer spec) · **Quality** (CoC, Fischerscope thickness report) · **Shipping** (packing slip, tracking) | Surfaced in the approved detail-page mockup and not contested. |
|
||||
| Default landing | `/my/home` (replaces stock Odoo home; already the case after the 2026-05-17 hotfix that renamed `portal_my_home_dashboard()` → `home()`) | See Critical Rule 16 in CLAUDE.md. |
|
||||
|
||||
## Scope
|
||||
|
||||
**IN SCOPE — full structural + visual redesign:**
|
||||
- `/my/home` — jobs-forward dashboard (template + controller already exist; need rewrite of template, controller data prep is mostly fine)
|
||||
- `/my/jobs` — list page, redesigned to use V2 card shape per row (or compact variant for list density)
|
||||
- `/my/jobs/<id>` — detail page using vertical timeline + grouped documents
|
||||
- Brand token system: a single SCSS partial (`_fp_portal_tokens.scss`) defining teal palette, button gradients, shadows, spacing scale, typography scale — referenced by every portal SCSS file
|
||||
- Reusable QWeb macros: `fp_portal_stepper`, `fp_portal_doc_chip`, `fp_portal_status_badge` — so every page renders consistent components
|
||||
|
||||
**IN SCOPE — cosmetic-only (new tokens applied, no structural change):**
|
||||
- `/my/quote_requests` (list + detail)
|
||||
- `/my/purchase_orders` (list + detail)
|
||||
- `/my/fp_invoices` (list + detail)
|
||||
- `/my/deliveries` (list + detail)
|
||||
- `/my/certifications` (list + detail)
|
||||
- `/my/configurator` (RFQ wizard)
|
||||
- Portal sidebar (just colour tokens; layout untouched)
|
||||
|
||||
**OUT OF SCOPE — deferred:**
|
||||
- Mobile-specific layout polish (the desktop design uses Bootstrap grid which collapses gracefully; explicit mobile breakpoints can ship in a follow-up)
|
||||
- Dark-mode parity (Odoo 19 compiles two bundles; if the customer toggles dark on their profile, the portal will fall back to the default Bootstrap dark and look broken until we ship the `$o-webclient-color-scheme` branch — flagged but not blocking)
|
||||
- Real-time push updates of job status (current refresh-on-load is acceptable; SSE / longpoll is a separate effort)
|
||||
- In-page document preview (PDFs download via existing `ir.binary` stream; no embedded viewer)
|
||||
- Internationalisation of the new strings (English / Canadian English; French follows the existing report_coc_fr pattern in a separate effort if needed)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Surfaces and data sources
|
||||
|
||||
| URL | Controller method | Template | Primary data |
|
||||
|---|---|---|---|
|
||||
| `/my/home` | `home()` (existing override at portal.py:125) | `fp_portal_home_dashboard` (rewrite) | counts + recent 5 of each: quotes, POs, jobs, certs, deliveries, invoices (already prepared) |
|
||||
| `/my/jobs` | `portal_my_jobs()` (existing at portal.py:512) | `portal_my_jobs` (rewrite) | `fusion.plating.portal.job` records for partner's commercial tree |
|
||||
| `/my/jobs/<id>` | `portal_my_job()` (existing at portal.py:565) | `portal_my_job` (rewrite) | Single `fusion.plating.portal.job` + linked docs |
|
||||
|
||||
### Controller changes
|
||||
|
||||
**No new routes.** The data already pulled by `home()` covers the new dashboard surface (counts + recent records of each type). What needs adding:
|
||||
|
||||
1. `home()` needs to also compute the **welcome-line summary** ("6 active jobs · 2 awaiting your review · 1 ready to ship") — three new counts based on `fp.portal.job.state` values.
|
||||
2. `portal_my_job()` needs a **timeline data prep helper** that walks the underlying `fp.job` (via `portal_job.job_id` or whatever the link is — verify during implementation) and emits a list of `{stage_label, started_at, completed_at, status, operator_name, notes}` records ordered by stage sequence.
|
||||
3. `portal_my_job()` needs a **document-grouping helper** that takes the job's related records (sale_order_id, receiving_id, certificate_ids, picking_id, invoice_ids) and emits a dict keyed by the 4 categories, with each entry being a list of `{name, mimetype, size, url, uploaded_date}`.
|
||||
|
||||
Both helpers live as private methods on `FpCustomerPortal`. **Possible exception**: if the existing `fusion.plating.portal.job` doesn't already store per-stage timestamps in queryable fields, the timeline helper will need either (a) compute fields added on the model, or (b) the controller to walk chatter messages on the underlying `fp.job`. See "Open items" §1 — decided during Phase 3 implementation.
|
||||
|
||||
### Template structure
|
||||
|
||||
```
|
||||
fusion_plating_portal/views/
|
||||
├── fp_portal_dashboard.xml — rewrite of fp_portal_home_dashboard
|
||||
├── fp_portal_templates.xml — rewrites of portal_my_jobs + portal_my_job
|
||||
├── fp_portal_macros.xml — NEW: shared QWeb macros
|
||||
│ ├── t-call="fp_portal_stepper" (job, stage_count)
|
||||
│ ├── t-call="fp_portal_status_badge" (state)
|
||||
│ ├── t-call="fp_portal_doc_chip" (doc, dense=False)
|
||||
│ └── t-call="fp_portal_doc_group" (label, docs)
|
||||
└── fp_portal_brand.xml — NEW: layout-level brand wrapper template (welcome strip, gradient CTA bar)
|
||||
```
|
||||
|
||||
Existing files preserved:
|
||||
- `fp_quote_request_views.xml` — only swap colour-coded badges to use the new tokens
|
||||
- `fp_portal_configurator_templates.xml` — only swap button styles
|
||||
- `fp_portal_breadcrumbs.xml`, `fp_sale_order_portal.xml`, `fp_menu.xml` — untouched
|
||||
|
||||
### SCSS structure
|
||||
|
||||
Per CLAUDE.md SCSS rules (no `@import` in custom SCSS; every file registered separately in `web.assets_frontend`; tokens loaded first):
|
||||
|
||||
```
|
||||
fusion_plating_portal/static/src/scss/
|
||||
├── _fp_portal_tokens.scss — NEW: brand colour, gradient, shadow, radius, spacing, typography vars (no rules)
|
||||
├── fp_portal_buttons.scss — NEW: gradient button system (primary/secondary/ghost/danger + hover/focus/active states)
|
||||
├── fp_portal_stepper.scss — NEW: circular stepper geometry + glow ring animations
|
||||
├── fp_portal_cards.scss — NEW: card/widget shells (radius, shadow, hover lift)
|
||||
├── fp_portal_badges.scss — NEW: status badge pill + dot + glow halo
|
||||
├── fp_portal_timeline.scss — NEW: vertical timeline geometry (line, dots, padding)
|
||||
├── fp_portal_dashboard.scss — NEW: jobs-forward grid + KPI tiles
|
||||
└── fusion_plating_portal.scss — EXISTS: trimmed down, only catch-all rules left
|
||||
```
|
||||
|
||||
Manifest assets registration order matters (tokens first, then dependencies, then leaf files):
|
||||
|
||||
```python
|
||||
'assets': {
|
||||
'web.assets_frontend': [
|
||||
'fusion_plating_portal/static/src/scss/_fp_portal_tokens.scss',
|
||||
'fusion_plating_portal/static/src/scss/fp_portal_buttons.scss',
|
||||
'fusion_plating_portal/static/src/scss/fp_portal_badges.scss',
|
||||
'fusion_plating_portal/static/src/scss/fp_portal_stepper.scss',
|
||||
'fusion_plating_portal/static/src/scss/fp_portal_cards.scss',
|
||||
'fusion_plating_portal/static/src/scss/fp_portal_timeline.scss',
|
||||
'fusion_plating_portal/static/src/scss/fp_portal_dashboard.scss',
|
||||
'fusion_plating_portal/static/src/scss/fusion_plating_portal.scss',
|
||||
'fusion_plating_portal/static/src/js/fp_rfq_form.js',
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
### Brand tokens (single source)
|
||||
|
||||
`_fp_portal_tokens.scss` defines the design system:
|
||||
|
||||
```scss
|
||||
// Brand palette (matches enplating.com live CSS variables)
|
||||
$fp-teal-light: #2eaf93;
|
||||
$fp-teal: #1a6b59;
|
||||
$fp-teal-dark: #0e3d2f;
|
||||
$fp-teal-deep: #0a3528;
|
||||
$fp-mint: #cbf3e6;
|
||||
$fp-mint-pastel: #f0fdf9;
|
||||
$fp-aqua: #9ae5d4;
|
||||
|
||||
// Surfaces
|
||||
$fp-page-bg: #f8fafb;
|
||||
$fp-section-bg: #f3f7f6;
|
||||
$fp-card-bg: #ffffff;
|
||||
$fp-card-border: #e5e7eb;
|
||||
|
||||
// Text
|
||||
$fp-text: #111827;
|
||||
$fp-text-body: #374151;
|
||||
$fp-muted: #6b7280;
|
||||
$fp-muted-light: #9ca3af;
|
||||
|
||||
// Status (functional, not brand)
|
||||
$fp-amber: #f59e0b;
|
||||
$fp-amber-bg: #fef3c7;
|
||||
$fp-amber-text: #92400e;
|
||||
$fp-success: #22c55e;
|
||||
$fp-success-text: #15803d;
|
||||
$fp-danger: #ef4444;
|
||||
$fp-danger-dark: #b91c1c;
|
||||
|
||||
// Gradients (3-stop ready; current designs use 2 stops)
|
||||
$fp-gradient-primary: linear-gradient(135deg, $fp-teal-light 0%, $fp-teal 100%);
|
||||
$fp-gradient-danger: linear-gradient(135deg, $fp-danger 0%, $fp-danger-dark 100%);
|
||||
$fp-gradient-mint: linear-gradient(135deg, $fp-mint-pastel 0%, $fp-mint 100%);
|
||||
$fp-gradient-icon: linear-gradient(135deg, $fp-mint 0%, $fp-aqua 100%);
|
||||
$fp-gradient-secondary: linear-gradient(180deg, #fff 0%, $fp-section-bg 100%);
|
||||
|
||||
// Shadows
|
||||
$fp-shadow-card: 0 1px 2px rgba(0,0,0,.03);
|
||||
$fp-shadow-card-hover: 0 1px 3px rgba(0,0,0,.04), 0 4px 12px rgba(0,0,0,.04);
|
||||
$fp-shadow-button: 0 1px 3px rgba(26,107,89,.25), 0 4px 12px rgba(26,107,89,.18);
|
||||
$fp-shadow-danger: 0 1px 3px rgba(185,28,28,.25), 0 4px 12px rgba(185,28,28,.15);
|
||||
$fp-glow-ring: 0 0 0 4px rgba(46,175,147,.20);
|
||||
|
||||
// Geometry
|
||||
$fp-radius-pill: 9999px;
|
||||
$fp-radius-card: 14px;
|
||||
$fp-radius-button: 9px;
|
||||
$fp-radius-chip: 8px;
|
||||
$fp-radius-icon: 7px;
|
||||
```
|
||||
|
||||
Per the SCSS gotcha in CLAUDE.md (rule 9, dark-mode awareness) the file should branch on `$o-webclient-color-scheme` for any colour that needs to flip in dark mode. Since dark mode is **deferred** in this spec, the branch is documented in `_fp_portal_tokens.scss` as a TODO with hex placeholders, not implemented.
|
||||
|
||||
### Document-categorisation logic
|
||||
|
||||
Per-category source maps:
|
||||
|
||||
| Category | Source records |
|
||||
|---|---|
|
||||
| **From You** (customer-uploaded) | `fusion.plating.portal.job.sale_order_id.message_attachment_ids` filtered to customer's uploads + `fusion.plating.portal.job.quote_request_id.line_ids.attachment_ids` (RFQ drawings) |
|
||||
| **Specifications** | `fusion.plating.portal.job.x_fc_part_catalog_id.x_fc_customer_spec_id.attachment_id` + any spec linked via the SO line `x_fc_customer_spec_id` |
|
||||
| **Quality** | **Prefer `fp.certificate` records linked to the job** (newer, post-Sub-12c; renders the full CoC with Fischerscope thickness pages already merged via the back-end merge — see CLAUDE.md S19); **fall back to** `fusion.plating.portal.job.coc_attachment_id` (legacy single-attachment field) only when no `fp.certificate` exists. Verify the exact linking field name (likely `x_fc_job_id` or via `sale_order_id`) during Phase 3. |
|
||||
| **Shipping** | `stock.picking` records linked to the SO with state=done — their packing slip PDF and tracking_reference |
|
||||
|
||||
Implementation note: each lookup needs a defensive `try/except` (some attachments may be missing) and `sudo()` where required (customer doesn't have read on the quality_check raw model — they only see its rendered PDF). All model paths in the table above are spec-time assumptions — verify against the live model during Phase 3 and update the spec inline if anything has drifted.
|
||||
|
||||
### Welcome-line summary logic
|
||||
|
||||
`home()` already pulls `recent_jobs` and `job_count`. Add:
|
||||
|
||||
```python
|
||||
active_jobs = Job.search_count([
|
||||
('partner_id', 'child_of', commercial.id),
|
||||
('state', 'in', ['received', 'in_progress', 'quality_check']),
|
||||
])
|
||||
awaiting_review = Quote.search_count([
|
||||
('partner_id', 'child_of', commercial.id),
|
||||
('state', '=', 'quoted'), # customer needs to accept/reject
|
||||
])
|
||||
ready_to_ship = Job.search_count([
|
||||
('partner_id', 'child_of', commercial.id),
|
||||
('state', '=', 'ready_to_ship'),
|
||||
])
|
||||
```
|
||||
|
||||
These three numbers populate the welcome line: "X active jobs · Y awaiting your review · Z ready to ship".
|
||||
|
||||
### Timeline data prep
|
||||
|
||||
Existing `fp.portal.job` has a 5-state machine: `received → in_progress → quality_check → ready_to_ship → shipped`. The detail page needs per-stage timestamps. Implementation approach:
|
||||
|
||||
1. Add helper `_fp_get_stage_timeline()` on `fp.portal.job` returning a list of 5 dicts: `{label, status, started_at, completed_at, operator_name, notes}`.
|
||||
2. `status` is one of `done | active | pending`.
|
||||
3. `started_at` / `completed_at` come from the existing chatter / write_date on related records (e.g., for `received` use `received_date`; for `in_progress` use the first `fp.job` confirm date; for `quality_check` use the QC check create_date; for `ready_to_ship` use the picking confirm date; for `shipped` use `actual_ship_date`).
|
||||
4. `operator_name` pulled from the chatter event author where available; defaults to "EN Plating".
|
||||
5. `notes` is a short human-readable sentence per stage — examples in the mockup: "2 cartons · 240 units counted matches PO", "Type II hard anodize, 0.002" target". For v1 these can be hardcoded templates per stage; v2 can render from the actual recipe step instructions.
|
||||
|
||||
### Mockups (single source of truth for visual fidelity)
|
||||
|
||||
Preserved in `.superpowers/brainstorm/1800-1778997036/content/`:
|
||||
- `design-direction.html` — A/B/C aesthetic options (Modern SaaS won)
|
||||
- `saas-refinements.html` — V1/V2/V3 card variants (V2 stepper + V3 timestamps won)
|
||||
- `dashboard-layout.html` — A vs B layout (B jobs-forward won)
|
||||
- `job-detail.html` — detail page using V3 layout (approved)
|
||||
- `branded-dashboard.html` — final brand-applied dashboard with gradient buttons (approved)
|
||||
|
||||
The implementation should match `branded-dashboard.html` for the dashboard, and the branded version of `job-detail.html` (apply the same teal/gradient swap) for the detail page.
|
||||
|
||||
## Migration / deployment
|
||||
|
||||
1. Bump `fusion_plating_portal/__manifest__.py` version `19.0.2.3.0 → 19.0.3.0.0` (minor bump signals a UI-significant change).
|
||||
2. Add new SCSS + macro XML files to the manifest `data` and `assets` lists.
|
||||
3. Deploy to entech per the standard runbook (see CLAUDE.md "Deployment / odoo-entech" section).
|
||||
4. Asset cache bust: `DELETE FROM ir_attachment WHERE url LIKE '/web/assets/%';` then restart, or just rely on the version bump.
|
||||
5. Verify the dashboard renders correctly at `https://enplating.com/my/home` (logged in as admin, then optionally grant portal access to a real customer for end-to-end verification).
|
||||
|
||||
## Implementation sequencing
|
||||
|
||||
To keep diffs reviewable, ship in 4 ordered phases:
|
||||
|
||||
1. **Phase 1 — Tokens + button system** (1 file: `_fp_portal_tokens.scss` + `fp_portal_buttons.scss`, manifest update). Visible change: only buttons re-skin; rest of portal unchanged. Low blast radius; deployable on its own.
|
||||
2. **Phase 2 — Macros + dashboard** (`fp_portal_macros.xml` + dashboard SCSS + `fp_portal_home_dashboard` template rewrite + welcome-summary counts in `home()`). Visible change: `/my/home` becomes the jobs-forward dashboard.
|
||||
3. **Phase 3 — Jobs list + detail** (`portal_my_jobs` + `portal_my_job` template rewrites + timeline + doc-group helpers in controller). Visible change: clicking a job card now lands on the new detail page.
|
||||
4. **Phase 4 — Cosmetic sweep across other `/my/*` pages** — apply the new badge / button / card tokens to quote requests, POs, invoices, deliveries, certifications, configurator. No structural changes.
|
||||
|
||||
Each phase bumps the patch version (19.0.3.0.0 → .1.0 → .2.0 → .3.0 → .4.0).
|
||||
|
||||
## Open items to verify during implementation
|
||||
|
||||
These are flagged as assumptions to confirm against the live model, not blockers:
|
||||
|
||||
1. **Stage timestamp sources** — the 5 stages on `fp.portal.job` may not all have explicit timestamp fields. Confirm: do we need to add fields, or pull from the underlying `fp.job` / chatter? If fields are missing, add them as `Datetime` on `fp.portal.job` with `compute=` from the underlying records.
|
||||
2. **Operator name surfacing** — customers seeing operator names is a privacy / policy question. Default in the spec is to show first-initial + last-name (e.g., "D. Mendez"). Confirm with EN Plating before shipping.
|
||||
3. **Stage-notes copy** — the example notes in the mockup ("Tank 4 · 45 min cycle · Day 3 of 7") are made up. Confirm what info is reasonable to share with the customer per stage.
|
||||
4. **Document linking edge cases** — what shows when there are 0 documents in a category? Spec assumes a "Will appear when…" placeholder card per the approved mockup. Verify EN Plating doesn't want the placeholder hidden entirely when empty.
|
||||
5. **Dark mode** — explicitly deferred. If a customer logs in with `color_scheme=dark` set, what should they see? Default Bootstrap dark fallback is ugly. Suggest: force the portal to `color_scheme=light` for `share=True` users, or add a `prefers-color-scheme: light` meta tag. Document the choice during Phase 1.
|
||||
|
||||
---
|
||||
|
||||
*Mockup references in `.superpowers/brainstorm/1800-1778997036/content/` — keep until implementation is verified.*
|
||||
Reference in New Issue
Block a user