Client Management System for Social Work
Situation
In social family support (SPFH), specialists work directly with families in often complex life situations. Before this CMS, documenting appointments, reports and support plans was heavily fragmented. Working hours, appointments and travel time were captured by hand, and reconciling delivered hours against each family’s approved weekly quota relied on error-prone spreadsheets. Admins had no real-time view of actual team workload, vacation overlaps or who was currently on duty. Sensitive case documents were sometimes shared locally or over insecure channels, and there was no strict, system-level separation between administrative rights and the write access specialists had to case files.
Task
The goal was a GDPR-compliant, performant and intuitive full-stack platform that minimizes administrative load and guarantees data security. Functionally it needed full client management including tandem support (two specialists sharing a case), complete tamper-proof time tracking with overtime calculation, and automated per-family quota utilization with fair distribution of cancelled time. Technically the hard parts were secure authentication for sensitive case files, scalable file handling that avoids server overhead on large documents, and end-to-end type safety so that API requests match the backend types at runtime and those types are mirrored one-to-one in the React frontend.
Action
Authentication uses stateful JWTs: short-lived access tokens (15 min, kept in frontend memory) and 7-day refresh tokens sent as httpOnly cookies, with only the SHA-256 hash stored in MongoDB. Refresh-token rotation with reuse detection treats any replay of an old token as theft and immediately revokes the whole token family. Express middlewares (protect, adminOnly) enforce not just route access but field-level write guards — a specialist may edit their family’s contact data but not its status, hours quota or assignment. The MongoDB layer is a referenced model (User, Client, Appointment) with sub-documents for tightly coupled data like calendar-event RSVPs. Business logic runs on aggregations: utilization is progressPercent = round((totalMinutes / (weeklyHoursQuota × 60)) × 100); cancelled appointments are credited at a flat 90 minutes (capped at 2 per family/month) and split fairly across tandem specialists at 45 minutes each with a 1.3 overhead factor; overtime is recomputed on every clock-out from the ISO-week session total against the user’s weekly target. The React 19 / Tailwind v4 frontend gives specialists personalized KPI strips and ring charts, and admins a live dashboard of who is clocked in plus team-workload tables. Documents upload directly to S3 via a presigned PUT URL, so the API never proxies the file; metadata is persisted only after the upload is confirmed. A single WeekView drives both personal and team calendars via a colorMode prop, and every frontend type mirrors the backend’s Zod shapes to rule out inconsistent payloads.
Result
Automated appointment totalling plus integrated time tracking removes several hours of manual transcription and cross-checking per specialist each week. Admins see bottlenecks and quota overruns instantly through visual indicators — filling hour rings instead of overflowing bars — and vacation requests are validated with a live preview of the working days consumed, cutting planning mistakes. The combination of Zod runtime validation, hashed token storage, restrictive S3 access (download links expire after one hour) and a complete audit trail on retroactive time-entry edits delivers strong data security and auditability for the provider.
Key Takeaways
Leaning on the native Node ecosystem paid off: native package.json#imports for path aliases and Node 22 features (--watch, --env-file) improved developer experience and cut dependencies. A small custom fetch wrapper that transparently refreshes the token on a 401 and retries the request proved more maintainable than Axios and kept the bundle small. For much higher case loads, the next step would be moving the current time-driven MongoDB aggregations toward an event-driven architecture (e.g. Change Streams) to compute overtime and utilization metrics asynchronously in the background.