- Go 43.8%
- JavaScript 38.9%
- CSS 13.2%
- HTML 4.1%
| .memsearch | ||
| cmd/tempo | ||
| db | ||
| internal/tempo | ||
| public | ||
| .gitignore | ||
| go.mod | ||
| logic.md | ||
| README.md | ||
Tempo
The goal of this project is to build a new timekeeping utility for internal teams.
Backend
Tempo uses a Go backend built only on the standard library. net/http serves the JSON API and static files from public/. The app is expected to run behind a TLS-terminating proxy, so it serves cleartext HTTP/1.1 plus cleartext HTTP/2 to the proxy through http.Protocols.SetUnencryptedHTTP2. It does not load certificates or serve internal TLS.
The temporary data store is tempo-data.json. It is an atomic JSON file store behind the tempo.Store interface so a SQL implementation can replace it later without changing handlers. If the file does not exist, the server seeds demo shifts, users, attendance history, and requests.
Shift scheduling is built around admin-controlled shift groups. A group owns the timezone, schedule kind, and cycle anchor; plans define concrete day/night/office times; segments define on/off windows such as 3 on, 4 off, 4 on, 3 off. Users still receive one concrete shift_id, so current assignment, scoping, and future SQL replacement stay simple.
Session cookies are Secure by default because external traffic should arrive through HTTPS at the proxy. For direct local browser testing without a proxy, run with TEMPO_SECURE_COOKIES=false.
If any authenticated API call fails with 401 Unauthorized, the server expires the session cookie and the frontend redirects to the login page. This keeps stale sessions from leaving the dashboard, admin page, or event stream in a broken loading state.
Realtime UI updates use a standard-library SSE bus at /api/events?topics=.... Mutating handlers publish topic events by metro, and the frontend subscribes with EventSource instead of polling for change timestamps.
On SIGINT or SIGTERM, the server drains through http.Server.Shutdown, closes active SSE streams, and only falls back to closing active connections if the shutdown window is exceeded.
Role overview widgets use transparent surfaces with clear headings, balanced spacing, and small translucent rows. Dense employee tempo charts, forms, and full calendar panels keep card boundaries when the structure improves readability. Tooltips use a tiny shared frontend helper that clamps to the viewport and draws a black arrow back to the source element.
Technician view
Technicians get a compact seven-day "This week" strip before the tempo chart. It shows current and upcoming work, scheduled-off days, PTO, OT, pending requests, late/out notices, and missing check-in state without replacing the 30-day tempo history. The request tracker lists pending requests first, then active/upcoming approvals, then recent denials, capped to keep the dashboard compact, with human copy like "Eight hours of OT on May 2" and "On vacation from May 2 to May 5." Consecutive OT requests with the same status, hours, and reason display as one readable range until a single day changes status, and OT requests can include an optional short reason for manager review. The header calendar icon opens /calendar.html, a 31-day personal schedule calendar with work/off/PTO/OT/attention counts and a selected-day detail panel.
Senior view
Seniors keep the technician tools and get compact read-only team widgets above the team tempo cards: a today coverage snapshot, an exception queue for red/yellow roster issues, and a live recent-changes feed driven by the SSE bus. Team data stays scoped to the senior's concrete shift, and schedule-off days do not count as missing coverage. Their /calendar.html view blends the personal 31-day calendar with the same read-only roster calendar managers use.
Admin view
Super admins get an overview on the admin panel with metro health, data quality, recent audit activity, and system status. The system card reports store type/path, last write time, SSE event history/client counts, cookie mode, and session lifetime. Shift management includes compact shift-group presets for 7a-7p 3/4/4/3, 7p-7a 3/4/4/3, and 9-5 weekday schedules; concrete shifts stay assignable to a group/plan from the shift table.
Manager view
Managers get a compact "today roster" summary and operation widgets for coverage risk, approval inbox, attendance trends, and OT utilization before the "Your team's tempo" cards. Coverage risk and OT utilization badges show "Next 7 days" by default and can be clicked to rotate 7, 14, and 30 day windows. Coverage risk uses compact calendar cells, with expected/core/OT/planned-off/scheduled-off stats listed vertically in black hover/focus tooltips. Links to the dedicated /manager-calendar.html roster view appear from the summary and header calendar icon; the calendar view header uses the dashboard icon to return to /dashboard.html. Summary status counts show visible labels plus tooltips listing techs under each color, and attention items render as a compact vertical list with bullet color matching status. The dedicated view shows today plus the next 13 days. Today shows gray/blue/green/yellow/red roster counts; future days show planned gray counts and only show approved-OT blue when scheduled OT exists, so green/yellow/red remain a record of current or past attendance outcomes. The selected day lists scheduled core employees plus OT visitors. Off-cycle core employees are excluded from expected and missing counts; approved time-off removes scheduled core employees from the expected roster and shows them as planned off for the day. Approved OT visitors show an OT badge and blue status; pending OT visitors show OT?.
Status colors:
| Color | Meaning |
|---|---|
| Gray | Scheduled core employee expected, or future pending OT request |
| Blue | Approved OT visitor expected, or OT visitor checked in |
| Green | Non-OT employee checked in |
| Yellow | Late notice, pending OT, or attention needed before grace window expires |
| Red | Out, missing after grace window, or late without notice |
Run
go run ./cmd/tempo
Useful environment variables:
| Variable | Default | Purpose |
|---|---|---|
PORT |
3000 |
Port used when TEMPO_ADDR is unset |
TEMPO_ADDR |
:3000 |
Full listen address |
TEMPO_DATA_PATH |
tempo-data.json |
JSON store path |
TEMPO_PUBLIC_DIR |
public |
Static asset directory |
SESSION_EXPIRES_HOURS |
24 |
Session lifetime |
TEMPO_SECURE_COOKIES |
true |
Mark session cookies Secure; set false only for direct local HTTP |
Test
go test ./...
Demo accounts
The seeded data includes these accounts:
| Role | Password | |
|---|---|---|
| Super admin | admin@tempo.local |
admin123 |
| SAMA manager | john.manager@tempo.local |
password123 |
| SAMA senior | sarah.senior@tempo.local |
password123 |
| Technician | mike.tech@tempo.local |
password123 |