Role-based employee operations and gamification platform built with Next.js, Supabase, TypeScript, TanStack Query, Zustand, and Tailwind CSS.
This repository powers a multi-role internal portal for:
- superadmin user management
- manager task assignment, verification, badge editing, and badge awarding
- HR reward inventory, redemption workflows, and leaderboard generation
- employee attendance, tasks, notifications, rewards, rankings, and profile management
Jump Links
- Project Summary
- Core Features
- Role Surface Map
- Tech Stack
- Architecture Overview
- Request Flow Patterns
- Repository Structure
- App Route Map
- Domain Model and Supabase Objects
- Environment Variables
- Getting Started
- Available Scripts
- Testing Strategy
- Feature Module Guide
- Storage and Media Conventions
- Important Implementation Notes
- Known Transitional Areas
- Deployment Notes
- Contribution Notes
- License
View Section
WorkHero Barangay Kusina is structured around role-specific operational flows rather than one generic dashboard. The app uses the Next.js App Router, server-side Supabase access, typed server actions, React Query caching, and a small set of Zustand stores for optimistic UI state.
The codebase is not a thin CRUD shell. It includes actual operational logic for:
- session-aware role routing
- welcome-email onboarding with first-login magic links
- attendance time windows and auto-absent behavior
- task lifecycle progression from assigned -> in review -> approved/rejected
- badge requirements and manual badge awards
- reward redemption inventory logic
- generated leaderboard periods with visibility controls
- realtime employee notifications through Supabase channels
View Section
- Role-based access control for
superadmin,manager,hr, andregular - Superadmin user CRUD with search, filters, pagination, profile photos, and welcome email delivery
- Manager task assignment with grouped task cards, per-employee views, optimistic updates, and notification inserts
- Manager verification workflow for reviewing submitted employee work
- Manager badge editor with badge conditions and image upload
- Manager badge assignment for manual badge awards
- HR Mercado inventory management with interval-based availability windows
- HR reward request approval and decline workflow
- HR leaderboard generation, visibility toggling, and top-10 notification fanout
- Employee attendance actions for time in, time out, break start, and break end
- Employee task board with review, redo, claim, and serving flow
- Employee profile and profile media management
- Realtime notifications using Supabase
postgres_changes - Jest unit/integration coverage plus Cypress coverage for superadmin and manager flows
View Section
| Role | Main Routes | Main Responsibilities |
|---|---|---|
superadmin |
/admin/manage |
manage users, update roles and employment data, upload/remove profile photos, send onboarding emails |
manager |
/manager/task-assignment, /manager/task-verification, /manager/task-editor, /manager/badge-assignment, /manager/badge-editor |
assign and update tasks, review work, manage KPI categories, manage badges |
hr |
/hr/reward-requests, /hr/mercado, /hr/leaderboard |
manage reward inventory, process redemption requests, generate and publish leaderboards |
regular |
/employee/dashboard, /employee/attendance, /employee/tasks, /employee/mercado, /employee/leaderboard |
track attendance, complete tasks, claim rewards, view rankings, manage profile |
View Section
| Area | Tools in use |
|---|---|
| Framework | Next.js 16.2.1, React 19.2.4, App Router |
| Language | TypeScript with strict mode |
| Backend platform | Supabase Auth, Postgres, Storage, Realtime, Edge Functions |
| Client data layer | TanStack Query v5 |
| Client state | Zustand |
| Validation | Zod |
| Forms | React Hook Form |
| Styling | Tailwind CSS v4, custom global CSS, shadcn/ui and Radix primitives |
| Notifications | Sonner toasts plus persisted Supabase Notification rows |
| Nodemailer SMTP flow using Brevo-compatible credentials | |
| Unit/integration tests | Jest + ts-jest |
| E2E tests | Cypress |
| Tooling | ESLint, Prettier, Husky, lint-staged |
View Section
At a high level, the repository is organized by product domain and then by execution layer.
UI components and route shells
-> TanStack query/mutation hooks
-> action handlers
-> server actions
-> Supabase tables/views/RPCs/storage
The codebase also contains one important variation:
superadmin UI
-> action handlers
-> server actions
-> internal route handlers under src/app/admin/tools/*
-> Supabase admin/public operations
That means the repo uses both of these server-side styles today:
- direct server actions
- server actions that still call internal route handlers for some superadmin work
View Section
This is the dominant pattern in manager, hr, employee, and shared profile/auth work.
component
-> action-handler
-> safeAction(...)
-> server action
-> Supabase
Responsibilities by layer:
| Layer | Folder | Responsibility |
|---|---|---|
| UI | src/components/*, src/app/* |
rendering, form interaction, route composition |
| Query/mutation hooks | src/hooks/tanstack/* |
cache keys, invalidation, background fetching, optimistic flow coordination |
| Action handlers | src/action-handlers/* |
user-facing error handling, toast messages, shaping return values |
| Server actions | src/actions/* |
auth checks, Supabase access, domain logic, validation |
| Utilities | src/lib/*, src/utils/* |
cross-cutting helpers such as notifications, date handling, role routing, safe-action |
Superadmin management is slightly different and still routes some work through internal route handlers:
component
-> useAddUser / useEditUser / useDeleteUser
-> src/action-handlers/superadmin/users.ts
-> src/actions/superadmin/users.ts
-> src/app/admin/tools/*/route.ts
-> Supabase admin client and public tables
This is important when tracing bugs in:
- user creation
- password changes
- user deletion
- user filtering
- welcome email and magic-link generation
This repo uses both TanStack Query and Zustand, but for different jobs:
- TanStack Query is the source of truth for server-backed cached data
- Zustand is used for local UI state and optimistic updates
Current optimistic stores include:
src/store/adminUserStore.tssrc/store/managerAssignmentStore.tssrc/store/managerBadgeAssignmentStore.tssrc/store/managerBadgeEditorStore.tssrc/store/hrRedemptionRequestStore.tssrc/store/employee/employeeTasksStore.ts
The app intentionally uses different Supabase clients by runtime:
| Client | File | Use case |
|---|---|---|
| server client | src/lib/supabase/server.ts |
server components and server actions with cookies/session |
| browser client | src/lib/supabase/client.ts |
client components and realtime subscriptions |
| admin client | src/lib/supabase/admin.ts |
privileged server-only operations using service role |
| request/session refresh | src/lib/supabase/proxy.ts and root proxy.ts |
session refresh and unauthenticated redirects |
View Section
The repository structure is documented visually below so the top-level nesting and feature boundaries are easy to scan.
Top-level layout:
.
|-- src/
| |-- app/ # App Router routes, layouts, route handlers, error/loading boundaries
| |-- actions/ # Server actions grouped by domain
| |-- action-handlers/ # Client-side wrappers around server actions
| |-- components/ # Feature and shared UI
| |-- hooks/ # Reusable hooks, including TanStack hooks
| |-- lib/ # Supabase helpers, providers, mail, shared utilities
| |-- store/ # Zustand stores for optimistic and UI state
| |-- types/ # Domain types
| |-- utils/ # Shared utility functions
| `-- zod/ # Validation schemas
|-- supabase/
| |-- migrations/ # database migrations
| |-- functions/ # edge function code
| `-- test/ # SQL schema/roles/data for test environments
|-- __tests___/ # Jest tests and mock data
|-- cypress/ # Cypress tests, tasks, fixtures, support
|-- scripts/ # small utility scripts
|-- public/ # static assets
|-- next.config.ts
|-- proxy.ts
`-- README.md
Detailed source nesting:
src/
|-- app/
| |-- admin/
| |-- auth/
| |-- employee/
| |-- hr/
| |-- manager/
| |-- profile/[userid]/
| |-- error.tsx
| |-- error/page.tsx
| |-- not-found.tsx
| |-- layout.tsx
| `-- page.tsx
|-- actions/
| |-- employee/
| |-- employees/
| |-- hr/
| |-- manager/
| |-- shared/
| `-- superadmin/
|-- action-handlers/
| |-- employee/
| |-- hr/
| |-- manager/
| |-- shared/
| `-- superadmin/
|-- hooks/
| |-- tanstack/
| | |-- mutations/
| | `-- queries/
| |-- hr/
| `-- shared utility hooks
|-- components/
| |-- admin/
| |-- attendance/
| |-- auth/
| |-- employee/
| |-- error/
| |-- homepage/
| |-- hr/
| |-- manager/
| |-- notifications/
| |-- profile/
| |-- shared/
| |-- sidebar/
| |-- smtp/
| `-- ui/
|-- lib/
| |-- providers/
| |-- smtp/
| |-- supabase/
| |-- users/
| `-- utils/
|-- store/
| |-- employee/
| `-- root stores
|-- types/
| |-- admin/
| |-- employee/
| |-- hr/
| |-- manager/
| `-- shared/
`-- zod/schemas/
View Section
| Route | Purpose |
|---|---|
/ |
marketing/home landing page |
/auth/login |
employee login page |
/auth/adminlogin |
superadmin login page |
/auth/confirm |
Supabase OTP/magic-link confirmation route |
/auth/welcome |
welcome/registration placeholder page |
/error |
query-string-driven error page |
| Route | Purpose |
|---|---|
/admin/manage |
user management UI |
/admin/tools/adduser |
internal route handler used by server actions |
/admin/tools/changepw |
internal route handler used by server actions |
/admin/tools/deluser |
internal route handler used by server actions |
/admin/tools/filter |
internal route handler used by server actions |
| Route | Purpose |
|---|---|
/manager/dashboard |
lightweight manager landing page |
/manager/task-assignment |
assign/update/unassign tasks |
/manager/task-verification |
approve or reject submitted tasks |
/manager/task-editor |
manage KPI categories |
/manager/badge-assignment |
manually award badges |
/manager/badge-editor |
create/edit/delete badges and conditions |
| Route | Purpose |
|---|---|
/hr/reward-requests |
review redemption requests |
/hr/mercado |
manage reward inventory |
/hr/leaderboard |
generate and publish rankings |
| Route | Purpose |
|---|---|
/employee |
employee surface entry point |
/employee/dashboard |
gamified employee landing screen |
/employee/attendance |
attendance actions and timeline |
/employee/tasks |
task board |
/employee/mercado |
reward marketplace |
/employee/leaderboard |
visible leaderboard view |
| Route | Purpose |
|---|---|
/profile/[userid] |
authenticated user profile view restricted to the session user |
View Section
The SQL under supabase/test/schema.sql and the baseline migration shows these core tables:
UserRoleAttendanceLogKPICategoryKPITaskBadgesBadgeRequirementsUserBadgesRewardRewardRequestNotificationLevelDishesRankingPeriodRankingEntry
The app relies on several database-level helpers and views, including:
user_attributestask_info_viewbadge_conditions_viewuser_collected_badges_viewranking_leaderboard_viewrpc_update_user_name_and_assign_roleget_leaderboard_as_ofevaluate_badgesRPC, also exposed through an edge function
| Domain | Key relationships used by the app |
|---|---|
| users and roles | User.role_id -> Role.id, auth claims also carry app_metadata.user_role |
| tasking | KPICategory defines the task template, KPITask stores assignment instances |
| badges | Badges is the main badge record, BadgeRequirements stores conditions, UserBadges stores awards |
| rewards | Reward is an inventory item, RewardRequest stores redemption attempts and approvals |
| rankings | RankingPeriod stores generated periods, RankingEntry stores per-user ranked results |
| notifications | Notification rows are inserted server-side and consumed through queries plus realtime |
View Section
Variables referenced directly in code:
| Variable | Required | Used for |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
yes | browser/server Supabase URL, image remote pattern construction |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY |
yes | browser and server session-based Supabase access |
SUPABASE_URL |
yes for admin flows | service-role client base URL |
SUPABASE_SERVICE_ROLE_KEY |
yes for admin flows | privileged server-only operations |
NEXT_PUBLIC_BASE_URL |
strongly recommended | internal superadmin route calls and first-login magic links |
SMTP_HOST |
required for welcome emails | SMTP hostname |
SMTP_PORT |
required for welcome emails | SMTP port |
SMTP_USER |
required for welcome emails | SMTP username |
SMTP_KEY |
required for welcome emails | SMTP password/key |
SMTP_FROM |
optional | sender identity |
SMTP_ALLOW_SELF_SIGNED |
optional | allow self-signed TLS certificates for SMTP transport |
SUPPORT_EMAIL |
optional | contact address inserted into onboarding emails |
Example .env.local:
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your_publishable_key
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
NEXT_PUBLIC_BASE_URL=http://localhost:3008
SMTP_HOST=smtp-relay.brevo.com
SMTP_PORT=587
SMTP_USER=your_smtp_user
SMTP_KEY=your_smtp_key
SMTP_FROM="WorkHero <noreply@example.com>"
SMTP_ALLOW_SELF_SIGNED=false
SUPPORT_EMAIL=support@example.comNotes:
SUPABASE_SERVICE_ROLE_KEYmust remain server-only.NEXT_PUBLIC_BASE_URLmatters in this repo because some server actions still call internal route handlers and because welcome emails generate absolute links.- SMTP is optional only if you are not exercising superadmin onboarding email flows.
View Section
- Node.js 20 LTS recommended
- npm
- a Supabase project with matching schema/storage/RPCs
- optional: Supabase CLI if you want to work with migrations or edge functions locally
npm installnpm run devDefault local URL:
http://localhost:3008
npm run typecheck
npm run lint
npm run formatnpm run build
npm run startView Section
From package.json:
| Script | Command | Purpose |
|---|---|---|
npm run dev |
next dev -p 3008 |
start local dev server |
npm test |
cross-env NODE_ENV=test jest --runInBand --verbose |
run all Jest tests |
npm run test:unit |
... __tests___/unit |
run unit tests only |
npm run test:integration |
... __tests___/integration |
run integration tests only |
npm run test:watch |
jest --watch --verbose |
watch mode |
npm run build |
cross-env NODE_ENV=production next build |
production build |
npm run start |
next start |
start built app |
npm run lint |
eslint . |
lint |
npm run format |
prettier --write . |
format |
npm run format:check |
prettier --check . |
format check |
npm run typecheck |
tsc --noEmit |
TypeScript verification |
npm run prepare |
husky |
install hooks |
npm run smtp:verify |
node scripts/smtp-verify.js |
verify SMTP transport |
npm run cypress:open |
cross-env NODE_ENV=test cypress open |
launch Cypress UI |
There is currently no cypress:run script in package.json. For headless Cypress runs, use:
npx cypress runView Section
Jest is configured through:
jest.config.tsjest.setup.tstsconfig.test.json
Current characteristics:
- Node test environment
ts-jesttransform fortsandtsx@/*path alias mapping tosrc/*.env.testloading in setupnext/navigationredirect mocked in tests
Current Jest folder layout:
__tests___/
|-- unit/
| |-- employee/
| |-- hr/
| |-- manager/
| |-- shared/
| `-- superadmin/
|-- integration/
| |-- employee/
| |-- hr/
| |-- manager/
| |-- shared/
| `-- superadmin/
|-- mockData/
`-- utils/
Important note: the repository test folder is named __tests___ with three trailing underscores, not the more common __tests__.
Cypress is configured in cypress.config.ts and currently includes:
- direct Supabase login tasks
- direct service-role test helpers for creating/deleting users and badges
- manager E2E specs
- superadmin E2E specs
Current E2E spec folders:
cypress/e2e/
|-- manager/
`-- superadmin/
View Section
Key files:
src/actions/superadmin/users.tssrc/action-handlers/superadmin/users.tssrc/hooks/tanstack/queries/userQueries.tssrc/hooks/tanstack/mutations/userMutations.tssrc/store/adminUserStore.tssrc/app/admin/manage/page.tsx
Highlights:
- paginated user search/filter/sort
- profile photo upload and delete
- first-login magic link creation
- welcome email delivery
- internal route-handler bridge for add/filter/delete/password flows
Key domains:
- task assignment:
src/actions/manager/assignments.ts,src/actions/manager/assigned-tasks.ts - task verification:
src/actions/manager/verification.ts - task editor:
src/actions/manager/editor.ts - badge editor:
src/actions/manager/badges.ts - badge assignment:
src/actions/manager/badge-assignment.ts
Highlights:
- grouped assigned-task views by task and by employee
- optimistic assignment editing through Zustand + TanStack Query
- verification approval/rejection flow
- badge condition authoring with task, user, and attendance requirement types
- manual badge awards with point updates and notifications
Key domains:
- rewards:
src/actions/hr/rewards.ts - redemptions:
src/actions/hr/redemptions.ts - leaderboard:
src/actions/hr/leaderboard.ts
Highlights:
- Mercado inventory management
- interval-based reward availability windows
- redemption approval/decline workflow
- generated weekly/monthly/yearly ranking periods
- visibility toggles for employee-facing leaderboards
- top-10 notifications when a ranking becomes visible
Key domains:
- attendance:
src/actions/employee/attendance.ts - tasks:
src/actions/employee/tasks.ts - stats:
src/actions/employee/stats.ts - redemptions:
src/actions/employee/redemptions.ts - notifications:
src/actions/employee/notifications.ts - badges:
src/actions/employee/badges.ts
Highlights:
- attendance time windows using Manila timezone defaults
- task lifecycle with submission, review, claim, redo, and continuation
- XP and level progression
- reward claiming and Mercado browsing
- realtime notifications
- leaderboard viewing and profile surfaces
Important shared files:
src/actions/shared/auth.tssrc/actions/shared/profile.tssrc/actions/shared/sidebar.tssrc/lib/utils/safe-action.tssrc/lib/notifications.tssrc/lib/smtp/welcome-email.tssrc/lib/users/email-availability.tssrc/hooks/useRealtimeNotifications.ts
These modules cover:
- server-side route protection
- session-aware redirects
- profile management
- reusable server action wrapping
- notification inserts
- welcome-email templating
- duplicate-email detection across public user data and auth users
View Section
The code expects these Supabase storage buckets:
| Bucket | Purpose | Example object path |
|---|---|---|
employees |
user profile pictures | {userId}/profile.png |
badges |
badge images | {badgeId}/badge.png |
reward |
reward images | {rewardId}/profile.png |
user-files |
uploaded user documents | {userId}/{filename} |
Media-related conventions:
- image uploads are validated in code to
5MBmax in several places - many public URLs add
?t=${Date.now()}for cache-busting next.config.tsderives an image remote pattern fromNEXT_PUBLIC_SUPABASE_URL
View Section
- Server-side protection lives in
src/actions/shared/auth.ts. - Layouts for
admin,manager,hr, andemployeeall protect routes on the server before rendering. proxy.tsrefreshes Supabase session state and redirects unauthenticated traffic.
Attendance defaults live in src/lib/attendance-config.ts:
- time in:
07:00 - late after:
07:30 - time out:
17:30 - overtime after:
17:30 - auto timeout:
23:59 - break duration:
01:00 - timezone:
Asia/Manila
Attendance actions handle:
- same-day log lookup
- auto-absent insertion when no time-in happens by close
- over-break detection
- auto-timeout/auto-absent behavior
- timeline generation for employee UI
- persisted notifications are inserted through
src/lib/notifications.ts - employee UI reads them through TanStack queries
- realtime toasts subscribe to
Notificationinserts withuseRealtimeNotifications
When a superadmin creates a user:
- the app validates email availability across both public and auth records
- the auth user and public user row are created
- a first-login magic link is generated
- an SMTP welcome email is sent using
src/components/smtp/email.html
HR leaderboard generation:
- computes a completed period
- calls the
get_leaderboard_as_ofRPC - persists
RankingPeriodandRankingEntry - exposes results through
ranking_leaderboard_view - optionally notifies visible top-10 users when the ranking is published
View Section
These are worth knowing before refactoring:
- Superadmin server actions still call internal route handlers under
src/app/admin/tools/*. src/lib/auth-context.tsxis a lightweight client auth wrapper, but server-side auth protection is the real access-control source.- There are older compatibility-style files alongside more domain-scoped ones, for example
src/actions/hr.tsvssrc/actions/hr/*andsrc/action-handlers/profile.tsvssrc/action-handlers/shared/profile.ts. - The repository contains a few debug/test-only UI helpers under employee and attendance component trees.
- The root session redirect logic in
proxy.tsand the server role redirect helpers should be treated carefully because they affect every authenticated route.
View Section
next.config.ts currently does a few important things:
- enables server action body size limit of
5mb - sets the Turbopack root
- configures remote image patterns from the Supabase host at runtime
This repository uses proxy.ts rather than a traditional middleware.ts file. It:
- refreshes Supabase auth state on requests
- skips static assets and image optimization routes
- redirects unauthenticated requests away from protected pages
The repo contains:
supabase/functions/evaluate-badges/index.ts
This edge function calls the evaluate_badges RPC and is intended for scheduled or manual badge evaluation.
View Section
When extending the codebase, these conventions match the current architecture best:
- put server-only domain logic in
src/actions/<domain>/ - wrap server actions in
src/action-handlers/<domain>/for toasts and client-safe return shaping - expose React Query access from
src/hooks/tanstack/queriesandsrc/hooks/tanstack/mutations - use Zod schemas from
src/zod/schemasfor user input validation - keep service-role logic on the server only
- prefer the appropriate Supabase client for the runtime context
- use the existing query-key factories when invalidating data
- use existing Zustand optimistic helpers instead of ad hoc local cache mutations
Before opening a PR, the usual local checks are:
npm run typecheck
npm run lint
npm testIf your change touches SMTP onboarding, reward flows, or task assignment/verification, also test the relevant UI path locally because those flows span several layers.
View Section
This project is licensed under the MIT License. See LICENSE.