A full-stack customer support ticket management system built with FastAPI, React, and PostgreSQL. Agents can create, track, filter, and resolve support tickets with internal notes and priority escalation.
| Homepage | New Ticket | Ticket Detail |
|---|---|---|
![]() |
![]() |
![]() |
🔗 https://supportdesk.up.railway.app - Available till 12th July on Railway
| Layer | Technology |
|---|---|
| Backend | Python, FastAPI, SQLAlchemy |
| Database | PostgreSQL (production), SQLite (local dev) |
| Frontend | React, Vite, Tailwind CSS |
| Deployment | Railway.app |
- Create and manage support tickets with customer details
- Four priority levels — Low, Medium, High, Critical
- Three status levels — Open, In Progress, Closed
- Real-time search by customer name, email, or subject
- Filter tickets by status and priority
- Assign tickets to agents
- Add internal notes to tickets (agent-only, not customer facing)
- Update ticket status, priority, and assignment
- Delete tickets with confirmation modal
- Stats bar showing live ticket counts by status and priority
- Mobile responsive layout
support_system/
│
├── backend/ # FastAPI backend
│ ├── app/
│ │ ├── __init__.py # Makes app a Python package
│ │ ├── main.py # App entry point, CORS config, router registration
│ │ ├── database.py # DB engine, session factory, Base class
│ │ ├── models.py # SQLAlchemy models — Ticket and Note tables
│ │ ├── schemas.py # Pydantic schemas for request/response validation
│ │ └── tickets.py # All API route handlers (6 endpoints)
│ ├── init_db.py # Creates tables and seeds sample data
│ ├── requirements.txt # Python dependencies
│ ├── Procfile.config # Railway start command
│ └── railway.json # Railway build and deploy config
│
└── frontend/ # React frontend
├── src/
│ ├── App.jsx # Root component, page routing
│ ├── main.jsx # React entry point
│ ├── index.css # Global styles, Tailwind directives, Google Fonts
│ ├── api.js # Centralized API client — all fetch calls in one place
│ ├── utils.js # Shared helpers, priority/status color config, date formatting
│ ├── components/
│ │ ├── Sidebar.jsx # Left navigation panel (desktop fixed, mobile slide-in)
│ │ ├── TicketCard.jsx # Single ticket row with priority color border
│ │ └── StatsBar.jsx # Summary counts — Total, Open, In Progress, Closed, Critical
│ └── pages/
│ ├── TicketList.jsx # Home page — ticket list, search, filters, stats
│ ├── NewTicket.jsx # Form to create a new ticket with validation
│ └── TicketDetail.jsx # Full ticket view — update status, add notes, delete
├── index.html # HTML entry point
├── tailwind.config.js # Tailwind config with custom color palette
├── vite.config.js # Vite bundler config
└── package.json # Node dependencies
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/tickets |
Create a new ticket |
GET |
/api/tickets |
List tickets with optional filtering and search |
GET |
/api/tickets/{ticket_id} |
Get a single ticket with all its notes |
PATCH |
/api/tickets/{ticket_id} |
Update status, priority, or assigned agent |
POST |
/api/tickets/{ticket_id}/notes |
Add an internal note to a ticket |
DELETE |
/api/tickets/{ticket_id} |
Permanently delete a ticket and its notes |
Interactive API docs available at /docs when the backend is running.
tickets
├── id INTEGER Primary key
├── ticket_id TEXT Unique human-readable ID (e.g. TKT-001)
├── customer_name TEXT Required
├── customer_email TEXT Required
├── subject TEXT Required
├── description TEXT Required
├── status TEXT Open / In Progress / Closed
├── priority TEXT Low / Medium / High / Critical
├── assigned_to TEXT Agent email (nullable)
├── created_at DATETIME Auto-set on creation
└── updated_at DATETIME Auto-updated on every change
notes
├── id INTEGER Primary key
├── ticket_id TEXT Foreign key → tickets.ticket_id
├── note_text TEXT Required
├── created_by TEXT Agent name or email (nullable)
└── created_at DATETIME Auto-set on creation
cd backend
# Create and activate virtual environment
python -m venv venv
# Mac/Linux
source venv/bin/activate
# Windows
venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Create tables and seed sample data
python init_db.py
# Start the development server
uvicorn app.main:app --reload --port 8000API and interactive docs available at:
cd frontend
# Install dependencies
npm install
# Start the development server
npm run devFrontend available at:
Both backend and frontend must be running at the same time for the app to work locally.
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Falls back to SQLite locally |
| Variable | Description |
|---|---|
VITE_API_URL |
Backend API base URL |
Browser (React + Vite)
│
│ HTTP / JSON
▼
FastAPI Backend (Python)
│
│ SQLAlchemy ORM
▼
PostgreSQL Database (Railway)
The frontend never talks to the database directly. All data goes through the REST API. This separation means the database, backend, and frontend can each be updated or scaled independently.
SQLite locally, PostgreSQL in production — The same SQLAlchemy codebase works with both. One environment variable (DATABASE_URL) switches between them. No code changes needed at deploy time.
PATCH not PUT for updates — Ticket updates use PATCH because only the changed fields are sent, not the entire ticket object. This prevents accidentally overwriting fields with null values.
Pydantic validation at the API layer — Input validation (required fields, email format, valid status/priority values) happens in schemas.py before any database operation runs. Invalid requests never reach the database.
Centralized API client — All fetch calls live in api.js. If the backend URL changes, it changes in one place only.
Priority color borders — Each ticket card has a colored left border (red = Critical, orange = High, yellow = Medium, green = Low). Priority is visible before reading a single word.


