Sheet 6 of 6 · Siera · Exploded assembly
Siera
Orientation platform for UPNVJ
Specification
- Role
- Frontend Contributor
- Team
- Veterantech team · I shipped frontend features
- Stack
- React 19, TypeScript, TanStack Router, TanStack Query, TanStack Form, Better Auth, Tailwind CSS 4, shadcn/ui, Base UI, Phosphor Icons, Zod, nuqs, date-fns, xlsx, Sentry, Cloudflare Pages
- Repos
- private · the app is login-gated
- Drawn by
- Praffi Ramadhani
- Date
- 2026-07-04
- Status
- active
- Sheet
- 6 / 6
Overview
Siera is the platform Veterantech built for PKKMB, the new student orientation program at Universitas Pembangunan Nasional “Veteran” Jakarta (UPNVJ).
I joined the team as a frontend contributor. Work arrived as Linear issues, shipped as pull requests, and went through code review before merge.
The problem
“Four roles share one application, admin, mentor, mentee, and stakeholder, each with its own routes and permissions.”
It carries the whole program: events and attendance, mentoring groups, assignments, announcements, violations, graduation, and fundraising.
A1 · Part 1 of 6 my part
attendance monitoring
A list of attendance-enabled sessions with present/total counts and completion. Opening one gives a summary header and attendance donut, a mentee tab with group rollups and the attendee list, a group drilldown route, and a staff tab with division rollups.
Coordinators can correct a staff record to present, absent, or excused behind a confirm dialog.
A2 · Part 2 of 6 my part
merch orders & reports
Order management with searchable group filters, sort and filter drawers, status badges, a detail drawer with the item breakdown, preview-only payment proof, and confirm/cancel flows that capture a cancel reason.
Reports add stat and revenue cards, an overview chart, a per-mentee table, and Excel export.
A3 · Part 3 of 6 the team's
the web app
The team’s React app, with an area for each of the four roles. Beyond my two modules, I shipped smaller work across it.
Also mine, smaller
- Editing published PPM tasks. Saving a published task sends a diff-based payload, so the PATCH request only includes fields that actually changed. Only task type and submission method stay locked; reviewer and scoring fields remain editable.
- Graduation view for stakeholders. Refactored into a summary list with a detail drawer.
- New task-submission file kinds. Mentees can submit CSV, Word (.doc, .docx) and video (.mp4, .mov, .webm, .mkv) files.
- Admin profile editing. Photo upload with cropping, and a save button that stays disabled until a field actually changes.
- Shared UI fixes. Drawers that left a stuck backdrop behind, alert dialog footers that broke on narrow viewports, and an error path that hid the reason a violation report failed.
A4 · Part 4 of 6 the team's
generated API client
The frontend talks to the API through a client generated from its spec. My modules read their display labels and status maps from that client’s enums.
A5 – A6 · Parts 5 to 6 the team's
the API
The team’s backend. It is drawn faintly on the sheet because none of it is my work.
- A5 the API Hono · Better Auth · Drizzle
- A6 data & jobs Postgres · BullMQ worker on Redis · S3 uploads
Key technical decisions
-
Attendance state lives in the URL.
Search text, status filters, ordering, and page number all round-trip through query params, not component state. A filtered group view is a link, so a coordinator can send it to someone else and they open the same rows.
-
Labels come from the generated client.
The UI reads its display labels and status maps from the generated API client’s enums. Nothing retypes that vocabulary by hand, so when the backend adds an attendance status the label appears instead of a blank cell.
-
A drawer timer that cleans up after itself.
The drawer work started as a bug report and ended as a small hook. Two components closed a drawer by animating it out and then unmounting it, and each kept its own 500ms timer so the unmount still ran if the animation event never fired. Neither cleared that timer when the component went away. I moved the logic into
useDrawerCloseSafety, which owns the timer and clears it on unmount. A separate fix keeps a closed backdrop inert while it is still mounted, covered by unit tests indrawer-overlay.test.ts.
Current status
Live.
Siera is live at siera.veterantech.id. The repository is private and the app is login-gated, so there is no public demo of the admin screens described here.