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>
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>
- Unified Tasks page: 3 role render blocks → 1, normalized row shape, single renderRow/sort/tabs
- Fixed role scoping: external = all tasks in member projects, client = all tasks in company projects
- Fixed client bug: was scoped by submitted_by, now company→project→tasks
- Aligned dashboard client scope to match tasks page
- Hot tasks dashboard: now filters to active statuses only (not completed/invoiced/paid)
- Removed Projects.jsx (dead), fixed /project/ → /projects/ links
- Renamed all page files to match folder path (Team*, External*, Client* prefixes)
- Renamed: RequestsPage→Tasks, Settings→Profile, CompaniesPage→Companies, ProjectDetailPage→ProjectDetail
- Dropped dead fetches from Tasks page (invoices, invoice_items, subcontractor_invoice_items)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DashboardPage, Projects, RequestsPage, ProjectDetailPage, RequestDetail: each now handles team/external/client in one file via role flags — removed 10 old role-specific sub-files
- Layout: client Company nav link goes directly to /company/:id when user has a single company
- FileBrowser: replace emoji icons with colored extension-text badges (square); folder icon stays 📁; Adobe/Figma/design-tool colors for design files
- CompaniesPage: merged team Companies + client company routing (single-company redirect, multi-company list)
- FileSharing: integrated FileBrowser component
- Removed: seafile API + lib, old ServerStatus, TaskDetail, role-split page files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove recursive directory size calculations (single Seafile API call per list)
- Remove 'Used in this location' usage display
- Fix move using v2 per-type endpoints instead of broken batch endpoint
- Send entry type from frontend for correct move routing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>