Replace KPI/tenant wording in admin UI and help
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-23 10:55:24 +01:00
parent 0eb3b85f06
commit 0089a14204
24 changed files with 346 additions and 75 deletions

View File

@@ -32,4 +32,4 @@ related:
- Begrüßungstext kurz halten (12 Sätze). - Begrüßungstext kurz halten (12 Sätze).
### Weitere Hilfe ### Weitere Hilfe
`event-prep-checklist` für die Vorbereitung oder `tenant-dashboard-overview` für den Status. Event-Vorbereitung: Checkliste für die Vorbereitung oder Event-Dashboard im Überblick für den Status.

View File

@@ -42,4 +42,4 @@ related:
- Mit einem Testgast den Ablauf einmal komplett durchspielen. - Mit einem Testgast den Ablauf einmal komplett durchspielen.
### Weitere Hilfe ### Weitere Hilfe
`control-room-moderation` für die Queue oder `tenant-dashboard-overview` für Status-Checks. Control Room für die Queue oder Event-Dashboard im Überblick für Status-Checks.

View File

@@ -32,4 +32,4 @@ related:
- Bei Moderationsbedarf AutoFreigabe deaktivieren. - Bei Moderationsbedarf AutoFreigabe deaktivieren.
### Weitere Hilfe ### Weitere Hilfe
`event-prep-checklist` für den Ablauf oder `guest-access-qr` für die Freigabe. Event-Vorbereitung: Checkliste für den Ablauf oder Gästezugang & QR für die Freigabe.

View File

@@ -35,4 +35,4 @@ Hier stellst du die Aufgaben zusammen, die Gäste im Event sehen und erfüllen s
- 1220 Aufgaben reichen meist aus. - 1220 Aufgaben reichen meist aus.
### Weitere Hilfe ### Weitere Hilfe
`event-prep-checklist` für die Vorbereitung oder `tenant-dashboard-overview` für den Status. Event-Vorbereitung: Checkliste für die Vorbereitung oder Event-Dashboard im Überblick für den Status.

View File

@@ -33,4 +33,4 @@ related:
- Mitgliederrollen für temporäre Helfer nutzen. - Mitgliederrollen für temporäre Helfer nutzen.
### Weitere Hilfe ### Weitere Hilfe
`event-settings` für Berechtigungen oder `tenant-dashboard-overview` für den Überblick. Event-Einstellungen für Berechtigungen oder Event-Dashboard im Überblick für den Gesamtüberblick.

View File

@@ -28,4 +28,4 @@ related:
- Link in gedruckte Einladungen integrieren. - Link in gedruckte Einladungen integrieren.
### Weitere Hilfe ### Weitere Hilfe
`event-prep-checklist` für den kompletten Ablauf. Event-Vorbereitung: Checkliste für den kompletten Ablauf.

View File

