# Frontend Design Rules
This file is the single source of truth for UI implementation conventions in the dashboard (`dashboard/src/`).
It applies to all feature work, including new pages, settings tabs, dialogs, and management surfaces.
When proposing or implementing frontend changes, follow these rules unless a specific exception is documented below.
This file should be updated whenever a new Syncfusion component is adopted, a color or pattern changes, or an exception is ratified.
---
## 1. Component Library — Syncfusion First
Use Syncfusion components as the default choice for every UI element.
The project uses the Syncfusion Material3 theme, registered globally in `dashboard/src/main.tsx`.
The following CSS packages are imported there and cover all components currently in use:
`base`, `navigations`, `buttons`, `inputs`, `dropdowns`, `popups`, `kanban`, `grids`, `schedule`, `filemanager`, `notifications`, `layouts`, `lists`, `calendars`, `splitbuttons`, `icons`.
When adding a new Syncfusion component, add its CSS import here — and add the new npm package to `optimizeDeps.include` in `vite.config.ts` to avoid Vite import-analysis errors in development.
Use non-Syncfusion elements only when:
- The Syncfusion equivalent does not exist (e.g., native `` for file upload)
- The Syncfusion component would require significantly more code than a simple HTML element for purely read-only or structural content (e.g., `
/
` for plain lists)
- A layout-only structure is needed (a wrapper `
` for spacing is fine)
**Never** use `window.confirm()` for destructive action confirmations — use `DialogComponent` instead.
`window.confirm()` exists in one place in `dashboard.tsx` (bulk restart) and is considered a deprecated pattern to avoid.
Do not introduce Tailwind utility classes — Tailwind has been removed from the project.
---
## 2. Component Defaults by Purpose
| Purpose | Component | Notes |
|---|---|---|
| Navigation tabs | `TabComponent` + `TabItemDirective` | `heightAdjustMode="Auto"`, controlled with `selectedItem` state |
| Data list or table | `GridComponent` | `allowPaging`, `allowSorting`, custom `template` for status/actions |
| Paginated list | `PagerComponent` | When a full grid is too heavy; default page size 5 or 10 |
| Text input | `TextBoxComponent` | Use `cssClass="e-outline"` on form-heavy sections |
| Numeric input | `NumericTextBoxComponent` | Always set `min`, `max`, `step`, `format` |
| Single select | `DropDownListComponent` | Always set `fields={{ text, value }}`; do **not** add `cssClass="e-outline"` — only `TextBoxComponent` uses outline style |
| Boolean toggle | `CheckBoxComponent` | Use `label` prop, handle via `change` callback |
| Buttons | `ButtonComponent` | See section 4 |
| Modal dialogs | `DialogComponent` | `isModal={true}`, `showCloseIcon={true}`, footer with Cancel + primary |
| Notifications | `ToastComponent` | Positioned `{ X: 'Right', Y: 'Top' }`, 3000ms timeout by default |
| Inline info/error | `MessageComponent` | Use `severity` prop: `'Error'`, `'Warning'`, `'Info'`, `'Success'` |
| Status/role badges | Plain `` with inline style | See section 6 for convention |
| Timeline/schedule | `ScheduleComponent` | Used for resource timeline views; see `ressourcen.tsx` |
| File management | `FileManagerComponent` | Used on the Media page for upload and organisation |
| Drag-drop board | `KanbanComponent` | Used on the Groups page; retain for drag-drop boards |
| User action menu | `DropDownButtonComponent` (`@syncfusion/ej2-react-splitbuttons`) | Used for header user menu; add to `optimizeDeps.include` in `vite.config.ts` |
| File upload | Native `` | No Syncfusion equivalent for raw file input |
---
## 3. Layout and Card Structure
Every settings tab section starts with a `
` wrapper.
Content blocks use Syncfusion card classes:
```jsx
Title
{/* content */}
```
Multiple cards in the same tab section use `style={{ marginBottom: 20 }}` between them.
For full-page views (not inside a settings tab), the top section follows this pattern:
```jsx
Page title
Subtitle or description
New item
```
---
## 4. Buttons
| Variant | `cssClass` | When to use |
|---|---|---|
| Primary action (save, confirm) | `e-primary` | Main save or confirm in forms and dialogs |
| Create / add new | `e-success` + `iconCss="e-icons e-plus"` | Top-level create action in page header |
| Destructive (delete, archive) | `e-flat e-danger` | Row actions and destructive dialog confirm |
| Secondary / cancel | `e-flat` | Cancel in dialog footer, low-priority options |
| Info / edit | `e-flat e-primary` or `e-flat e-info` | Row-level edit and info actions |
| Outline secondary | `e-outline` | Secondary actions needing a visible border (e.g., preview URL) |
All async action buttons must be `disabled` during the in-flight operation: `disabled={isBusy}`.
Button text must change to indicate the pending state: `Speichere…`, `Erstelle...`, `Archiviere…`, `Lösche...`.
---
## 5. Dialogs
All create, edit, and destructive action dialogs use `DialogComponent`:
- `isModal={true}`
- `showCloseIcon={true}`
- `width="500px"` for forms (wider if tabular data is shown inside)
- `header` prop with specific context text (include item name where applicable)
- `footerTemplate` always has at minimum: Cancel (`e-flat`) + primary action (`e-primary`)
- Dialog body wrapped in `
`
- All fields disabled when `formBusy` is true
For destructive confirmations (archive, delete), the dialog body must clearly explain what will happen and whether it is reversible.
For blocked actions, use `MessageComponent` with `severity="Warning"` or `severity="Error"` inside the dialog body to show exact blocker details (e.g., linked event count, recurrence spillover).
---
## 6. Status and Type Badges
Plain `` badges with inline style — no external CSS classes needed:
```jsx
Label
```
See section 12 for the fixed color palette.
**Icon conventions**: Use inline SVG or icon font classes for small visual indicators next to text. Established precedents:
- Skip-holidays events render a TentTree icon immediately to the left of the main event-type icon; **always black** (`color: 'black'` or no color override).
- Recurring events rely on Syncfusion's native lower-right recurrence badge — do not add a custom recurrence icon.
**Role badge color mapping** (established in `users.tsx`; apply consistently for any role display):
| Role | Color |
|---|---|
| user | `#6c757d` (neutral gray) |
| editor | `#0d6efd` (info blue) |
| admin | `#28a745` (success green) |
| superadmin | `#dc3545` (danger red) |
---
## 7. Toast Notifications
Use a component-local `ToastComponent` with a `ref`:
```jsx
const toastRef = React.useRef(null);
// ...
```
Default `timeOut: 3000`. Use `4000` for messages that need more reading time.
CSS class conventions:
- `e-toast-success` — successful operations
- `e-toast-danger` — errors
- `e-toast-warning` — non-critical issues or partial results
- `e-toast-info` — neutral informational messages
---
## 8. Form Fields
All form labels:
```jsx
```
Help/hint text below a field:
```jsx
Hint text here.
```
Empty state inside a card:
```jsx
Keine Einträge vorhanden.
```
Vertical spacing between field groups: `marginBottom: 16`.
---
## 9. Tab Structure
Top-level and nested tabs use controlled `selectedItem` state with separate index variables per tab level.
This prevents sub-tab resets when parent state changes.
```jsx
const [academicTabIndex, setAcademicTabIndex] = React.useState(0);
setAcademicTabIndex(e.selectedIndex ?? 0)}
>
```
Tab header text uses an emoji prefix followed by a German label, consistent with all existing tabs.
Each nested tab level has its own separate index state variable.
---
## 10. Statistics Summary Cards
Used above grids and lists to show aggregate counts:
```jsx
Label
42
```
---
## 11. Inline Warning Messages
For important warnings inside forms or dialogs:
```jsx
⚠️ Warning message text here.
```
For structured in-page errors or access-denied states, use `MessageComponent`:
```jsx
```
---
## 12. Color Palette
Only the following colors are used in status and UI elements across the dashboard.
Do not introduce new colors for new components.
| Use | Color |
|---|---|
| Success / active / online | `#28a745` |
| Danger / delete / offline | `#dc3545` |
| Warning / partial | `#f39c12` |
| Info / edit blue | `#0d6efd` |
| Neutral / archived / subtitle | `#6c757d` |
| Help / secondary text | `#666` |
| Inactive/muted | `#868e96` |
| Warning background | `#fff3cd` |
| Warning border | `#ffc107` |
---
## 13. Dedicated CSS Files
Use inline styles for settings tab sections and simpler pages.
Only create a dedicated `.css` file if the component requires complex layout, custom animations, or selector-based styles that are not feasible with inline styles.
Existing precedents: `monitoring.css`, `ressourcen.css`.
Do not use Tailwind — it has been removed from the project.
---
## 14. Loading States
For full-page loading, use a simple centered placeholder:
```jsx
Lade Daten...
```
Do not use spinners or animated components unless a Syncfusion component provides them natively (e.g., busy state on `ButtonComponent`).
---
## 15. Locale and Language
All user-facing strings are in German.
Date formatting uses `toLocaleString('de-DE')` or `toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })`.
Never use English strings in labels, buttons, tooltips, or dialog headers visible to the end user.
**UTC time parsing**: The API returns ISO timestamps **without** a `Z` suffix (e.g., `"2025-11-27T20:03:00"`). Always append `Z` before constructing a `Date` to ensure correct UTC interpretation:
```tsx
const utcStr = dateStr.endsWith('Z') ? dateStr : dateStr + 'Z';
const date = new Date(utcStr);
```
When sending dates back to the API, use `date.toISOString()` (already UTC with `Z`).