Skip to content

Commit 076b44e

Browse files
Refactor assignment-assistant routing, schemas, and components (#300)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent d8d590d commit 076b44e

File tree

19 files changed

+897
-23
lines changed

19 files changed

+897
-23
lines changed

.trivyignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@ CVE-2025-47907
33
CVE-2025-58754
44
CVE-2025-47906
55
AVD-DS-0029
6+
CVE-2025-61724
7+
CVE-2025-58188
8+
CVE-2025-58187
9+
CVE-2025-58186
10+
CVE-2025-58183
11+
CVE-2025-47912

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM oven/bun:1.2-slim AS base
1+
FROM oven/bun:1.3-slim AS base
22

33
FROM base AS dual
44
WORKDIR /temp

WARP.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# WARP.md
2+
3+
This file provides guidance to WARP (warp.dev) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
MUNify DELEGATOR is a registration and organization management system for Model United Nations conferences. Built with SvelteKit 2, it handles delegation registration, assignment, and management workflows for MUN conferences.
8+
9+
**Tech Stack**: SvelteKit 2 (Svelte 5 runes mode), TypeScript, Prisma ORM (PostgreSQL), GraphQL (Pothos + Yoga), Tailwind CSS 4 + DaisyUI, Houdini (GraphQL client), Paraglide (i18n)
10+
11+
## Development Commands
12+
13+
### Setup & Running
14+
15+
```bash
16+
# Install dependencies
17+
bun install
18+
19+
# Start full dev environment (Docker services + dev server)
20+
bun run dev
21+
22+
# OR run separately:
23+
bun run dev:docker # Start PostgreSQL and other services
24+
bun run dev:server # Start SvelteKit dev server with Vite
25+
26+
# Install git hooks for automated linting
27+
bunx lefthook install
28+
```
29+
30+
### Database Management
31+
32+
```bash
33+
# Migrate database to latest schema
34+
bunx prisma migrate dev
35+
36+
# Reset database (WARNING: deletes all data)
37+
bunx prisma migrate reset
38+
39+
# Seed database with development data
40+
bun prisma/seed/dev/seed.ts
41+
42+
# Open Prisma Studio (database GUI)
43+
bun run studio
44+
```
45+
46+
### Code Quality
47+
48+
```bash
49+
# Format code
50+
bun run format
51+
52+
# Lint (runs both prettier check and eslint)
53+
bun run lint
54+
55+
# Type check
56+
bun run typecheck
57+
58+
# Svelte type checking
59+
bun run check
60+
61+
# Continuous type checking
62+
bun run check:watch
63+
```
64+
65+
### Building
66+
67+
```bash
68+
# Build for production
69+
bun run build
70+
```
71+
72+
### Translations
73+
74+
```bash
75+
# Add new translation key
76+
bun run add-translation
77+
78+
# Machine translate missing keys
79+
bun run machine-translate
80+
```
81+
82+
## Architecture Overview
83+
84+
### Directory Structure
85+
86+
- **`src/routes/`** - SvelteKit file-based routing
87+
- `(authenticated)/` - Protected routes requiring authentication
88+
- `dashboard/[conferenceId]/` - Participant-facing conference dashboard
89+
- `management/[conferenceId]/` - Admin conference management UI
90+
- `registration/[conferenceId]/` - Registration flows (delegation, individual, supervisor)
91+
- `assignment-assistant/` - Committee assignment tooling
92+
- `api/graphql/` - GraphQL API endpoint
93+
- `auth/` - OIDC authentication flows
94+
95+
- **`src/api/`** - GraphQL API implementation
96+
- `resolvers/` - Pothos GraphQL resolvers organized by entity
97+
- `abilities/` - CASL permission definitions per entity
98+
- `context/` - Request context (OIDC, permissions)
99+
- `services/` - Business logic services
100+
101+
- **`src/lib/`** - Shared utilities
102+
- `components/` - Reusable Svelte components
103+
- `queries/` - Houdini GraphQL queries/mutations
104+
- `services/` - Frontend services
105+
- `schemata/` - Zod validation schemas
106+
- `db/` - Database utilities
107+
- `paraglide/` - Generated i18n code
108+
109+
- **`src/tasks/`** - Background tasks (email sync, conference status updates)
110+
111+
- **`prisma/`** - Database schema, migrations, and seed scripts
112+
- `schema.prisma` - Main database schema
113+
- `migrations/` - Database migration history
114+
- `seed/dev/` - Development seed scripts
115+
116+
### Key Architectural Patterns
117+
118+
#### GraphQL API Layer
119+
120+
- **Schema Generation**: Pothos schema builder with Prisma plugin generates GraphQL schema from Prisma models
121+
- **Resolvers**: Organized by entity in `src/api/resolvers/modules/` (e.g., `conference.ts`, `delegation.ts`)
122+
- **Auto-generated CRUD**: `prisma-generator-pothos-codegen` creates base CRUD operations
123+
- **Server**: GraphQL Yoga serves the API at `/api/graphql`
124+
125+
#### Frontend Data Flow
126+
127+
- **Houdini Client**: Type-safe GraphQL client with automatic cache management
128+
- **Code Generation**: `houdini.config.js` watches schema and generates TypeScript types
129+
- **Queries**: Store queries in `src/lib/queries/` for reuse across components
130+
- **Load Functions**: SvelteKit `+page.ts` files use Houdini queries for SSR/CSR data loading
131+
132+
#### Authentication & Authorization
133+
134+
- **OIDC Integration**: Uses OpenID Connect (designed for ZITADEL but supports any OIDC provider)
135+
- **Context Building**: `src/api/context/context.ts` constructs request context with OIDC data
136+
- **Permission System**: CASL ability-based authorization
137+
- Definitions in `src/api/abilities/entities/`
138+
- Admins get full access, team members get scoped access based on roles
139+
- Roles: `admin`, `PROJECT_MANAGEMENT`, `PARTICIPANT_CARE`, etc.
140+
141+
#### Database & ORM
142+
143+
- **Prisma Models**: Single source of truth in `prisma/schema.prisma`
144+
- **Generators**: Three generators run on schema changes:
145+
1. `@prisma/client` - TypeScript client
146+
2. `prisma-pothos-types` - Pothos integration types
147+
3. `prisma-generator-pothos-codegen` - Auto-generated resolvers
148+
- **Migrations**: All schema changes must create migrations (`bunx prisma migrate dev`)
149+
150+
#### Internationalization
151+
152+
- **Paraglide.js**: Compile-time i18n with URL-based locale switching
153+
- **Message Files**: JSON files in `messages/` directory per locale
154+
- **Middleware**: `hooks.server.ts` uses `paraglideMiddleware` to set locale from URL
155+
156+
#### UI Components
157+
158+
- **Tailwind CSS 4**: Utility-first styling with DaisyUI component library
159+
- **Svelte 5 Runes**: Uses modern runes mode (`$state`, `$derived`, `$effect`)
160+
- **Houdini Integration**: `houdini-svelte` plugin provides reactive stores
161+
162+
### State Management Concepts
163+
164+
- **Conference States**: `PRE``PARTICIPANT_REGISTRATION``PREPARATION``ACTIVE``POST`
165+
- **Delegations**: Groups of participants representing countries
166+
- **Single Participants**: Individuals applying for custom roles
167+
- **Committee Assignment**: Matching delegations to nations in committees
168+
- **Background Tasks**: `src/tasks/` contains scheduled jobs (mail sync, status updates)
169+
170+
### Testing Database Changes
171+
172+
After modifying `prisma/schema.prisma`:
173+
174+
1. Run `bunx prisma migrate dev` to create migration
175+
2. Run `bun prisma/seed/dev/seed.ts` to seed test data
176+
3. Use Prisma Studio (`bun run studio`) to verify data structure
177+
178+
### Configuration
179+
180+
- **Environment Variables**: Copy `.env.example` to `.env` and configure:
181+
- `DATABASE_URL` - PostgreSQL connection string
182+
- `PUBLIC_OIDC_AUTHORITY` - OIDC provider URL
183+
- `PUBLIC_OIDC_CLIENT_ID` - OIDC client identifier
184+
- `SECRET` - Session encryption key
185+
- `CERTIFICATE_SECRET` - Certificate signing key (generate with `openssl rand -base64 32`)
186+
187+
- **Aliases**: Configured in `svelte.config.js`:
188+
- `$api``src/api`
189+
- `$assets``src/assets`
190+
- `$db``prisma`
191+
- `$config``src/config`
192+
- `$houdini``.houdini`
193+
194+
## Development Workflow
195+
196+
1. **Schema Changes**: Edit `prisma/schema.prisma` → run migrations → regenerate GraphQL schema
197+
2. **API Changes**: Modify resolvers in `src/api/resolvers/modules/` → schema regenerates automatically
198+
3. **Frontend Changes**: Edit Svelte components → Vite hot-reloads
199+
4. **Adding GraphQL Operations**: Create `.gql` files or use `graphql()` function → Houdini generates types
200+
5. **Permission Changes**: Edit ability files in `src/api/abilities/entities/`
201+
202+
## Commit Convention
203+
204+
Follow [Conventional Commits](https://www.conventionalcommits.org/):
205+
206+
- `feat:` - New feature
207+
- `fix:` - Bug fix
208+
- `refactor:` - Code restructure without behavior change
209+
- `style:` - UI/UX changes
210+
- `docs:` - Documentation
211+
- `test:` - Tests
212+
- `chore:` - Maintenance
213+
- `ci:` - CI/CD changes
214+
- `build:` - Build system changes
215+
- `perf:` - Performance improvements
216+
217+
Example: `feat(delegation): add nation preference selection`
218+
219+
## Docker Deployment
220+
221+
Use provided Docker images: `deutschemodelunitednations/delegator`
222+
223+
- Example compose file in `example/` directory
224+
- Requires external OIDC provider (ZITADEL recommended)
225+
- Environment variables must be configured (see `.env.example`)

messages/de.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,22 @@
168168
"cleanupIndividualParticipantsButton": "Einzelteilnehmende aufräumen",
169169
"cleanupIndividualParticipantsDescription": "Mit einem Klick auf den unteren Button werden alle Einzelteilnehmende, die keine zugewiesenen Rollen haben, aus der Konferenz entfernt.",
170170
"cleanupIndividualParticipantsSuccess": "Einzelteilnehmende wurden erfolgreich aufgeräumt.",
171+
"cleanupNormalizeSchoolsClearSelected": "Auswahl löschen: {count} Schule(n)",
172+
"cleanupNormalizeSchoolsColumnDelegationMembers": "Delegationsmitglieder",
173+
"cleanupNormalizeSchoolsColumnDelegations": "Delegationen",
174+
"cleanupNormalizeSchoolsColumnSchool": "Schulname",
175+
"cleanupNormalizeSchoolsColumnTotalParticipants": "Gesamt",
176+
"cleanupNormalizeSchoolsDescription": "Da Menschen unterschiedliche Varianten ihres Schulnamens eintragen, ist es oft schwer zu überblicken, wie viele Personen von derselben Schule kommen. Mit der folgenden Tabelle kannst du Schulnamen zusammenführen und anpassen, um sie zu vereinheitlichen.",
177+
"cleanupNormalizeSchoolsEnterNewName": "Bitte gib einen neuen Schulnamen ein",
178+
"cleanupNormalizeSchoolsFailed": "Vereinheitlichen der Schulen fehlgeschlagen",
179+
"cleanupNormalizeSchoolsNewName": "Neuer Schulname",
180+
"cleanupNormalizeSchoolsNewNamePlaceholder": "Gib den vereinheitlichten Schulnamen ein",
181+
"cleanupNormalizeSchoolsNoSchools": "Keine Schulen für diese Konferenz gefunden.",
182+
"cleanupNormalizeSchoolsNormalizeMany": "Ausgewählte Schulen vereinheitlichen",
183+
"cleanupNormalizeSchoolsRenameOne": "Ausgewählte Schule umbenennen",
184+
"cleanupNormalizeSchoolsSelectAtLeastOne": "Bitte wähle mindestens 1 Schule aus",
185+
"cleanupNormalizeSchoolsSuccess": "Erfolgreich {count} Schule(n) vereinheitlicht",
186+
"cleanupNormalizeSchoolsTitle": "Schulnamen vereinheitlichen",
171187
"cleanupSupervisors": "Betreuende aufräumen",
172188
"cleanupSupervisorsButton": "Betreuende aufräumen",
173189
"cleanupSupervisorsDescription": "Mit einem Klick auf den unteren Button werden alle Betreuende, die nicht mindestens eine Delegation mit einer zugewiesenen Rolle betreuen, aus der Konferenz entfernt.",
@@ -318,6 +334,7 @@
318334
"end": "Ende",
319335
"enterCode": "Code eingeben",
320336
"enterDateOfdateReceipt": "Empfangsdatum eingeben",
337+
"enterNewSchoolName": "Neuen Schulnamen eingeben",
321338
"entryCode": "Eintrittscode",
322339
"experience": "Erfahrung",
323340
"exportFrom": "Export aus {appName}",

messages/en.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,22 @@
168168
"cleanupIndividualParticipantsButton": "Clean up single participants",
169169
"cleanupIndividualParticipantsDescription": "By clicking on the button below, all single participants who do not have assigned roles will be removed from the conference.",
170170
"cleanupIndividualParticipantsSuccess": "Single participants have been cleaned up successfully.",
171+
"cleanupNormalizeSchoolsClearSelected": "Clear selected {count} school(s)",
172+
"cleanupNormalizeSchoolsColumnDelegationMembers": "Delegation Members",
173+
"cleanupNormalizeSchoolsColumnDelegations": "Delegations",
174+
"cleanupNormalizeSchoolsColumnSchool": "School Name",
175+
"cleanupNormalizeSchoolsColumnTotalParticipants": "Total Participants",
176+
"cleanupNormalizeSchoolsDescription": "Because people enter varying versions of their school names, it can be hard to see how many people come from one school. Use the table below to merge and adapt school names to a unified, normalized name.",
177+
"cleanupNormalizeSchoolsEnterNewName": "Please enter a new school name",
178+
"cleanupNormalizeSchoolsFailed": "Failed to normalize schools",
179+
"cleanupNormalizeSchoolsNewName": "New School Name",
180+
"cleanupNormalizeSchoolsNewNamePlaceholder": "Enter the normalized school name",
181+
"cleanupNormalizeSchoolsNoSchools": "No schools found for this conference.",
182+
"cleanupNormalizeSchoolsNormalizeMany": "Normalize Selected Schools",
183+
"cleanupNormalizeSchoolsRenameOne": "Rename Selected School",
184+
"cleanupNormalizeSchoolsSelectAtLeastOne": "Please select at least 1 school",
185+
"cleanupNormalizeSchoolsSuccess": "Successfully normalized {count} school(s)",
186+
"cleanupNormalizeSchoolsTitle": "Normalize Schools",
171187
"cleanupSupervisors": "Clean up Supervisors",
172188
"cleanupSupervisorsButton": "Clean up supervisors",
173189
"cleanupSupervisorsDescription": "By clicking on the button below, all supervisors who do not supervise at least one delegation with an assigned role will be removed from the conference.",

schema.graphql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ type Conference {
320320
postalStreet: String
321321
postalZip: String
322322
registrationDeadlineGracePeriodMinutes: Int!
323+
schools: [ConferenceSchools!]!
323324
singleParticipants: [SingleParticipant!]!
324325
startAssignment: DateTime!
325326
startConference: DateTime!
@@ -648,6 +649,14 @@ input ConferenceScalarRelationFilter {
648649
isNot: ConferenceWhereInput
649650
}
650651

652+
type ConferenceSchools {
653+
delegationCount: Int!
654+
delegationMembers: Int!
655+
school: String!
656+
singleParticipants: Int!
657+
sumParticipants: Int!
658+
}
659+
651660
enum ConferenceState {
652661
ACTIVE
653662
PARTICIPANT_REGISTRATION
@@ -1974,6 +1983,7 @@ type Mutation {
19741983
deleteOneTeamMember(where: TeamMemberWhereUniqueInput!): TeamMember
19751984
deleteOneUser(where: UserWhereUniqueInput!): User
19761985
deleteOneWaitingListEntry(where: WaitingListEntryWhereUniqueInput!): WaitingListEntry
1986+
normalizeSchoolsInConference(conferenceId: String!, newSchoolName: String!, schoolsToMerge: [String!]!): Conference
19771987
rotateSupervisorConnectionCode(id: ID!): ConferenceSupervisor!
19781988
seedNewConference(data: JSONObject!): SeedNewConferenceResult!
19791989
sendAssignmentData(data: JSONObject!, where: ConferenceWhereUniqueInput!): SetAssignmentDataResult!

0 commit comments

Comments
 (0)