Commit Graph

137 Commits

Author SHA1 Message Date
Krao Hasanee 26ed7790b1 Merge branch 'fix/folder-reconcile' 2026-07-21 14:21:29 -04:00
Krao Hasanee 4c78b624f8 feat: add folder reconcile endpoint to backfill missing FileBrowser folders
Folder sync failed silently whenever FileBrowser was unreachable: every
call site swallows the error with console.error, so projects and tasks
were created in the portal with no matching folder on the drive.

Add api/folder-reconcile.js, a secret-protected endpoint that walks
companies/projects/tasks/subcontractors and creates only the folders that
are missing. It never deletes or renames. Folders on disk with no portal
record are reported as orphans for manual review, since they predate the
portal and hold real client work.

Extract shared FileBrowser helpers into api/_lib/filebrowser.js. Note that
this build returns directory listings under a `folders` key rather than
`items`; reading the wrong key yields an empty list and makes the whole
tree look missing, so listDirectories handles both shapes.

Reconcile lists before writing and creates leaves directly rather than
re-walking each path from the root, which keeps a full run at a few
seconds instead of timing out at the 300s function limit.

Schedule hourly via pg_cron: Vercel Hobby caps crons at once per day.
Requires app.folder_reconcile_secret to be set on the database separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 14:18:53 -04:00
Krao Hasanee 2cfbbec9dc cleanup: remove dead code, drop unused deps, refactor derived-state effects
Lint went from 84 problems to 0. Build passes.

Dead code:
- TeamInvoices: drop the orphaned Subcontractor PO block (updateSubcontractorPO
  plus send/ready-to-pay/paid/reopen/cancel/delete/download/CPA-export handlers
  and ~25 derived vars). That feature moved to the routed
  TeamCreateSubcontractorPO / TeamSubcontractorPODetail pages in the Layout2
  redesign; these were leftovers. 2100 -> 1880 lines.
- Tasks: drop unrendered project sort/tab/completion logic in TasksPageShell.
- Remove 59 unused vars, imports, and handlers across 12 other files.

Removed wasted queries:
- TeamInvoices ran a nested subcontractor_payments join (profiles, projects,
  companies, PO items, tasks) on every page load into state nothing read.
- TeamInvoices and ExternalMyInvoices each fetched a task-type map on invoice
  open and discarded it; the ExternalMyInvoices one blocked the detail popup.
- RequestForm queried sign_families on mount into unread state.

Dependencies:
- Drop heic2any (zero imports). heic-to is the one in use and already lazy-loads.

Effects and exports:
- RequestForm: remove 4 effects. Sign-row sizing and company-change resets move
  into their change handlers; single-company selection is derived during render
  rather than written back into form state.
- FilterDropdown: measure position on click instead of in an effect, which also
  removes a one-frame stale-position flicker.
- BrandBook: latest-ref pattern so the 900ms address debounce is not reset by
  handler identity.
- ClientMyInvoices: hoist MONTHS to module scope.
- Split non-component exports: POPUP_FIELD_LABEL moves to lib/popupStyles.js;
  resolveProfileAvatar and useLiveClock are internal-only; getGreeting deleted
  as nothing referenced it.

Companies.jsx keeps one scoped eslint-disable with a reason: load() only
setStates after awaiting its queries, so set-state-in-effect is a false positive
there, and load() is shared with the create/delete handlers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 15:13:01 -04:00
Krao Hasanee 1f52b6e96c fix: sub billing only applies to work an external sub delivered
Report was flagging every unbilled delivered version as "Not Invoiced"
for sub billing, even when a team member delivered it — team work is
billed to the client directly and is never sub-invoiced. Now traces
each version's actual deliverer and shows N/A when it's a team member,
regardless of who delivered earlier versions on the same task.

