7.5 KiB
Legal Consent Hub - AI Context
Overview
Platform for digital applications, approvals, and discussions with GDPR compliance features. Enables organizations to create, manage, and track application forms with version control and PDF export.
Architecture
| Layer | Stack |
|---|---|
| Frontend | Nuxt 4.2.0, Vue 3, Nuxt UI 4.3.0, Pinia, TypeScript, i18n (de/en) |
| Backend | Spring Boot 3.4.2, Kotlin 1.9.25, Java 21, PostgreSQL, Liquibase |
| Auth | Keycloak (OAuth2/JWT), nuxt-auth-utils |
| LaTeX (lualatex) via Thymeleaf templates | |
| API | OpenAPI 3.0.3 spec → auto-generated clients |
Form Structure (3-Level Hierarchy)
ApplicationForm
└── Section (FormElementSection)
├── title, shortTitle, description
├── isTemplate, templateReference, titleTemplate ← for spawning
└── SubSection (FormElementSubSection)
└── FormElement
├── id (UUID), reference (string key)
├── type: SELECT | CHECKBOX | RADIOBUTTON | TEXTFIELD | TEXTAREA | SWITCH | RICH_TEXT | DATE | TABLE
├── options: FormOption[] (value, label, processingPurpose, employeeDataCategory, columnConfig)
├── visibilityConditions[] (AND logic)
├── sectionSpawnTriggers[]
├── isClonable, tableRowPreset
Key Features
Visibility Conditions
- Elements shown/hidden based on other element values
- Multiple conditions use AND logic
- Operators:
EQUALS,NOT_EQUALS,IS_EMPTY,IS_NOT_EMPTY - Reference elements by
referencekey (not ID) - Hidden elements: excluded from validation, PDF export, and values cleared
Section Spawning
- Template sections (
isTemplate: true) spawn when trigger conditions met - Title interpolation:
{{triggerValue}}placeholder - Spawned sections linked via
spawnedFromElementReference - One element can have multiple
sectionSpawnTriggers
Clonable Elements
isClonable: trueshows "Add another" button- Deep cloning with auto-generated references (
modul_1→modul_2) - Values reset on clone (TEXTAREA/TEXTFIELD)
Table Cross-References
- Column references other table's column:
columnConfig.sourceTableReference - Row presets from source table:
tableRowPreset - Filter conditions limit available values
Composables
| Composable | Purpose |
|---|---|
useFormElementVisibility |
Evaluate visibility conditions |
useSectionSpawning |
Process spawn triggers, clone templates |
useClonableElements |
Clone elements with reference generation |
useFormElementValueClearing |
Clear values when elements become hidden |
useTableCrossReferences |
Resolve table column/row references |
useApplicationFormValidator |
Form validation rules |
useLogger |
Consola logger instance |
API Endpoints
# Forms
GET/POST /application-forms
GET/PUT/DELETE /application-forms/{id}
POST /application-forms/{id}/submit
# Templates
GET/POST /application-form-templates
GET/PUT/DELETE /application-form-templates/{id}
# Versions
GET /application-forms/{id}/versions
GET /application-forms/{id}/versions/{n}
POST /application-forms/{id}/versions/{n}/restore
GET /application-forms/{id}/versions/{n}/pdf
# Comments
GET /application-forms/{id}/comments
POST /application-forms/{id}/form-elements/{elementId}/comments
PUT/DELETE /comments/{id}
# Notifications
GET /notifications?organizationId=
GET /notifications/unread/count?userId=&organizationId= (public)
PUT /notifications/{id}/mark-read
PUT /notifications/mark-all-read
DELETE /notifications/clear-all
Roles (Keycloak-managed)
CHIEF_EXECUTIVE_OFFICER, BUSINESS_DEPARTMENT, IT_DEPARTMENT, HUMAN_RESOURCES, HEAD_OF_WORKS_COUNCIL, WORKS_COUNCIL, EMPLOYEE
Development
# Frontend (port 3001)
cd legalconsenthub && pnpm install && pnpm run dev
# Backend (port 8080)
cd legalconsenthub-backend && ./gradlew bootRun
# Generate API clients
pnpm run api:generate # Frontend
./gradlew generate_legalconsenthub_server # Backend
Seed Data Maintenance
The application automatically seeds initial data on first startup:
1. Template Seeding
Seeder: InitialApplicationFormTemplateSeeder
File: legalconsenthub-backend/src/main/resources/seed/initial_application_form_template.yaml
Condition: Seeds if no templates exist (isTemplate = true)
Purpose: Comprehensive IT system approval workflow template
IMPORTANT: Keep this file updated when form structure or validation rules change.
2. Application Form Seeding
Seeder: InitialApplicationFormSeeder
File: legalconsenthub-backend/src/main/resources/seed/initial_application_form.yaml
Condition: Seeds if no forms exist for empty organizationId (isTemplate = false)
Purpose: Realistic SAP S/4HANA application form for development and UI testing
organizationId: Empty string (global form visible to all organizations)
Content: Pre-filled IT system introduction form based on the template structure with realistic SAP S/4HANA values
IMPORTANT: Keep this file synchronized with template changes. When template structure changes, update the demo form accordingly.
Note:
- Forms with empty/null organizationId act as "global" forms and are visible to all organizations
- This allows the demo form to appear regardless of the current organization context
- The demo form is derived from the template structure with values filled for realistic testing
- Demonstrates visibility conditions, section spawning, clonable elements, and GDPR compliance features
Key Files
| File | Purpose |
|---|---|
api/legalconsenthub.yml |
OpenAPI spec (source of truth) |
legalconsenthub-backend/src/main/resources/templates/application_form_latex_template.tex |
PDF template |
legalconsenthub-backend/src/main/resources/seed/initial_application_form_template.yaml |
Initial form template (isTemplate=true) |
legalconsenthub-backend/src/main/resources/seed/initial_application_form.yaml |
SAP S/4HANA demo application form (global, filled with realistic values) |
legalconsenthub-backend/src/main/resources/db/changelog/ |
Liquibase migrations |
.github/workflows/pipeline.yaml |
CI/CD workflow |
Rules for AI
- Never auto-generate SQL migrations - Liquibase migrations are manual
- Use mapper classes for all DTO↔Entity conversions (never in services/controllers)
- No hardcoded UI strings - Use i18n (
de.json,en.json) - API-first - Modify OpenAPI spec, then regenerate clients
- Organization context - Always consider
organizationIdfor multi-tenancy. Forms with empty/null organizationId are "global" forms visible to all organizations - Form structure is 3-level - Section → SubSection → Element
- Roles managed in Keycloak - Not in application database
- Nuxt UI 4 - For any UI-related questions, use the Nuxt UI MCP server to get current component docs and examples
PDF Export Validation
After changes to form elements, visibility, or spawning:
- Test both HTML and PDF export for real form instances
- Verify all element types render correctly
- Ensure template sections excluded, spawned sections included
- Hotspots:
ApplicationFormFormatService.kt,application_form_latex_template.tex
Code Style
- Order functions top-down by abstraction (public API first, helpers below)
- Keep
CLAUDE.mdupdated when introducing architectural changes