Files
fourge-portal/CLAUDE.md
T
Krao Hasanee 71a8ed1b43 docs: document team performance calculation logic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 23:44:44 -04:00

5.3 KiB

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:

<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

Team Performance calculation

Source: deliveries table (not tasks).

Each row in deliveries = one unit of work by one person:

  • sent_by — name of person who submitted the work
  • version_number — 0 = new task delivery, 1+ = revision delivery
  • sent_at — when submitted (bucketed by EST month)

Rules:

  • R00 → newCount += 1 for sent_by
  • R01, R02, R03… → revCount += 1 each for sent_by
  • Same task can have deliveries by different people — each counts independently
  • Task status is ignored — a delivery record means work was done regardless of current task status (task goes back to not_started when revision is requested)
  • Top 5 by total (new + revisions) shown per month
  • Months shown only if delivery data exists for that month (EST timezone)