A full-stack internal support ticketing system with role-based access control, audit logging, and a modern UI. Built with React, TypeScript, Express.js, PostgreSQL, and Prisma.
| Role | Access |
|---|---|
| Employee | Create and manage own tickets |
| Support | View, update, and resolve all tickets |
| Admin | Full access + user management + audit logs |
Frontend
- React 18 + TypeScript
- Vite
- Tailwind CSS + shadcn/ui
- React Router v6
- Axios
- React Hook Form + Zod
- Sonner (toasts)
- date-fns
Backend
- Node.js + Express.js + TypeScript
- Prisma ORM
- PostgreSQL
- JWT authentication
- bcrypt
Run npx prisma db seed to populate the database with demo accounts and sample data.
| Role | Password | |
|---|---|---|
| Admin | admin@ticketsys.com | Demo123! |
| Support | support@ticketsys.com | Demo123! |
| Employee | employee@ticketsys.com | Demo123! |
Requires Docker and Docker Compose installed.
git clone <your-repo-url>
cd ticketing-system
cp .env.example .env
# Edit .env with your values
docker compose up --build| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend | http://localhost:8080 |
| PostgreSQL | localhost:5432 |
The seed script runs automatically on first boot.
- Node.js v18+
- PostgreSQL running locally
git clone <your-repo-url>
cd ticketing-systemcd backend
npm install
cp .env.example .envEdit backend/.env:
DATABASE_URL=postgresql://postgres:yourpassword@localhost:5432/ticketsys
JWT_SECRET=your_jwt_secret_here
PORT=8080npx prisma migrate dev --name init
npx prisma db seed
npm run devBackend runs on http://localhost:8080
cd ../frontend
npm install
cp .env.example .envEdit frontend/.env:
VITE_API_URL=http://localhost:8080npm run devFrontend runs on http://localhost:5173
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/auth/register | Register new employee | Public |
| POST | /api/auth/login | Login | Public |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/tickets | List tickets (scoped by role) | Employee+ |
| GET | /api/tickets/stats/dashboard | Dashboard stats | Employee+ |
| GET | /api/tickets/:id | Get ticket detail | Employee+ |
| POST | /api/tickets | Create ticket | Employee+ |
| PATCH | /api/tickets/:id | Update ticket | Employee+ |
| DELETE | /api/tickets/:id | Delete ticket | Employee+ |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/users | List all users | Admin |
| POST | /api/users/support | Create support account | Admin |
| PATCH | /api/users/:id/role | Update user role | Admin |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/audit-logs | Get paginated audit logs | Admin |
ticketing-system/
├── docker-compose.yml
├── .env.example
├── frontend/
│ ├── Dockerfile
│ ├── nginx.conf
│ └── src/
│ ├── components/
│ │ ├── layout/ # Sidebar, Header, DashboardLayout
│ │ ├── tickets/ # CreateTicketDrawer
│ │ └── ui/ # ConfirmModal, ThemeToggle
│ ├── context/ # AuthContext, ThemeContext
│ ├── pages/ # Dashboard, Tickets, TicketDetail, Users, AuditLogs
│ ├── routes/ # AppRoutes, ProtectedRoute, RoleRoute
│ └── services/ # auth.service.ts
└── backend/
├── Dockerfile
├── prisma/
│ ├── schema.prisma
│ └── seed.ts
└── src/
├── controllers/ # auth, ticket, user, auditLog, dashboard
├── middlewares/ # auth, role
├── routes/ # auth, ticket, user, auditLog
├── services/ # audit.service
└── utils/ # createAuditLog
- JWT authentication with role-based access control
- Ticket management with priority and status tracking
- Role-scoped ticket visibility (employees see only their own)
- Status-based ticket sections (Active / Resolved & Closed)
- Sliding drawer for create and edit ticket flows
- Confirmation modal for destructive actions
- Real-time dashboard stats
- Admin user management with role assignment
- Paginated audit log with action filtering
- Collapsible sidebar with role-based navigation
- Dark mode with persistent preference
- Seed script for instant demo setup
| Variable | Description |
|---|---|
| POSTGRES_USER | PostgreSQL username |
| POSTGRES_PASSWORD | PostgreSQL password |
| POSTGRES_DB | Database name |
| JWT_SECRET | JWT signing secret |
| Variable | Description |
|---|---|
| DATABASE_URL | PostgreSQL connection string |
| JWT_SECRET | JWT signing secret |
| PORT | Server port (default: 8080) |
| Variable | Description |
|---|---|
| VITE_API_URL | Backend base URL |
- PostgreSQL was chosen over MySQL for native ENUM support and stronger data integrity
- All
.envfiles are gitignored - The seed script uses
upsertso it's safe to run multiple times