rework internal order system

This commit is contained in:
Matthias Hochmeister
2026-03-24 08:59:46 +01:00
parent 3c0a8a6832
commit 6ff5cc89ad
8 changed files with 240 additions and 154 deletions

View File

@@ -1,17 +1,16 @@
import { Card, CardContent, Typography, Box, Chip, List, ListItem, ListItemText, Divider, Skeleton } from '@mui/material';
import { Card, CardContent, Typography, Box, Chip, Skeleton } from '@mui/material';
import { Build } from '@mui/icons-material';
import { useQuery } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { ausruestungsanfrageApi } from '../../services/ausruestungsanfrage';
import { AUSRUESTUNG_STATUS_LABELS, AUSRUESTUNG_STATUS_COLORS } from '../../types/ausruestungsanfrage.types';
import type { AusruestungAnfrageStatus } from '../../types/ausruestungsanfrage.types';
import type { AusruestungWidgetOverview } from '../../types/ausruestungsanfrage.types';
function AusruestungsanfrageWidget() {
const navigate = useNavigate();
const { data: requests, isLoading, isError } = useQuery({
queryKey: ['ausruestungsanfrage-widget-requests'],
queryFn: () => ausruestungsanfrageApi.getRequests({ status: 'offen' }),
const { data: overview, isLoading, isError } = useQuery<AusruestungWidgetOverview>({
queryKey: ['ausruestungsanfrage-widget-overview'],
queryFn: () => ausruestungsanfrageApi.getWidgetOverview(),
refetchInterval: 5 * 60 * 1000,
retry: 1,
});
@@ -27,76 +26,42 @@ function AusruestungsanfrageWidget() {
);
}
if (isError) {
if (isError || !overview) {
return (
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>Interne Bestellungen</Typography>
<Typography variant="body2" color="text.secondary">
Anfragen konnten nicht geladen werden.
Daten konnten nicht geladen werden.
</Typography>
</CardContent>
</Card>
);
}
const pendingCount = requests?.length ?? 0;
if (pendingCount === 0) {
return (
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>Interne Bestellungen</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, color: 'text.secondary' }}>
<Build fontSize="small" />
<Typography variant="body2">Keine offenen Anfragen</Typography>
</Box>
</CardContent>
</Card>
);
}
const hasAny = overview.total_count > 0;
return (
<Card>
<Card
sx={{ cursor: 'pointer', '&:hover': { bgcolor: 'action.hover' } }}
onClick={() => navigate('/ausruestungsanfrage')}
>
<CardContent>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1.5 }}>
<Typography variant="h6">Interne Bestellungen</Typography>
<Chip label={`${pendingCount} offen`} size="small" color="warning" />
<Build fontSize="small" color="action" />
</Box>
<List dense disablePadding>
{(requests ?? []).slice(0, 5).map((req, idx) => (
<Box key={req.id}>
{idx > 0 && <Divider />}
<ListItem
disablePadding
sx={{ cursor: 'pointer', py: 0.5, '&:hover': { bgcolor: 'action.hover' } }}
onClick={() => navigate('/ausruestungsanfrage?tab=2')}
>
<ListItemText
primary={`Anfrage #${req.id}`}
secondary={req.anfrager_name || 'Unbekannt'}
primaryTypographyProps={{ variant: 'body2' }}
secondaryTypographyProps={{ variant: 'caption' }}
/>
<Chip
label={AUSRUESTUNG_STATUS_LABELS[req.status as AusruestungAnfrageStatus]}
color={AUSRUESTUNG_STATUS_COLORS[req.status as AusruestungAnfrageStatus]}
size="small"
sx={{ ml: 1 }}
/>
</ListItem>
</Box>
))}
</List>
{pendingCount > 5 && (
<Typography
variant="caption"
color="primary"
sx={{ cursor: 'pointer', mt: 1, display: 'block' }}
onClick={() => navigate('/ausruestungsanfrage?tab=2')}
>
Alle {pendingCount} Anfragen anzeigen
</Typography>
{!hasAny ? (
<Typography variant="body2" color="text.secondary">Keine Anfragen vorhanden.</Typography>
) : (
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
<Chip label={`${overview.pending_count} Offen`} size="small" color={overview.pending_count > 0 ? 'warning' : 'default'} variant="outlined" />
<Chip label={`${overview.approved_count} Genehmigt`} size="small" color={overview.approved_count > 0 ? 'info' : 'default'} variant="outlined" />
{overview.unhandled_count > 0 && (
<Chip label={`${overview.unhandled_count} Neu`} size="small" color="error" variant="outlined" />
)}
<Chip label={`${overview.total_count} Gesamt`} size="small" variant="outlined" />
</Box>
)}
</CardContent>
</Card>