Session 2026-05-30: tasks/projects unification, code consolidation, file renames

- 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>
This commit is contained in:
Krao Hasanee
2026-05-30 10:19:23 -04:00
parent 6fe7ab1059
commit 13ef1f4ded
30 changed files with 1177 additions and 1258 deletions
+23 -4
View File
@@ -215,10 +215,29 @@ Deno.serve(async (req) => {
}
if (op === 'download') {
const res = await fetch(`${FBQ_URL}/api/raw?source=${SOURCE}&path=${encodeURIComponent(resolvedPath)}`, { headers: fbqHeaders });
const ct = res.headers.get('content-type') ?? 'application/octet-stream';
const cd = res.headers.get('content-disposition') ?? `attachment; filename="${resolvedPath.split('/').pop()}"`;
return new Response(res.body, { status: res.status, headers: { ...cors(origin), 'Content-Type': ct, 'Content-Disposition': cd } });
const rawUrl = `${FBQ_URL}/api/raw?source=${SOURCE}&path=${encodeURIComponent(resolvedPath)}`;
const rawRes = await fetch(rawUrl, { headers: fbqHeaders });
if (rawRes.ok) {
const ct = rawRes.headers.get('content-type') ?? 'application/octet-stream';
const cd = rawRes.headers.get('content-disposition') ?? `attachment; filename="${resolvedPath.split('/').pop()}"`;
return new Response(rawRes.body, { status: rawRes.status, headers: { ...cors(origin), 'Content-Type': ct, 'Content-Disposition': cd } });
}
const rawErrorText = await rawRes.text();
const shouldFallbackToResourcesDownload = rawRes.status === 400 && rawErrorText.toLowerCase().includes('no files specified');
if (!shouldFallbackToResourcesDownload) {
return new Response(rawErrorText, { status: rawRes.status, headers: { ...cors(origin), 'Content-Type': 'application/json' } });
}
const directDownloadUrl = `${FBQ_URL}/api/resources/download?source=${SOURCE}&file=${encodeURIComponent(resolvedPath)}`;
const directRes = await fetch(directDownloadUrl, { headers: fbqHeaders });
const directCt = directRes.headers.get('content-type') ?? 'application/octet-stream';
const directCd = directRes.headers.get('content-disposition') ?? `attachment; filename="${resolvedPath.split('/').pop() ?? 'download'}"`;
return new Response(directRes.body, {
status: directRes.status,
headers: { ...cors(origin), 'Content-Type': directCt, 'Content-Disposition': directCd },
});
}
if (op === 'upload') {