Taxshila Companion is the operating system for a real, running study library.
One QR code at the front desk turns attendance, study streaks, seats, fees, and push alerts into a single loop, on the web and on Android, from one codebase.
By the numbers#
This isn’t a demo. It runs a real library, in production, every day.
| 100K+ | 100+ | 1 |
| database reads & writes served per day | active members checking in daily | developer, designed, built & shipped end-to-end |
| Web + Android | ~25K | 8 |
| one codebase, two platforms | lines of TypeScript across 144 files | service modules behind a clean API |
The one idea behind it#
There is a single printed QR code taped to the reception desk. A member opens the app, taps “Scan to check in,” points the camera, and they are marked present, a short buzz confirms it. Checking out is one tap. That is the whole loop, and everything else exists to make that loop honest and keep people coming back tomorrow.
The check-in QR stand on the reception desk
The scanner reads the code with the phone’s built-in BarcodeDetector when it exists and falls back to a hand-tuned jsQR decoder on a cropped, downscaled frame otherwise. That fallback is the difference between “works” and “works instantly” on cheap phones and inside an Android WebView.
The public face#
A new person from Google Maps/JustDial meets a landing page built to sell the room, a bold “Focus on Demand” hero, amenities, pricing, reviews and FAQs, in a punchy editorial style that looks nothing like a templated dashboard.
Marketing landing page - Focus on Demand hero
And a house-rules page, “The Code of Conduct”, that turns thirteen library rules into something people actually read: quiet-hours and monitoring badges up top, then plain-spoken, color-coded cards.
House rules - The Code of Conduct
What members get#
A member lives on one screen: a live session timer the moment they check in, their current day streak, hours logged this week, fee status, and their seat.
- 🟩 A GitHub-style contribution grid of study activity, paired with a two-week bar chart.
- 🔥 Streak cards that warm up as the run grows, the color and wording shift the longer the streak gets.
- 📅 Attendance calendar and a fees & history view (payment happens at the desk; the app just tells you exactly where you stand).
- ⚙️ Profile controls, seat/shift change requests and a one-tap notifications switch, plus an alerts inbox.
Member dashboard |
Fees & payment history |
Attendance calendar |
Notifications & alerts |
What admins get#
The admin side is built for someone standing at a desk being asked questions all day.
- A dashboard that leads with the numbers that matter, headcount, revenue, seats in use, recent joins and exits, with virtualized live lists that stay smooth at scale.
- A leaderboard of top weekly hours and longest active streaks, so it’s obvious who’s grinding and who’s about to break a good run. Tap anyone to open their profile.
- Full student management, register, edit, move between shifts and seats, and flag members who’ve gone quiet, with per-shift seat availability.
- Fees, dues, complete payment history, and revenue over time.
- Communication, broadcast or one-to-one alerts, and an inbox for the feedback that comes back.
- One-click CSV import/export of the whole dataset for backups and migrations.
Admin dashboard - headcount, revenue & live activity
Student & shift management
Under the hood#
The parts you’d care about.
Two push channels, picked automatically. Inside the Android app, notifications run through OneSignal (native registration on first launch). On the web, they run through Firebase Cloud Messaging. The app detects which surface it’s on and routes fee reminders, attendance nudges, payment confirmations, and announcements down the right pipe.
A real service layer, not scattered queries. All data access lives behind a dedicated service layer, students, attendance, fees, communication, notifications, so pages read like intent (getMemberStudyStats(...)) instead of raw Firestore calls. Shared aggregation helpers mean the dashboard and the attendance page compute streaks from a single source of truth.
Role-based access, enforced on both sides. admin and member roles, plus a read-only reviewer guest account. The rule lives in one place (src/lib/auth-utils.ts) and is enforced client-side and re-verified server-side (src/lib/api-auth.ts), the client is never trusted alone.
Server-authoritative writes. Sensitive mutations (creating auth users, deleting students, bulk import/export) go through Next.js API routes backed by firebase-admin, with the service account kept server-only. The browser gets React Query caching and optimistic UI; the server gets the final say.
Built to stay fast as it grows. @tanstack/react-virtual keeps long student and attendance lists at 60fps, React Query dedupes and caches reads, and Turbopack keeps the dev loop tight.
Engineering highlights#
- Dual-platform from one Next.js codebase, the same app is the website and, wrapped with Median, the Android app.
- Resilient QR pipeline, native
BarcodeDetectorwith a downscaledjsQRfallback, tuned for low-end hardware. - Type-safe end to end, TypeScript in
strictmode with builds that fail on type or lint errors (noignoreBuildErrorsescape hatch). - Validated everywhere,
react-hook-form+zodschemas guard every form and server boundary. - Tested where it counts, 57 Vitest tests cover the auth rules, CSV import/export, and the fee & attendance service logic.
- CI on every push, GitHub Actions runs lint, type-check, and the full Vitest suite on every push and PR; Husky + lint-staged also lint staged files before they’re committed.
Tech stack#
| Layer | Choices |
|---|---|
| Framework | Next.js 15 (App Router), React 19, TypeScript |
| Backend & data | Firebase, Firestore, Auth, Cloud Functions, firebase-admin on the server |
| UI | Tailwind CSS, Radix UI primitives, Recharts, Lucide |
| State & forms | TanStack Query, TanStack Virtual, react-hook-form, zod |
| Push | OneSignal (Android native) + Firebase Cloud Messaging (web) |
| Scanning | Browser BarcodeDetector + jsQR fallback |
| Mobile | Median Android wrapper |
| Tooling | Vitest, ESLint, Husky, lint-staged, Turbopack, GitHub Actions |
Designed, built, and shipped by Shreyash Rai.
Questions, feedback, or just want to say hi → contact@shreyashrai.com