0b4705311b
- New TaskDetail replaces /tasks/:id (was v2 at /requests/:id/v2) - Overview tab: R00 request info, Requested By/Date/Sign Count inline row, Notes, Sign Family, files, amendments - Revisions tab: R01+ from submissions, newest first, same layout as overview, per-entry Amend button - Comments tab: single-line input, post on Enter, delete own comments, Supabase task_comments table - Folder tab: placeholder for file sharing - Tab badges showing revision/comment counts - Amend Request modal: drag-drop file zone, popupOverlayStyle/Surface, Save+Cancel buttons - Request Revision modal for clients on approved/invoiced/paid tasks - JSZip download-all with progress popup for submission files - Upload progress popup for Add Task and amend file uploads - FileAttachment drop zone: 8px radius, transparent bg, label style fix (no all-caps override) - PageLoader on task detail load - task_comments migration with RLS policies Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
309 lines
17 KiB
Markdown
309 lines
17 KiB
Markdown
# Fourge Portal Layout System
|
|
|
|
This is the single source of truth for dashboard/profile visual structure and UI geometry.
|
|
|
|
## 1) Global Frame
|
|
- Viewport app shell: `height: 100vh`, `overflow: hidden`
|
|
- Main content gutter: `24px` all sides
|
|
- Sidebar: `width: 76px`, `top: 24px`, `left: 24px`, `height: calc(100vh - 48px)`, `border-radius: 8px`
|
|
- Main wrapper offset from sidebar: `margin-left: 100px`
|
|
- Page rhythm unit: `24px` (header spacing, card gaps, section gaps)
|
|
|
|
## 2) Theme + Background
|
|
- Background ownership is `body` via `background: var(--bg)`.
|
|
- Dark base token `--bg` is full gradient:
|
|
- radial glow + vertical dark gradient.
|
|
- Light base token `--bg` is full gradient:
|
|
- gray radial glow + white vertical gradient.
|
|
- Do not use `html` theme-gradient scripting for Safari chrome behavior.
|
|
|
|
## 3) Tokens
|
|
- Accent: `#F5A523`
|
|
- Card bg dark: `rgba(255,255,255,0.02)`
|
|
- Card bg light: `rgba(0,0,0,0.02)`
|
|
- Sidebar/menu shell uses the same transparent card background token as cards
|
|
- Secondary card tone dark: `rgba(255,255,255,0.08)`
|
|
- Secondary card tone light: `rgba(0,0,0,0.08)`
|
|
- Border dark: `rgba(245,165,35,0.15)`
|
|
- Border light: `rgba(0,0,0,0.1)`
|
|
- Text primary dark/light: `#ffffff / #0d0d0d`
|
|
- Text secondary dark/light: `#a8a8a8 / rgba(0,0,0,0.6)`
|
|
- Text muted dark/light: `#666666 / rgba(0,0,0,0.38)`
|
|
|
|
## 4) Typography
|
|
- Font family: `Fourge`, then `-apple-system`, `BlinkMacSystemFont`, `'Segoe UI'`, `sans-serif`
|
|
- Base font size: `14px`
|
|
- Header title: `28px`, `500`, `line-height: 1.2`
|
|
- Header subtitle: `13px`
|
|
- Widget title: `11px`, `500`, uppercase, `letter-spacing: 0.8px`
|
|
- Section header (new standard): `18px`, `500`, Title Case, `letter-spacing: 0.2px`, `line-height: 1.1`
|
|
- Body table text: `12px/13px` by column importance
|
|
|
|
## 5) Card System
|
|
- Default widget shell:
|
|
- `background: var(--card-bg)`
|
|
- `border: 1px solid var(--border)`
|
|
- `border-radius: 8px`
|
|
- `padding: 18px 21px`
|
|
- `backdrop-filter: blur(12px)` + `-webkit-backdrop-filter`
|
|
- Card width is not an intrinsic card token.
|
|
- Width is owned by the page/grid/container the card lives in.
|
|
- Cards should fill the slot provided by that page layout unless a page-level rule explicitly fixes a track.
|
|
- Compact card radius (legacy generic `.card`): `4px` (do not use for new dashboard widgets)
|
|
|
|
## 6) Header + Top Right Controls
|
|
- Site header: `padding-top: 24px`, `padding-bottom: 24px`
|
|
- Right control row:
|
|
- Search icon button: `32x32`
|
|
- Search button to theme toggle space: `7px` (`search-wrap margin-right`)
|
|
- Theme toggle: `32x32`
|
|
- Theme toggle to avatar: `14px` (`avatar-wrap margin-left`)
|
|
- Avatar button: `49x49`, circle, `2px` inner ring + `2px` accent outline
|
|
|
|
## 7) Dashboard Grids (Team)
|
|
- Stat row: `grid-template-columns: 1fr 1fr 1fr 1.5fr`, `gap: 24`, `margin-bottom: 0`
|
|
- Row 2: `grid-template-columns: 1fr 1fr 280px`, `gap: 24`, `margin-top: 24`
|
|
- Order: Recent Activity (left), Tasks In Progress (center), Calendar (right, fixed width)
|
|
- Row 3: `grid-template-columns: 1fr 1fr`, `gap: 24`, `margin-top: 24`
|
|
- Row 4 full-width: `margin-top: 24`
|
|
|
|
## 8) Stat Cards
|
|
- Card min height: `120px`
|
|
- Internal row gap: `21px`
|
|
- Label/value/sub spacing:
|
|
- Label: `margin-bottom: 5px`
|
|
- Value: `30px`, `400`, `letter-spacing: -0.5`, `line-height: 1.1`
|
|
- Sub: `12px`, `margin-top: 5px`
|
|
- Icon badge: `27x27`, circle
|
|
- Icon glyph: `13x13`
|
|
|
|
## 9) Calendar
|
|
- Card uses widget shell
|
|
- Header-to-grid gap: `14px`
|
|
- Weekday label: `10px`, `600`, `letter-spacing: 0.5`
|
|
- Day cell button: `28x28`, circular
|
|
- Day number: `12px`
|
|
- Today style: bg `#F5A523`, text `#0d0d0d`, `700`
|
|
- Dots: up to 3, each `3x3`, gap `2`
|
|
- Popover:
|
|
- Anchored left of cell: `right: calc(100% + 8px)`, vertical centered
|
|
- `width: 210px`, `padding: 10px 12px`, `border-radius: 8px`
|
|
- shadow `0 12px 32px rgba(0,0,0,0.45)`
|
|
- row dot `6x6`, row text `12px`
|
|
|
|
## 10) Activity + Performance Rows
|
|
- Visible rows target: 5
|
|
- Row layout: `display:flex`, `align-items:center`, `gap:10px`
|
|
- Row spacing: `margin-top: 10px` from second row onward
|
|
- Name text: `13px`
|
|
- Meta/date text: `11px`
|
|
- Progress track: `height: 4px`, `radius: 2px`
|
|
- Percentage width slot: `min-width: 28px`
|
|
- Empty states in all cards (dashboard, profile, tasks, projects, file sharing, etc.):
|
|
- any `No ...` message is centered in the card body (`display:flex`, `align-items:center`, `justify-content:center`)
|
|
- use shared class treatment (`.card-empty-center`) for consistency
|
|
- avoid top-offset-only placement for empty text
|
|
|
|
## 11) Tables
|
|
- General table layout in dashboard cards: `table-layout: fixed`, `border-collapse: collapse`
|
|
- Column widths are table-specific, not universal tokens.
|
|
- Widths belong to the individual table/view that defines them.
|
|
- Reuse widths only when the same table pattern is intentionally repeated.
|
|
- Header cells:
|
|
- `font-size: 10px`, `font-weight: 500`, uppercase, `letter-spacing: 0.6px`
|
|
- bottom spacing: `padding-bottom: 12px`
|
|
- sticky behavior for scrollable tables:
|
|
- table headers stay fixed while body scrolls
|
|
- use shared sticky-head treatment for all app tables (`position: sticky; top: 0`)
|
|
- table scrollbars are visually hidden for table scroll containers; wheel/trackpad scrolling remains active
|
|
- Body cells:
|
|
- primary text: `13px`
|
|
- secondary/metrics text: `12px`
|
|
- row vertical spacing via cell padding: typically `5px`
|
|
- Hot Tasks column widths:
|
|
- check `10%`, task `40%`, requested by `35%`, due by `15%`
|
|
- Client Highlight column widths:
|
|
- icon `5%`, company `22%`, contact `23%`, projects `13%`, open `13%`, outstanding `12%`, paid `12%`
|
|
- Sorting rule:
|
|
- Every visible data column header must be sortable.
|
|
- Use clickable header controls (`SortTh`) with ascending/descending indicator.
|
|
- Exclude only non-data utility/action columns (checkbox-only, icon-only status marker, action buttons).
|
|
|
|
## 12) Profile Page
|
|
- Container: full available content width, column, `gap: 24`
|
|
- Top row: `grid-template-columns: 60fr 40fr`, `gap: 24`
|
|
- At `<=1200px`: top row stacks to one column
|
|
- Main profile card uses widget shell
|
|
- Profile card width is determined by the profile page grid, not by the card itself.
|
|
- Internal card layout:
|
|
- row `gap: 20px`
|
|
- portrait max `140x140`, circle
|
|
- portrait aligns flush in the row without extra wrapper padding/side space
|
|
- detail grid `140px 1fr`, `row-gap: 8`, `column-gap: 12`, `margin-top: 14`
|
|
- profile name/title: `18px`, `500`, `line-height: 1.2`
|
|
- company subtitle: `13px`, secondary text
|
|
- contact/detail rows: `13px`, primary text
|
|
- social row `margin-top: 14`, `gap: 8`
|
|
- company line under title is always visible; fallback display is `—` when no company is assigned
|
|
- right-side meta labels (`Member Since`, `Role`) use widget-title sizing: `11px`, `500`, uppercase, `letter-spacing: 0.8px`
|
|
- right-side meta values use body sizing: `13px`
|
|
- self-only edit button: `position: absolute`, `top: 18px`, `right: 21px` (aligns to card padding), `border-radius: 8px` (matches card), `height: 30px`, `font-size: 12px`, `padding: 0 12px`
|
|
- default outline button hover/focus fills with accent gold and uses dark text
|
|
- Right calendar card shows only tasks/events assigned to the viewed profile user
|
|
- Profile page left column includes a `Tasks In Progress` card above `Recent Activity`
|
|
- Profile `Tasks In Progress` follows the dashboard table/card pattern:
|
|
- same widget shell and header treatment
|
|
- same show-all behavior (5 visible by default)
|
|
- profile-specific filter is tasks assigned to the viewed user with statuses `in_progress`, `on_hold`, or `client_review`
|
|
- columns are `Task` and `Status`
|
|
- Profile activity feed action text pattern: `Task started`, `Task submitted`, `Task approved`, `Task rejected` (sentence title case)
|
|
- Activity feed includes:
|
|
- actions performed by the viewed user
|
|
- `Task approved` and `Task rejected` entries for tasks assigned to that viewed user (even if actioned by someone else)
|
|
## 12.5) File Sharing
|
|
- Main split: `grid-template-columns: minmax(0, 1fr) minmax(0, 3fr)` for an effective `25 / 75` layout, `gap: 24`
|
|
- Left stacked cards (`Pinned`, `Navigation`): `gap: 24`
|
|
- Canonical folder structure:
|
|
- team and subcontractor profile folders live at `Team/<User Name>`
|
|
- company folders live at `Clients/<Company Name>`
|
|
- project folders live at `Clients/<Company Name>/Projects/<Project Name>`
|
|
- every project auto-creates `00 Project Files`
|
|
- task folders live at `Clients/<Company Name>/Projects/<Project Name>/<Task Name>`
|
|
- every task auto-creates `Request Info`, `Working Files`, and `Old Books`
|
|
- request submission files are copied into `Request Info/R00`, `R01`, and higher revision folders
|
|
- renaming a team user, subcontractor user, company, project, or task must rename the matching filesystem folder
|
|
- folder repair/backfill is additive only: create missing folders, leave existing folders untouched
|
|
- request-info backfill must skip files that already exist and must not overwrite added working files or user-managed files
|
|
- deleting a task in the portal moves its task folder into `Archive/Clients/<Company Name>/Projects/<Project Name>/<Task Name>` instead of deleting it
|
|
- deleting a project in the portal moves its project folder into `Archive/Clients/<Company Name>/Projects/<Project Name>` instead of deleting it
|
|
- orphan reconciliation may move folders into archive when they no longer map to portal records
|
|
- orphan team folders move to `Archive/Team/<Folder Name>` when no team or subcontractor profile matches that folder name
|
|
- orphan project folders move to `Archive/Clients/<Company Name>/Projects/<Project Name>` when no portal project matches that folder name under that company
|
|
- orphan reconciliation does not remove company roots or overwrite archived content; it merge-moves into archive
|
|
- Access/home rules:
|
|
- team members have full file access
|
|
- client users with exactly one tied company use that company folder as home: `/Clients/<Company Name>`
|
|
- client users tied to multiple companies use `/Clients` as home and only see company folders they are tied to
|
|
- `/Clients` acts as a virtual parent for multi-company client users and must not expose unrelated company folders
|
|
- subcontractor users use virtual home `/`
|
|
- subcontractor virtual home shows `Team` plus only the project folders they are assigned to
|
|
- subcontractor users skip all other root folders and must not see unrelated client or internal roots
|
|
- subcontractor `Team` maps to the real shared `Team` folder
|
|
- each subcontractor project folder is a virtual root that maps to the real client project path `/Clients/<Company>/Projects/<Project>`
|
|
- Right card header:
|
|
- title label is `File Browser`
|
|
- drag/drop note sits directly under the title and uses subtext sizing: `12px`
|
|
- current file path uses the same `11px` uppercase label treatment as the title row family
|
|
- toolbar does not show `+ Folder` or `Upload` buttons
|
|
- upload happens by dragging and dropping files or folders into the right card
|
|
- delete action uses the same geometry as the profile edit button (`top: 18px`, `right: 21px`, `8px` radius, `30px` height, `12px` text, `0 12px` padding) but uses danger styling with red hover/fill
|
|
- Right card table:
|
|
- uses dashboard table styling (`table-layout: fixed`, transparent header row, `10px` uppercase headers, `13px/12px` body text)
|
|
- card padding matches dashboard widget shell (`18px 21px`)
|
|
- clickable text inside the table highlights in accent; rows do not use block hover fill
|
|
- body cells are borderless like dashboard card tables
|
|
- body cell padding matches dashboard tables exactly: `5px`
|
|
- visible headers (`Name`, `Size`, `Modified`) use shared sortable header controls
|
|
- file/folder icon footprint is compact so row height stays on the same cadence as dashboard tables
|
|
- alternating rows use a very subtle theme-aware tint for scanability
|
|
- Navigation tree:
|
|
- `Home` aligns to the first arrow column
|
|
- connector line runs through the arrow center column
|
|
- pinned folders are user-specific and must not be shared across users on the same device/browser session
|
|
- hover is text-only accent color, no row fill
|
|
|
|
## 13) Radius + Geometry Rules
|
|
- Dashboard/profile widgets: `8px` radius
|
|
- Sidebar: `8px` radius
|
|
- Standard app buttons use the `Edit Profile` geometry:
|
|
- height: `30px`
|
|
- horizontal padding: `0 12px`
|
|
- border-radius: `8px`
|
|
- font-size: `11px`
|
|
- font-weight: `500`
|
|
- letter-spacing: `0.8px`
|
|
- text-transform: uppercase
|
|
- line-height: `1`
|
|
- label color follows theme text roles:
|
|
- dark mode: primary button labels use dark text on accent fill; outline labels use primary text on transparent surface
|
|
- light mode: outline labels use light-theme primary text; danger labels use danger text
|
|
- outline button border uses the same `var(--border)` token as card outlines unless a button role explicitly overrides it
|
|
- keep existing button role styling intact (`outline`, `primary`, `danger`); geometry changes, not role colors
|
|
- Inputs/dropdowns may use their own control radius unless a page-specific rule says otherwise
|
|
- Circular elements (avatar/day/icon badges): `50%`
|
|
|
|
## 14) Z-Index Stack
|
|
- Sidebar: `200`
|
|
- Header dropdowns/tooltips: `300`
|
|
- Calendar hover popover: `1002` within card context (`card can be 1001 active`)
|
|
- Modal overlay: `1200`
|
|
|
|
## 14.5) Loading Popup
|
|
- Use shared `PageLoader` component for loading overlays instead of page-specific popup implementations.
|
|
- Loading popups/overlays, including shared site-wide page loaders and file-sharing loading states, use the same widget shell as dashboard cards:
|
|
- `background: var(--popup-bg)`
|
|
- `border: 1px solid var(--border)`
|
|
- `border-radius: 8px`
|
|
- `padding: 18px 21px`
|
|
- `backdrop-filter: blur(12px)` + `-webkit-backdrop-filter`
|
|
- Popup surface is theme-aware:
|
|
- dark mode: deep translucent dark surface
|
|
- light mode: bright translucent white surface
|
|
- Loading overlay scrim is theme-aware:
|
|
- dark mode: `rgba(0,0,0,0.58)`
|
|
- light mode: soft light scrim via `--overlay-scrim`
|
|
- Loading title uses widget header treatment: `11px`, `500`, uppercase, `letter-spacing: 0.8px`, secondary text
|
|
- Progress track:
|
|
- height: `4px`
|
|
- radius: `2px`
|
|
- track color: secondary card tone (`var(--card-bg-2)`)
|
|
- progress fill: accent
|
|
- Progress/meta text under the bar uses subtext sizing (`12px`) and secondary text
|
|
|
|
## 14.6) Modal Form Fields
|
|
- Modal forms that follow `Edit Profile` styling (including `Add Task`/`New Task`) share the same field typography and theme tokens:
|
|
- modal surface transparency matches `Edit Profile` card shell (`background: var(--card-bg)`), not a denser popup-specific override
|
|
- field labels: `11px`, `500`, uppercase, `letter-spacing: 0.8px`, color `var(--text-secondary)`
|
|
- input/select text: `13px`, color `var(--text-primary)`, left aligned
|
|
- textarea text: `13px`, color `var(--text-primary)`
|
|
- field surfaces and borders use global theme tokens (`var(--card-bg-2)` + `var(--border)`) for dark/light parity
|
|
- modal form action buttons use the standard outline geometry/style (`btn btn-outline`) unless a page explicitly defines a different role
|
|
|
|
## 15) Motion
|
|
- Motion vars:
|
|
- fast `160ms`
|
|
- base `220ms`
|
|
- easing `cubic-bezier(0.22, 1, 0.36, 1)`
|
|
- Dropdown animation: `ui-fade-up` from `translateY(4px)` + opacity 0 -> 1
|
|
|
|
## 17) Hover Interaction Contract
|
|
- Sidebar, header icon buttons, dropdown items, and avatar menu items must show a visible hover surface before click.
|
|
- Single hover source-of-truth block controls these elements.
|
|
- Dark hover surface baseline: `#1f1f1f`.
|
|
- Light hover surface baseline: `rgba(0,0,0,0.08)`.
|
|
- Nav icon opacity must lift from muted to full on hover (`opacity: 1`).
|
|
- Clickable text links use text-only highlight on hover/focus (`color: var(--accent)`), not whole-row/background fill.
|
|
- Hover and active must be visually distinct:
|
|
- hover uses stronger temporary contrast (`bg` + thin border),
|
|
- active remains persistent selected-state background.
|
|
- File Sharing left navigation exception:
|
|
- `Pinned` + `Navigation` tree rows use text-only hover highlight (`color: var(--accent)`).
|
|
- Do not apply row background fill for hover in this section.
|
|
|
|
## 16) Non-Negotiable Implementation Rules
|
|
- Keep gradient backgrounds on `html`, not `body`
|
|
- Keep widget shell values (`18px 21px`, `8px`, blur 12, border token) consistent
|
|
- Maintain global `24px` spacing rhythm for page/frame/grid gaps
|
|
- Keep team dashboard card order:
|
|
1. Open Tasks
|
|
2. Active Projects
|
|
3. Net Profit
|
|
4. Revenue (wide)
|
|
- Keep row 2 order: Recent Activity (left), Tasks In Progress (center), Calendar (right fixed at `280px`)
|
|
|
|
## 18) Invoice Rules
|
|
- New work (`R00`) is billed at the new-service rate.
|
|
- Revision `R01` is free.
|
|
- Revisions `R02+` are billed incrementally: each uninvoiced revision entry contributes exactly `1x` revision charge.
|
|
- Previously invoiced revisions are excluded; later invoices only charge newly uninvoiced revisions (e.g. if `R02` was already invoiced, at `R04` only `R03` + `R04` are billed).
|