96e90561b9
- Expense detail popup: 80vw×80vh, inline editing, save gated on dirty state, Download Receipt in footer - Add expense form: same layout with FileAttachment drag-drop on right - New invoice popup: 80vw×80vh inline form replacing navigate-away page - Tasks table: suppress row hover background, only Name/Project table-links highlight - TaskDetail Submissions tab: reads from deliveries table, shows R## version, sent_by, message - Submit for Review: writes to deliveries + delivery_files (deliveries bucket) - deliveries.version_number column added and backfilled from revision history - Port 4173 for dev server Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
110 lines
4.5 KiB
Markdown
110 lines
4.5 KiB
Markdown
# Fourge Portal — Claude Instructions
|
|
|
|
## Project
|
|
React + Vite SPA. Supabase backend. Deployed on Vercel. Local dev on port 5173 (or next available). Remote: `gitea` remote at `gitea.krao.us`.
|
|
|
|
## Dev workflow
|
|
- Dev server: `npm run dev` (Vite, HMR active — no restart needed for code changes)
|
|
- Deploy: `vercel --prod` after every change, no need to ask
|
|
- Push: `git push gitea main`
|
|
|
|
## Design system — follow layout.md as single source of truth
|
|
|
|
### Card shell (all new widgets)
|
|
```
|
|
background: var(--card-bg)
|
|
border: 1px solid var(--border)
|
|
border-radius: 8px
|
|
padding: 18px 21px
|
|
backdrop-filter: blur(12px)
|
|
-webkit-backdrop-filter: blur(12px)
|
|
```
|
|
|
|
### Tokens
|
|
- Accent: `#F5A523`
|
|
- Card bg: `var(--card-bg)` = `rgba(255,255,255,0.02)` dark / `rgba(0,0,0,0.02)` light
|
|
- Secondary card: `var(--card-bg-2)`
|
|
- Border: `var(--border)`
|
|
- Text: `var(--text-primary)`, `var(--text-secondary)`, `var(--text-muted)`
|
|
|
|
### Widget title (card section headers)
|
|
```
|
|
font-size: 11px
|
|
font-weight: 500
|
|
text-transform: uppercase
|
|
letter-spacing: 0.8px
|
|
color: var(--text-secondary)
|
|
margin-bottom: 14px
|
|
```
|
|
|
|
### Tables
|
|
- `table-layout: fixed`, `border-collapse: collapse`
|
|
- Headers: `font-size: 10px`, `font-weight: 500`, uppercase, `letter-spacing: 0.6px`, `padding-bottom: 12px`, `border: none`, `background: transparent`
|
|
- Use `SortTh` component for all sortable columns — every data column must be sortable
|
|
- Body cells: primary `13px`, secondary `12px`, `padding: 5px 0`, `border: none`, `background: transparent`
|
|
- Scrollable tables: container gets `className="scrollbar-thin-theme table-scroll-hidden"`, table gets `className="table-sticky-head"`
|
|
- Do NOT add `background: transparent` inline on TD — it blocks the `tr:hover td` CSS rule
|
|
|
|
### Row hover
|
|
- Standard table rows: `tr:hover td { background: rgba(255,255,255,0.02) }` via global CSS — do not add inline bg to TD
|
|
- Clickable text inside rows: use `className="table-link"` — turns gold on hover, no full-row fill
|
|
- Do NOT use whole-row click + cursor pointer as the primary interaction pattern for new tables
|
|
|
|
### Section control bar (tabs + actions)
|
|
```
|
|
display: flex, align-items: center, gap: 4, margin-bottom: 10, flex-shrink: 0
|
|
```
|
|
- Tabs on the left
|
|
- Action buttons on the right via `margin-left: auto` — do NOT use hardcoded spacer divs for alignment
|
|
- Reference: Tasks.jsx tabs + `+ Task` button pattern
|
|
|
|
### Buttons
|
|
- All buttons use `.btn` class with CSS vars (`--btn-height: 22px`, `--btn-radius: 8px`, etc.)
|
|
- Do not hardcode button geometry inline — use `btn btn-outline`, `btn btn-primary`, `btn btn-danger`
|
|
- Height controlled by `--h-control` = `--btn-height`
|
|
|
|
### Modal forms
|
|
- Surface: `background: var(--card-bg)`, use `popupSurfaceStyle` + `popupOverlayStyle` from `../../lib/popupStyles`
|
|
- Field labels: `11px`, `500`, uppercase, `letter-spacing: 0.8px`, `color: var(--text-secondary)`
|
|
- Inputs: `font-size: 13px`, `background: var(--card-bg-2)`, `border: 1px solid var(--border)`, `border-radius: 6px`, `padding: 6px 10px`
|
|
- Form gap: `14px` between fields
|
|
- Action buttons: right-aligned, `btn btn-outline`, gap `8px`
|
|
- Header: single `11px` uppercase secondary label — no large heading
|
|
|
|
### Status badges
|
|
- Use `<StatusBadge status={...} />` — do not hand-roll status tags
|
|
- Badge height: `20px`, `border-radius: 4px`, `font-size: 10px`
|
|
|
|
### Spacing rhythm
|
|
- Page/grid/section gaps: `24px`
|
|
- Tabs-to-card gap: `margin-bottom: 10` on tabs bar
|
|
|
|
### Height-filling layouts
|
|
For pages where cards should fill the window:
|
|
```jsx
|
|
<Layout>
|
|
<div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
|
|
{/* fixed sections: flexShrink: 0 */}
|
|
{/* filling section: flex: 1, minHeight: 0 */}
|
|
</div>
|
|
</Layout>
|
|
```
|
|
Cards that scroll internally: `display: flex, flexDirection: column, minHeight: 0` + inner `overflowY: auto, flex: 1, minHeight: 0`
|
|
|
|
### File browser paths (per role)
|
|
- `team`: `/Clients/{Co}/Projects/{Proj}/{Task}` (real path)
|
|
- `client`: `/{Co}/Projects/{Proj}/{Task}` (virtual — no `/Clients/` prefix)
|
|
- `external`: `/Projects/{Proj}/{Task}` (virtual)
|
|
|
|
## Empty states
|
|
Use `.card-empty-center` class for all "No …" messages inside cards.
|
|
|
|
## Key files
|
|
- `layout.md` — visual spec, always check before building new UI
|
|
- `src/index.css` — global tokens and shared classes
|
|
- `src/components/SortTh.jsx` — sortable table header
|
|
- `src/components/StatusBadge.jsx` — status tags
|
|
- `src/lib/popupStyles.js` — modal overlay/surface styles
|
|
- `api/filebrowser.js` — file browser API (Vercel serverless)
|
|
- `supabase/functions/fbq-proxy/` — Supabase edge function for FileBrowser Quantum
|