Festival shift planning tool for small teams (25–35 people). Admins build a shift schedule, team members vote on preferences, an allocation algorithm assigns shifts fairly, and the result is published as a printable PNG or PDF.
Admin
- Build shift schedules on a lane-based drag-and-drop calendar (React Flow)
- Create shift templates with lane types, colors, and capacity
- Run a 3-phase allocation algorithm with configurable weights and attribute rules
- Preview proposed assignments before committing
- Manually reassign shifts (dropouts, late additions)
- Audit log with rollback
- Export schedule as PNG or PDF table
Users
- Claim a pseudonymous identity (alias + avatar)
- Vote WANT / DONT_WANT on visible shifts
- See assigned shifts and preference outcomes in a two-section list
- Request shift swaps
Event lifecycle: PLANNING → OPEN_FOR_PREFERENCES → ASSIGNING → FINALIZED → COMPLETED. Each status unlocks specific capabilities and locks others.
| Layer | Technology |
|---|---|
| Framework | Next.js 15.5.14 (App Router) |
| UI | React 19, Tailwind CSS v4 |
| Canvas | @xyflow/react 12.10 (React Flow) |
| ORM | Prisma 5.22 |
| Database | PostgreSQL (Docker) |
| Validation | Zod 3.25 |
| Testing | Vitest 4.1.1 |
| Export | html-to-image 1.11.13 |
Prerequisites: Node.js 20+, Docker
# 1. Start database only (compose also defines an app service on :43000; use local Next below)
docker-compose up -d db
# 2. Install dependencies
npm install
# 3. Apply migrations and seed
npx prisma migrate dev
npm run db:seed
# 4. Start dev server
npm run dev
# → http://localhost:3000Admin login: see .env.local for ADMIN_PASSWORD.
app/
├── api/ # REST API routes (Route Layer)
├── admin/ # Admin pages (setup, schedule, team, audit)
├── app/ # User pages (identity, calendar)
└── globals.css # Tailwind v4 design tokens
components/
├── features/ # Domain components (LaneCalendar, TemplatePalette, ...)
├── layout/ # Header, sidebars
└── ui/ # Atoms (Button, Badge, GlassPanel, ...)
lib/
├── algorithm/ # Allocation engine (optimizer, scorer, validator, rule-validator)
├── repositories/ # Data access layer (Prisma abstraction)
├── services/ # Business logic layer
├── hooks/ # React context hooks
└── validations/ # Zod schemas
prisma/
├── schema.prisma # Database schema (source of truth)
└── seed.ts # Test data
tests/
├── unit/ # Unit tests (repositories, services, algorithm)
└── integration.test.ts
npm run dev # Dev server on :3000
npm run build # Production build
npm test # Run all unit tests (Vitest)
npm run db:studio # Prisma Studio (database GUI)
npm run db:migrate # Apply pending migrations
npm run db:seed # Seed test data
npm run db:generate # Regenerate Prisma clientThe latest image is published to GHCR: ghcr.io/salkinii/shiftaware:latest
On startup the container runs prisma migrate deploy && node server.js — migrations apply automatically. It needs a PostgreSQL database and three environment variables.
Minimal docker-compose.yml:
services:
db:
image: postgres:16
environment:
POSTGRES_USER: shiftaware
POSTGRES_PASSWORD: changeme
POSTGRES_DB: shiftaware
volumes:
- pgdata:/var/lib/postgresql/data
app:
image: ghcr.io/salkinii/shiftaware:latest
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://shiftaware:changeme@db:5432/shiftaware
SESSION_SECRET: '<32-byte hex — node -e "console.log(require(''crypto'').randomBytes(32).toString(''hex''))">'
ADMIN_PASSWORD: your-admin-password
depends_on:
- db
volumes:
pgdata:docker-compose up -d
# → http://localhost:3000Admin login: navigate to /admin and use ADMIN_PASSWORD.
| Doc | What's in it |
|---|---|
| docs/ARCHITECTURE.md | Three-layer architecture, event lifecycle, data flow, file structure |
| docs/DESIGN.md | Design tokens, coordinate system, component patterns |
| docs/API.md | All API endpoints with params and response shapes |
| docs/ALGORITHM.md | Allocation engine deep-dive |
| docs/PROJECT-OVERVIEW.md | Navigation index and concept glossary |