尚未检测到更新。GitHub 发布版本后会显示在这里。
Real-time dispatch & end-of-day closing platform for a last-mile delivery station: Next.js, TypeScript, Firebase.
翻译准备中
正在加载完整 README。
查看完整 README ↗A real-time dispatch platform for a last-mile delivery station. It replaces a paper tracking sheet and WhatsApp updates with one flow from opening dispatch to end-of-day closing.
Live: closing-swfl.vercel.app. Access needs a station key, so the app itself is private to the team.
Stack: Next.js 16 · TypeScript · React 19 · Tailwind CSS v4 · Firebase (Firestore, Admin SDK) · Vercel
Driver closing sheet for an Amazon delivery station. Replaces two things: the dispatcher's WhatsApp posts, and the closer's handwritten paper grid.
Three roles, one codebase:
/dispatch — laptop. Roster, per-driver ETA/returns entry, All Returning./closer — phone. Live arrivals, van checks, End Day → PDF./hr — laptop. Tonight's roster, and one pasted time edit per driver.
Reads everything, writes one map on the session and nothing else.| Concern | Choice |
|---|---|
| Framework | Next.js 16 (App Router), TypeScript, React 19 |
| Styling | Tailwind CSS v4 |
| Database / realtime | Firebase Firestore |
| Privileged server work | Firebase Admin SDK in route handlers |
@react-pdf/renderer, Node runtime | |
| Spreadsheet | exceljs, server-side |
| Hosting | Vercel |
npm install
cp .env.example .env.local # then fill it in
npm run dev
Open http://localhost:3000.
Without .env.local the login screen tells you what is missing instead of
pretending to work.
To check the closer screen from your phone on the same Wi-Fi, run
npm run dev -- -H 0.0.0.0 and visit http://<your-laptop-ip>:3000/closer.
src/
app/
layout.tsx fonts, design tokens, AuthProvider
page.tsx login — one key, four role buttons
api/login/route.ts server-side key check → Firebase custom token
(dispatch)/ desktop chrome
layout.tsx
dispatch/page.tsx
(closer)/ phone chrome, safe-area aware
layout.tsx
closer/page.tsx
(hr)/ desk chrome, narrow, no nav
layout.tsx
hr/page.tsx
components/
RoleGate.tsx keeps the wrong role off the wrong screen
lib/
constants.ts station timezone, metrics, roles, date helpers
timeEdits.ts HR's edits, keyed by driver — read by both screens
auth/AuthProvider.tsx session state + role claim
firebase/client.ts browser SDK (public config)
firebase/admin.ts Admin SDK — server only, never imported by a client
firestore.rules the actual security boundary
(dispatch), (closer) and (hr) are route groups: they give each role its
own persistent chrome without adding a segment to the URL.
src/app/globals.css; components use
bg-surface, text-ink-muted, border-overdue-line — never a raw hex.tnum class so digits line up.STATION_TIMEZONE. The device
clock is never trusted, and clock-out uses serverTimestamp().stationNightKey(), which
counts anything before 7am as still belonging to the previous night. A van
stamped in at 00:40 lands on the sheet it belongs to, and a sheet nobody sent
is still on screen for whoever opens the app at six. Note that eta.ts reads
a bare hour against its own EVENING_AFTER_HOUR (4) — an ETA of "5" is five
in the afternoon, and that must not move with the rollover.status: 'closed'./api/login compares it to
APP_ACCESS_KEY and mints a custom token carrying a role claim;
firestore.rules enforces what each role may read and write.jose overridepackage.json pins jose to v5. Do not remove it without testing a deployed
build.
firebase-admin 14 depends on jwks-rsa 4, which does require('jose') from
CommonJS while asking for jose 6 — and jose 6 is ESM-only. Node throws
ERR_REQUIRE_ESM the moment firebase-admin/auth is imported, so every login
500s. It does not reproduce on a recent local Node, which supports
require(esm); it only shows up on the deployment runtime. jose 5 still
ships a CommonJS build and has the four functions jwks-rsa calls.
To reproduce the old failure on purpose:
node --no-experimental-require-module -e "require('firebase-admin/auth')".
firestore.rules is not applied by deploying the app. After editing it, paste
it into Firebase console → Firestore Database → Rules → Publish.
| Phase | State | |
|---|---|---|
| 0 | Scaffold and deploy | ✅ |
| 1 | Firebase wiring and auth | ✅ |
| 2 | Driver database and roster entry | ✅ |
| 3 | Dispatcher entry form | ✅ |
| 4 | Closer live list | — |
| 5 | Closer detail sheet | — |
| 6 | All Returning + returns spreadsheet | — |
| 7 | End Day, PDF, archive | — |
| 8 | One-time closer keys | — |
| 9 | Offline and polish | — |