Two problems reported by the user:
1. File downloads broken on the lab side.
The link in JobFilesPanel pointed straight at Appwrite's
/storage/.../view URL. Storage permissions are scoped to the job's two
teams, but the browser only has a session cookie for our app domain,
not for db.kovaksoft.com — so the cross-origin request hit Appwrite
as a guest and 401'd.
New /api/jobs/[jobId]/files/[fileId]/download route. requireTenant()
first, then verify the caller's tenant is one of (clinicTenantId,
labTenantId) on the parent job, then storage.getFileDownload via the
admin SDK and stream the buffer back with Content-Disposition:
attachment so the browser saves it under the original filename.
listJobFiles now hands out that relative URL instead of the Appwrite
one — same anchor in the panel, just routed through us.
2. Saves and edits feel slow whenever a notification is involved.
Every mutation was awaiting logAudit, createNotification and
syncFinanceForJob in sequence. None of these need to block the user
response — audit is best-effort logging, notifications are async UX,
and the finance sync is idempotent and re-runs on the next mutation
anyway. Switched all 46 call sites across the action modules to
void-fire-and-forget (matching the pattern we already used in
clinic-pricing-actions). Net effect: each mutation drops ~3 sequential
Appwrite roundtrips before the server action returns.