@@ -14,18 +14,18 @@ related: []
Dieses Portal sammelt alles, was Event-Admins für Vorbereitung, Kern-Workflows und Abschluss brauchen. Dieses Portal sammelt alles, was Event-Admins für Vorbereitung, Kern-Workflows und Abschluss brauchen.
| Bereich | Kernfrage | Artikel-Slug | | Bereich | Kernfrage | Artikel |
| --- | --- | --- | | --- | --- | --- |
| Dashboard | Wo sehe ich Status, KPIs und Schnellaktionen? | `tenant-dashboard-overview` | | Dashboard | Wo sehe ich Status, Kennzahlen und Schnellaktionen? | Event-Dashboard im Überblick |
| Event-Einstellungen | Wo bearbeite ich Event-Basics und Status? | `event-settings` | | Event-Einstellungen | Wo bearbeite ich Event-Basics und Status? | Event-Einstellungen |
| Branding | Wie lade ich Logos hoch und setze Farben? | `event-branding-assets` | | Branding | Wie lade ich Logos hoch und setze Farben? | Branding & Assets |
| Aufgaben & Sammlungen | Wie verwalte ich Aufgaben und Prompts? | `event-tasks-setup` | | Aufgaben & Sammlungen | Wie verwalte ich Aufgaben und Prompts? | Event-Aufgaben & Sammlungen |
| Gästezugang | Wie teile ich Join-Link oder QR? | `guest-access-qr` | | Gästezugang | Wie teile ich Join-Link oder QR? | Gästezugang & QR |
| Team & Einladungen | Wie lade ich Helfer ein und vergebe Rollen? | `event-team-invites` | | Team & Einladungen | Wie lade ich Helfer ein und vergebe Rollen? | Event-Team & Einladungen |
| Control Room | Wie moderiere ich Uploads und steuere die Queue? | `control-room-moderation` | | Control Room | Wie moderiere ich Uploads und steuere die Queue? | Control Room: Moderation & Queue |
| Live Show | Wie richte ich den Live-Show-Player ein? | `live-show-setup` | | Live Show | Wie richte ich den Live-Show-Player ein? | Live Show einrichten |
| Abrechnung & Exporte | Wo verwalte ich Pakete und Exporte? | `billing-packages-exports` | | Abrechnung & Exporte | Wo verwalte ich Pakete und Exporte? | Pakete, Abrechnung & Exporte |
| Abschluss & Compliance | Was erledige ich nach dem Event? | `post-event-wrapup` | | Abschluss & Compliance | Was erledige ich nach dem Event? | Nachbereitung & Abschluss |
| FAQ | Was sind die häufigsten Fragen? | `faq-admin` | | FAQ | Was sind die häufigsten Fragen? | FAQ: Event-Admin |
Nutze das Help Center in der App für mobilfreundliches Lesen oder öffne `/help/admin` am Desktop. Nutze das Help Center in der App für mobilfreundliches Lesen oder öffne `/help/admin` am Desktop.

View File

@@ -3,7 +3,7 @@ title: "Event-Dashboard im Überblick"
locale: de locale: de
slug: tenant-dashboard-overview slug: tenant-dashboard-overview
audience: admin audience: admin
summary: "Status, KPIs und Setup-Checkliste deines Events auf einen Blick." summary: "Status, Kennzahlen und Setup-Checkliste deines Events auf einen Blick."
version_introduced: 2025.4 version_introduced: 2025.4
requires_app_version: "^3.2.0" requires_app_version: "^3.2.0"
status: draft status: draft
@@ -21,14 +21,14 @@ Direkt nach dem Anlegen eines Events oder als schneller Gesundheitscheck vor dem
## Was das Dashboard zeigt ## Was das Dashboard zeigt
- **Event-Status** (Entwurf / Veröffentlicht), damit klar ist, was Gäste sehen. - **Event-Status** (Entwurf / Veröffentlicht), damit klar ist, was Gäste sehen.
- **KPIs** zu Gästen, Uploads und Engagement für einen schnellen Puls-Check. - **Kennzahlen** zu Gästen, Uploads und Engagement für einen schnellen Puls-Check.
- **Setup-Checkliste** mit den wichtigsten Restschritten bis zum Start. - **Setup-Checkliste** mit den wichtigsten Restschritten bis zum Start.
- **Schnellaktionen** zu Aufgaben, Live Show, Branding und QR-Tools. - **Schnellaktionen** zu Aufgaben, Live Show, Branding und QR-Tools.
## So nutzt du es ## So nutzt du es
1. **Status prüfen** und das Event bis zur Freigabe im Entwurf lassen. 1. **Status prüfen** und das Event bis zur Freigabe im Entwurf lassen.
2. **Checkliste abarbeiten** sie bildet den typischen Vorbereitungsweg ab. 2. **Checkliste abarbeiten** sie bildet den typischen Vorbereitungsweg ab.
3. **KPIs beobachten**, um Einladungen und Uploads zu verifizieren. 3. **Kennzahlen beobachten**, um Einladungen und Uploads zu verifizieren.
4. **Details öffnen** (Aufgaben, QR, Live Show) über die Schnellaktionen. 4. **Details öffnen** (Aufgaben, QR, Live Show) über die Schnellaktionen.
### Tipps ### Tipps

View File

@@ -24,7 +24,7 @@ related:
- Use the package shop for upgrades when needed. - Use the package shop for upgrades when needed.
## Data exports ## Data exports
- Request a tenantwide or eventspecific export. - Request an accountwide or eventspecific export.
- Large exports can take a few minutes; refresh the list to see progress. - Large exports can take a few minutes; refresh the list to see progress.
## Retention reminders ## Retention reminders

