Sheet 6 of 6 · Siera · Exploded assembly

Siera

Orientation platform for UPNVJ

adminmentormenteestakeholderFRONTEND CONTRIBUTION · FAINT = THE TEAM'S WORK, GREEN = MY PARTALSO MINE, SMALLER: PPM task edits · graduation view refactor ·new task-submission file types · admin profile editing · shareddrawer and dialog fixes
The Siera architecture as an exploded drawing. Each part links to its section.

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

live app

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

  1. 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.

  2. 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.

  3. 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 in drawer-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.