frontend_nyla/src/pages/Dashboard.tsx
2026-04-11 19:44:52 +02:00

135 lines
4.5 KiB
TypeScript

/**
* Dashboard Page
*
* System-Übersicht für den User.
* Zeigt alle verfügbaren Feature-Instanzen pro Mandant als Karten an.
* Daten kommen vom Backend via GET /api/navigation.
*/
import React from 'react';
import { Link } from 'react-router-dom';
import useNavigation from '../hooks/useNavigation';
import type { NavigationMandate, MandateFeature, FeatureInstance as NavFeatureInstance } from '../hooks/useNavigation';
import { getPageIcon } from '../config/pageRegistry';
import { FaArrowRight, FaBuilding } from 'react-icons/fa';
import OnboardingAssistant from '../components/OnboardingAssistant';
import styles from './Dashboard.module.css';
import { useLanguage } from '../providers/language/LanguageContext';
// =============================================================================
// INSTANCE CARD
// =============================================================================
interface InstanceCardProps {
instance: NavFeatureInstance;
feature: MandateFeature;
}
const InstanceCard: React.FC<InstanceCardProps> = ({ instance, feature }) => {
// Ersten verfügbaren View-Pfad vom Backend nehmen
const targetPath = instance.views.length > 0 ? instance.views[0].uiPath : undefined;
if (!targetPath) return null;
return (
<Link to={targetPath} className={styles.instanceCard}>
<div className={styles.cardIcon}>
{getPageIcon(feature.uiComponent)}
</div>
<div className={styles.cardContent}>
<div className={styles.cardHeader}>
<span className={styles.featureLabel}>{feature.uiLabel}</span>
</div>
<h3 className={styles.instanceLabel}>{instance.uiLabel}</h3>
</div>
<div className={styles.cardArrow}>
<FaArrowRight />
</div>
</Link>
);
};
// =============================================================================
// DASHBOARD PAGE
// =============================================================================
export const DashboardPage: React.FC = () => {
const { t } = useLanguage();
const { dynamicBlock, loading } = useNavigation();
// Alle Mandate und deren Features/Instanzen aus der Navigation
const mandates: NavigationMandate[] = dynamicBlock?.mandates || [];
// Gesamtzahl Instanzen und Mandate berechnen
let totalInstances = 0;
const totalMandates = mandates.length;
mandates.forEach(m => m.features.forEach(f => {
totalInstances += f.instances.length;
}));
if (loading) {
return (
<div className={styles.dashboard}>
<header className={styles.header}>
<h1>{t('Übersicht')}</h1>
<p className={styles.subtitle}>{t('Lade')}</p>
</header>
</div>
);
}
return (
<div className={styles.dashboard}>
<header className={styles.header}>
<h1>{t('Übersicht')}</h1>
{totalInstances > 0 && (
<p className={styles.subtitle}>
{t('Du hast Zugriff auf {instanceCount} {instanceWord} in {mandateCount} {mandateWord}.', {
instanceCount: totalInstances,
instanceWord: totalInstances === 1 ? t('Feature-Instanz') : t('Feature-Instanzen'),
mandateCount: totalMandates,
mandateWord: totalMandates === 1 ? t('Mandant') : t('Mandanten'),
})}
</p>
)}
</header>
<OnboardingAssistant />
<main className={styles.content}>
{mandates
.filter(mandate => mandate.features.some(f => f.instances.length > 0))
.map(mandate => {
// Alle Instanzen dieses Mandats sammeln (flach, ohne Feature-Gruppierung)
const mandateInstances: { instance: NavFeatureInstance; feature: MandateFeature }[] = [];
for (const feature of mandate.features) {
for (const instance of feature.instances) {
mandateInstances.push({ instance, feature });
}
}
return (
<section key={mandate.id} className={styles.featureSection}>
<h2 className={styles.sectionTitle}>
<FaBuilding />
<span>{mandate.uiLabel}</span>
</h2>
<div className={styles.instanceGrid}>
{mandateInstances.map(({ instance, feature }) => (
<InstanceCard
key={instance.id}
instance={instance}
feature={feature}
/>
))}
</div>
</section>
);
})}
</main>
</div>
);
};
export default DashboardPage;