Italian Adventist Church Union
HR & admin platform
The Union’s staff and ministers report their work every month, request time off and expenses, and wait for the right people to approve them. I led the team that built the platform for it: first a first-generation app, then the rewrite that replaced it.
The problem
Two kinds of staff with different rules — employees under the national labour contract and ministers — each filing a monthly report, plus absences, medical expenses, travel invitations and treasury exports.
Every request has to climb a hierarchy before it counts, and at the end of the month treasury and the secretariat need exports they can use without retyping anything.
Constraints
- Italian by default, with Spanish and English for an international staff.
- Sign-in with the organization’s Microsoft accounts; email through Microsoft Graph.
- A team of three developers, with two more rotating in.
- Replace the first-generation app, which I had also started.
What I built
The first generation, Rapportopastori: a NestJS and Next.js monorepo with Microsoft single sign-on, PDF reports rendered with Puppeteer and deploys to AWS EC2 through GitHub Actions. I set up the monorepo and the single sign-on.
Then PDOA, its replacement, on the same architecture as Libretto: Next.js route handlers in front of a NestJS 11 API on Prisma and MySQL, stateless JWT sessions in HttpOnly cookies, Microsoft Entra ID sign-in, files on S3 and email through Graph. I wrote the initial stack, authentication and single sign-on, and led the team through the rest.
The hard parts
Request submitted
Level 3
Approver, level 2
Snapshot stored
Approver, level 1
Snapshot stored
Final approval
Whoever holds the final-approval permission
Timesheet filled
Approved absences become entries in the monthly report
Approval chains from the hierarchy
Every role has a hierarchy level. When someone submits a request, the chain is built by walking up through their managers — a level-3 requester needs approvers at levels 2 and 1 — with protection against cycles, and a final step for whoever holds the final-approval permission. Each step stores a snapshot of its approver and level, so a later reorganization doesn’t rewrite history.
Absences that fill the timesheet
When an absence is approved, the matching vacation, sick-leave or permit entries appear in that person’s monthly report, linked back to the request that created them. Nobody types the same day twice.
Reports that close themselves
Ministers’ monthly reports close automatically a set number of business days after the end of the month — skipping holidays — with configurable reminders before the deadline.
Auditing every page
We audited all 23 pages one by one with a custom Claude Code skill driving Playwright, signing in with locally minted sessions so the single sign-on flow didn’t have to be automated.
Result
- In production for the Union; the first generation has been retired.
- About 520 automated tests, 29 data models and 29 API modules.
- Every page audited for behaviour and permissions.
The code belongs to the client and stays private. Figures are illustrations with sample data; no real records appear on this page.