Multi-tenant ticket management built with Next.js 16, Drizzle ORM, and PostgreSQL.
- Multi-tenant data isolation by company
- Role-based permissions
- Tickets, clients, and service catalog
- Dashboard metrics and server-generated ticket invoices (PDF)
- Mobile-friendly UI (responsive lists, touch targets, accessibility)
- Installable PWA (
start_url→/dashboard; internet required, no offline mode in v1) - UI with shadcn/ui and Tailwind CSS
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack dev on port 3069) |
| Database | PostgreSQL + Drizzle ORM |
| Auth | NextAuth v5 (Credentials, JWT sessions) |
| Forms | React Hook Form + Zod |
| Tests | Jest, React Testing Library, Playwright (e2e/) |
- Node.js 20.9+
- PostgreSQL 14+ (local, Docker, or hosted)
- npm
git clone https://github.com/Jorg3L3on/zigzag.git
cd zigzag
npm installCopy .env.example to .env (or .env.local) and set at least:
DATABASE_URL— PostgreSQL URL (database namezigzagin examples)DIRECT_URL— optional locally; use Neon direct URL in production for migrationsNEXTAUTH_URL—http://localhost:3069for local devNEXTAUTH_SECRETorAUTH_SECRET— random secret (openssl rand -base64 32)
npm run db:generate # after schema changes in src/db/schema.ts
npm run db:migrate # apply migrations
npm run seed # optional seed data
npm run dev # http://localhost:3069| Command | Purpose |
|---|---|
npm run dev |
Dev server (Turbopack, port 3069) |
npm run build |
Production build |
npm start |
Production server (port 3069) |
npm run lint |
ESLint |
npm run db:generate |
Generate SQL migrations |
npm run db:migrate |
Apply migrations locally |
npm run migrate:deploy |
Apply migrations in production (DIRECT_URL when set) |
npm run db:studio |
Drizzle Studio |
npm run seed |
Seed via scripts/seed.ts |
npm run db:prod:setup |
migrate:deploy + seed (first-time prod only) |
npm test |
Jest unit/integration tests |
npm run test:watch |
Jest watch mode |
npm run test:coverage |
Coverage report |
npm run test:e2e |
Playwright E2E tests |
src/
├── actions/ # Server Actions (primary mutations)
├── app/ # App Router pages and API routes
├── components/ # UI components
├── contexts/ # React context (e.g. company selection)
├── db/ # Drizzle schema
├── lib/ # Auth, DB client, errors, security
├── proxy.ts # Route protection for / and /dashboard/**
└── types/
drizzle/ # SQL migrations
scripts/seed.ts # Seed script
docs/ # Production runbook and ops notes
Contributor and architecture details: AGENTS.md. PRD → issues → PR workflow: docs/agents/workflow.md · CONTRIBUTING.md.
Optional RAG tooling over internal docs: rag/README.md (npm run rag:index, rag:search, rag:ask).
npm test
npm run test:e2eCI-style Jest: npm test -- --runInBand.
Playwright runs against Desktop Chrome (e2e/). The smoke suite includes an unauthenticated redirect test; the dashboard test runs only when credentials are set:
export E2E_EMAIL="your-user@example.com"
export E2E_PASSWORD="your-password"
npm run test:e2eA dedicated mobile device project (iPhone / Pixel) is planned — see tasks/prd-mobile-testing.md.
Install ZigZag on a phone or tablet for quick access from the home screen. After install, the app opens on the Dashboard (/dashboard). If your session expired, sign in again.
Internet required: There is no offline mode in the current version. You need a network connection to load and save data (no service worker or offline sync).
Instalar en iPhone (Safari)
- Abre ZigZag en Safari.
- Toca Compartir → Añadir a pantalla de inicio.
- Confirma el nombre ZigZag y toca Añadir.
Instalar en Android (Chrome)
- Abre ZigZag en Chrome.
- Toca el menú → Instalar app o Añadir a pantalla de inicio (según el dispositivo).
- Confirma la instalación.
Tras instalar, la app abre en el Panel (/dashboard). Si la sesión expiró, inicia sesión de nuevo.
Probar en la red local (opcional): con npm run dev en el puerto 3069, abre http://<tu-ip>:3069 desde el teléfono en la misma Wi‑Fi.
Install on iPhone (Safari)
- Open ZigZag in Safari.
- Tap Share → Add to Home Screen.
- Confirm the name ZigZag and tap Add.
Install on Android (Chrome)
- Open ZigZag in Chrome.
- Tap the menu → Install app or Add to home screen (wording varies by device).
- Confirm the install.
After install, the app opens on the Dashboard (/dashboard). Sign in again if your session expired.
Test on your LAN (optional): with npm run dev on port 3069, open http://<your-ip>:3069 from your phone on the same Wi‑Fi.
| Platform | Supported |
|---|---|
| iOS | Safari (last 2 major versions) |
| Android | Chrome (last 2 major versions) |
| Desktop | Chrome, Edge (current versions) |
English: Ticket PDFs are generated on the server in v1. If download fails on iOS, retry on Wi‑Fi or contact your administrator.
Español: Las facturas PDF se generan en el servidor en v1. Si la descarga falla en iOS, reintenta con Wi‑Fi o contacta al administrador.
Use .env.production.example for production variables. Full checklist, rollback, and incidents: docs/production-runbook.md.
Summary:
- Set
DATABASE_URL(pooled),DIRECT_URL(direct),NEXTAUTH_URL, andNEXTAUTH_SECRET/AUTH_SECRETin Vercel. - Run
npm run migrate:deployagainst the target database before or as part of first deploy. - Optionally run
npm run db:prod:setuponce for seed data. - Deploy;
vercel.jsonusesnpm run vercel-build(next build). - Smoke-test
/api/health, login, and a clients/services/tickets flow.
Pre-deploy locally: npm run lint, npm test, npm run build.
| Issue | Check |
|---|---|
| DB connection | DATABASE_URL, Postgres running, database zigzag exists |
| Auth / redirects | NEXTAUTH_URL matches deployed URL; secret is set |
| Build | rm -rf .next and reinstall node_modules if needed |
| Migrations | Run npm run migrate:deploy with DIRECT_URL set |
MIT — see LICENSE. Copyright (c) 2026 Jorge León.