backend: per-doctor patient visibility, transfer, provider picker & scoped activity

- Add patients.primary_provider_id (FK to user) + migration; persist it through
  create/update and surface it on the Patient shape.
- Scope patient list/get for the `doctor` role to their own panel (with a
  createdBy fallback for legacy rows); admin/owner/member/reception/viewer keep
  seeing every patient.
- Add POST /api/patients/:fileNumber/transfer to reassign a chart (updates the
  provider link + PCP label, records activity, notifies the clinic).
- Add GET /api/staff/providers (any member) listing clinical-capable members for
  the PCP picker and transfer dialog.
- Scope the activity feed: non-admins see only their own actions; owners/admins
  see the whole clinic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-09 19:51:17 +03:00
parent 4f8793c765
commit 6a0fab97ae
11 changed files with 2697 additions and 8 deletions
+11 -1
View File
@@ -8,9 +8,19 @@ export const activityRouter = Router();
// The audit feed is readable by any clinic member.
activityRouter.use(requireAuth, requireOrg);
// Whether the caller runs the clinic (owner/admin) and may therefore see the
// whole feed. Everyone else is scoped to their own actions.
function isClinicAdmin(memberRole: string | undefined): boolean {
return String(memberRole ?? "")
.split(",")
.map((s) => s.trim())
.some((r) => r === "owner" || r === "admin");
}
activityRouter.get("/", async (req, res, next) => {
try {
res.json(await service.listActivity(req.organizationId!));
const actorId = isClinicAdmin(req.memberRole) ? undefined : req.user!.id;
res.json(await service.listActivity(req.organizationId!, { actorId }));
} catch (err) {
next(err);
}