Also: SubcontractorInvoiceForm no longer masks fetch errors as "No
completed tasks available to invoice" — the real error now shows in
the Completed Tasks panel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 20:15:04 -04:00
Krao Hasanee 2c1ff61b29 fix: popups no longer close on backdrop click, only via explicit close/cancel
Clicking the dimmed area outside a modal (+Task, +Invoice, expense,
review, etc.) accidentally dismissed it and lost in-progress input.
Every popup already has an explicit Cancel/Close/✕ button, so the
backdrop click handler is removed; also drops the now-dead
onClose/blockClose prop plumbing that only existed to support it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 16:45:37 -04:00
Krao Hasanee bf70646a54 fix: sub invoice eligibility follows delivery author, not task assignment
Completed Tasks list scoped by task.assigned_to meant a reassigned task
made the original sub's already-delivered versions permanently unbillable
by anyone. Now scoped by deliveries.sent_by directly, so each version
stays billable by whoever actually delivered it, independent of who
currently owns the task.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 16:04:24 -04:00
Krao Hasanee 0d6ac3666e fix: revert leftover Project→Client mislabels, drop tracked .env.backfill
Rename in 91045a1 was only partially reverted by later commits, leaving
~20 UI spots calling the project entity "Client" while table headers
already said "Project". Reverted all of them plus the /clients/:id
route (now /projects/:id canonical again, nothing was live yet).

Also: stop tracking .env.backfill (had a live admin password/token
committed - rotate those credentials), remove stale pre-redesign
layout.md superseded by REDESIGN-LAYOUT2.md.
2026-07-15 15:14:57 -04:00
Krao Hasanee 567c941151 rename: task column "Project Name" → "Project"; request form field → "Project / Location"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 10:54:05 -04:00
Krao Hasanee 6d8aa6bda4 rename: task "Name" column → "Project Name"; request form field → "Project Name / Location / Title"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 10:51:05 -04:00
Krao Hasanee bcdb177244 fix: report billing counts only sent/paid invoices as Invoiced
Draft invoices and orphaned invoice line items (invoice deleted → null)
no longer count as Invoiced on the billing report, matching what the
Finances page shows as issued. Not-invoiced/invoiced/paid buckets still
sum to total version rows. No-op on current data (no drafts/orphans);
guards future drafts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 10:17:19 -04:00
Krao Hasanee 91045a1897 redesign: rename Project→Client, finance single-card + filters, mobile tasks, sticky headers
- Rename "Project" → "Client" across UI (labels + /projects→/clients routes with redirect); normalize company-meaning labels to "Company"
- Finance tabs: merge dual cards into one per tab, add header column filters (expenses/subs/invoices), overview as 3-section card, move +Expense/+Invoice to card right edge
- Tasks: responsive mobile stats grid, progressive column hiding (min Status+Name+Assigned), sidebar mobile footer (profile/theme/signout)
- Hide Company column for single-company users; +Task shows disabled Company field instead of hiding
- Sticky table headers site-wide with opaque card background
- Pin dev server to port 5173

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 09:39:54 -04:00
Krao Hasanee c91e292066 redesign: Layout2 design-system + dashboard/tasks rework + perf
Design system (Layout2):
- Solid token-driven theme (no animated grid); single-source tokens for bg,
  cards (bg/border/radius), popups, fonts (Inter), and full semantic +
  data-viz color palette. Swept ~168 hardcoded colors to tokens.

Dashboard:
- Reflow: To Do + Calendar row (fixed right rail) over Activity / In-Progress /
  Team Performance; CSS height-match (To Do = Calendar); responsive stacking.
- 'Tasks Not Started' -> 'To Do' card with R#/Assigned columns; compact money fmt.

Tasks page:
- Combined tabs into Tasks + Completed with a Status column.
- Column-header sort + filter (Status, R#/new-vs-revision, Project, Task Type,
  Company) via portaled, scrollable FilterDropdown; removed toolbar filters and
  the projects side panel; headers stay visible when empty; renamed to 'Tasks'.

Perf:
- FK/filter index migration; removed redundant client/external scope waterfall
  (rely on RLS); parallel follow-up queries.

