Some checks failed
Deploy Nyla Frontend to Integration / deploy (push) Failing after 52s
130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
// Copyright (c) 2026 PowerOn AG
|
|
// All rights reserved.
|
|
/**
|
|
* 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 { StackLayout } from '../components/Layout/StackLayout';
|
|
import { Panel } from '../components/Layout/Panel';
|
|
import styles from './Dashboard.module.css';
|
|
import { useLanguage } from '../providers/language/LanguageContext';
|
|
|
|
interface InstanceCardProps {
|
|
instance: NavFeatureInstance;
|
|
feature: MandateFeature;
|
|
}
|
|
|
|
const InstanceCard: React.FC<InstanceCardProps> = ({ instance, feature }) => {
|
|
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>
|
|
);
|
|
};
|
|
|
|
export const DashboardPage: React.FC = () => {
|
|
const { t } = useLanguage();
|
|
const { dynamicBlock, loading } = useNavigation();
|
|
|
|
const mandates: NavigationMandate[] = dynamicBlock?.mandates || [];
|
|
|
|
let totalInstances = 0;
|
|
const totalMandates = mandates.length;
|
|
mandates.forEach(m => m.features.forEach(f => {
|
|
totalInstances += f.instances.length;
|
|
}));
|
|
|
|
const mandateSections = mandates
|
|
.filter(mandate => mandate.features.some(f => f.instances.length > 0))
|
|
.map(mandate => {
|
|
const mandateInstances: { instance: NavFeatureInstance; feature: MandateFeature }[] = [];
|
|
for (const feature of mandate.features) {
|
|
for (const instance of feature.instances) {
|
|
mandateInstances.push({ instance, feature });
|
|
}
|
|
}
|
|
return { mandate, mandateInstances };
|
|
});
|
|
|
|
if (loading) {
|
|
return (
|
|
<StackLayout variant="dashboard">
|
|
<StackLayout.Header>
|
|
<h1 style={{ fontSize: '1.5rem', fontWeight: 600, margin: 0 }}>{t('Übersicht')}</h1>
|
|
<p className={styles.subtitle}>{t('Lade')}</p>
|
|
</StackLayout.Header>
|
|
</StackLayout>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<StackLayout variant="dashboard">
|
|
<StackLayout.Header>
|
|
<h1 style={{ fontSize: '1.5rem', fontWeight: 600, margin: 0 }}>{t('Übersicht')}</h1>
|
|
{totalInstances > 0 && (
|
|
<p className={styles.subtitle}>
|
|
{t('{instanceCount} Feature-Instanzen in {mandateCount} Mandanten', {
|
|
instanceCount: String(totalInstances),
|
|
mandateCount: String(totalMandates),
|
|
})}
|
|
</p>
|
|
)}
|
|
</StackLayout.Header>
|
|
<StackLayout.Body>
|
|
<Panel variant="card">
|
|
<OnboardingAssistant />
|
|
</Panel>
|
|
|
|
{mandateSections.map(({ mandate, mandateInstances }) => (
|
|
<Panel
|
|
key={mandate.id}
|
|
variant="dashboard"
|
|
title={(
|
|
<span className={styles.sectionTitle}>
|
|
<FaBuilding />
|
|
<span>{mandate.uiLabel}</span>
|
|
</span>
|
|
)}
|
|
>
|
|
<div className={styles.instanceGrid}>
|
|
{mandateInstances.map(({ instance, feature }) => (
|
|
<InstanceCard
|
|
key={instance.id}
|
|
instance={instance}
|
|
feature={feature}
|
|
/>
|
|
))}
|
|
</div>
|
|
</Panel>
|
|
))}
|
|
</StackLayout.Body>
|
|
</StackLayout>
|
|
);
|
|
};
|
|
|
|
export default DashboardPage;
|