149 lines
4.9 KiB
TypeScript
149 lines
4.9 KiB
TypeScript
/**
|
|
* Dashboard Page
|
|
*
|
|
* System-Übersicht für den User.
|
|
* Zeigt alle verfügbaren Feature-Instanzen 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 } from 'react-icons/fa';
|
|
import styles from './Dashboard.module.css';
|
|
|
|
// =============================================================================
|
|
// INSTANCE CARD
|
|
// =============================================================================
|
|
|
|
interface InstanceCardProps {
|
|
instance: NavFeatureInstance;
|
|
feature: MandateFeature;
|
|
mandateLabel: string;
|
|
}
|
|
|
|
const InstanceCard: React.FC<InstanceCardProps> = ({ instance, feature, mandateLabel }) => {
|
|
// 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>
|
|
<p className={styles.mandateName}>{mandateLabel}</p>
|
|
</div>
|
|
<div className={styles.cardArrow}>
|
|
<FaArrowRight />
|
|
</div>
|
|
</Link>
|
|
);
|
|
};
|
|
|
|
// =============================================================================
|
|
// EMPTY STATE
|
|
// =============================================================================
|
|
|
|
const EmptyState: React.FC = () => (
|
|
<div className={styles.emptyState}>
|
|
<div className={styles.emptyIcon}>📋</div>
|
|
<h2>Willkommen bei PowerOn</h2>
|
|
<p>Du hast aktuell Zugriff auf keine Feature-Instanzen.</p>
|
|
<p>Kontaktiere einen Administrator, um Zugriff zu erhalten.</p>
|
|
</div>
|
|
);
|
|
|
|
// =============================================================================
|
|
// DASHBOARD PAGE
|
|
// =============================================================================
|
|
|
|
export const DashboardPage: React.FC = () => {
|
|
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>Übersicht</h1>
|
|
<p className={styles.subtitle}>Lade...</p>
|
|
</header>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (totalInstances === 0) {
|
|
return <EmptyState />;
|
|
}
|
|
|
|
// Gruppiere Instanzen nach Feature (über alle Mandate)
|
|
const featureGroups: { feature: MandateFeature; instances: { instance: NavFeatureInstance; mandateLabel: string }[] }[] = [];
|
|
const featureMap = new Map<string, typeof featureGroups[0]>();
|
|
|
|
for (const mandate of mandates) {
|
|
for (const feature of mandate.features) {
|
|
const key = feature.uiComponent;
|
|
let group = featureMap.get(key);
|
|
if (!group) {
|
|
group = { feature, instances: [] };
|
|
featureMap.set(key, group);
|
|
featureGroups.push(group);
|
|
}
|
|
for (const instance of feature.instances) {
|
|
group.instances.push({ instance, mandateLabel: mandate.uiLabel });
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className={styles.dashboard}>
|
|
<header className={styles.header}>
|
|
<h1>Übersicht</h1>
|
|
<p className={styles.subtitle}>
|
|
Du hast Zugriff auf {totalInstances} Feature-Instanz{totalInstances !== 1 ? 'en' : ''} in {totalMandates} Mandant{totalMandates !== 1 ? 'en' : ''}.
|
|
</p>
|
|
</header>
|
|
|
|
<main className={styles.content}>
|
|
{featureGroups.map(({ feature, instances }) => (
|
|
<section key={feature.uiComponent} className={styles.featureSection}>
|
|
<h2 className={styles.sectionTitle}>
|
|
{getPageIcon(feature.uiComponent)}
|
|
<span>{feature.uiLabel}</span>
|
|
</h2>
|
|
<div className={styles.instanceGrid}>
|
|
{instances.map(({ instance, mandateLabel }) => (
|
|
<InstanceCard
|
|
key={instance.id}
|
|
instance={instance}
|
|
feature={feature}
|
|
mandateLabel={mandateLabel}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
))}
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DashboardPage;
|