change label

This commit is contained in:
Matthias Hochmeister
2026-04-15 11:18:25 +02:00
parent 4c01683c10
commit 916aa488d2
3 changed files with 190 additions and 190 deletions

View File

@@ -257,41 +257,41 @@ export default function AusruestungsanfrageDetail() {
{editItems.map((item, idx) => ( {editItems.map((item, idx) => (
<Box key={idx} sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}> <Box key={idx} sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}> <Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<Autocomplete <Autocomplete
freeSolo freeSolo
options={catalogItems} options={catalogItems}
getOptionLabel={o => typeof o === 'string' ? o : o.bezeichnung} getOptionLabel={o => typeof o === 'string' ? o : o.bezeichnung}
value={item.artikel_id ? catalogItems.find(c => c.id === item.artikel_id) || item.bezeichnung : item.bezeichnung} value={item.artikel_id ? catalogItems.find(c => c.id === item.artikel_id) || item.bezeichnung : item.bezeichnung}
onChange={(_, v) => { onChange={(_, v) => {
if (typeof v === 'string') { if (typeof v === 'string') {
updateEditItem(idx, 'bezeichnung', v); updateEditItem(idx, 'bezeichnung', v);
updateEditItem(idx, 'artikel_id', undefined); updateEditItem(idx, 'artikel_id', undefined);
} else if (v) { } else if (v) {
setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, artikel_id: v.id, bezeichnung: v.bezeichnung } : it)); setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, artikel_id: v.id, bezeichnung: v.bezeichnung } : it));
loadEigenschaftenForItem(v.id); loadEigenschaftenForItem(v.id);
setEditItemEigenschaftValues(prev => { const n = { ...prev }; delete n[idx]; return n; }); setEditItemEigenschaftValues(prev => { const n = { ...prev }; delete n[idx]; return n; });
} }
}} }}
onInputChange={(_, val, reason) => { onInputChange={(_, val, reason) => {
if (reason === 'input') { if (reason === 'input') {
setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, bezeichnung: val, artikel_id: undefined } : it)); setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, bezeichnung: val, artikel_id: undefined } : it));
} }
}} }}
renderInput={params => <TextField {...params} label="Artikel" size="small" />} renderInput={params => <TextField {...params} label="Artikel" size="small" />}
sx={{ flexGrow: 1 }} sx={{ flexGrow: 1 }}
/> />
<TextField <TextField
size="small" size="small"
type="number" type="number"
label="Menge" label="Menge"
value={item.menge} value={item.menge}
onChange={e => updateEditItem(idx, 'menge', Math.max(1, Number(e.target.value)))} onChange={e => updateEditItem(idx, 'menge', Math.max(1, Number(e.target.value)))}
sx={{ width: 90 }} sx={{ width: 90 }}
inputProps={{ min: 1 }} inputProps={{ min: 1 }}
/> />
<IconButton size="small" onClick={() => removeEditItem(idx)}> <IconButton size="small" onClick={() => removeEditItem(idx)}>
<DeleteIcon fontSize="small" /> <DeleteIcon fontSize="small" />
</IconButton> </IconButton>
</Box> </Box>
{item.artikel_id && editItemEigenschaften[item.artikel_id]?.length > 0 && ( {item.artikel_id && editItemEigenschaften[item.artikel_id]?.length > 0 && (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, ml: 1, pl: 1.5, borderLeft: '2px solid', borderColor: 'divider' }}> <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, ml: 1, pl: 1.5, borderLeft: '2px solid', borderColor: 'divider' }}>
@@ -430,7 +430,7 @@ export default function AusruestungsanfrageDetail() {
<Chip label="Im Haus" size="small" color="success" /> <Chip label="Im Haus" size="small" color="success" />
)} )}
{p.geliefert && p.zuweisung_typ === 'keine' && ( {p.geliefert && p.zuweisung_typ === 'keine' && (
<Chip label="Nicht verfolgt" size="small" color="default" variant="outlined" /> <Chip label="Nicht zugewiesen" size="small" color="default" variant="outlined" />
)} )}
{p.geliefert && p.zuweisung_typ === 'persoenlich' && ( {p.geliefert && p.zuweisung_typ === 'persoenlich' && (
<Chip label="Persönlich" size="small" color="primary" variant="outlined" /> <Chip label="Persönlich" size="small" color="primary" variant="outlined" />

View File

@@ -241,7 +241,7 @@ export default function AusruestungsanfrageZuweisung() {
> >
<ToggleButton value="ausruestung" disabled={!pos.artikel_id}>Ausrüstung</ToggleButton> <ToggleButton value="ausruestung" disabled={!pos.artikel_id}>Ausrüstung</ToggleButton>
<ToggleButton value="persoenlich" disabled={!pos.artikel_id}>Persönlich</ToggleButton> <ToggleButton value="persoenlich" disabled={!pos.artikel_id}>Persönlich</ToggleButton>
<ToggleButton value="keine">Nicht verfolgt</ToggleButton> <ToggleButton value="keine">Nicht zuweisen</ToggleButton>
</ToggleButtonGroup> </ToggleButtonGroup>
{a.typ === 'ausruestung' && ( {a.typ === 'ausruestung' && (

View File

@@ -102,166 +102,166 @@ function PersoenlicheAusruestungPage() {
<Tabs value={activeTab} onChange={(_e, v) => setActiveTab(v)} sx={{ mb: 3 }}> <Tabs value={activeTab} onChange={(_e, v) => setActiveTab(v)} sx={{ mb: 3 }}>
<Tab label="Zuweisungen" /> <Tab label="Zuweisungen" />
<Tab label="Katalog" /> <Tab label="Katalog" />
{canApprove && <Tab label="Nicht verfolgt" />} {canApprove && <Tab label="Nicht zugewiesen" />}
</Tabs> </Tabs>
{activeTab === 0 && ( {activeTab === 0 && (
<> <>
{/* Filters */} {/* Filters */}
<Box sx={{ display: 'flex', gap: 2, mb: 3, flexWrap: 'wrap', alignItems: 'center' }}> <Box sx={{ display: 'flex', gap: 2, mb: 3, flexWrap: 'wrap', alignItems: 'center' }}>
<TextField <TextField
size="small" size="small"
placeholder="Suche…" placeholder="Suche…"
value={search} value={search}
onChange={(e) => setSearch(e.target.value)} onChange={(e) => setSearch(e.target.value)}
sx={{ minWidth: 200 }} sx={{ minWidth: 200 }}
/> />
<TextField <TextField
size="small" size="small"
select select
label="Zustand" label="Zustand"
value={filterZustand} value={filterZustand}
onChange={(e) => setFilterZustand(e.target.value)} onChange={(e) => setFilterZustand(e.target.value)}
sx={{ minWidth: 140 }} sx={{ minWidth: 140 }}
> >
<MenuItem value="">Alle</MenuItem> <MenuItem value="">Alle</MenuItem>
{ZUSTAND_OPTIONS.map(([key, label]) => ( {ZUSTAND_OPTIONS.map(([key, label]) => (
<MenuItem key={key} value={key}>{label}</MenuItem> <MenuItem key={key} value={key}>{label}</MenuItem>
))} ))}
</TextField> </TextField>
{canViewAll && ( {canViewAll && (
<Autocomplete <Autocomplete
size="small" size="small"
options={memberOptions} options={memberOptions}
getOptionLabel={(o) => o.name} getOptionLabel={(o) => o.name}
value={memberOptions.find((m) => m.id === filterUser) ?? null} value={memberOptions.find((m) => m.id === filterUser) ?? null}
onChange={(_e, v) => setFilterUser(v?.id ?? '')} onChange={(_e, v) => setFilterUser(v?.id ?? '')}
renderInput={(params) => <TextField {...params} label="Benutzer" />} renderInput={(params) => <TextField {...params} label="Benutzer" />}
sx={{ minWidth: 200 }} sx={{ minWidth: 200 }}
/> />
)} )}
<Typography variant="body2" color="text.secondary" sx={{ ml: 'auto' }}> <Typography variant="body2" color="text.secondary" sx={{ ml: 'auto' }}>
{filtered.length} {filtered.length === 1 ? 'Eintrag' : 'Einträge'} {filtered.length} {filtered.length === 1 ? 'Eintrag' : 'Einträge'}
</Typography> </Typography>
</Box> </Box>
{/* Table */} {/* Table */}
<Box sx={{ overflowX: 'auto' }}> <Box sx={{ overflowX: 'auto' }}>
<Box <Box
component="table" component="table"
sx={{ sx={{
width: '100%', width: '100%',
borderCollapse: 'collapse', borderCollapse: 'collapse',
'& th, & td': { '& th, & td': {
textAlign: 'left', textAlign: 'left',
py: 1.5, py: 1.5,
px: 2, px: 2,
borderBottom: '1px solid', borderBottom: '1px solid',
borderColor: 'divider', borderColor: 'divider',
}, },
'& th': { '& th': {
fontWeight: 600, fontWeight: 600,
fontSize: '0.75rem', fontSize: '0.75rem',
textTransform: 'uppercase', textTransform: 'uppercase',
color: 'text.secondary', color: 'text.secondary',
letterSpacing: '0.06em', letterSpacing: '0.06em',
}, },
'& tbody tr': { '& tbody tr': {
cursor: 'pointer', cursor: 'pointer',
'&:hover': { bgcolor: 'action.hover' }, '&:hover': { bgcolor: 'action.hover' },
}, },
}} }}
> >
<thead> <thead>
<tr> <tr>
<th>Bezeichnung</th> <th>Bezeichnung</th>
<th>Kategorie</th> <th>Kategorie</th>
{canViewAll && <th>Benutzer</th>} {canViewAll && <th>Benutzer</th>}
<th>Größe</th> <th>Größe</th>
<th>Zustand</th> <th>Zustand</th>
<th>Anschaffung</th> <th>Anschaffung</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{isLoading ? ( {isLoading ? (
<tr> <tr>
<td colSpan={canViewAll ? 6 : 5}> <td colSpan={canViewAll ? 6 : 5}>
<Typography color="text.secondary" sx={{ py: 4, textAlign: 'center' }}> <Typography color="text.secondary" sx={{ py: 4, textAlign: 'center' }}>
Lade Daten Lade Daten
</Typography>
</td>
</tr>
) : filtered.length === 0 ? (
<tr>
<td colSpan={canViewAll ? 6 : 5}>
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', py: 6 }}>
<CheckroomIcon sx={{ fontSize: 48, color: 'text.disabled', mb: 1 }} />
<Typography color="text.secondary">
Keine Einträge gefunden
</Typography>
</Box>
</td>
</tr>
) : (
filtered.map((item) => (
<tr key={item.id} onClick={() => navigate(`/persoenliche-ausruestung/${item.id}`)}>
<td>
<Typography variant="body2" fontWeight={500}>
{item.bezeichnung}
</Typography>
{item.artikel_bezeichnung && (
<Typography variant="caption" color="text.secondary">
{item.artikel_bezeichnung}
</Typography>
)}
{item.eigenschaften && item.eigenschaften.length > 0 && (
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', mt: 0.5 }}>
{item.eigenschaften.map((e) => (
<Chip
key={e.id}
label={`${e.name}: ${e.wert}`}
size="small"
variant="outlined"
sx={{ height: 20, fontSize: '0.7rem' }}
/>
))}
</Box>
)}
</td>
<td>
<Typography variant="body2">{item.kategorie ?? '—'}</Typography>
</td>
{canViewAll && (
<td>
<Typography variant="body2">
{item.user_display_name ?? item.benutzer_name ?? '—'}
</Typography> </Typography>
</td> </td>
)} </tr>
<td> ) : filtered.length === 0 ? (
<Typography variant="body2">{item.groesse ?? '—'}</Typography> <tr>
</td> <td colSpan={canViewAll ? 6 : 5}>
<td> <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', py: 6 }}>
<Chip <CheckroomIcon sx={{ fontSize: 48, color: 'text.disabled', mb: 1 }} />
label={ZUSTAND_LABELS[item.zustand]} <Typography color="text.secondary">
color={ZUSTAND_COLORS[item.zustand]} Keine Einträge gefunden
size="small" </Typography>
variant="outlined" </Box>
/> </td>
</td> </tr>
<td> ) : (
<Typography variant="body2"> filtered.map((item) => (
{item.anschaffung_datum <tr key={item.id} onClick={() => navigate(`/persoenliche-ausruestung/${item.id}`)}>
? new Date(item.anschaffung_datum).toLocaleDateString('de-AT') <td>
: '—'} <Typography variant="body2" fontWeight={500}>
</Typography> {item.bezeichnung}
</td> </Typography>
</tr> {item.artikel_bezeichnung && (
)) <Typography variant="caption" color="text.secondary">
)} {item.artikel_bezeichnung}
</tbody> </Typography>
</Box> )}
</Box> {item.eigenschaften && item.eigenschaften.length > 0 && (
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', mt: 0.5 }}>
{item.eigenschaften.map((e) => (
<Chip
key={e.id}
label={`${e.name}: ${e.wert}`}
size="small"
variant="outlined"
sx={{ height: 20, fontSize: '0.7rem' }}
/>
))}
</Box>
)}
</td>
<td>
<Typography variant="body2">{item.kategorie ?? '—'}</Typography>
</td>
{canViewAll && (
<td>
<Typography variant="body2">
{item.user_display_name ?? item.benutzer_name ?? '—'}
</Typography>
</td>
)}
<td>
<Typography variant="body2">{item.groesse ?? '—'}</Typography>
</td>
<td>
<Chip
label={ZUSTAND_LABELS[item.zustand]}
color={ZUSTAND_COLORS[item.zustand]}
size="small"
variant="outlined"
/>
</td>
<td>
<Typography variant="body2">
{item.anschaffung_datum
? new Date(item.anschaffung_datum).toLocaleDateString('de-AT')
: '—'}
</Typography>
</td>
</tr>
))
)}
</tbody>
</Box>
</Box>
</> </>
)} )}