View File

@@ -32,4 +32,4 @@ related:
- Keep welcome text short (12 sentences). - Keep welcome text short (12 sentences).
### Need more help? ### Need more help?
See `event-prep-checklist` for the full prep flow or `tenant-dashboard-overview` for the status view. See the Event Preparation Checklist for the full prep flow or the Event Dashboard overview for the status view.

View File

@@ -42,4 +42,4 @@ related:
- Use a test guest to verify the full flow end-to-end. - Use a test guest to verify the full flow end-to-end.
### Need more help? ### Need more help?
Open `control-room-moderation` for queue handling or revisit `tenant-dashboard-overview` for status checks. Open the Control Room article for queue handling or revisit the Event Dashboard overview for status checks.

View File

@@ -32,4 +32,4 @@ related:
- If moderation is needed, disable autoapproval. - If moderation is needed, disable autoapproval.
### Need more help? ### Need more help?
See `event-prep-checklist` for the full flow or `guest-access-qr` for sharing. See the Event Preparation Checklist for the full flow or Guest Access & QR for sharing.

View File

@@ -35,4 +35,4 @@ Use this page to shape the guest experience: tasks guide what guests should capt
- Aim for 1220 tasks per event to avoid overwhelming guests. - Aim for 1220 tasks per event to avoid overwhelming guests.
### Need more help? ### Need more help?
Open `event-prep-checklist` for the full prep flow or `tenant-dashboard-overview` for the status view. Open the Event Preparation Checklist for the full prep flow or the Event Dashboard overview for status.

View File

@@ -33,4 +33,4 @@ related:
- Use member roles for temporary helpers. - Use member roles for temporary helpers.
### Need more help? ### Need more help?
See `event-settings` for permissions or `tenant-dashboard-overview` for the full event overview. See Event Settings for permissions or the Event Dashboard overview for the full event view.

View File

@@ -28,4 +28,4 @@ Open the QR page from the event dashboard. You can copy the join link or downloa
- Add the link to printed invitations if the event is preannounced. - Add the link to printed invitations if the event is preannounced.
### Need more help? ### Need more help?
See `event-prep-checklist` for the full prep flow. See the Event Preparation Checklist for the full prep flow.

View File

@@ -14,18 +14,18 @@ related: []
This portal collects everything event admins need to configure events, run key workflows, and close out events smoothly. This portal collects everything event admins need to configure events, run key workflows, and close out events smoothly.
| Section | Key Questions | Article Slug | | Section | Key Questions | Article |
| --- | --- | --- | | --- | --- | --- |
| Dashboard | Where do I see status, KPIs, and quick actions? | `tenant-dashboard-overview` | | Dashboard | Where do I see status, key numbers, and quick actions? | Event Dashboard Overview |
| Event Settings | Where do I edit the event basics and publish status? | `event-settings` | | Event Settings | Where do I edit the event basics and publish status? | Event Settings |
| Branding | How do I upload logos and set colors? | `event-branding-assets` | | Branding | How do I upload logos and set colors? | Branding & Assets |
| Tasks & Collections | How do I manage guest tasks and prompts? | `event-tasks-setup` | | Tasks & Collections | How do I manage guest tasks and prompts? | Event Tasks & Collections |
| Guest Access | How do I share the join link or QR? | `guest-access-qr` | | Guest Access | How do I share the join link or QR? | Guest Access & QR |
| Team & Invites | How do I invite staff and assign roles? | `event-team-invites` | | Team & Invites | How do I invite staff and assign roles? | Event Team & Invites |
| Control Room | How do I moderate uploads and manage the queue? | `control-room-moderation` | | Control Room | How do I moderate uploads and manage the queue? | Control Room: Moderation & Queue |
| Live Show | How do I configure the Live Show player? | `live-show-setup` | | Live Show | How do I configure the Live Show player? | Set up & run Live Show |
| Billing & Exports | Where do I manage packages and data exports? | `billing-packages-exports` | | Billing & Exports | Where do I manage packages and data exports? | Packages, Billing & Exports |
| Wrap-up & Compliance | What should I do after the event ends? | `post-event-wrapup` | | Wrap-up & Compliance | What should I do after the event ends? | Post-Event Wrap-up |
| FAQ | What are the most common questions? | `faq-admin` | | FAQ | What are the most common questions? | FAQ: Event Admin |
Use the in-app help center for mobile-friendly reading, or open `/help/admin` on desktop for the full layout. Use the in-app help center for mobile-friendly reading, or open `/help/admin` on desktop for the full layout.

