Alif Najmi
← Back to Alif Notes · Side project

A dashboard 150 teachers actually open

6 November 2026 · 5 min read

SMK Subang's data manager ran the whole school through a page of seven links and one huge spreadsheet called SEMAKAN. About 150 teachers clicked through to Google Sheets to find what was due, deadlines got missed, and chasing who hadn't submitted meant scrolling through 150 names. I built them one dashboard instead. It's in use every day, mostly on phones.

The public dashboard with open tasks, countdowns and submission progress
The public page: open tasks with countdowns and progress like "133/150 dihantar". Sample data.

Don't make anyone change how they work

The easiest way to get 150 busy teachers to use something new is to not ask them to change anything. So the spreadsheets stay the source of truth. People keep editing the same Google Sheets and Forms they always have, every view links back to its sheet, and the dashboard just reads them every hour.

How the SMK Subang dashboard fits togetherThe browser talks to one Vercel project with a Next.js site and an Express API. The API reads and writes Supabase. Apps Script in the school account reads Google Sheets and Forms and pushes to the API every hour.One Vercel project/*/api/*readsPOST /api/sync · hourlyBrowsermostly phonesweb · Next.jspublic page + Panel Adminapi · Expresschecks the role on every requestSupabasePostgres · RLS · AuthGoogle Sheetsand FormsApps Scriptin the school's account
One Vercel project with two services, Supabase behind it, and Apps Script pushing the sheets in every hour. Swipe sideways to see all of it.

Who sees what

The public page is open to anyone. Names of teachers who haven't submitted, leave data and the audit log are only for the principal and the data manager.

PublicPrincipalData manager
Dashboard, counts, charts✅✅✅
Who hasn't submitted, leave summary❌✅✅
Sync status, audit log❌✅✅
Edit links and announcements❌❌✅
Manage accounts and roles❌❌✅
Panel Admin overview with pending submissions, data problems and recent activity
Panel Admin: who hasn't submitted (with a one-tap "copy for WhatsApp"), data problems to fix, and an activity log.

Privacy, at every layer

Teachers' names and leave records are personal data under Malaysia's PDPA, so I didn't want privacy to depend on one check somewhere. Each layer assumes the one before it might fail.

Privacy in four layersFour layers in order: the sync sends only totals to public tables; the database has no policy on private tables; the API checks the role on every request; the page is noindex and never logs tokens.1 · Synconly totals reachpublic tables2 · Databaseprivate tables haveno RLS policy at all3 · APIrole checked onevery request4 · Pagenoindex · no-referrerno tokens in analytics
Each layer protects the data on its own. Swipe sideways to see all of it.

Two details I'm happy with. Teachers check their own leave through a personal secret link instead of typing an IC number, which is easy to guess. And new admins never get a password from me: they get a one-time link, valid for 24 hours, sent through Telegram, the app the teachers actually use. The database stores only a hash of it.

Messy data, fixed at the source

Real data is messy. One score column had 9,286 in a 0 to 100 field, and some tasks were still marked open weeks after their deadline. Instead of quietly cleaning it, the sync flags each problem with its sheet and row so the data manager fixes it where it came from, and leaves bad scores out of the averages until then. Words like "belum respons" count as pending, and teachers on leave aren't counted as missing.

Built for phones first

The dashboard on a phoneThe account menu as a bottom sheet on a phoneThe login screen on a phone
On a phone: the dashboard, the account menu as a bottom sheet, and login. It installs to the home screen with the school crest.

Most teachers open it on a phone, and early on the header alone took 600 pixels before a single task showed. Version by version it got tighter:

VersionWhat changed
2.14One-time set-password links instead of passwords pasted into chats
2.15Instant view switching, card layouts instead of sideways-scrolling tables, a compact phone header
2.16The school crest as the home-screen icon
2.17One account menu at the top right for both the public page and Panel Admin

Every change ships with a short "what's new" note in plain Malay, inside the app.

The dashboard in dark mode
Dark mode, with hand-drawn SVG charts that work in both themes.

What I took away

  • The user guide shaped the UI. Writing the teachers' guide in plain Malay shaped the screens as much as the code did.
  • Keeping the spreadsheets made adoption easy. Nobody had to learn a new place to type things.
  • Privacy is a design decision, not a feature. It's much easier to build in on day one than to bolt on.

The public copy of the code, with school details removed, is on GitHub.

← Nine charts before one screen From coursework to something people could trust →