update docs

This commit is contained in:
ValueOn AG 2026-01-13 20:01:11 +01:00
parent c55d17bd0f
commit cc4953da50
4 changed files with 2181 additions and 31 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,400 @@
# User Authentication UI Adaptations
## Overview
This document describes the necessary UI changes and adaptations required to implement the magic link-based user authentication process described in `doc_userauth_process_concept.md`.
**Last Updated**: Based on codebase analysis of current frontend implementation.
## Current Frontend State
### Existing Pages
1. **`frontend_agents/public/login.html`**
- Contains login form with username/password fields
- Has buttons for Microsoft and Google authentication
- Has registration link
- **Missing**: Password reset button
2. **`frontend_agents/public/register.html`**
- Contains registration form with:
- Username field
- Password field (required)
- Confirm password field (required)
- Email field
- Full name field
- Language selector
- **Needs modification**: Remove password fields, add email-only registration
3. **`frontend_agents/public/js/security/auth.js`**
- Contains `setupRegisterPage()` function
- Contains `validateRegistrationForm()` function that requires password
- Contains email validation logic
- **Needs modification**: Remove password validation, add shared email validation function
4. **`frontend_agents/public/js/shared/apiCalls.js`**
- Contains `register()` function that sends password
- **Missing**: Password reset request and reset password API calls
## Required UI Changes
### 1. Login Page (`frontend_agents/public/login.html`)
#### Changes Required:
- Add "Password Reset" button/link below the login form
- Button should link to `/password-reset-request.html`
- Style should match existing button styles (use `btn` class with appropriate variant)
#### Implementation:
```html
<!-- Add after login form, before register-options div -->
<div class="password-reset-link">
<a href="password-reset-request.html" class="btn btn-link">
<i class="fas fa-key"></i> Passwort zurücksetzen
</a>
</div>
```
#### Styling Considerations:
- Use existing CSS classes from `htmlparts/styles.css`
- Match styling with registration link
- Ensure responsive design matches login page layout
### 2. Registration Page (`frontend_agents/public/register.html`)
#### Changes Required:
- **Remove** password and confirm password fields
- **Keep** username, email, fullName, language fields
- Update form validation to not require password
- Update success message to indicate email will be sent
- Add spam folder reminder message
#### Implementation:
```html
<!-- Remove these fields: -->
<!-- <div class="login-form-group">
<label for="password">Passwort*</label>
<input type="password" id="password" name="password" required minlength="8">
<div class="field-error"></div>
</div>
<div class="login-form-group">
<label for="confirm-password">Passwort bestätigen*</label>
<input type="password" id="confirm-password" name="confirm-password" required>
<div class="field-error"></div>
</div> -->
<!-- Add info message after form: -->
<div class="registration-info">
<p>Nach der Registrierung erhalten Sie eine E-Mail mit einem Link zum Setzen Ihres Passworts.</p>
<p class="spam-reminder">Bitte prüfen Sie auch Ihren Spam-Ordner, falls Sie keine E-Mail erhalten.</p>
</div>
```
#### JavaScript Changes (`frontend_agents/public/js/security/auth.js`):
- Update `validateRegistrationForm()` to remove password validation
- Update `setupRegisterPage()` to handle no-password registration
- Create shared `validateEmailFormat()` function for reuse
### 3. New Page: Password Reset Request (`frontend_agents/public/password-reset-request.html`)
#### Purpose:
Allow users to request a password reset by entering their email address.
#### Structure:
- Similar layout to `register.html`
- Single email input field
- Submit button
- Link back to login page
- Success/error message area
#### Implementation:
```html
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PowerOn - Passwort zurücksetzen</title>
<link rel="stylesheet" href="./htmlparts/styles_variables.css">
<link rel="stylesheet" href="./htmlparts/styles_base.css">
<link rel="stylesheet" href="./htmlparts/styles.css">
<link rel="stylesheet" href="./htmlparts/styles_icons.css">
</head>
<body>
<div class="login-container">
<h1>Passwort zurücksetzen</h1>
<div id="reset-request-error" class="login-error-message"></div>
<form id="password-reset-request-form">
<div class="login-form-group">
<label for="email">E-Mail-Adresse*</label>
<input type="email" id="email" name="email" required autocomplete="email">
<div class="field-error"></div>
</div>
<button type="submit" class="btn btn-success login-btn">Reset-Link anfordern</button>
</form>
<div class="register-link">
<p>Zurück zum <a href="login.html">Login</a></p>
</div>
</div>
<!-- Benötigte globale Utilities -->
<script type="module" src="js/shared/utils.js"></script>
<!-- Module -->
<script type="module" src="js/security/passwordResetRequest.js"></script>
</body>
</html>
```
#### JavaScript Module (`frontend_agents/public/js/security/passwordResetRequest.js`):
- Handle form submission
- Validate email format using shared function from `auth.js`
- Call password reset request API
- Show generic success message
- Redirect to login page after showing message
### 4. New Page: Password Reset (`frontend_agents/public/reset.html`)
#### Purpose:
Allow users to set a new password using the token from the magic link.
#### Structure:
- Similar layout to `register.html`
- Password field (with strength indicator)
- Confirm password field
- Submit button
- Extract token from URL parameter (`?token=<UUID>`)
- Success/error message area
#### Implementation:
```html
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PowerOn - Neues Passwort setzen</title>
<link rel="stylesheet" href="./htmlparts/styles_variables.css">
<link rel="stylesheet" href="./htmlparts/styles_base.css">
<link rel="stylesheet" href="./htmlparts/styles.css">
<link rel="stylesheet" href="./htmlparts/styles_icons.css">
</head>
<body>
<div class="login-container">
<h1>Neues Passwort setzen</h1>
<div id="reset-error" class="login-error-message"></div>
<form id="reset-password-form">
<div class="login-form-group">
<label for="password">Neues Passwort*</label>
<input type="password" id="password" name="password" required minlength="8">
<div class="field-error"></div>
<small class="password-hint">Mindestens 8 Zeichen</small>
</div>
<div class="login-form-group">
<label for="confirm-password">Passwort bestätigen*</label>
<input type="password" id="confirm-password" name="confirm-password" required>
<div class="field-error"></div>
</div>
<button type="submit" class="btn btn-success login-btn">Passwort setzen</button>
</form>
<div class="register-link">
<p>Zurück zum <a href="login.html">Login</a></p>
</div>
</div>
<!-- Benötigte globale Utilities -->
<script type="module" src="js/shared/utils.js"></script>
<!-- Module -->
<script type="module" src="js/security/reset.js"></script>
</body>
</html>
```
#### JavaScript Module (`frontend_agents/public/js/security/reset.js`):
- Extract token from URL parameter
- Validate token format (UUID)
- Handle form submission
- Validate password strength (min 8 chars, match requirements from config)
- Validate password confirmation matches
- Call password reset API
- Show success message with spam folder reminder
- Redirect to login page after 3 seconds
### 5. API Calls (`frontend_agents/public/js/shared/apiCalls.js`)
#### New Functions Required:
1. **`requestPasswordReset(email)`**
```javascript
requestPasswordReset: async function(email) {
try {
return await privateApi.post('/api/local/password-reset-request', { email });
} catch (error) {
ui.log.error('Password reset request error:', error);
throw error;
}
}
```
2. **`resetPassword(token, password)`**
```javascript
resetPassword: async function(token, password) {
try {
return await privateApi.post('/api/local/password-reset', { token, password });
} catch (error) {
ui.log.error('Password reset error:', error);
throw error;
}
}
```
3. **Update `register()` function**
- Remove password from request body
- Update to handle new registration flow (no password required)
### 6. Shared Email Validation (`frontend_agents/public/js/security/auth.js`)
#### New Function:
```javascript
/**
* Validates email format
* @param {string} email - Email address to validate
* @returns {boolean} - True if valid, false otherwise
*/
export function validateEmailFormat(email) {
if (!email) return false;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email.trim());
}
```
#### Usage:
- Used by registration form
- Used by password reset request form
- Ensures consistent email validation across the application
## UI/UX Considerations
### Error Handling
1. **Registration Errors**:
- Username already exists → Show error on username field
- Email already exists → Show generic success (security: don't reveal email exists)
- Email sending fails → Show generic success (don't reveal email issues)
2. **Password Reset Request Errors**:
- Invalid email format → Show error on email field
- Email not found → Show generic success (security: don't reveal email doesn't exist)
- Rate limiting → Show error message
3. **Password Reset Errors**:
- Invalid/expired token → Show error message, link back to password reset request
- Password too weak → Show specific requirements
- Password mismatch → Show error on confirm password field
### Success Messages
1. **Registration Success**:
```
"Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mail, um Ihr Passwort zu setzen.
Falls Sie keine E-Mail erhalten, prüfen Sie bitte auch Ihren Spam-Ordner."
```
2. **Password Reset Request Success**:
```
"Falls ein Konto mit dieser E-Mail-Adresse existiert, wurde ein Reset-Link gesendet.
Bitte prüfen Sie Ihre E-Mail und auch Ihren Spam-Ordner."
```
3. **Password Reset Success**:
```
"Passwort erfolgreich gesetzt! Sie werden zum Login weitergeleitet..."
```
### Accessibility
- All form fields should have proper labels
- Error messages should be associated with form fields using ARIA attributes
- Success messages should be announced to screen readers
- Form validation should provide clear, actionable feedback
### Responsive Design
- All pages should work on mobile devices
- Form layouts should adapt to smaller screens
- Buttons should be appropriately sized for touch interfaces
- Error messages should be readable on all screen sizes
## Testing Checklist
### Registration Flow
- [ ] User can register without password
- [ ] Email validation works correctly
- [ ] Success message displays correctly
- [ ] Redirect to login works
- [ ] Error handling for duplicate username
- [ ] Error handling for duplicate email (should show generic success)
### Password Reset Request Flow
- [ ] User can access password reset request page from login
- [ ] Email validation works correctly
- [ ] Success message displays correctly
- [ ] Redirect to login works
- [ ] Error handling for invalid email format
- [ ] Error handling for rate limiting
### Password Reset Flow
- [ ] User can access reset page with valid token
- [ ] Token extraction from URL works
- [ ] Password validation works correctly
- [ ] Password confirmation validation works
- [ ] Success message displays correctly
- [ ] Redirect to login works after 3 seconds
- [ ] Error handling for invalid token
- [ ] Error handling for expired token
- [ ] Error handling for weak password
### Integration Testing
- [ ] End-to-end registration flow works
- [ ] End-to-end password reset flow works
- [ ] Email links work correctly
- [ ] Token expiration handling works
- [ ] Multiple reset requests invalidate old tokens
## Implementation Order
1. **Backend Changes First** (prerequisites):
- Add resetToken fields to UserInDB model
- Implement password reset endpoints
- Implement email sending functionality
2. **Frontend API Layer**:
- Add password reset API calls to `apiCalls.js`
- Update registration API call
3. **Frontend Pages**:
- Create `password-reset-request.html`
- Create `reset.html`
- Update `login.html` (add reset button)
- Update `register.html` (remove password fields)
4. **Frontend JavaScript**:
- Create `passwordResetRequest.js`
- Create `reset.js`
- Update `auth.js` (remove password validation, add email validation function)
- Update `register.js` if needed
5. **Testing**:
- Test each flow independently
- Test integration between frontend and backend
- Test error scenarios
- Test edge cases
## Notes
- All text should be in German to match existing UI (`login.html` uses German)
- CSS classes should match existing patterns from `htmlparts/styles.css`
- Form validation should use existing patterns from `auth.js`
- Error handling should use existing patterns from `auth.js` (showFieldError, clearFieldError)
- Success messages should use existing patterns (showSuccessMessage)
- API calls should use existing patterns from `apiCalls.js` (privateApi.post, handleResponse)

View file

@ -0,0 +1,265 @@
# Connector Generic Refactoring Analysis
## Problem
The `connectorDbPostgre.py` has **hardcoded field names** that make it non-generic:
- Lines 61-70: Hardcoded field names in `_get_model_fields()` to force JSONB type
- Lines 689-700: Hardcoded field names in `_loadTable()` for None value defaults
- Lines 891-902: Hardcoded field names in `getRecordset()` for None value defaults
## Hardcoded Fields in Connector
### Location 1: `_get_model_fields()` (Lines 61-70)
```python
or field_name in [
"execParameters",
"expectedDocumentFormats",
"resultDocuments",
"logs",
"messages",
"stats",
"tasks",
]
```
### Location 2: `_loadTable()` (Lines 689-700)
```python
if field_name in [
"logs",
"messages",
"tasks",
"expectedDocumentFormats",
"resultDocuments",
]:
record[field_name] = []
elif field_name in ["execParameters", "stats"]:
record[field_name] = {}
```
### Location 3: `getRecordset()` (Lines 891-902)
```python
if field_name in [
"logs",
"messages",
"tasks",
"expectedDocumentFormats",
"resultDocuments",
]:
record[field_name] = []
elif field_name in ["execParameters", "stats"]:
record[field_name] = {}
```
---
## Field Usage Analysis
### Fields Used in Models:
1. **`logs`, `messages`, `stats`, `tasks`** → `ChatWorkflow` model (`datamodelChat.py`)
- Used by: `interfaceDbChatObjects.py`
- Type: `List[...]` (should be JSONB)
2. **`execParameters`, `expectedDocumentFormats`** → `ActionItem` model (`datamodelChat.py`)
- Used by: `interfaceDbChatObjects.py` (via `AutomationDefinition`)
- Type: `Dict[str, Any]` and `Optional[List[Dict[str, str]]]` (should be JSONB)
3. **`resultDocuments`** → Not found in current models (may be legacy or unused)
---
## Current Interface Handling
### ✅ `interfaceDbChatObjects.py` - HAS Field Separation Logic
- **Has:** `_separateObjectFields()` method (lines 210-254)
- **Handles:** Separates `documents`, `stats` to `objectFields` (lines 226-228)
- **Still has hardcoded fields:** Lines 234: `execParameters`, `expectedDocumentFormats`, `resultDocuments`
- **Status:** ✅ Partially handles field separation, but still relies on connector for JSONB detection
### ❌ `interfaceDbComponentObjects.py` - NO Field Separation Logic
- **Has:** Direct calls to `db.recordCreate()`, `db.recordModify()`
- **Handles:** None - relies entirely on connector's hardcoded logic
- **Status:** ❌ Needs field separation logic
### ❌ `interfaceDbAppObjects.py` - NO Field Separation Logic
- **Has:** Direct calls to `db.recordCreate()`, `db.recordModify()`
- **Handles:** None - relies entirely on connector's hardcoded logic
- **Status:** ❌ Needs field separation logic
### ⚠️ `interfaceRbac.py` - HAS Hardcoded Field Names
- **Has:** Helper function `getRecordsetWithRBAC()` (lines 113-120)
- **Handles:** Hardcoded field names for None defaults (same as connector)
- **Status:** ⚠️ Also needs to be made generic
---
## Required Changes
### 1. **connectorDbPostgre.py** - REMOVE Hardcoded Logic
#### Change 1: `_get_model_fields()` (Lines 43-87)
**REMOVE:**
```python
or field_name in [
"execParameters",
"expectedDocumentFormats",
"resultDocuments",
"logs",
"messages",
"stats",
"tasks",
]
```
**REPLACE WITH:** Pure type-based detection only
```python
# Check for JSONB fields (Dict, List, or complex types)
if (
field_type == dict
or field_type == list
or (
hasattr(field_type, "__origin__")
and field_type.__origin__ in (dict, list)
)
):
fields[field_name] = "JSONB"
```
#### Change 2: `_loadTable()` (Lines 689-700)
**REMOVE:** Hardcoded field name checks for None defaults
**REPLACE WITH:** Generic type-based handling or remove entirely (let interfaces handle)
#### Change 3: `getRecordset()` (Lines 891-902)
**REMOVE:** Hardcoded field name checks for None defaults
**REPLACE WITH:** Generic type-based handling or remove entirely (let interfaces handle)
---
### 2. **interfaceDbChatObjects.py** - ENHANCE Field Separation
#### Change: `_separateObjectFields()` (Lines 210-254)
**CURRENT:** Lines 226-228 handle `documents`, `stats` as object fields
**CURRENT:** Lines 234 still has hardcoded `execParameters`, `expectedDocumentFormats`, `resultDocuments`
**UPDATE TO:**
- Remove hardcoded field names from line 234
- Rely purely on type detection (Dict, List)
- Handle relational fields (`documents`, `stats`, `logs`, `messages`) as object fields (not stored in main table)
- Handle JSONB fields (`execParameters`, `expectedDocumentFormats`) as simple fields (stored as JSONB)
**Logic should be:**
```python
# Relational fields (stored in separate normalized tables)
if fieldName in ['documents', 'stats', 'logs', 'messages']:
objectFields[fieldName] = value
continue
# JSONB fields (stored as JSONB in main table)
if (fieldType == dict or fieldType == list or
(hasattr(fieldType, '__origin__') and fieldType.__origin__ in (dict, list))):
simpleFields[fieldName] = value # Store as JSONB
```
---
### 3. **interfaceDbComponentObjects.py** - ADD Field Separation
#### Add: `_separateObjectFields()` method
**LOCATION:** After `_initializeDatabase()` method (around line 140)
**IMPLEMENTATION:** Similar to `interfaceDbChatObjects.py` but adapted for ComponentObjects models
- Check which models used by this interface have relational/JSONB fields
- Implement type-based field separation
- Use before calling `db.recordCreate()` and `db.recordModify()`
**Models used:** `Prompt`, `FileItem`, `FileData`, `VoiceSettings`, `MessagingSubscription`, etc.
- Check if any have relational fields that need separate handling
---
### 4. **interfaceDbAppObjects.py** - ADD Field Separation
#### Add: `_separateObjectFields()` method
**LOCATION:** After `_initializeDatabase()` method (around line 135)
**IMPLEMENTATION:** Similar to `interfaceDbChatObjects.py` but adapted for AppObjects models
- Check which models used by this interface have relational/JSONB fields
- Implement type-based field separation
- Use before calling `db.recordCreate()` and `db.recordModify()`
**Models used:** `User`, `Mandate`, `UserInDB`, `Token`, `AuthEvent`, `UserConnection`, etc.
- Check if any have relational fields that need separate handling
---
## Impact Analysis
### Models Affected:
1. **ChatWorkflow** (`datamodelChat.py`)
- Fields: `logs`, `messages`, `stats`, `tasks` (List types)
- Currently: Hardcoded in connector
- After: Handled by `interfaceDbChatObjects._separateObjectFields()`
2. **ActionItem** (`datamodelChat.py`)
- Fields: `execParameters` (Dict), `expectedDocumentFormats` (List)
- Currently: Hardcoded in connector
- After: Handled by `interfaceDbChatObjects._separateObjectFields()`
3. **AutomationDefinition** (`datamodelChat.py`)
- Contains `ActionItem` objects
- Currently: Hardcoded in connector
- After: Handled by `interfaceDbChatObjects._separateObjectFields()`
---
## Migration Steps
### Step 1: Update `interfaceDbChatObjects.py`
1. Enhance `_separateObjectFields()` to handle all JSONB fields based on type only
2. Remove hardcoded field names
3. Test with ChatWorkflow and AutomationDefinition models
### Step 2: Add Field Separation to Other Interfaces
1. Add `_separateObjectFields()` to `interfaceDbComponentObjects.py`
2. Add `_separateObjectFields()` to `interfaceDbAppObjects.py`
3. Update all `recordCreate()` and `recordModify()` calls to use field separation
4. Update `interfaceRbac.py` to remove hardcoded field names from `getRecordsetWithRBAC()`
### Step 3: Clean Up Connector
1. Remove hardcoded field names from `_get_model_fields()`
2. Remove hardcoded None defaults from `_loadTable()` and `getRecordset()`
3. Make connector purely type-based
### Step 4: Testing
1. Test all CRUD operations for ChatWorkflow
2. Test all CRUD operations for AutomationDefinition
3. Test all CRUD operations for ComponentObjects models
4. Test all CRUD operations for AppObjects models
5. Verify JSONB fields are stored/loaded correctly
---
## Summary of Changes Needed
| File | Change Type | Description |
|------|-------------|-------------|
| `connectorDbPostgre.py` | **REMOVE** | Remove hardcoded field names from `_get_model_fields()` (lines 61-70) |
| `connectorDbPostgre.py` | **REMOVE** | Remove hardcoded None defaults from `_loadTable()` (lines 689-700) |
| `connectorDbPostgre.py` | **REMOVE** | Remove hardcoded None defaults from `getRecordset()` (lines 891-902) |
| `interfaceDbChatObjects.py` | **UPDATE** | Remove hardcoded fields from `_separateObjectFields()` line 234 |
| `interfaceDbChatObjects.py` | **UPDATE** | Add `logs`, `messages` to relational fields list (line 226) |
| `interfaceDbComponentObjects.py` | **ADD** | Add `_separateObjectFields()` method |
| `interfaceDbComponentObjects.py` | **UPDATE** | Use field separation before all `recordCreate()`/`recordModify()` calls |
| `interfaceDbAppObjects.py` | **ADD** | Add `_separateObjectFields()` method |
| `interfaceDbAppObjects.py` | **UPDATE** | Use field separation before all `recordCreate()`/`recordModify()` calls |
| `interfaceRbac.py` | **UPDATE** | Remove hardcoded field names from `getRecordsetWithRBAC()` (lines 115-118) |
| `interfaceRbac.py` | **UPDATE** | Use type-based None defaults instead of hardcoded field names |
---
## Notes
- The connector should be **completely generic** and only use type information from Pydantic models
- Interfaces should handle domain-specific logic (which fields are relational vs JSONB)
- The `_separateObjectFields()` pattern in `interfaceDbChatObjects.py` is the correct approach
- Other interfaces should follow the same pattern

View file

@ -8,6 +8,29 @@ Dieses Dokument beschreibt die **UI-Architektur und Frontend-Implementierung** f
**UI-Demo**: Eine interaktive HTML-Demo zur Visualisierung der UI-Struktur findet sich in [`doc_trustee_feature_ui_demo.html`](./doc_trustee_feature_ui_demo.html).
### Backend-Status ✅ IMPLEMENTIERT
Das Backend ist vollständig implementiert und bereit für die Frontend-Entwicklung:
| Komponente | Datei | Status |
|------------|-------|--------|
| **Datenmodelle** | `gateway/modules/datamodels/datamodelTrustee.py` | ✅ |
| **Interface** | `gateway/modules/interfaces/interfaceDbTrusteeObjects.py` | ✅ |
| **API-Routes** | `gateway/modules/routes/routeDataTrustee.py` | ✅ |
| **RBAC-Regeln** | `gateway/modules/interfaces/interfaceBootstrap.py` | ✅ |
| **App-Registrierung** | `gateway/app.py` | ✅ |
**API-Basis-URL**: `/api/trustee/`
**Implementierte Entities**:
- `TrusteeOrganisation` - Trustee-Organisationen
- `TrusteeRole` - Feature-spezifische Rollen (userreport, admin, operate)
- `TrusteeAccess` - Benutzerzugriffe auf Organisationen (mit optionalem Contract)
- `TrusteeContract` - Kundenverträge
- `TrusteeDocument` - Dokumente/Belege
- `TrusteePosition` - Buchungspositionen
- `TrusteePositionDocument` - Verknüpfung Position-Dokument
**Frontend-Stack**: React 19, TypeScript, Vite
**Pattern**:
- **Logic-Hook** (`use*Logic.tsx`) - Handhabt Daten laden, CRUD, State-Management, Column/Action/Field-Konfigurationen
@ -1069,15 +1092,24 @@ export function useOrganisationenLogic() {
let options = attr.options;
// Wenn options eine String-Referenz ist, dynamisch laden
// Backend verwendet jetzt PascalCase-Referenzen: TrusteeOrganisation, TrusteeRole, etc.
if (typeof attr.options === 'string' && attr.type === 'select') {
// Beispiel: "trustee.organisation" -> API-Call zu /api/trustee/organisations/
if (attr.options.startsWith('trustee.')) {
const resource = attr.options.replace('trustee.', '');
// Beispiel: "TrusteeOrganisation" -> API-Call zu /api/trustee/organisations
const optionMap: Record<string, () => Promise<any>> = {
'TrusteeOrganisation': trusteeApi.getOrganisationen,
'TrusteeRole': trusteeApi.getRollen,
'TrusteeContract': trusteeApi.getContracts,
'TrusteeDocument': trusteeApi.getDocuments,
'TrusteePosition': trusteeApi.getPositions,
'User': () => api.get('/api/users'),
};
if (optionMap[attr.options]) {
try {
const response = await trusteeApi[`get${resource.charAt(0).toUpperCase() + resource.slice(1)}`]();
const response = await optionMap[attr.options]();
options = (response.data.items || response.data).map((item: any) => ({
value: item.id,
label: item.label || item.name || item.id
label: item.label || item.name || item.fullName || item.id
}));
} catch (err) {
console.error(`Error loading options for ${attr.options}:`, err);
@ -1204,60 +1236,63 @@ export function useOrganisationenLogic() {
**Datei**: `src/api.ts` erweitern
```typescript
// Trustee API functions
// Trustee API functions - Implementiert in gateway/modules/routes/routeDataTrustee.py
export const trusteeApi = {
// Organisationen
getOrganisationen: () => api.get('/api/trustee/organisations/'),
// Organisationen (TrusteeOrganisation)
getOrganisationen: (pagination?: PaginationParams) => api.get('/api/trustee/organisations', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
getOrganisation: (id: string) => api.get(`/api/trustee/organisations/${id}`),
createOrganisation: (data: any) => api.post('/api/trustee/organisations/', data),
createOrganisation: (data: any) => api.post('/api/trustee/organisations', data),
updateOrganisation: (id: string, data: any) => api.put(`/api/trustee/organisations/${id}`, data),
deleteOrganisation: (id: string) => api.delete(`/api/trustee/organisations/${id}`),
// Rollen
getRollen: () => api.get('/api/trustee/roles/'),
// Rollen (TrusteeRole)
getRollen: (pagination?: PaginationParams) => api.get('/api/trustee/roles', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
getRolle: (id: string) => api.get(`/api/trustee/roles/${id}`),
createRolle: (data: any) => api.post('/api/trustee/roles/', data),
createRolle: (data: any) => api.post('/api/trustee/roles', data),
updateRolle: (id: string, data: any) => api.put(`/api/trustee/roles/${id}`, data),
deleteRolle: (id: string) => api.delete(`/api/trustee/roles/${id}`),
// Access
getAccess: () => api.get('/api/trustee/access/'),
// Access (TrusteeAccess)
getAccess: (pagination?: PaginationParams) => api.get('/api/trustee/access', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
getAccessById: (id: string) => api.get(`/api/trustee/access/${id}`),
getAccessForOrganisation: (orgId: string) => api.get(`/api/trustee/access/organisation/${orgId}`),
createAccess: (data: any) => api.post('/api/trustee/access/', data),
getAccessForUser: (userId: string) => api.get(`/api/trustee/access/user/${userId}`),
createAccess: (data: any) => api.post('/api/trustee/access', data),
updateAccess: (id: string, data: any) => api.put(`/api/trustee/access/${id}`, data),
deleteAccess: (id: string) => api.delete(`/api/trustee/access/${id}`),
// Contracts
getContracts: () => api.get('/api/trustee/contracts/'),
// Contracts (TrusteeContract)
getContracts: (pagination?: PaginationParams) => api.get('/api/trustee/contracts', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
getContract: (id: string) => api.get(`/api/trustee/contracts/${id}`),
getContractsForOrganisation: (orgId: string) => api.get(`/api/trustee/contracts/organisation/${orgId}`),
createContract: (data: any) => api.post('/api/trustee/contracts/', data),
createContract: (data: any) => api.post('/api/trustee/contracts', data),
updateContract: (id: string, data: any) => api.put(`/api/trustee/contracts/${id}`, data),
deleteContract: (id: string) => api.delete(`/api/trustee/contracts/${id}`),
// Documents
getDocuments: () => api.get('/api/trustee/documents/'),
// Documents (TrusteeDocument)
getDocuments: (pagination?: PaginationParams) => api.get('/api/trustee/documents', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
getDocument: (id: string) => api.get(`/api/trustee/documents/${id}`),
getDocumentData: (id: string) => api.get(`/api/trustee/documents/${id}/data`, { responseType: 'blob' }),
getDocumentsForContract: (contractId: string) => api.get(`/api/trustee/documents/contract/${contractId}`),
getDocumentsForPosition: (positionId: string) => api.get(`/api/trustee/documents/position/${positionId}`),
createDocument: (data: FormData) => api.post('/api/trustee/documents/', data, { headers: { 'Content-Type': 'multipart/form-data' } }),
createDocument: (data: FormData) => api.post('/api/trustee/documents', data, { headers: { 'Content-Type': 'multipart/form-data' } }),
updateDocument: (id: string, data: any) => api.put(`/api/trustee/documents/${id}`, data),
deleteDocument: (id: string) => api.delete(`/api/trustee/documents/${id}`),
// Positions
getPositions: () => api.get('/api/trustee/positions/'),
// Positions (TrusteePosition)
getPositions: (pagination?: PaginationParams) => api.get('/api/trustee/positions', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
getPosition: (id: string) => api.get(`/api/trustee/positions/${id}`),
getPositionsForContract: (contractId: string) => api.get(`/api/trustee/positions/contract/${contractId}`),
getPositionsForDocument: (documentId: string) => api.get(`/api/trustee/positions/document/${documentId}`),
createPosition: (data: any) => api.post('/api/trustee/positions/', data),
getPositionsForOrganisation: (orgId: string) => api.get(`/api/trustee/positions/organisation/${orgId}`),
createPosition: (data: any) => api.post('/api/trustee/positions', data),
updatePosition: (id: string, data: any) => api.put(`/api/trustee/positions/${id}`, data),
deletePosition: (id: string) => api.delete(`/api/trustee/positions/${id}`),
// Position-Documents
getPositionDocuments: () => api.get('/api/trustee/position-documents/'),
getPositionDocumentsForPosition: (positionId: string) => api.get(`/api/trustee/position-documents/position/${positionId}`),
getPositionDocumentsForDocument: (documentId: string) => api.get(`/api/trustee/position-documents/document/${documentId}`),
createPositionDocument: (data: any) => api.post('/api/trustee/position-documents/', data),
// Position-Documents (TrusteePositionDocument)
getPositionDocuments: (pagination?: PaginationParams) => api.get('/api/trustee/position-documents', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
getPositionDocument: (id: string) => api.get(`/api/trustee/position-documents/${id}`),
getDocumentsForPosition: (positionId: string) => api.get(`/api/trustee/position-documents/position/${positionId}`),
getPositionsForDocument: (documentId: string) => api.get(`/api/trustee/position-documents/document/${documentId}`),
createPositionDocument: (data: any) => api.post('/api/trustee/position-documents', data),
deletePositionDocument: (id: string) => api.delete(`/api/trustee/position-documents/${id}`),
};
```