ui-nyla/src/pages/Dashboard.tsx
ValueOn AG d579df1c92
Some checks failed
Deploy Nyla Frontend to Integration / deploy (push) Failing after 52s
panel ui
2026-06-11 16:43:53 +02:00

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;