View File

@@ -3,7 +3,7 @@ title: "Event Dashboard Overview"
locale: en locale: en
slug: tenant-dashboard-overview slug: tenant-dashboard-overview
audience: admin audience: admin
summary: "Understand event status, KPIs, and the setup checklist at a glance." summary: "Understand event status, key numbers, and the setup checklist at a glance."
version_introduced: 2025.4 version_introduced: 2025.4
requires_app_version: "^3.2.0" requires_app_version: "^3.2.0"
status: draft status: draft
@@ -21,14 +21,14 @@ Use this guide right after creating an event or whenever you need a quick health
## What the dashboard shows ## What the dashboard shows
- **Event status** (Draft / Published) so you always know what guests can see. - **Event status** (Draft / Published) so you always know what guests can see.
- **KPIs** for guests, uploads, and engagement so you can spot momentum early. - **Key numbers** for guests, uploads, and engagement so you can spot momentum early.
- **Setup checklist** with the remaining steps before launch. - **Setup checklist** with the remaining steps before launch.
- **Quick actions** that jump to tasks, Live Show, branding, and QR tools. - **Quick actions** that jump to tasks, Live Show, branding, and QR tools.
## How to use it ## How to use it
1. **Confirm the status** and keep the event in Draft until everything is ready. 1. **Confirm the status** and keep the event in Draft until everything is ready.
2. **Work the checklist** from top to bottom; it mirrors the most common prep path. 2. **Work the checklist** from top to bottom; it mirrors the most common prep path.
3. **Check KPIs** to validate that invites and uploads are coming in. 3. **Check the numbers** to validate that invites and uploads are coming in.
4. **Jump into details** (tasks, QR, Live Show) using the quick actions. 4. **Jump into details** (tasks, QR, Live Show) using the quick actions.
### Tips ### Tips

View File