Mobile:
- Responsive grids; mobile topbar = hamburger only, blends with bg, proper offset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 10:08:40 -04:00
Krao Hasanee c5f020cf44 fix: project auto-complete trigger + reject reason must persist before flipping status
- Add sync_project_status trigger so projects flip active<->completed with their tasks; backfill existing rows.
- handleSubmitReject now aborts (and alerts) if the rejection-reason submission fails to save, instead of silently flipping status to in_progress and logging a reason-less rejection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:04:01 -04:00
Krao Hasanee 8eacd86b04 fix: invoice integrity — atomic numbering, safe delete, single create path
- Invoice numbers now come from DB function next_invoice_number()
  (max+1 per year under advisory lock) with a unique index; the old
  row-count method reused numbers after deletes and raced concurrent
  creates, which broke public pay links
- Remove dead standalone TeamCreateInvoice page; the TeamInvoices
  modal is the single create path (page had already drifted)
- Invoice delete now asks for confirmation and only un-bills tasks/
  submissions not billed on another invoice
- Created invoice shows correct "sent" status in list without reload
- Invoice/due dates computed at save time, not module load
- Reopening a paid invoice clears stale stripe_fee
- Stripe webhook markPaid is idempotent (no double receipts)
- subcontractor_invoice_items.version_number column stores billing
  version explicitly; description parsing kept as legacy fallback
- Drop unused buildInvoiceStatusByKey/deriveVersionStatus

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:02:28 -04:00
Krao Hasanee aff3d98929 fix: fourge_error revisions always bill $0 to client
Fourge's own error revisions should never be charged to the client.
getRevisionChargeQuantity now returns 0 for revision_type=fourge_error
regardless of version number. Applied at all 4 invoice picker call sites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 23:09:53 -04:00
Krao Hasanee 1784af909d fix: invoice picker skips review-shadow rows for service_type/pricing
Review-shadow submissions are type=initial with blank service_type; the
picker grabbed the first initial found, hitting a shadow -> null service ->
$0 price. New pickInitialServiceType() skips shadows and prefers a real
service_type. Fixes Harlingen sites showing $0 on invoice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:29:42 -04:00
Krao Hasanee 4ef73d193d fix: invoice delete un-bills only, never overwrites task work status
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:35:45 -05:00
Krao Hasanee 6b9f008fff fix: report Version Status collapses Approved into Completed
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:33:34 -05:00
Krao Hasanee c891f53d2a feat: add Sub Billing column to billing report (sub invoiced/paid per version)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:32:42 -05:00
Krao Hasanee 12c9a1a093 fix: report Version Status shows work state only, billing leak collapsed to approved
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:24:37 -05:00
Krao Hasanee 1cbf06a6c0 fix: revert per-version rows; reset stuck revision tasks in DB
Tasks/projects page: reverted back to one row per task (work status only,
no billing state on that page). Per-version R## tracking lives in invoicing
only (invoice_items, sub invoice form).

DB fix: 7 Public Storage tasks had pending revision versions (R01/R02)
with no delivery but were stuck as 'paid' from old invoice lifecycle sync.
Reset to 'not_started' so they appear in the Revisions tab for work:
  68664, 68666, 68721 (R02), 68808, 68809 (R02), 68810 (R02), 68811 (R02)

Removed unused invoiceItems state/fetch from Tasks + ProjectDetail.
invoiceVersionRules helpers retained for invoicing use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:47:21 -05:00
Krao Hasanee 5b2d5b65a4 feat: per-version R## tracking and billing separation
Each revision (R00, R01, R02) is now an independent trackable unit:

invoiceVersionRules: add deriveVersionStatus, buildInvoiceStatusByKey,
  parseVersionFromItemDescription for per-version status derivation

