A web application for requesting university chair resources. Provides structured, validated forms for VM provisioning, access requests, Artemis developer accounts, TUM guest accounts, and support requests.
- Framework: React 19, Vite, TypeScript
- Styling: Tailwind CSS, shadcn/ui (New York style), Lucide React icons
- Forms: React Hook Form + Zod validation
- Routing: React Router v7
- Auth: OIDC Client TS (Keycloak)
- Code Quality: Biome
- Framework: FastAPI, Pydantic, Pydantic Settings
- Database: PostgreSQL, SQLAlchemy (async), Alembic migrations, asyncpg
- Auth: Keycloak OIDC, role-based access control
- Ticket Systems: Jira, Redmine, Debug, NoOp (configurable via
TICKET_SYSTEM) - Code Quality: Ruff (lint + format), ty (type checking)
request-v2/
├── client/ # React frontend
│ └── src/
│ ├── components/ # UI components by feature
│ │ ├── admin/ # External links admin (drag-and-drop)
│ │ ├── artemis-request/ # Multi-step form + steps/
│ │ ├── layout/ # Header, Footer, PageLayout, ProtectedRoute
│ │ ├── providers/ # AuthProvider
│ │ ├── shared/ # Shared form components
│ │ ├── start-page/ # Landing page sections
│ │ ├── support-request/ # Single-page form
│ │ ├── tum-guest-request/ # Multi-step form + steps/
│ │ ├── ui/ # shadcn/ui components
│ │ ├── vm-access-request/ # Single-page form
│ │ └── vm-request/ # Multi-step form + steps/
│ ├── config/ # App configuration
│ ├── content/ # Static content (About, Imprint, Privacy)
│ ├── hooks/ # useAuth, useExternalLinks, useExternalLinksAdmin
│ ├── lib/ # Utilities, validation
│ ├── pages/ # Page components
│ ├── services/ # API client services
│ └── types/ # Zod schemas and TypeScript types
├── server/ # FastAPI backend
│ └── request_server/
│ ├── api/routes/ # Route handlers
│ ├── core/ # Config, security
│ ├── db/ # Session, base model
│ ├── models/ # SQLAlchemy ORM models
│ ├── schemas/ # Pydantic request/response schemas
│ └── services/
│ ├── descriptions/ # Ticket description builders
│ └── ticket/ # Ticket system implementations
├── e2e/ # Playwright end-to-end tests
│ ├── tests/ # Test specs (9 files)
│ ├── fixtures/ # Auth fixtures, test data
│ └── helpers/ # Form fillers, debug API
└── deploy/
├── client/ # Client Dockerfile (nginx)
├── server/ # Server Dockerfile (uvicorn)
└── helm/ # Kubernetes Helm chart
cd client
npm install
npm run dev # dev server on :5173
npm run build # production build
npm run lint # Biome lint + format
npm run typecheck # TypeScript checkcd server
uv sync
uv run alembic upgrade head
uv run uvicorn request_server.main:app --reload
# Quality
uv run ruff check . # lint
uv run ruff check . --fix # lint with auto-fix
uv run ruff format . # format
uv run ty check . # type check
uv run pytest # testscd e2e
npx playwright install # first time only
npx playwright test # run all (auto-starts client + server)
npx playwright test --ui # interactive UI modeTests use PostgreSQL on port 5433, AUTH_BYPASS=true, and TICKET_SYSTEM=debug.
See TESTING.md for detailed instructions on writing e2e tests, patterns, and best practices.
| Form | Route | Auth | Type |
|---|---|---|---|
| VM Request | /request/vm |
Required | Multi-step (6 steps) |
| VM Access | /request/vm-access |
Required | Single-page |
| Artemis Developer | /request/artemis |
Optional | Multi-step |
| TUM Guest Account | /request/tum-guest |
Optional | Multi-step |
| Support Request | /request/support |
Optional | Single-page |
| External Links Admin | /admin/external-links |
Admin | Admin dashboard |
Make sure to always try to reuse existing components. Never use recreate similar components. Check regularly if you can merge similar components.
Always check if a shadcn component exists before creating custom ones (you can should the shadcn MCP server if available):
npx shadcn@latest add [component-name]- Forms: Form, Field, Input, Textarea, Select, Checkbox, Radio, Switch
- Layout: Card, Separator, Tabs, Accordion, Sheet, Dialog
- Feedback: Alert, Toast, Badge, Progress, Skeleton
- Navigation: Button, Dropdown Menu, Breadcrumb
ui/step-progress.tsx— Responsive multi-step progress indicator (circles, check marks, connector lines)ui/step-header.tsx— Step title and description header
Each multi-step form defines its own steps array and wraps StepProgress.
- Use Tailwind CSS utility classes
- Mobile-first responsive design
- CSS variables for theme colors (defined in
index.css)
src/
├── types/{form-name}-request.ts # Zod schemas + TypeScript types
├── components/{form-name}-request/
│ ├── {FormName}RequestForm.tsx # Main form component
│ ├── StepProgress.tsx # Step config (multi-step only)
│ └── steps/ # Step components (multi-step only)
├── pages/{FormName}RequestPage.tsx # Page wrapper with submit handling
└── services/{form-name}-requests.ts # API client
- Use
z.discriminatedUnion("isLoggedIn", [...])for auth-aware forms - Use
superRefinefor cross-field validation - Export schema, inferred type, and default values from type files
const form = useForm<FormRequest>({
resolver: zodResolver(formSchema),
defaultValues: getDefaultValues(isAuthenticated),
mode: "onChange",
});Validate per-step before advancing. Each step component receives the form via useFormContext.
Same pattern without step navigation. Single Card with all fields.
Each form page handles: submission state, success/error result display, and API call. Uses useAuth() to include user info when authenticated.
useAuth()hook forisAuthenticatedanduserz.discriminatedUnion("isLoggedIn", [...])for conditional schemas- Different steps/fields based on auth state
- User info included in submission payload when authenticated
Request flow: Route → Schema validation → Service → Model → Database
- Routes (
api/routes/): FastAPI endpoints, auth dependency injection - Schemas (
schemas/): Pydantic models for request/response validation - Models (
models/): SQLAlchemy ORM with async sessions - Services (
services/): Business logic, ticket creation - Descriptions (
services/descriptions/): Build formatted ticket descriptions from request data - Ticket (
services/ticket/): Abstract base + implementations (Jira, Redmine, Debug, NoOp)
- Strict mode, path aliases:
@/components,@/lib,@/hooks,@/ui - Biome: 2-space indent, double quotes, auto import organization
- Ruff: line length 100, rules E/W/F/I/B/C4/UP/ARG/SIM
- Pydantic for all API schemas, Pydantic Settings for config
- Async throughout (asyncpg, async SQLAlchemy sessions)
- TypeScript types / Pydantic schemas defined
- Biome lint passes (client), Ruff + ty pass (server)
- Responsive design (mobile, tablet, desktop)
- Accessibility (ARIA labels, keyboard navigation)
- Error and loading states handled
- Form validation working
- shadcn components used where applicable