feat: implement dynamic dashboard displaying active modules and clean up sidebar menu.
This commit is contained in:
@@ -22,3 +22,5 @@ File riassuntivo dello stato di sviluppo di Zentral.
|
|||||||
- [Menu Refactoring](./devlog/menu-refactoring.md) - Riorganizzazione menu e moduli (Dashboard, Clienti, Articoli, Risorse)
|
- [Menu Refactoring](./devlog/menu-refactoring.md) - Riorganizzazione menu e moduli (Dashboard, Clienti, Articoli, Risorse)
|
||||||
- [2025-12-03 Implementazione Modulo Personale](./devlog/2025-12-03_implementazione_modulo_personale.md) - **In Corso**
|
- [2025-12-03 Implementazione Modulo Personale](./devlog/2025-12-03_implementazione_modulo_personale.md) - **In Corso**
|
||||||
- Implementazione entità, API e Frontend per gestione Personale (Dipendenti, Contratti, Assenze, Pagamenti).
|
- Implementazione entità, API e Frontend per gestione Personale (Dipendenti, Contratti, Assenze, Pagamenti).
|
||||||
|
- [2025-12-04 Zentral Dashboard and Menu Cleanup](./devlog/2025-12-04-023000_zentral_dashboard.md) - **Completato**
|
||||||
|
- Pulizia menu Zentral (rimozione voci ridondanti) e creazione nuova Dashboard principale con riepilogo moduli attivi.
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Zentral Dashboard and Menu Cleanup
|
||||||
|
|
||||||
|
## Stato Attuale
|
||||||
|
Completato.
|
||||||
|
|
||||||
|
## Lavoro Svolto
|
||||||
|
1. **Pulizia Menu Zentral**:
|
||||||
|
- Verificato che le voci "Clienti", "Articoli" e "Risorse" nel menu "Zentral" erano ridondanti o non funzionanti.
|
||||||
|
- "Articoli" è gestito dal modulo Warehouse (`/warehouse/articles`).
|
||||||
|
- "Clienti" e "Risorse" erano link non funzionanti (`/clienti`, `/risorse` non definiti nelle rotte).
|
||||||
|
- Rimossi questi elementi dal menu laterale (`Sidebar.tsx`).
|
||||||
|
- Appiattito il menu "Zentral" in un'unica voce di primo livello "Zentral Dashboard" che punta direttamente alla home page.
|
||||||
|
|
||||||
|
2. **Nuova Zentral Dashboard**:
|
||||||
|
- Aggiornato `src/frontend/src/pages/Dashboard.tsx` per diventare la nuova homepage "Zentral Dashboard".
|
||||||
|
- La dashboard ora mostra:
|
||||||
|
- Un messaggio di benvenuto con il conteggio dei moduli attivi.
|
||||||
|
- Una griglia di card per ogni modulo attivo, con icona, nome, descrizione e pulsante per aprire l'applicazione.
|
||||||
|
- Gestione dello stato di caricamento e caso di nessun modulo attivo.
|
||||||
|
- La dashboard utilizza `useModules` per recuperare dinamicamente i moduli attivi.
|
||||||
|
- Integrata con il sistema di Tab (`openTab`) per aprire le applicazioni.
|
||||||
|
|
||||||
|
## Prossimi Passi Suggeriti
|
||||||
|
- Implementare endpoint di backend per recuperare statistiche globali reali (es. numero ordini aperti, valore magazzino, ecc.) da mostrare nella dashboard principale.
|
||||||
|
- Aggiungere widget personalizzabili nella dashboard.
|
||||||
@@ -17,8 +17,6 @@ import {
|
|||||||
Event as EventIcon,
|
Event as EventIcon,
|
||||||
People as PeopleIcon,
|
People as PeopleIcon,
|
||||||
Place as PlaceIcon,
|
Place as PlaceIcon,
|
||||||
Inventory as InventoryIcon,
|
|
||||||
Person as PersonIcon,
|
|
||||||
CalendarMonth as CalendarIcon,
|
CalendarMonth as CalendarIcon,
|
||||||
Print as PrintIcon,
|
Print as PrintIcon,
|
||||||
Extension as ModulesIcon,
|
Extension as ModulesIcon,
|
||||||
@@ -84,15 +82,10 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) {
|
|||||||
|
|
||||||
const menuStructure: MenuItem[] = [
|
const menuStructure: MenuItem[] = [
|
||||||
{
|
{
|
||||||
id: 'core',
|
id: 'dashboard',
|
||||||
label: 'Zentral',
|
label: 'Zentral Dashboard',
|
||||||
icon: <DashboardIcon />,
|
icon: <DashboardIcon />,
|
||||||
children: [
|
path: '/',
|
||||||
{ id: 'dashboard', label: t('menu.dashboard'), icon: <DashboardIcon />, path: '/' },
|
|
||||||
{ id: 'clients', label: t('menu.clients'), icon: <PeopleIcon />, path: '/clienti' },
|
|
||||||
{ id: 'articles', label: t('menu.articles'), icon: <InventoryIcon />, path: '/articoli' },
|
|
||||||
{ id: 'resources', label: t('menu.resources'), icon: <PersonIcon />, path: '/risorse' },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'warehouse',
|
id: 'warehouse',
|
||||||
|
|||||||
@@ -1,20 +1,205 @@
|
|||||||
import { Box, Typography, Grid, Paper } from '@mui/material';
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Typography,
|
||||||
|
Grid,
|
||||||
|
Paper,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardActions,
|
||||||
|
Button,
|
||||||
|
Chip,
|
||||||
|
useTheme,
|
||||||
|
alpha
|
||||||
|
} from '@mui/material';
|
||||||
|
import {
|
||||||
|
Dashboard as DashboardIcon,
|
||||||
|
Event as EventIcon,
|
||||||
|
People as PeopleIcon,
|
||||||
|
ShoppingCart as ShoppingCartIcon,
|
||||||
|
Sell as SellIcon,
|
||||||
|
Factory as ProductionIcon,
|
||||||
|
Settings as SettingsIcon,
|
||||||
|
ArrowForward as ArrowForwardIcon,
|
||||||
|
Storage as StorageIcon
|
||||||
|
} from '@mui/icons-material';
|
||||||
|
import { useModules } from '../contexts/ModuleContext';
|
||||||
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
import { useTabs } from '../contexts/TabContext';
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
|
const { activeModules, isLoading } = useModules();
|
||||||
|
const { t } = useLanguage();
|
||||||
|
const { openTab } = useTabs();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const getModuleIcon = (code: string) => {
|
||||||
|
switch (code) {
|
||||||
|
case 'warehouse': return <StorageIcon fontSize="large" />;
|
||||||
|
case 'purchases': return <ShoppingCartIcon fontSize="large" />;
|
||||||
|
case 'sales': return <SellIcon fontSize="large" />;
|
||||||
|
case 'production': return <ProductionIcon fontSize="large" />;
|
||||||
|
case 'events': return <EventIcon fontSize="large" />;
|
||||||
|
case 'hr': return <PeopleIcon fontSize="large" />;
|
||||||
|
default: return <DashboardIcon fontSize="large" />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getModulePath = (code: string) => {
|
||||||
|
switch (code) {
|
||||||
|
case 'warehouse': return '/warehouse';
|
||||||
|
case 'purchases': return '/purchases/orders'; // Default to orders for now
|
||||||
|
case 'sales': return '/sales/orders';
|
||||||
|
case 'production': return '/production';
|
||||||
|
case 'events': return '/events/list';
|
||||||
|
case 'hr': return '/hr/dipendenti';
|
||||||
|
default: return '/';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModuleClick = (module: any) => {
|
||||||
|
const path = getModulePath(module.code);
|
||||||
|
openTab(path, module.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Box sx={{ p: 3 }}>Loading...</Box>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box sx={{ p: 3 }}>
|
||||||
<Typography variant="h4" gutterBottom>
|
<Box sx={{ mb: 4 }}>
|
||||||
Dashboard
|
<Typography variant="h3" gutterBottom sx={{ fontWeight: 'bold', color: 'primary.main' }}>
|
||||||
|
Zentral Dashboard
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography variant="h6" color="text.secondary">
|
||||||
|
Overview of your active modules and system status
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
|
{/* System Status / Welcome Card */}
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
<Paper sx={{ p: 2 }}>
|
<Paper
|
||||||
<Typography variant="h6">Welcome to Zentral</Typography>
|
sx={{
|
||||||
<Typography variant="body1">
|
p: 3,
|
||||||
Select a module from the menu to get started.
|
background: `linear-gradient(45deg, ${theme.palette.primary.main} 30%, ${theme.palette.primary.dark} 90%)`,
|
||||||
|
color: 'white',
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: 3
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid container alignItems="center" spacing={2}>
|
||||||
|
<Grid size={{ xs: 12, md: 8 }}>
|
||||||
|
<Typography variant="h4" gutterBottom>
|
||||||
|
Welcome back!
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
You have {activeModules.length} active modules running.
|
||||||
|
Select a module below to start working or manage your subscriptions in the Admin panel.
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: 12, md: 4 }} sx={{ textAlign: 'right' }}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="secondary"
|
||||||
|
startIcon={<SettingsIcon />}
|
||||||
|
onClick={() => openTab('/modules', t('menu.modules'))}
|
||||||
|
sx={{ bgcolor: 'white', color: 'primary.main', '&:hover': { bgcolor: 'grey.100' } }}
|
||||||
|
>
|
||||||
|
Manage Modules
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
{/* Active Modules Grid */}
|
||||||
|
<Grid size={12}>
|
||||||
|
<Typography variant="h5" sx={{ mb: 2, mt: 2, fontWeight: 'medium' }}>
|
||||||
|
Active Applications
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{activeModules.map((module) => (
|
||||||
|
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 3 }} key={module.code}>
|
||||||
|
<Card
|
||||||
|
sx={{
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
transition: 'transform 0.2s, box-shadow 0.2s',
|
||||||
|
'&:hover': {
|
||||||
|
transform: 'translateY(-4px)',
|
||||||
|
boxShadow: 6,
|
||||||
|
cursor: 'pointer'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onClick={() => handleModuleClick(module)}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ flexGrow: 1 }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
p: 1.5,
|
||||||
|
borderRadius: 2,
|
||||||
|
bgcolor: alpha(theme.palette.primary.main, 0.1),
|
||||||
|
color: 'primary.main',
|
||||||
|
mr: 2
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getModuleIcon(module.code)}
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6" component="div">
|
||||||
|
{module.name}
|
||||||
|
</Typography>
|
||||||
|
<Chip
|
||||||
|
label="Active"
|
||||||
|
size="small"
|
||||||
|
color="success"
|
||||||
|
variant="outlined"
|
||||||
|
sx={{ height: 20, fontSize: '0.7rem' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
{module.description || `Manage your ${module.name.toLowerCase()} efficiently.`}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
<CardActions sx={{ p: 2, pt: 0 }}>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
endIcon={<ArrowForwardIcon />}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleModuleClick(module);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Open App
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{activeModules.length === 0 && (
|
||||||
|
<Grid size={12}>
|
||||||
|
<Paper sx={{ p: 4, textAlign: 'center', bgcolor: 'background.default', borderStyle: 'dashed' }}>
|
||||||
|
<Typography variant="h6" color="text.secondary" gutterBottom>
|
||||||
|
No active modules found
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
startIcon={<SettingsIcon />}
|
||||||
|
onClick={() => openTab('/modules', t('menu.modules'))}
|
||||||
|
sx={{ mt: 2 }}
|
||||||
|
>
|
||||||
|
Go to Store
|
||||||
|
</Button>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user