fix(frontend): order status button colors, delivery gate logic, partial delivery chips, and scheduled message form tweaks

This commit is contained in:
Matthias Hochmeister
2026-04-17 10:50:42 +02:00
parent b91cf88812
commit 7d2ea57c17
5 changed files with 31 additions and 11 deletions

View File

@@ -108,7 +108,11 @@ function MeineAnfragenTab() {
<TableCell>{formatOrderId(r)}</TableCell> <TableCell>{formatOrderId(r)}</TableCell>
<TableCell>{r.bezeichnung || '-'}</TableCell> <TableCell>{r.bezeichnung || '-'}</TableCell>
<TableCell><Chip label={AUSRUESTUNG_STATUS_LABELS[r.status]} color={AUSRUESTUNG_STATUS_COLORS[r.status]} size="small" /></TableCell> <TableCell><Chip label={AUSRUESTUNG_STATUS_LABELS[r.status]} color={AUSRUESTUNG_STATUS_COLORS[r.status]} size="small" /></TableCell>
<TableCell>{r.im_haus ? <Chip label="Im Haus" color="success" size="small" /> : null}</TableCell> <TableCell>
{r.im_haus && r.geliefert_count != null && r.positionen_count != null && r.geliefert_count < r.positionen_count
? <Chip label="Teilweise im Haus" color="warning" size="small" />
: r.im_haus ? <Chip label="Im Haus" color="success" size="small" /> : null}
</TableCell>
<TableCell>{r.positionen_count ?? r.items_count ?? '-'}</TableCell> <TableCell>{r.positionen_count ?? r.items_count ?? '-'}</TableCell>
<TableCell> <TableCell>
{r.positionen_count != null && r.positionen_count > 0 {r.positionen_count != null && r.positionen_count > 0
@@ -219,7 +223,11 @@ function AlleAnfragenTab() {
<TableCell>{r.bezeichnung || '-'}</TableCell> <TableCell>{r.bezeichnung || '-'}</TableCell>
<TableCell>{r.fuer_benutzer_name || r.anfrager_name || r.anfrager_id}</TableCell> <TableCell>{r.fuer_benutzer_name || r.anfrager_name || r.anfrager_id}</TableCell>
<TableCell><Chip label={AUSRUESTUNG_STATUS_LABELS[r.status]} color={AUSRUESTUNG_STATUS_COLORS[r.status]} size="small" /></TableCell> <TableCell><Chip label={AUSRUESTUNG_STATUS_LABELS[r.status]} color={AUSRUESTUNG_STATUS_COLORS[r.status]} size="small" /></TableCell>
<TableCell>{r.im_haus ? <Chip label="Im Haus" color="success" size="small" /> : null}</TableCell> <TableCell>
{r.im_haus && r.geliefert_count != null && r.positionen_count != null && r.geliefert_count < r.positionen_count
? <Chip label="Teilweise im Haus" color="warning" size="small" />
: r.im_haus ? <Chip label="Im Haus" color="success" size="small" /> : null}
</TableCell>
<TableCell>{r.positionen_count ?? r.items_count ?? '-'}</TableCell> <TableCell>{r.positionen_count ?? r.items_count ?? '-'}</TableCell>
<TableCell> <TableCell>
{r.positionen_count != null && r.positionen_count > 0 {r.positionen_count != null && r.positionen_count > 0

View File

@@ -218,9 +218,13 @@ export default function AusruestungsanfrageDetail() {
</Typography> </Typography>
{anfrage && ( {anfrage && (
<> <>
{detail?.im_haus && ( {detail?.im_haus && (() => {
<Chip label="Im Haus" color="success" /> const total = detail.positionen.length;
)} const delivered = detail.positionen.filter(p => p.geliefert).length;
return total > 0 && delivered < total
? <Chip label="Teilweise im Haus" color="warning" />
: <Chip label="Im Haus" color="success" />;
})()}
<Chip <Chip
label={AUSRUESTUNG_STATUS_LABELS[anfrage.status]} label={AUSRUESTUNG_STATUS_LABELS[anfrage.status]}
color={AUSRUESTUNG_STATUS_COLORS[anfrage.status]} color={AUSRUESTUNG_STATUS_COLORS[anfrage.status]}

View File

@@ -546,7 +546,8 @@ export default function BestellungDetail() {
const canExport = hasPermission('bestellungen:export'); const canExport = hasPermission('bestellungen:export');
const validTransitions = bestellung ? STATUS_TRANSITIONS[bestellung.status] : []; const validTransitions = bestellung ? STATUS_TRANSITIONS[bestellung.status] : [];
const allCostsEntered = positionen.length === 0 || positionen.every(p => p.einzelpreis != null && Number(p.einzelpreis) > 0); const allCostsEntered = positionen.length === 0 || positionen.every(p => p.einzelpreis != null && Number(p.einzelpreis) > 0);
const allItemsReceived = positionen.length === 0 || positionen.every(p => Number(p.erhalten_menge) >= Number(p.menge)); const allItemsReceived = positionen.length > 0 && positionen.every(p => Number(p.erhalten_menge) >= Number(p.menge));
const anyItemReceived = positionen.some(p => Number(p.erhalten_menge) > 0);
// All statuses except current, for force override // All statuses except current, for force override
const ALL_STATUSES: BestellungStatus[] = ['entwurf', 'wartet_auf_genehmigung', 'bereit_zur_bestellung', 'bestellt', 'teillieferung', 'lieferung_pruefen', 'abgeschlossen']; const ALL_STATUSES: BestellungStatus[] = ['entwurf', 'wartet_auf_genehmigung', 'bereit_zur_bestellung', 'bestellt', 'teillieferung', 'lieferung_pruefen', 'abgeschlossen'];
@@ -1205,15 +1206,19 @@ export default function BestellungDetail() {
}) })
.map((s) => { .map((s) => {
const isReject = bestellung.status === 'wartet_auf_genehmigung' && s === 'entwurf'; const isReject = bestellung.status === 'wartet_auf_genehmigung' && s === 'entwurf';
const isApprove = bestellung.status === 'wartet_auf_genehmigung' && s === 'bereit_zur_bestellung';
const label = isReject ? 'Ablehnen' : BESTELLUNG_STATUS_LABELS[s]; const label = isReject ? 'Ablehnen' : BESTELLUNG_STATUS_LABELS[s];
const color = isReject ? 'error' : 'primary'; const color = isReject ? 'error' : isApprove ? 'success' : 'info';
const isAbgeschlossen = s === 'abgeschlossen'; const isAbgeschlossen = s === 'abgeschlossen';
return ( return (
<Button <Button
key={s} key={s}
variant="contained" variant="contained"
color={color as 'error' | 'primary'} color={color as 'error' | 'info' | 'success'}
disabled={isAbgeschlossen && (!allCostsEntered || !allItemsReceived)} disabled={
(isAbgeschlossen && (!allCostsEntered || !allItemsReceived)) ||
(s === 'teillieferung' && !anyItemReceived)
}
onClick={() => { setStatusForce(false); setStatusConfirmTarget(s); }} onClick={() => { setStatusForce(false); setStatusConfirmTarget(s); }}
> >
{label} {label}

View File

@@ -29,7 +29,7 @@ const MESSAGE_TYPE_LABELS: Record<MessageType, string> = {
const TRIGGER_LABELS: Record<string, string> = { const TRIGGER_LABELS: Record<string, string> = {
day_of_week: 'Wochentag + Uhrzeit', day_of_week: 'Wochentag + Uhrzeit',
days_before_month_start: 'N Tage vor Monatsbeginn', days_before_month_start: 'Tage vor Monatsbeginn',
event: 'Ereignis', event: 'Ereignis',
}; };

View File

@@ -224,11 +224,12 @@ export default function GeplanteMachrichtenForm({ ruleId }: GeplanteMachrichtenF
<FormControl> <FormControl>
<FormLabel>Auslöser</FormLabel> <FormLabel>Auslöser</FormLabel>
<RadioGroup <RadioGroup
row
value={triggerMode} value={triggerMode}
onChange={(e) => setTriggerMode(e.target.value as TriggerMode)} onChange={(e) => setTriggerMode(e.target.value as TriggerMode)}
> >
<FormControlLabel value="day_of_week" control={<Radio />} label="Wochentag + Uhrzeit" /> <FormControlLabel value="day_of_week" control={<Radio />} label="Wochentag + Uhrzeit" />
<FormControlLabel value="days_before_month_start" control={<Radio />} label="N Tage vor Monatsbeginn" /> <FormControlLabel value="days_before_month_start" control={<Radio />} label="Tage vor Monatsbeginn" />
</RadioGroup> </RadioGroup>
</FormControl> </FormControl>
@@ -263,6 +264,7 @@ export default function GeplanteMachrichtenForm({ ruleId }: GeplanteMachrichtenF
value={daysBeforeMonthStart} value={daysBeforeMonthStart}
onChange={(e) => setDaysBeforeMonthStart(Number(e.target.value))} onChange={(e) => setDaysBeforeMonthStart(Number(e.target.value))}
size="small" size="small"
sx={{ minWidth: 220 }}
inputProps={{ min: 0, max: 28 }} inputProps={{ min: 0, max: 28 }}
/> />
</Box> </Box>
@@ -276,6 +278,7 @@ export default function GeplanteMachrichtenForm({ ruleId }: GeplanteMachrichtenF
<FormControl> <FormControl>
<FormLabel>Zeitfenster</FormLabel> <FormLabel>Zeitfenster</FormLabel>
<RadioGroup <RadioGroup
row
value={windowMode} value={windowMode}
onChange={(e) => setWindowMode(e.target.value as WindowMode)} onChange={(e) => setWindowMode(e.target.value as WindowMode)}
> >