Tasks.jsx + ProjectDetail.jsx:
  - Fetch invoice_items joined with invoice.status (team only)
  - allRows/rows now flatMap per version (one row per R##)
  - Status derived from: invoice_items > past version > task.status
  - R00 paid shows as paid; R01 in_progress shows separately
  - tr keys use rowKey (taskId:version) to avoid duplicate key warnings

Invoice lifecycle (TeamInvoiceDetail, TeamInvoices, TeamCreateInvoice):
  - Remove task.status sync on invoice paid/sent/created
  - Keep invoiced:true flag for double-billing prevention
  - Task work status (not_started → client_approved) stays work-flow only

ProjectDetail completion bar uses task.status === client_approved only
  (no longer polluted by invoiced/paid status from invoice sync)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:36:36 -05:00
Krao Hasanee da0e5d5c39 fix: guard task status updates on invoice lifecycle transitions
Prevent overwriting task status when task has moved to a new revision
cycle after being invoiced. Now uses .eq('status', guardStatus) to only
update tasks still at the expected prior state:
- invoice creation: only update tasks at client_approved → invoiced
- mark sent: only client_approved → invoiced
- mark paid: only invoiced → paid
- reopen: only paid → client_approved

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:22:18 -05:00
Krao Hasanee 8b0cb31bc6 fix: hide counter on All tab in projects table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:48:16 -04:00
Krao Hasanee 4faf0e80b6 fix: hide counter on All tabs; reduce card-bg alpha ~20%
- Tasks + ProjectDetail: skip count badge when tab.id === 'all'
- card-bg 0.07 → 0.055, card-bg-2 0.12 → 0.09

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:46:02 -04:00
Krao Hasanee 623dcb7983 fix: subcontractor PDF quality, footer on all pages, header gap
- Canvas 3x scale + JPEG 0.97 (matches main invoice quality)
- Footer drawn on every page canvas
- Continuation header gap increased (headerH+28 → +50 with label)
- Pagination pre-calculated before rendering (no mid-render addPage)
- 'PO Date' label → 'Invoice Date'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:17:20 -04:00
Krao Hasanee 48c8fbb7ff style: white-based card-bg 0.07, blur 12px→20px for less transparency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:06:56 -04:00
Krao Hasanee 1dd7ca1922 style: card-bg dark-based alpha to match Safari panel look
rgba(255,255,255,0.06) with backdrop blur renders lighter in Chrome.
rgba(0,0,0,0.35) gives consistent dark panel across all browsers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 16:01:23 -04:00
Krao Hasanee fa8ade83d6 fix: subcontractor PDF title + multi-page support
- 'PURCHASE ORDER' → 'SUBCONTRACTOR INVOICE'
- Items loop now calls addPage() when hitting safeBottom (792-80pt)
- Continuation pages get header with invoice number + '(continued)'
- Summary/Notes sections also page-break if needed
- Footer text updated to match invoice context
- Footer line position dynamic (tracks y), not hardcoded at 704

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 15:58:42 -04:00
Krao Hasanee e450f1b0d1 style: increase card-bg alpha to match Safari rendering
0.02 → 0.06 dark theme, card-bg-2 0.08 → 0.10

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 15:54:59 -04:00
Krao Hasanee e3b2df6e2c fix: restore popupOverlayStyle import in ExternalMyInvoices
Removed during popup refactor but still used by +Invoice form popup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 14:16:11 -04:00
Krao Hasanee e8513125b9 fix: lock company/email fields on sent/paid invoices
Company dropdown and Email To input only editable on draft invoices.
Sent and paid invoices show read-only text values instead.
Applied to both popup (embedded) and standalone page views.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 14:03:58 -04:00
Krao Hasanee 0945b548c1 fix: remove cards from invoice popups, match original flat layout
Replace SubcontractorInvoiceDetailView (card-based) with flat layout
matching the original sub-invoice popup: meta strip + raw table + notes.

- InvoiceDetailPopup: add metaContent/metaActions/metaCols props + export POPUP_FIELD_LABEL
- New InvoicePopupTable: flat sortable 5-col table, no card wrapper
- All 4 popups (client, team invoice, team sub-invoice, external): use
  flat meta strip (4-col grid, borderBottom) and InvoicePopupTable
- Sub-invoice + external: Submitted | Paid/Created | Items | Total
- Client invoice: Date | Due | Status | Total
- Team invoice: Date | Due | Company | Email | Total (+ stripe if paid)
  with Edit Dates button in metaActions slot

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 14:01:59 -04:00
Krao Hasanee 6e4e99c33c feat: unify all invoice popups to shared InvoiceDetailPopup shell
All invoice popup modals (team invoice, sub-invoice team, client invoice,
external sub-invoice) now share InvoiceDetailPopup + SubcontractorInvoiceDetailView.
Same overlay, header, scroll body, footer action row — just different data per role.

- New InvoiceDetailPopup component (overlay shell, header, scrollable body, footer)
- SubcontractorInvoiceDetailView: respects item._inferredType if pre-set
- ClientInvoiceModal: uses shared components, adds sortable headers
- TeamInvoiceDetailPanel embedded: uses shared components, keeps edit-dates/email/company
- TeamInvoices sub-invoice popup: uses shared components, cleans descriptions
- ExternalMyInvoices popup: uses shared components, fixes detailSort vars (was unused)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 13:46:08 -04:00
Krao Hasanee 04e0911e9f fix: crash bugs in TeamInvoices + faster load
Bug fixes:
- TeamInvoices: add useAuth import/currentUser (invoice-create crash)
- TeamInvoices: setChartYear -> setExportYear (year-dropdown crash)
- TeamDashboard: drop redundant setState-in-effect (cascading renders)
- Companies: delete dead _UnusedClientCompanies (illegal hook calls)
- annotate intentional empty PDF-fallback catches

Load speed:
- preconnect/dns-prefetch to Supabase origin
- lazy-load heic-to in Converters: page chunk 2737KB -> 9KB
- split recharts into its own 'charts' vendor chunk

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:59:11 -04:00
Krao Hasanee 85625f4d95 fix: delivery zip downloads from correct deliveries storage bucket
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 23:59:34 -04:00
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
Krao Hasanee 2c387c074f feat: performance dashboard counts per delivery, submission tab shows deliveries, task row hover fix
- Team performance: count each delivery record individually (sent_by + version_number), not per task — R00/R01/R02 by different people all count separately
- Performance uses deliveries table directly, EST timezone bucketing, month-gated tabs
- TaskDetail Submissions tab: reads from deliveries table, shows R## version, sent_by, sent_at
- Submit for Review: writes to deliveries + delivery_files (deliveries bucket), stores version_number
- Revision request popup: uses FileAttachment drag-drop component
- Task table: only Name and Project columns highlight on hover
- deliveries.version_number column added and backfilled

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:21:58 -04:00
Krao Hasanee 96e90561b9 feat: expense/invoice popups, task row hover fix, delivery submissions
- 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>
2026-06-02 21:41:02 -04:00
Krao Hasanee 65f10036d8 fix: divider line under breadcrumbs on folder tab and filesharing 2026-06-02 15:18:03 -04:00
Krao Hasanee aae5214cde fix: context menu via createPortal, renders at viewport coords 2026-06-02 15:15:54 -04:00
Krao Hasanee 36f1179c04 fix: remove toolbar divider border 2026-06-02 14:48:56 -04:00
Krao Hasanee 8b6e2739ae fix: remove up-folder row, context menu flips up when near bottom 2026-06-02 14:48:24 -04:00
Krao Hasanee 144bda426b feat: FileBrowser matches FileSharing — no inline row buttons, context menu copy/cut/paste/rename/delete, selection Download+Delete panel, drag hint 2026-06-02 14:43:05 -04:00
Krao Hasanee 959dd571d2 fix: submissions tab row gap matches revisions (20px) 2026-06-02 14:36:13 -04:00
Krao Hasanee e912f82520 feat: review popup drag-drop attachments, .md styling 2026-06-02 14:33:50 -04:00
Krao Hasanee 894ec323f2 fix: use type=initial for all submissions, single unified type 2026-06-02 14:30:53 -04:00
Krao Hasanee 585fc7500c fix: Submissions tab includes legacy initial-type submissions 2026-06-02 14:29:37 -04:00
Krao Hasanee d222d8b573 feat: review popup notes+attachments, Submissions tab 2026-06-02 14:28:59 -04:00
Krao Hasanee 0d4b1f8096 feat: place-in-review attachment popup, remove-from-review button 2026-06-02 14:25:32 -04:00