@@ -155,7 +155,7 @@
}, },
"addOns": { "addOns": {
"title": "Add-on-Verlauf", "title": "Add-on-Verlauf",
"description": "Einmalige Add-ons, die für diesen Tenant gebucht wurden.", "description": "Einmalige Add-ons, die für diesen Account gebucht wurden.",
"empty": "Noch keine Add-ons gebucht.", "empty": "Noch keine Add-ons gebucht.",
"badge": "Add-ons", "badge": "Add-ons",
"table": { "table": {
@@ -2031,7 +2031,7 @@
"sections": { "sections": {
"mode": "Standard vs. Event-spezifisch", "mode": "Standard vs. Event-spezifisch",
"toggleTitle": "Branding-Quelle wählen", "toggleTitle": "Branding-Quelle wählen",
"toggleDescription": "Nutze das Standard-Branding des Tenants oder überschreibe es nur für dieses Event.", "toggleDescription": "Nutze das Standard-Branding des Accounts oder überschreibe es nur für dieses Event.",
"palette": "Palette & Modus", "palette": "Palette & Modus",
"colorsTitle": "Farben & Light/Dark", "colorsTitle": "Farben & Light/Dark",
"colorsDescription": "Primär-, Sekundär-, Hintergrund- und Surface-Farbe festlegen.", "colorsDescription": "Primär-, Sekundär-, Hintergrund- und Surface-Farbe festlegen.",
@@ -2047,7 +2047,7 @@
}, },
"useDefault": "Standard nutzen", "useDefault": "Standard nutzen",
"useCustom": "Event-spezifisch", "useCustom": "Event-spezifisch",
"toggleHint": "Standard übernimmt die Tenant-Farben, Event-spezifisch überschreibt sie.", "toggleHint": "Standard übernimmt die Account-Farben, Event-spezifisch überschreibt sie.",
"standard": "Standard", "standard": "Standard",
"custom": "Event", "custom": "Event",
"toggleAria": "Event-spezifisches Branding aktivieren", "toggleAria": "Event-spezifisches Branding aktivieren",
@@ -2095,10 +2095,10 @@
"saved": "Branding gespeichert.", "saved": "Branding gespeichert.",
"saveError": "Branding konnte nicht gespeichert werden.", "saveError": "Branding konnte nicht gespeichert werden.",
"footer": { "footer": {
"default": "Standard-Farben des Tenants aktiv.", "default": "Standard-Farben des Accounts aktiv.",
"custom": "Event-spezifisches Branding aktiv." "custom": "Event-spezifisches Branding aktiv."
}, },
"usingDefault": "Tenant-Branding aktiv", "usingDefault": "Account-Branding aktiv",
"usingCustom": "Event-Branding aktiv", "usingCustom": "Event-Branding aktiv",
"preview": { "preview": {
"demoTitle": "Demo-Event", "demoTitle": "Demo-Event",
@@ -2662,7 +2662,7 @@
"mobileSettings": { "mobileSettings": {
"title": "Einstellungen", "title": "Einstellungen",
"accountTitle": "Account", "accountTitle": "Account",
"tenantBadge": "Tenant #{{id}}", "tenantBadge": "Konto #{{id}}",
"notificationsTitle": "Benachrichtigungen", "notificationsTitle": "Benachrichtigungen",
"notificationsLoading": "Lade Einstellungen ...", "notificationsLoading": "Lade Einstellungen ...",
"pushTitle": "App Push", "pushTitle": "App Push",
@@ -2848,12 +2848,12 @@
"includeMediaHint": "Größeres ZIP; nur bei Bedarf." "includeMediaHint": "Größeres ZIP; nur bei Bedarf."
}, },
"scopes": { "scopes": {
"tenant": "Mandantenexport", "tenant": "Kontoexport",
"event": "Event-Export" "event": "Event-Export"
}, },
"history": { "history": {
"title": "Letzte Exporte", "title": "Letzte Exporte",
"hint": "Die letzten 10 Exporte für Mandant und Events.", "hint": "Die letzten 10 Exporte für dein Konto und Events.",
"empty": "Noch keine Exporte." "empty": "Noch keine Exporte."
}, },
"status": { "status": {

View File

@@ -155,7 +155,7 @@
}, },
"addOns": { "addOns": {
"title": "Add-on history", "title": "Add-on history",
"description": "One-time add-ons purchased for this tenant.", "description": "One-time add-ons purchased for this account.",
"empty": "No add-ons purchased yet.", "empty": "No add-ons purchased yet.",
"badge": "Add-ons", "badge": "Add-ons",
"table": { "table": {
@@ -2027,7 +2027,7 @@
"sections": { "sections": {
"mode": "Default vs. event-specific", "mode": "Default vs. event-specific",
"toggleTitle": "Choose branding source", "toggleTitle": "Choose branding source",
"toggleDescription": "Use tenant defaults or override only for this event.", "toggleDescription": "Use account defaults or override only for this event.",
"palette": "Palette & mode", "palette": "Palette & mode",
"colorsTitle": "Colors & light/dark", "colorsTitle": "Colors & light/dark",
"colorsDescription": "Set primary, secondary, background, and surface colors.", "colorsDescription": "Set primary, secondary, background, and surface colors.",
@@ -2043,7 +2043,7 @@
}, },
"useDefault": "Use default", "useDefault": "Use default",
"useCustom": "Event-specific", "useCustom": "Event-specific",
"toggleHint": "Default uses tenant colors; event-specific overrides them.", "toggleHint": "Default uses account colors; event-specific overrides them.",
"standard": "Default", "standard": "Default",
"custom": "Event", "custom": "Event",
"toggleAria": "Toggle event-specific branding", "toggleAria": "Toggle event-specific branding",
@@ -2091,10 +2091,10 @@
"saved": "Branding saved.", "saved": "Branding saved.",
"saveError": "Branding could not be saved.", "saveError": "Branding could not be saved.",
"footer": { "footer": {
"default": "Tenant default colors active.", "default": "Account default colors active.",
"custom": "Event-specific branding active." "custom": "Event-specific branding active."
}, },
"usingDefault": "Tenant branding active", "usingDefault": "Account branding active",
"usingCustom": "Event branding active", "usingCustom": "Event branding active",
"preview": { "preview": {
"demoTitle": "Demo event", "demoTitle": "Demo event",
@@ -2664,7 +2664,7 @@
"mobileSettings": { "mobileSettings": {
"title": "Settings", "title": "Settings",
"accountTitle": "Account", "accountTitle": "Account",
"tenantBadge": "Tenant #{{id}}", "tenantBadge": "Account #{{id}}",
"notificationsTitle": "Notifications", "notificationsTitle": "Notifications",
"notificationsLoading": "Loading settings ...", "notificationsLoading": "Loading settings ...",
"pushTitle": "App Push", "pushTitle": "App Push",
@@ -2839,7 +2839,7 @@
"title": "Data exports", "title": "Data exports",
"request": { "request": {
"title": "Export request", "title": "Export request",
"hint": "Export tenant data or a specific event archive.", "hint": "Export account data or a specific event archive.",
"progress": "Export is running. This list refreshes automatically." "progress": "Export is running. This list refreshes automatically."
}, },
"fields": { "fields": {
@@ -2850,12 +2850,12 @@
"includeMediaHint": "Bigger ZIP; choose when needed." "includeMediaHint": "Bigger ZIP; choose when needed."
}, },
"scopes": { "scopes": {
"tenant": "Tenant export", "tenant": "Account export",
"event": "Event export" "event": "Event export"
}, },
"history": { "history": {
"title": "Recent exports", "title": "Recent exports",
"hint": "Latest 10 exports for your tenant and events.", "hint": "Latest 10 exports for your account and events.",
"empty": "No exports yet." "empty": "No exports yet."
}, },
"status": { "status": {

View File

@@ -685,7 +685,7 @@ export default function MobileBrandingPage() {
</XStack> </XStack>
<Text fontSize="$xs" color={muted}> <Text fontSize="$xs" color={muted}>
{form.useDefaultBranding {form.useDefaultBranding
? t('events.branding.usingDefault', 'Tenant-Branding aktiv') ? t('events.branding.usingDefault', 'Account-Branding aktiv')
: t('events.branding.usingCustom', 'Event-Branding aktiv')} : t('events.branding.usingCustom', 'Event-Branding aktiv')}
</Text> </Text>
</MobileCard> </MobileCard>

View File

@@ -197,7 +197,7 @@ export function DataExportsPanel({
{t('dataExports.request.title', 'Export request')} {t('dataExports.request.title', 'Export request')}
</Text> </Text>
<Text fontSize="$xs" color={muted}> <Text fontSize="$xs" color={muted}>
{t('dataExports.request.hint', 'Export tenant data or a specific event archive.')} {t('dataExports.request.hint', 'Export account data or a specific event archive.')}
</Text> </Text>
<YStack space="$2"> <YStack space="$2">
{!isRecap ? ( {!isRecap ? (
@@ -211,7 +211,7 @@ export function DataExportsPanel({
compact compact
style={{ minWidth: 140, maxWidth: 180 }} style={{ minWidth: 140, maxWidth: 180 }}
> >
<option value="tenant">{t('dataExports.scopes.tenant', 'Tenant')}</option> <option value="tenant">{t('dataExports.scopes.tenant', 'Account')}</option>
<option value="event">{t('dataExports.scopes.event', 'Event')}</option> <option value="event">{t('dataExports.scopes.event', 'Event')}</option>
</MobileSelect> </MobileSelect>
</XStack> </XStack>
@@ -281,7 +281,7 @@ export function DataExportsPanel({
{t('dataExports.history.title', 'Recent exports')} {t('dataExports.history.title', 'Recent exports')}
</Text> </Text>
<Text fontSize="$xs" color={muted}> <Text fontSize="$xs" color={muted}>
{t('dataExports.history.hint', 'Latest 10 exports for your tenant and events.')} {t('dataExports.history.hint', 'Latest 10 exports for your account and events.')}
</Text> </Text>
{loading ? ( {loading ? (
<YStack space="$2"> <YStack space="$2">
@@ -301,7 +301,7 @@ export function DataExportsPanel({
<Text fontSize="$sm" fontWeight="700" color={textStrong}> <Text fontSize="$sm" fontWeight="700" color={textStrong}>
{entry.scope === 'event' {entry.scope === 'event'
? t('dataExports.scopes.event', 'Event export') ? t('dataExports.scopes.event', 'Event export')
: t('dataExports.scopes.tenant', 'Tenant export')} : t('dataExports.scopes.tenant', 'Account export')}
</Text> </Text>
{entry.event ? ( {entry.event ? (
<Text fontSize="$xs" color={muted}> <Text fontSize="$xs" color={muted}>

View File

@@ -221,7 +221,7 @@ export default function MobileSettingsPage() {
{user?.name ?? user?.email ?? t('settings.session.unknown', 'Benutzer')} {user?.name ?? user?.email ?? t('settings.session.unknown', 'Benutzer')}
</Text> </Text>
{user?.tenant_id ? ( {user?.tenant_id ? (
<PillBadge tone="muted">{t('mobileSettings.tenantBadge', 'Tenant #{{id}}', { id: user.tenant_id })}</PillBadge> <PillBadge tone="muted">{t('mobileSettings.tenantBadge', 'Account #{{id}}', { id: user.tenant_id })}</PillBadge>
) : null} ) : null}
<XStack space="$2"> <XStack space="$2">
<CTAButton label={t('settings.profile.actions.openProfile', 'Profil bearbeiten')} onPress={() => navigate(ADMIN_PROFILE_ACCOUNT_PATH)} /> <CTAButton label={t('settings.profile.actions.openProfile', 'Profil bearbeiten')} onPress={() => navigate(ADMIN_PROFILE_ACCOUNT_PATH)} />

View File

@@ -0,0 +1,119 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
const navigateMock = vi.fn();
const tMock = (
_key: string,
fallback?: string | Record<string, unknown>,
options?: Record<string, unknown>,
) => {
let value = typeof fallback === 'string' ? fallback : _key;
if (options) {
Object.entries(options).forEach(([key, val]) => {
value = value.replaceAll(`{{${key}}}`, String(val));
});
}
return value;
};
vi.mock('react-router-dom', () => ({
useNavigate: () => navigateMock,
}));
vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: tMock }),
initReactI18next: {
type: '3rdParty',
init: () => undefined,
},
}));
vi.mock('../hooks/useBackNavigation', () => ({
useBackNavigation: () => vi.fn(),
}));
vi.mock('../theme', () => ({
useAdminTheme: () => ({
textStrong: '#111827',
text: '#111827',
muted: '#6b7280',
border: '#e5e7eb',
danger: '#b91c1c',
}),
}));
vi.mock('../components/MobileShell', () => ({
MobileShell: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
HeaderActionButton: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('../components/Primitives', () => ({
MobileCard: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
CTAButton: ({ label, onPress }: { label: string; onPress?: () => void }) => (
<button type="button" onClick={onPress}>
{label}
</button>
),
PillBadge: ({ children }: { children: React.ReactNode }) => <span>{children}</span>,
SkeletonCard: () => <div>Loading...</div>,
}));
vi.mock('../components/FormControls', () => ({
MobileSelect: ({ children, ...props }: { children: React.ReactNode }) => (
<select {...props}>{children}</select>
),
}));
vi.mock('@tamagui/stacks', () => ({
YStack: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
XStack: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('@tamagui/text', () => ({
SizableText: ({ children }: { children: React.ReactNode }) => <span>{children}</span>,
}));
vi.mock('@tamagui/switch', () => ({
Switch: Object.assign(
({ children }: { children: React.ReactNode }) => <div>{children}</div>,
{ Thumb: () => <div /> },
),
}));
vi.mock('@tamagui/react-native-web-lite', () => ({
Pressable: ({
children,
onPress,
...rest
}: {
children: React.ReactNode;
onPress?: () => void;
[key: string]: unknown;
}) => (
<button type="button" onClick={onPress} {...rest}>
{children}
</button>
),
FlatList: ({ data = [], renderItem }: { data?: unknown[]; renderItem?: (info: any) => React.ReactNode }) => (
<div>{renderItem ? data.map((item, index) => renderItem({ item, index })) : null}</div>
),
}));
vi.mock('../../api', () => ({
listTenantDataExports: vi.fn().mockResolvedValue([]),
getEvents: vi.fn().mockResolvedValue([]),
requestTenantDataExport: vi.fn(),
downloadTenantDataExport: vi.fn(),
}));
import MobileDataExportsPage from '../DataExportsPage';
describe('MobileDataExportsPage', () => {
it('renders account scope label', async () => {
render(<MobileDataExportsPage />);
expect(await screen.findByText('Account')).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,152 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
const navigateMock = vi.fn();
const logoutMock = vi.fn();
const tMock = (
_key: string,
fallback?: string | Record<string, unknown>,
options?: Record<string, unknown>,
) => {
let value = typeof fallback === 'string' ? fallback : _key;
if (options) {
Object.entries(options).forEach(([key, val]) => {
value = value.replaceAll(`{{${key}}}`, String(val));
});
}
return value;
};
vi.mock('react-router-dom', () => ({
useNavigate: () => navigateMock,
}));
vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: tMock }),
initReactI18next: {
type: '3rdParty',
init: () => undefined,
},
}));
vi.mock('../../auth/context', () => ({
useAuth: () => ({ user: { name: 'Test User', tenant_id: 123 }, logout: logoutMock }),
}));
vi.mock('../../api', () => ({
getNotificationPreferences: vi.fn().mockResolvedValue({ defaults: {}, preferences: {} }),
updateNotificationPreferences: vi.fn(),
}));
vi.mock('../components/MobileShell', () => ({
MobileShell: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('../components/Primitives', () => ({
MobileCard: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
CTAButton: ({ label, onPress }: { label: string; onPress?: () => void }) => (
<button type="button" onClick={onPress}>
{label}
</button>
),
PillBadge: ({ children }: { children: React.ReactNode }) => <span>{children}</span>,
}));
vi.mock('../components/MobileInstallBanner', () => ({
MobileInstallBanner: () => null,
}));
vi.mock('../hooks/useAdminPushSubscription', () => ({
useAdminPushSubscription: () => ({ supported: false, permission: 'default', subscribed: false }),
}));
vi.mock('../hooks/useDevicePermissions', () => ({
useDevicePermissions: () => ({
loading: false,
notifications: 'granted',
camera: 'granted',
storage: 'available',
requestPersistentStorage: vi.fn().mockResolvedValue(false),
refresh: vi.fn(),
}),
}));
vi.mock('../hooks/useInstallPrompt', () => ({
useInstallPrompt: () => ({
isInstalled: false,
isStandalone: false,
canInstall: false,
isIos: false,
promptInstall: vi.fn(),
}),
}));
vi.mock('../hooks/useBackNavigation', () => ({
useBackNavigation: () => vi.fn(),
}));
vi.mock('../hooks/useOnlineStatus', () => ({
useOnlineStatus: () => true,
}));
vi.mock('../lib/installBanner', () => ({
getInstallBannerDismissed: () => false,
setInstallBannerDismissed: () => undefined,
shouldShowInstallBanner: () => false,
}));
vi.mock('../lib/mobileTour', () => ({
setTourSeen: () => undefined,
}));
vi.mock('../theme', () => ({
useAdminTheme: () => ({
text: '#111827',
muted: '#6b7280',
border: '#e5e7eb',
danger: '#b91c1c',
}),
}));
vi.mock('@tamagui/stacks', () => ({
YStack: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
XStack: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('@tamagui/text', () => ({
SizableText: ({ children }: { children: React.ReactNode }) => <span>{children}</span>,
}));
vi.mock('@tamagui/group', () => ({
YGroup: Object.assign(({ children }: { children: React.ReactNode }) => <div>{children}</div>, {
Item: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}),
}));
vi.mock('@tamagui/list-item', () => ({
ListItem: ({ title, subTitle }: { title?: React.ReactNode; subTitle?: React.ReactNode }) => (
<div>
{title}
{subTitle}
</div>
),
}));
vi.mock('@tamagui/switch', () => ({
Switch: Object.assign(
({ children }: { children: React.ReactNode }) => <div>{children}</div>,
{ Thumb: () => <div /> },
),
}));
import MobileSettingsPage from '../SettingsPage';
describe('MobileSettingsPage', () => {
it('renders account badge for the current user', async () => {
render(<MobileSettingsPage />);
expect(await screen.findByText('Account #123')).toBeInTheDocument();
});
});