mirror of
https://github.com/mosh-hamedani/helpdesk.git
synced 2026-05-21 11:58:19 +02:00
5.8 KiB
5.8 KiB
****# Helpdesk - AI-Powered Ticket Management System
Project Overview
A ticket management system that uses AI to classify, respond to, and route support tickets. See project-scope.md for full requirements and implementation-plan.md for phased task breakdown.
Tech Stack
- Frontend: React + TypeScript + Vite (port 5173) + shadcn/ui
- Backend: Express + TypeScript + Bun (port 3000)
- Database: PostgreSQL with Prisma ORM
- AI: OpenAI GPT-5 Nano via Vercel AI SDK (
@ai-sdk/openai) - Auth: Better Auth (email/password, database sessions)
Project Structure
/core - Shared code (Zod schemas, types) — Bun workspace package
/client - React frontend (Vite)
/server - Express backend
/e2e - Playwright E2E tests
Development
# Start server
cd server && bun run dev
# Start client
cd client && bun run dev
The client proxies /api/* requests to the server via Vite config (target is configurable via VITE_API_URL env var, defaults to http://localhost:3000).
Key Conventions
- Use Bun as the runtime and package manager (not npm/yarn)
- Use TypeScript throughout
- Use context7 MCP server to fetch up-to-date documentation for libraries
- Use shadcn/ui components for all UI (import from
@/components/ui/*) - Use the
@/path alias for imports (maps to./src/) - Use shadcn's semantic color tokens (e.g.
bg-background,text-muted-foreground,text-destructive) instead of hardcoded Tailwind colors - Organize server endpoints into Express
Routermodules underserver/src/routes/(e.g.routes/users.ts), mounted inindex.ts - Define shared Zod schemas in the
corepackage undercore/schemas/(e.g.core/schemas/users.ts) and import them in both client and server (e.g.import { createUserSchema } from "core/schemas/users") - Use Zod for validation (import from
zod/v4) - Validate request bodies in route handlers using the shared
validatehelper (import { validate } from "../lib/validate"). It takes a Zod schema, the request body, and theresobject — returns parsed data ornull(after sending a 400 response). - Parse and validate numeric ID route params with the shared
parseIdhelper (import { parseId } from "../lib/parse-id"). Returns a positive integer ornullfor invalid values. - Do not wrap async route handlers in try/catch — Express 5 automatically catches rejected promises
- Use the shared
Roleconstant instead of hardcoded"admin"/"agent"strings (import fromcore/constants/role.ts, e.g.import { Role } from "core/constants/role.ts") - Define shared constants and domain types in
core/constants/as union types (notenum— the client haserasableSyntaxOnlyenabled). Useas constobjects when runtime access is needed (e.g.Role), and plain union types when only type checking is needed (e.g.type TicketStatus = "open" | "resolved" | "closed"). - Use React Hook Form with Zod resolver for client-side form validation (
useForm+zodResolverfrom@hookform/resolvers/zod) - Use Axios for HTTP requests (not
fetch) - Use TanStack React Query (
useQuery,useMutation) for server state management (notuseEffect+useState) - Use the
ErrorAlertcomponent for error messages (import ErrorAlert from "@/components/ErrorAlert"). For static messages:<ErrorAlert message="Failed to load data" />. For mutation/query errors with automatic Axios message extraction:<ErrorAlert error={mutation.error} fallback="Failed to save" />. - Use the
ErrorMessagecomponent for field validation errors (import ErrorMessage from "@/components/ErrorMessage"):{errors.name && <ErrorMessage message={errors.name.message} />}
Authentication
- Library: Better Auth with Prisma adapter
- Server config:
server/src/lib/auth.ts— mounted at/api/auth/{*any}(must be beforeexpress.json()) - Client config:
client/src/lib/auth-client.ts— exportssignIn,signOut,useSession - Middleware:
server/src/middleware/require-auth.ts—requireAuthguard that setsreq.userandreq.session - Route protection (client):
ProtectedRoutecomponent wraps authenticated routes; redirects to/loginif unauthenticated - Admin route protection (client):
AdminRoutecomponent wraps admin-only routes; redirects non-admins to/ - Sign-up is disabled — users are seeded via
prisma/seed.ts - User roles:
adminandagent(defined as Prisma enum, defaultagent) - Rate limiting: Auth routes are rate-limited, but only enforced when
NODE_ENV=production
Testing
- Prefer component tests for the majority of coverage (rendering, states, data display, error handling). Reserve E2E tests for things that truly need a real browser + server: navigation, auth redirects, and full-stack integration flows (e.g. webhook creates data that appears in the UI).
Component Tests
- Framework: Vitest + React Testing Library
- Run with
cd client && bun run test(single run) orbun run test:watch(watch mode) - Place test files next to the component:
ComponentName.test.tsx - Use
renderWithQueryfrom@/test/renderto wrap components that use TanStack React Query - Mock Axios with
vi.mock("axios")andvi.mocked(axios, { deep: true })
E2E Tests
- Framework: Playwright
- Use the
e2e-test-writeragent for writing Playwright E2E tests - Run with
bun run test:e2efrom root - Only use for things that truly require a real browser + server — never duplicate what unit tests already cover
- Valid E2E scenarios: auth redirects, cross-page navigation, data persistence after reload, full-stack integration flows (e.g. webhook creates data → UI displays it)
- Invalid E2E scenarios: rendering, display logic, component states, API call verification, form validation, error messages — use component tests for these