Skip to content

sonnymay/supportops

Repository files navigation

SupportOps

CI Python FastAPI License: MIT Live Demo

A lightweight ticketing and RMA workflow tool built by a tech support engineer, for tech support engineers.

πŸ”— Live demo: supportops.vercel.app πŸ“Έ Screenshots: see below


Table of contents

⏱️ First load can take up to a minute. The demo backend runs on Render's free tier, which sleeps after 15 min of inactivity. The frontend fires a warm-up ping on app load and shows an informative loading state while the server wakes β€” so just give it a moment on the first visit. Subsequent requests are <1s.


Why this exists

After 9 years on the front lines of technical support, I kept hitting the same walls with the tools I was given:

  • Tickets, customers, and devices lived in three different systems that didn't talk to each other.
  • RMA tracking was a spreadsheet someone forgot to update.
  • Status changes had no audit trail when a customer asked "who closed my ticket and why?"
  • The "enterprise" platforms were slow, bloated, and built for managers β€” not the agent actually working the queue.

SupportOps is the tool I wished I had. Tickets, devices, customers, RMAs, and a full status history β€” in one place, fast, and built around how support actually works.


What this code shows

If you're reviewing this as a portfolio piece, the interesting bits are:

  • FastAPI + Pydantic as a thin validation/business-rules layer over Supabase's PostgREST API β€” no ORM, no schema duplication.
  • Automatic audit logging β€” every ticket status change appends a row to ticket_history from the API layer, not the client.
  • AI Resolution Suggester β€” Anthropic Claude (Haiku) reads a ticket plus its full notes history and proposes next steps. Wired in backend/ai.py.
  • Resilient frontend β€” frontend/src/api.js wraps fetch with AbortController timeouts, status checks, and a useApiResource hook that gives every page the same loading / error / retry UX.
  • Cold-start UX β€” the frontend warms the backend on mount and explains the wait, so a sleeping free-tier dyno never silently looks like a broken app.
  • Secret-free tests β€” backend tests mock Supabase and AI boundaries, so CI can validate behavior without production keys.

Features

  • 🎫 Tickets with status, priority, assignee, and linked customer + device
  • πŸ‘₯ Customers directory (name, email, phone, company)
  • πŸ’» Devices tied to customers by serial number and product type
  • πŸ“ Ticket notes for agent-side context and handoffs
  • πŸ•“ Automatic status history β€” every status change is logged with who and when
  • πŸ“¦ RMA tracking β€” RMA number, serial, shipping status, resolution status, linked to the originating ticket
  • πŸ€– AI Suggestions β€” Claude-powered next-step recommendations per ticket
  • πŸ” Ticket search β€” full-text search across titles and descriptions via GET /tickets/search?q=
  • πŸ—‚οΈ Ticket filtering β€” filter by status, priority, and assignee via GET /tickets/filter

Stack

Layer Tech
Frontend React 19, Vite, Tailwind CSS v4, React Router
Backend FastAPI (Python 3.11+), Pydantic
Database Supabase (Postgres + PostgREST)
AI Anthropic Claude Haiku 4.5
Hosting Vercel (frontend) Β· Render (backend)

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ React + Vite  β”‚ ──▢ β”‚   FastAPI    β”‚ ──▢ β”‚   Supabase   β”‚
β”‚   (Vercel)    β”‚    β”‚   (Render)   β”‚    β”‚ (PostgREST)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚
                            β–Ό
                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                     β”‚  Anthropic   β”‚
                     β”‚    Claude    β”‚
                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The FastAPI layer is intentionally thin β€” it validates with Pydantic, applies business rules (e.g. writing to ticket_history on every status change), proxies CRUD to Supabase's REST API, and brokers calls to Claude for the AI Suggester.


Engineering tradeoffs

Choice Why it fits this project
Supabase PostgREST instead of an ORM Keeps the backend small and avoids duplicating schema definitions across Python and Postgres.
Backend-owned ticket history Prevents the client from accidentally skipping audit logs when ticket status changes.
Mocked Supabase/AI tests CI stays fast, deterministic, and safe to run without live service credentials.
Render free tier with warm-up UX Keeps hosting cost at zero while making cold starts visible instead of mysterious.
AI suggester behind the API Keeps provider keys off the client and lets the app degrade if AI is unavailable.

Local development

docker compose up --build

Docker starts the API at http://localhost:8000. Create backend/.env from backend/.env.example before first run.

Prerequisites

  • Python 3.11+
  • Node.js 20+
  • A Supabase project (free tier is fine) β€” grab your SUPABASE_URL and SUPABASE_KEY
  • (Optional) An Anthropic API key if you want the AI Suggester to work locally

Backend

cd backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
pre-commit install
cp .env.example .env # then fill in SUPABASE_URL, SUPABASE_KEY, ANTHROPIC_API_KEY
uvicorn main:app --reload

API runs at http://localhost:8000. Health check: GET /health. Interactive docs: /docs. Dependency check: GET /health/dependencies. The Supabase table setup lives in backend/supabase/schema.sql.

Frontend

cd frontend
cp .env.example .env # set VITE_API_BASE_URL=http://localhost:8000
npm install
npm run dev

App runs at http://localhost:5173.


Deployment

  • Frontend β€” Vercel, SPA rewrites in frontend/vercel.json. Env: VITE_API_BASE_URL.
  • Backend β€” Render web service from render.yaml at repo root. Python 3.11.9. Env: SUPABASE_URL, SUPABASE_KEY, ANTHROPIC_API_KEY, ANTHROPIC_MODEL. Health check: /health.

Free-tier dynos sleep after 15 min of inactivity. The frontend warms the backend on app load β€” see frontend/src/api.js (warmupBackend).


API surface

Resource Endpoints
Health GET /health, GET /health/dependencies
Dashboard GET /dashboard
Customers GET/POST/PUT/DELETE /customers
Devices GET/POST/PUT /devices
Tickets GET/POST/PUT /tickets, GET /tickets/{id}
Ticket Search GET /tickets/search?q={term}
Ticket Filter GET /tickets/filter?status=&priority=&assignee=
Ticket Notes GET /tickets/{id}/notes, POST /notes
Ticket Hist. GET /tickets/{id}/history (auto-appended on status change)
RMAs GET/POST/PUT /rmas
AI POST /ai/suggest

Status changes on tickets automatically append a row to ticket_history.

Full OpenAPI spec available at http://localhost:8000/docs when the backend is running.


Screenshots

Dashboard Ticket Detail AI Suggestions


Roadmap

  • Full-text search across tickets (GET /tickets/search)
  • Filter tickets by status, priority, assignee (GET /tickets/filter)
  • SLA timers with breach alerts
  • Saved views / filters per agent
  • CSV export of tickets and RMAs
  • Role-based permissions (agent / lead / admin)

Contributing

Issues and PRs are welcome β€” especially from anyone who's worked a support queue and has opinions on what's missing. Please open an issue describing the change before sending a large PR.


License

MIT Β© Sonny May

About

Ticketing + RMA workflow tool with AI suggestions, audit trail, and live demo. Built with FastAPI, React, and Supabase.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors