Production-ready full-stack web application for managing a karate dojo — students, attendance, payments, insurance policies and equipment inventory.
![]() Login Page |
![]() Mobile View |
![]() Sensei Dashboard |
![]() Karateca Dashboard |
![]() Student Management |
![]() Payment Tracking |
Budokan SKIF is a production-ready management system built for a real karate dojo and actively used by its sensei and students. It supports two authenticated roles — Sensei (administrator) and Karateca (student) — each with a dedicated dashboard and scoped feature set. The system covers the full operational lifecycle of a dojo: student enrollment, kyu/dan progression tracking, attendance management, monthly fee control, insurance policy monitoring with expiry alerts, and equipment inventory.
|
Sensei (Admin)
|
Karateca (Student)
|
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | React 19 + Vite 6 | UI framework and build tool |
| Frontend | Tailwind CSS 3 | Utility-first styling with custom dojo design tokens |
| Frontend | Zustand 5 | Lightweight auth state management with localStorage persistence |
| Frontend | React Hook Form 7 + Zod 4 | Form handling with runtime schema validation |
| Frontend | React Router v7 | Client-side routing with protected route guards |
| Frontend | Lucide React | Consistent icon system |
| Frontend | @fontsource/inter | Self-hosted Inter typeface |
| Backend | Node.js + Express 5 | REST API server |
| Backend | Prisma ORM 5 | Type-safe database client with migration support |
| Backend | PostgreSQL | Relational database |
| Backend | JSON Web Tokens | Stateless authentication |
| Backend | bcryptjs | Secure password hashing |
| Testing | Jest + Supertest | Backend unit and integration tests with mocked Prisma |
| DevOps | Vercel | Frontend hosting with SPA rewrite rules |
| DevOps | Railway | Backend API and PostgreSQL hosting |
budokan-app/
├── backend/
│ ├── src/
│ │ ├── controllers/ # Route handlers (auth, karateca, asistencia, mensualidad, poliza, inventario, dashboard, config)
│ │ ├── middlewares/ # JWT auth guard, role-based access control
│ │ ├── routes/ # Express routers
│ │ ├── tests/ # Jest + Supertest test suites
│ │ └── utils/ # JWT helpers, seed scripts
│ ├── docs/
│ │ └── API.md # Full API reference
│ └── prisma/
│ └── schema.prisma # 6-model relational schema
└── frontend/
└── src/
├── components/
│ ├── karatecas/ # KaratecasTable, KaratecasCards, NuevoKaratecaModal, EditarKaratecaModal
│ └── ui/ # Design system: Input, Button, Badge, Card, Modal, Skeleton, EmptyState
├── lib/ # API client (axios), kyu utilities, date utilities
├── pages/
│ ├── karateca/ # Dashboard, Asistencia, Mensualidades, Poliza, Tecnico
│ └── sensei/ # Dashboard, Karatecas, Asistencia, Mensualidades, Polizas, Inventario
└── store/ # Zustand auth store
6 Prisma models with the following relationships:
User(1) → (1)KaratecaKarateca(1) → (N)Asistencia,Mensualidad,PolizaUser(1) → (N)Asistencia(asregistradoPor— the sensei who recorded attendance)
Enums: Rol (SENSEI | KARATECA), CategoriaInventario (PROTECCION | INSTRUMENTO), EstadoInventario (BUENO | REGULAR | MALO)
Notable patterns: soft delete on Karateca (activo boolean), computed poliza estado field (activa | por_vencer | vencida) derived at query time, mesInicioMensualidades for per-student fee tracking start date.
A custom component library was built from scratch under src/components/ui/:
- Design tokens: three brand colors (negro
#111111, rojo#CC0000, dorado#C9A84C) extended into Tailwind config asdojo.negro/dojo.rojo/dojo.dorado/dojo.surface/dojo.subtle - Typography: Inter (self-hosted via
@fontsource/inter, weights 400/500/600/700) set as Tailwind's default sans font - Components:
Input(labeled, error state),Button(primary/secondary/ghost, sm/md),Badge(gold/success/danger/warning/muted),Card(hover lift animation when clickable),Modal(focus trap, Escape-to-close, mobile full-screen),Skeleton+SkeletonCard(pulse animation),EmptyState(icon + title + description + optional action) - All components use only Tailwind utility classes — zero inline styles
- JWT Bearer authentication on all protected endpoints
- Role-based access control enforced at the middleware level — SENSEI-only routes reject KARATECA tokens with 403
- Passwords hashed with bcryptjs (10 rounds) — never returned in API responses
- Password reset flow requires SENSEI authorization with target
userId - Frontend protected routes redirect unauthenticated users to
/login - Auth state hydrated from localStorage with a
hydratedflag to prevent flash of unauthenticated content
- Monorepo structure: frontend and backend in a single repo for easier portfolio review and coordinated deploys
- Prisma over raw SQL: type-safe queries, automatic migrations, and generated TypeScript types reduce runtime errors
- Zustand over Redux: auth state is simple (user + token); Zustand's minimal API avoids boilerplate without sacrificing reactivity
- Zod on both ends: frontend forms validate with the same schema constraints as backend — consistent error messages without duplication
- Timezone-safe date parsing: a custom
dateUtils.jsparses ISO strings by slicingYYYY-MM-DDand constructingDate(y, m-1, d)to avoid UTC→local day-shift bugs (UTC-5 Colombia) - SPA routing:
vercel.jsonrewrite rule sends all routes toindex.html, fixing 404 on browser refresh - Component split:
Karatecas.jsx(originally 617 lines) was split into 4 focused components —KaratecasTable,KaratecasCards,NuevoKaratecaModal,EditarKaratecaModal— each under 150 lines
- Node.js 18+
- PostgreSQL database
- Git
git clone https://github.com/dacq7/budokan-app.git
cd budokan-appBackend:
cd backend
cp .env.example .env
npm install
npx prisma migrate deploy
npm run devFrontend:
cd frontend
cp .env.example .env
npm install
npm run devBackend (.env):
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
JWT_SECRET |
Secret key for JWT signing |
PORT |
Server port (default: 3001) |
Frontend (.env):
| Variable | Description |
|---|---|
VITE_API_URL |
Backend API base URL |
cd backend
node src/utils/seedDemo.js| Role | Document | Password |
|---|---|---|
| Sensei | 11111111 | demo2025 |
| Karateca | 22222222 | demo2025 |
Full API reference: backend/docs/API.md
9 route groups — Auth, Karatecas, Asistencia, Mensualidades, Pólizas, Inventario, Dashboard, Config, Health. All protected endpoints require Authorization: Bearer <token>. Role enforcement: SENSEI for write operations, both roles for reads.
cd backend
npm test26 tests across 2 suites (auth.test.js and karateca.test.js). Prisma is fully mocked — tests run without a database connection. Covers: input validation, authentication flows, role-protected routes, Prisma error code mapping (P2002 → 409 Conflict, P2025 → 404 Not Found), and transaction rollback scenarios.
MIT
Built by Diego Correa — Veridis Dev · El Carmen de Viboral, Antioquia, Colombia





