feat(ausruestung): catalog-driven item tracking, im_haus in overview, order quantity override, fix stale queries
This commit is contained in:
@@ -324,7 +324,13 @@ async function getRequests(filters?: { status?: string; anfrager_id?: string })
|
|||||||
COALESCE(u2.given_name || ' ' || u2.family_name, u2.name) AS bearbeitet_von_name,
|
COALESCE(u2.given_name || ' ' || u2.family_name, u2.name) AS bearbeitet_von_name,
|
||||||
a.fuer_benutzer_name,
|
a.fuer_benutzer_name,
|
||||||
(SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id) AS positionen_count,
|
(SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id) AS positionen_count,
|
||||||
(SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id AND p.geliefert) AS geliefert_count
|
(SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id AND p.geliefert) AS geliefert_count,
|
||||||
|
EXISTS(
|
||||||
|
SELECT 1 FROM ausruestung_anfrage_bestellung ab
|
||||||
|
JOIN bestellungen b ON b.id = ab.bestellung_id
|
||||||
|
WHERE ab.anfrage_id = a.id
|
||||||
|
AND b.status IN ('lieferung_pruefen', 'abgeschlossen')
|
||||||
|
) AS im_haus
|
||||||
FROM ausruestung_anfragen a
|
FROM ausruestung_anfragen a
|
||||||
LEFT JOIN users u ON u.id = a.anfrager_id
|
LEFT JOIN users u ON u.id = a.anfrager_id
|
||||||
LEFT JOIN users u2 ON u2.id = a.bearbeitet_von
|
LEFT JOIN users u2 ON u2.id = a.bearbeitet_von
|
||||||
@@ -339,7 +345,13 @@ async function getMyRequests(userId: string) {
|
|||||||
const result = await pool.query(
|
const result = await pool.query(
|
||||||
`SELECT a.*,
|
`SELECT a.*,
|
||||||
(SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id) AS positionen_count,
|
(SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id) AS positionen_count,
|
||||||
(SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id AND p.geliefert) AS geliefert_count
|
(SELECT COUNT(*)::int FROM ausruestung_anfrage_positionen p WHERE p.anfrage_id = a.id AND p.geliefert) AS geliefert_count,
|
||||||
|
EXISTS(
|
||||||
|
SELECT 1 FROM ausruestung_anfrage_bestellung ab
|
||||||
|
JOIN bestellungen b ON b.id = ab.bestellung_id
|
||||||
|
WHERE ab.anfrage_id = a.id
|
||||||
|
AND b.status IN ('lieferung_pruefen', 'abgeschlossen')
|
||||||
|
) AS im_haus
|
||||||
FROM ausruestung_anfragen a
|
FROM ausruestung_anfragen a
|
||||||
WHERE a.anfrager_id = $1
|
WHERE a.anfrager_id = $1
|
||||||
ORDER BY a.erstellt_am DESC`,
|
ORDER BY a.erstellt_am DESC`,
|
||||||
@@ -1100,7 +1112,7 @@ async function getUnassignedPositions() {
|
|||||||
FROM ausruestung_anfrage_positionen p
|
FROM ausruestung_anfrage_positionen p
|
||||||
JOIN ausruestung_anfragen a ON a.id = p.anfrage_id
|
JOIN ausruestung_anfragen a ON a.id = p.anfrage_id
|
||||||
LEFT JOIN users u ON u.id = a.anfrager_id
|
LEFT JOIN users u ON u.id = a.anfrager_id
|
||||||
WHERE p.geliefert = true AND p.zuweisung_typ IS NULL
|
WHERE p.geliefert = true AND (p.zuweisung_typ IS NULL OR p.zuweisung_typ = 'keine')
|
||||||
ORDER BY a.bestell_jahr DESC NULLS LAST, a.bestell_nummer DESC NULLS LAST, p.bezeichnung
|
ORDER BY a.bestell_jahr DESC NULLS LAST, a.bestell_nummer DESC NULLS LAST, p.bezeichnung
|
||||||
`);
|
`);
|
||||||
return result.rows;
|
return result.rows;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import App from './App';
|
|||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
staleTime: 30 * 1000, // 30 seconds
|
||||||
gcTime: 10 * 60 * 1000, // keep cache 10 minutes
|
gcTime: 10 * 60 * 1000, // keep cache 10 minutes
|
||||||
retry: 1,
|
retry: 1,
|
||||||
refetchOnWindowFocus: false, // prevent refetch on every tab switch
|
refetchOnWindowFocus: false, // prevent refetch on every tab switch
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ function MeineAnfragenTab() {
|
|||||||
<TableCell>Anfrage ID</TableCell>
|
<TableCell>Anfrage ID</TableCell>
|
||||||
<TableCell>Bezeichnung</TableCell>
|
<TableCell>Bezeichnung</TableCell>
|
||||||
<TableCell>Status</TableCell>
|
<TableCell>Status</TableCell>
|
||||||
|
<TableCell>Im Haus</TableCell>
|
||||||
<TableCell>Positionen</TableCell>
|
<TableCell>Positionen</TableCell>
|
||||||
<TableCell>Geliefert</TableCell>
|
<TableCell>Geliefert</TableCell>
|
||||||
<TableCell>Erstellt am</TableCell>
|
<TableCell>Erstellt am</TableCell>
|
||||||
@@ -107,6 +108,7 @@ 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.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
|
||||||
@@ -204,6 +206,7 @@ function AlleAnfragenTab() {
|
|||||||
<TableCell>Bezeichnung</TableCell>
|
<TableCell>Bezeichnung</TableCell>
|
||||||
<TableCell>Anfrage für</TableCell>
|
<TableCell>Anfrage für</TableCell>
|
||||||
<TableCell>Status</TableCell>
|
<TableCell>Status</TableCell>
|
||||||
|
<TableCell>Im Haus</TableCell>
|
||||||
<TableCell>Positionen</TableCell>
|
<TableCell>Positionen</TableCell>
|
||||||
<TableCell>Geliefert</TableCell>
|
<TableCell>Geliefert</TableCell>
|
||||||
<TableCell>Erstellt am</TableCell>
|
<TableCell>Erstellt am</TableCell>
|
||||||
@@ -216,6 +219,7 @@ 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.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
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export default function AusruestungsanfrageZuBestellung() {
|
|||||||
// ── State ──
|
// ── State ──
|
||||||
const [assignments, setAssignments] = useState<Record<number, VendorAssignment | null>>({});
|
const [assignments, setAssignments] = useState<Record<number, VendorAssignment | null>>({});
|
||||||
const [orderNames, setOrderNames] = useState<Record<number, string>>({});
|
const [orderNames, setOrderNames] = useState<Record<number, string>>({});
|
||||||
|
const [quantities, setQuantities] = useState<Record<number, number>>({});
|
||||||
|
|
||||||
// New vendor dialog
|
// New vendor dialog
|
||||||
const [newVendorDialog, setNewVendorDialog] = useState(false);
|
const [newVendorDialog, setNewVendorDialog] = useState(false);
|
||||||
@@ -153,6 +154,8 @@ export default function AusruestungsanfrageZuBestellung() {
|
|||||||
showSuccess(`${result.created_bestellungen.length} Bestellung(en) erfolgreich erstellt`);
|
showSuccess(`${result.created_bestellungen.length} Bestellung(en) erfolgreich erstellt`);
|
||||||
queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage', requestId] });
|
queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage', requestId] });
|
||||||
navigate('/bestellungen');
|
navigate('/bestellungen');
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage'] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
},
|
},
|
||||||
onError: () => showError('Bestellungen konnten nicht erstellt werden'),
|
onError: () => showError('Bestellungen konnten nicht erstellt werden'),
|
||||||
});
|
});
|
||||||
@@ -191,7 +194,7 @@ export default function AusruestungsanfrageZuBestellung() {
|
|||||||
positionen: g.positionen.map(p => ({
|
positionen: g.positionen.map(p => ({
|
||||||
position_id: p.id,
|
position_id: p.id,
|
||||||
bezeichnung: p.bezeichnung,
|
bezeichnung: p.bezeichnung,
|
||||||
menge: p.menge,
|
menge: quantities[p.id] ?? p.menge,
|
||||||
einheit: p.einheit,
|
einheit: p.einheit,
|
||||||
notizen: p.notizen,
|
notizen: p.notizen,
|
||||||
artikel_id: p.artikel_id,
|
artikel_id: p.artikel_id,
|
||||||
@@ -281,6 +284,7 @@ export default function AusruestungsanfrageZuBestellung() {
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>Artikel</TableCell>
|
<TableCell>Artikel</TableCell>
|
||||||
<TableCell align="right" sx={{ width: 80 }}>Menge</TableCell>
|
<TableCell align="right" sx={{ width: 80 }}>Menge</TableCell>
|
||||||
|
<TableCell align="right" sx={{ width: 140 }}>Bestellmenge</TableCell>
|
||||||
<TableCell sx={{ width: 320 }}>Lieferant</TableCell>
|
<TableCell sx={{ width: 320 }}>Lieferant</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
@@ -301,6 +305,20 @@ export default function AusruestungsanfrageZuBestellung() {
|
|||||||
<TableCell align="right">
|
<TableCell align="right">
|
||||||
<Typography variant="body2">{pos.menge} {pos.einheit ?? 'Stk'}</Typography>
|
<Typography variant="body2">{pos.menge} {pos.einheit ?? 'Stk'}</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell align="right">
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
size="small"
|
||||||
|
sx={{ width: 80 }}
|
||||||
|
value={quantities[pos.id] ?? pos.menge}
|
||||||
|
onChange={e => {
|
||||||
|
const val = Math.min(pos.menge, Math.max(1, Number(e.target.value)));
|
||||||
|
setQuantities(prev => ({ ...prev, [pos.id]: val }));
|
||||||
|
}}
|
||||||
|
inputProps={{ min: 1, max: pos.menge }}
|
||||||
|
helperText={pos.menge > 1 ? `max ${pos.menge}` : undefined}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||||
<Autocomplete<Lieferant>
|
<Autocomplete<Lieferant>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { PageHeader } from '../components/templates';
|
|||||||
import { useNotification } from '../contexts/NotificationContext';
|
import { useNotification } from '../contexts/NotificationContext';
|
||||||
import { usePermissionContext } from '../contexts/PermissionContext';
|
import { usePermissionContext } from '../contexts/PermissionContext';
|
||||||
import { ausruestungsanfrageApi } from '../services/ausruestungsanfrage';
|
import { ausruestungsanfrageApi } from '../services/ausruestungsanfrage';
|
||||||
|
import { personalEquipmentApi } from '../services/personalEquipment';
|
||||||
import { vehiclesApi } from '../services/vehicles';
|
import { vehiclesApi } from '../services/vehicles';
|
||||||
import { membersService } from '../services/members';
|
import { membersService } from '../services/members';
|
||||||
import type { AusruestungAnfragePosition } from '../types/ausruestungsanfrage.types';
|
import type { AusruestungAnfragePosition } from '../types/ausruestungsanfrage.types';
|
||||||
@@ -24,8 +25,6 @@ interface PositionAssignment {
|
|||||||
standort?: string;
|
standort?: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
benutzerName?: string;
|
benutzerName?: string;
|
||||||
groesse?: string;
|
|
||||||
kategorie?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUnassignedPositions(positions: AusruestungAnfragePosition[]): AusruestungAnfragePosition[] {
|
function getUnassignedPositions(positions: AusruestungAnfragePosition[]): AusruestungAnfragePosition[] {
|
||||||
@@ -92,6 +91,12 @@ export default function AusruestungsanfrageZuweisung() {
|
|||||||
name: v.bezeichnung ?? v.kurzname,
|
name: v.bezeichnung ?? v.kurzname,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const { data: allPersonalItems = [] } = useQuery({
|
||||||
|
queryKey: ['persoenliche-ausruestung', 'all-for-count'],
|
||||||
|
queryFn: () => personalEquipmentApi.getAll(),
|
||||||
|
staleTime: 2 * 60 * 1000,
|
||||||
|
});
|
||||||
|
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
|
||||||
const updateAssignment = (posId: number, patch: Partial<PositionAssignment>) => {
|
const updateAssignment = (posId: number, patch: Partial<PositionAssignment>) => {
|
||||||
@@ -137,12 +142,12 @@ export default function AusruestungsanfrageZuweisung() {
|
|||||||
standort: a.typ === 'ausruestung' ? a.standort : undefined,
|
standort: a.typ === 'ausruestung' ? a.standort : undefined,
|
||||||
userId: a.typ === 'persoenlich' ? a.userId : undefined,
|
userId: a.typ === 'persoenlich' ? a.userId : undefined,
|
||||||
benutzerName: a.typ === 'persoenlich' ? (a.benutzerName || anfrage.fuer_benutzer_name || anfrage.anfrager_name) : undefined,
|
benutzerName: a.typ === 'persoenlich' ? (a.benutzerName || anfrage.fuer_benutzer_name || anfrage.anfrager_name) : undefined,
|
||||||
groesse: a.typ === 'persoenlich' ? a.groesse : undefined,
|
|
||||||
kategorie: a.typ === 'persoenlich' ? a.kategorie : undefined,
|
|
||||||
}));
|
}));
|
||||||
await ausruestungsanfrageApi.assignItems(anfrageId, payload);
|
await ausruestungsanfrageApi.assignItems(anfrageId, payload);
|
||||||
showSuccess('Gegenstände zugewiesen');
|
showSuccess('Gegenstände zugewiesen');
|
||||||
navigate(`/ausruestungsanfrage/${id}`);
|
navigate(`/ausruestungsanfrage/${id}`);
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['persoenliche-ausruestung'] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage'] });
|
||||||
} catch {
|
} catch {
|
||||||
showError('Fehler beim Zuweisen');
|
showError('Fehler beim Zuweisen');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -233,11 +238,10 @@ export default function AusruestungsanfrageZuweisung() {
|
|||||||
size="small"
|
size="small"
|
||||||
onChange={(_e, val) => val && updateAssignment(pos.id, { typ: val })}
|
onChange={(_e, val) => val && updateAssignment(pos.id, { typ: val })}
|
||||||
sx={{ mb: 1.5 }}
|
sx={{ mb: 1.5 }}
|
||||||
disabled={!pos.artikel_id}
|
|
||||||
>
|
>
|
||||||
<ToggleButton value="ausruestung">Ausrüstung</ToggleButton>
|
<ToggleButton value="ausruestung" disabled={!pos.artikel_id}>Ausrüstung</ToggleButton>
|
||||||
<ToggleButton value="persoenlich">Persönlich</ToggleButton>
|
<ToggleButton value="persoenlich" disabled={!pos.artikel_id}>Persönlich</ToggleButton>
|
||||||
<ToggleButton value="keine">Nicht erfassen</ToggleButton>
|
<ToggleButton value="keine">Nicht verfolgt</ToggleButton>
|
||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup>
|
||||||
|
|
||||||
{a.typ === 'ausruestung' && (
|
{a.typ === 'ausruestung' && (
|
||||||
@@ -262,7 +266,7 @@ export default function AusruestungsanfrageZuweisung() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{a.typ === 'persoenlich' && (
|
{a.typ === 'persoenlich' && (
|
||||||
<Box sx={{ display: 'flex', gap: 1.5, flexWrap: 'wrap' }}>
|
<Box sx={{ display: 'flex', gap: 1.5, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
size="small"
|
size="small"
|
||||||
options={memberOptions}
|
options={memberOptions}
|
||||||
@@ -278,20 +282,19 @@ export default function AusruestungsanfrageZuweisung() {
|
|||||||
)}
|
)}
|
||||||
sx={{ minWidth: 200, flex: 1 }}
|
sx={{ minWidth: 200, flex: 1 }}
|
||||||
/>
|
/>
|
||||||
<TextField
|
{(() => {
|
||||||
size="small"
|
if (!a.userId || !pos.artikel_id) return null;
|
||||||
label="Größe"
|
const count = allPersonalItems.filter(i => i.user_id === a.userId && i.artikel_id === pos.artikel_id).length;
|
||||||
value={a.groesse ?? ''}
|
if (count === 0) return null;
|
||||||
onChange={(e) => updateAssignment(pos.id, { groesse: e.target.value })}
|
return <Typography variant="caption" color="text.secondary">Hat bereits {count} Stk.</Typography>;
|
||||||
sx={{ minWidth: 100 }}
|
})()}
|
||||||
/>
|
{pos.eigenschaften && pos.eigenschaften.length > 0 && (
|
||||||
<TextField
|
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', mb: 1 }}>
|
||||||
size="small"
|
{pos.eigenschaften.map(e => (
|
||||||
label="Kategorie"
|
<Chip key={e.eigenschaft_id} label={`${e.eigenschaft_name}: ${e.wert}`} size="small" variant="outlined" />
|
||||||
value={a.kategorie ?? ''}
|
))}
|
||||||
onChange={(e) => updateAssignment(pos.id, { kategorie: e.target.value })}
|
</Box>
|
||||||
sx={{ minWidth: 140 }}
|
)}
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ export default function BestellungDetail() {
|
|||||||
mutationFn: ({ status, force }: { status: string; force?: boolean }) => bestellungApi.updateStatus(orderId, status, force),
|
mutationFn: ({ status, force }: { status: string; force?: boolean }) => bestellungApi.updateStatus(orderId, status, force),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
showSuccess('Status aktualisiert');
|
showSuccess('Status aktualisiert');
|
||||||
setStatusConfirmTarget(null);
|
setStatusConfirmTarget(null);
|
||||||
setStatusForce(false);
|
setStatusForce(false);
|
||||||
@@ -225,6 +226,7 @@ export default function BestellungDetail() {
|
|||||||
mutationFn: (data: BestellpositionFormData) => bestellungApi.addLineItem(orderId, data),
|
mutationFn: (data: BestellpositionFormData) => bestellungApi.addLineItem(orderId, data),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
setNewItem({ ...emptyItem });
|
setNewItem({ ...emptyItem });
|
||||||
setSelectedKatalogItem(null);
|
setSelectedKatalogItem(null);
|
||||||
setKatalogEigenschaften([]);
|
setKatalogEigenschaften([]);
|
||||||
@@ -238,6 +240,7 @@ export default function BestellungDetail() {
|
|||||||
mutationFn: (itemId: number) => bestellungApi.deleteLineItem(itemId),
|
mutationFn: (itemId: number) => bestellungApi.deleteLineItem(itemId),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
setDeleteItemTarget(null);
|
setDeleteItemTarget(null);
|
||||||
showSuccess('Position gelöscht');
|
showSuccess('Position gelöscht');
|
||||||
},
|
},
|
||||||
@@ -249,6 +252,7 @@ export default function BestellungDetail() {
|
|||||||
bestellungApi.updateReceivedQty(itemId, menge),
|
bestellungApi.updateReceivedQty(itemId, menge),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
},
|
},
|
||||||
onError: () => showError('Fehler beim Aktualisieren'),
|
onError: () => showError('Fehler beim Aktualisieren'),
|
||||||
});
|
});
|
||||||
@@ -257,6 +261,7 @@ export default function BestellungDetail() {
|
|||||||
mutationFn: (file: File) => bestellungApi.uploadFile(orderId, file),
|
mutationFn: (file: File) => bestellungApi.uploadFile(orderId, file),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
showSuccess('Datei hochgeladen');
|
showSuccess('Datei hochgeladen');
|
||||||
},
|
},
|
||||||
onError: () => showError('Fehler beim Hochladen der Datei'),
|
onError: () => showError('Fehler beim Hochladen der Datei'),
|
||||||
@@ -266,6 +271,7 @@ export default function BestellungDetail() {
|
|||||||
mutationFn: (fileId: number) => bestellungApi.deleteFile(fileId),
|
mutationFn: (fileId: number) => bestellungApi.deleteFile(fileId),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
setDeleteFileTarget(null);
|
setDeleteFileTarget(null);
|
||||||
showSuccess('Datei gelöscht');
|
showSuccess('Datei gelöscht');
|
||||||
},
|
},
|
||||||
@@ -276,6 +282,7 @@ export default function BestellungDetail() {
|
|||||||
mutationFn: (data: ErinnerungFormData) => bestellungApi.addReminder(orderId, data),
|
mutationFn: (data: ErinnerungFormData) => bestellungApi.addReminder(orderId, data),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
setReminderForm({ faellig_am: '', nachricht: '' });
|
setReminderForm({ faellig_am: '', nachricht: '' });
|
||||||
setReminderFormOpen(false);
|
setReminderFormOpen(false);
|
||||||
showSuccess('Erinnerung erstellt');
|
showSuccess('Erinnerung erstellt');
|
||||||
@@ -287,6 +294,7 @@ export default function BestellungDetail() {
|
|||||||
mutationFn: (reminderId: number) => bestellungApi.markReminderDone(reminderId),
|
mutationFn: (reminderId: number) => bestellungApi.markReminderDone(reminderId),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
},
|
},
|
||||||
onError: () => showError('Fehler beim Aktualisieren'),
|
onError: () => showError('Fehler beim Aktualisieren'),
|
||||||
});
|
});
|
||||||
@@ -295,6 +303,7 @@ export default function BestellungDetail() {
|
|||||||
mutationFn: (reminderId: number) => bestellungApi.deleteReminder(reminderId),
|
mutationFn: (reminderId: number) => bestellungApi.deleteReminder(reminderId),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
setDeleteReminderTarget(null);
|
setDeleteReminderTarget(null);
|
||||||
showSuccess('Erinnerung gelöscht');
|
showSuccess('Erinnerung gelöscht');
|
||||||
},
|
},
|
||||||
@@ -351,6 +360,7 @@ export default function BestellungDetail() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
await queryClient.invalidateQueries({ queryKey: ['bestellung', orderId] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
|
||||||
showSuccess('Änderungen gespeichert');
|
showSuccess('Änderungen gespeichert');
|
||||||
setEditMode(false);
|
setEditMode(false);
|
||||||
setEditItemsData({});
|
setEditItemsData({});
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ 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 Zugewiesen" />}
|
{canApprove && <Tab label="Nicht verfolgt" />}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
{activeTab === 0 && (
|
{activeTab === 0 && (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { useParams, useNavigate } from 'react-router-dom';
|
|||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||||
import { personalEquipmentApi } from '../services/personalEquipment';
|
import { personalEquipmentApi } from '../services/personalEquipment';
|
||||||
|
import { ausruestungsanfrageApi } from '../services/ausruestungsanfrage';
|
||||||
import { membersService } from '../services/members';
|
import { membersService } from '../services/members';
|
||||||
import { usePermissionContext } from '../contexts/PermissionContext';
|
import { usePermissionContext } from '../contexts/PermissionContext';
|
||||||
import { useNotification } from '../contexts/NotificationContext';
|
import { useNotification } from '../contexts/NotificationContext';
|
||||||
@@ -64,10 +65,17 @@ export default function PersoenlicheAusruestungEdit() {
|
|||||||
}));
|
}));
|
||||||
}, [membersList]);
|
}, [membersList]);
|
||||||
|
|
||||||
|
const { data: artikelEigenschaften = [] } = useQuery({
|
||||||
|
queryKey: ['ausruestungsanfrage', 'eigenschaften', item?.artikel_id],
|
||||||
|
queryFn: () => ausruestungsanfrageApi.getArtikelEigenschaften(item!.artikel_id!),
|
||||||
|
enabled: !!item?.artikel_id,
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [catalogEigenschaftValues, setCatalogEigenschaftValues] = useState<Record<number, string>>({});
|
||||||
|
|
||||||
// Form state
|
// Form state
|
||||||
const [bezeichnung, setBezeichnung] = useState('');
|
const [bezeichnung, setBezeichnung] = useState('');
|
||||||
const [kategorie, setKategorie] = useState('');
|
|
||||||
const [groesse, setGroesse] = useState('');
|
|
||||||
const [seriennummer, setSeriennummer] = useState('');
|
const [seriennummer, setSeriennummer] = useState('');
|
||||||
const [inventarnummer, setInventarnummer] = useState('');
|
const [inventarnummer, setInventarnummer] = useState('');
|
||||||
const [anschaffungDatum, setAnschaffungDatum] = useState('');
|
const [anschaffungDatum, setAnschaffungDatum] = useState('');
|
||||||
@@ -80,8 +88,6 @@ export default function PersoenlicheAusruestungEdit() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
setBezeichnung(item.bezeichnung);
|
setBezeichnung(item.bezeichnung);
|
||||||
setKategorie(item.kategorie ?? '');
|
|
||||||
setGroesse(item.groesse ?? '');
|
|
||||||
setSeriennummer(item.seriennummer ?? '');
|
setSeriennummer(item.seriennummer ?? '');
|
||||||
setInventarnummer(item.inventarnummer ?? '');
|
setInventarnummer(item.inventarnummer ?? '');
|
||||||
setAnschaffungDatum(item.anschaffung_datum ? item.anschaffung_datum.split('T')[0] : '');
|
setAnschaffungDatum(item.anschaffung_datum ? item.anschaffung_datum.split('T')[0] : '');
|
||||||
@@ -95,6 +101,13 @@ export default function PersoenlicheAusruestungEdit() {
|
|||||||
wert: e.wert,
|
wert: e.wert,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
if (item.artikel_id && item.eigenschaften) {
|
||||||
|
const vals: Record<number, string> = {};
|
||||||
|
item.eigenschaften.forEach(e => {
|
||||||
|
if (e.eigenschaft_id != null) vals[e.eigenschaft_id] = e.wert;
|
||||||
|
});
|
||||||
|
setCatalogEigenschaftValues(vals);
|
||||||
|
}
|
||||||
}, [item]);
|
}, [item]);
|
||||||
|
|
||||||
// Set userId when item + memberOptions are ready
|
// Set userId when item + memberOptions are ready
|
||||||
@@ -117,21 +130,27 @@ export default function PersoenlicheAusruestungEdit() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (!bezeichnung.trim()) return;
|
if (!bezeichnung.trim() || !item) return;
|
||||||
|
|
||||||
const payload: UpdatePersoenlicheAusruestungPayload = {
|
const payload: UpdatePersoenlicheAusruestungPayload = {
|
||||||
bezeichnung: bezeichnung.trim(),
|
bezeichnung: bezeichnung.trim(),
|
||||||
kategorie: kategorie || null,
|
kategorie: null,
|
||||||
user_id: userId?.id || null,
|
user_id: userId?.id || null,
|
||||||
groesse: groesse || null,
|
groesse: null,
|
||||||
seriennummer: seriennummer || null,
|
seriennummer: seriennummer || null,
|
||||||
inventarnummer: inventarnummer || null,
|
inventarnummer: inventarnummer || null,
|
||||||
anschaffung_datum: anschaffungDatum || null,
|
anschaffung_datum: anschaffungDatum || null,
|
||||||
zustand,
|
zustand,
|
||||||
notizen: notizen || null,
|
notizen: notizen || null,
|
||||||
eigenschaften: eigenschaften
|
eigenschaften: item.artikel_id
|
||||||
.filter(e => e.name.trim() && e.wert.trim())
|
? Object.entries(catalogEigenschaftValues)
|
||||||
.map(e => ({ eigenschaft_id: e.eigenschaft_id, name: e.name, wert: e.wert })),
|
.filter(([, v]) => v.trim())
|
||||||
|
.map(([id, wert]) => ({
|
||||||
|
eigenschaft_id: Number(id),
|
||||||
|
name: artikelEigenschaften.find(e => e.id === Number(id))?.name ?? '',
|
||||||
|
wert,
|
||||||
|
}))
|
||||||
|
: eigenschaften.filter(e => e.name.trim() && e.wert.trim()).map(e => ({ eigenschaft_id: e.eigenschaft_id, name: e.name, wert: e.wert })),
|
||||||
};
|
};
|
||||||
updateMutation.mutate(payload);
|
updateMutation.mutate(payload);
|
||||||
};
|
};
|
||||||
@@ -198,20 +217,6 @@ export default function PersoenlicheAusruestungEdit() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<TextField
|
|
||||||
label="Kategorie"
|
|
||||||
size="small"
|
|
||||||
value={kategorie}
|
|
||||||
onChange={(e) => setKategorie(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextField
|
|
||||||
label="Größe"
|
|
||||||
size="small"
|
|
||||||
value={groesse}
|
|
||||||
onChange={(e) => setGroesse(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
label="Seriennummer"
|
label="Seriennummer"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -257,6 +262,28 @@ export default function PersoenlicheAusruestungEdit() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Eigenschaften */}
|
{/* Eigenschaften */}
|
||||||
|
{item.artikel_id ? (
|
||||||
|
<>
|
||||||
|
<Typography variant="subtitle2">Eigenschaften</Typography>
|
||||||
|
{artikelEigenschaften.map(e =>
|
||||||
|
e.typ === 'options' && e.optionen?.length ? (
|
||||||
|
<TextField key={e.id} select size="small" label={e.name} required={e.pflicht}
|
||||||
|
value={catalogEigenschaftValues[e.id] ?? ''}
|
||||||
|
onChange={ev => setCatalogEigenschaftValues(prev => ({ ...prev, [e.id]: ev.target.value }))}
|
||||||
|
>
|
||||||
|
<MenuItem value="">—</MenuItem>
|
||||||
|
{e.optionen.map(opt => <MenuItem key={opt} value={opt}>{opt}</MenuItem>)}
|
||||||
|
</TextField>
|
||||||
|
) : (
|
||||||
|
<TextField key={e.id} size="small" label={e.name} required={e.pflicht}
|
||||||
|
value={catalogEigenschaftValues[e.id] ?? ''}
|
||||||
|
onChange={ev => setCatalogEigenschaftValues(prev => ({ ...prev, [e.id]: ev.target.value }))}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<Typography variant="subtitle2">Eigenschaften</Typography>
|
<Typography variant="subtitle2">Eigenschaften</Typography>
|
||||||
{eigenschaften.map((e, idx) => (
|
{eigenschaften.map((e, idx) => (
|
||||||
<Box key={idx} sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
<Box key={idx} sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||||
@@ -282,6 +309,8 @@ export default function PersoenlicheAusruestungEdit() {
|
|||||||
<Button size="small" startIcon={<AddIcon />} onClick={addEigenschaft}>
|
<Button size="small" startIcon={<AddIcon />} onClick={addEigenschaft}>
|
||||||
Eigenschaft hinzufügen
|
Eigenschaft hinzufügen
|
||||||
</Button>
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'flex-end', mt: 1 }}>
|
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'flex-end', mt: 1 }}>
|
||||||
<Button onClick={() => navigate(`/persoenliche-ausruestung/${id}`)}>
|
<Button onClick={() => navigate(`/persoenliche-ausruestung/${id}`)}>
|
||||||
|
|||||||
@@ -34,13 +34,12 @@ export default function PersoenlicheAusruestungNeu() {
|
|||||||
|
|
||||||
const canViewAll = hasPermission('persoenliche_ausruestung:view_all');
|
const canViewAll = hasPermission('persoenliche_ausruestung:view_all');
|
||||||
|
|
||||||
const [formBezeichnung, setFormBezeichnung] = useState<string | AusruestungArtikel | null>(null);
|
const [formArtikel, setFormArtikel] = useState<AusruestungArtikel | null>(null);
|
||||||
const [formKategorie, setFormKategorie] = useState('');
|
|
||||||
const [formUserId, setFormUserId] = useState<{ id: string; name: string } | null>(null);
|
const [formUserId, setFormUserId] = useState<{ id: string; name: string } | null>(null);
|
||||||
const [formBenutzerName, setFormBenutzerName] = useState('');
|
const [formBenutzerName, setFormBenutzerName] = useState('');
|
||||||
const [formGroesse, setFormGroesse] = useState('');
|
|
||||||
const [formZustand, setFormZustand] = useState<PersoenlicheAusruestungZustand>('gut');
|
const [formZustand, setFormZustand] = useState<PersoenlicheAusruestungZustand>('gut');
|
||||||
const [formNotizen, setFormNotizen] = useState('');
|
const [formNotizen, setFormNotizen] = useState('');
|
||||||
|
const [eigenschaftValues, setEigenschaftValues] = useState<Record<number, string>>({});
|
||||||
|
|
||||||
const { data: catalogItems } = useQuery({
|
const { data: catalogItems } = useQuery({
|
||||||
queryKey: ['ausruestungsanfrage-items-catalog'],
|
queryKey: ['ausruestungsanfrage-items-catalog'],
|
||||||
@@ -48,6 +47,13 @@ export default function PersoenlicheAusruestungNeu() {
|
|||||||
staleTime: 10 * 60 * 1000,
|
staleTime: 10 * 60 * 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { data: artikelEigenschaften = [] } = useQuery({
|
||||||
|
queryKey: ['ausruestungsanfrage', 'eigenschaften', formArtikel?.id],
|
||||||
|
queryFn: () => ausruestungsanfrageApi.getArtikelEigenschaften(formArtikel!.id),
|
||||||
|
enabled: !!formArtikel?.id,
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
});
|
||||||
|
|
||||||
const { data: membersList } = useQuery({
|
const { data: membersList } = useQuery({
|
||||||
queryKey: ['members-list-compact'],
|
queryKey: ['members-list-compact'],
|
||||||
queryFn: () => membersService.getMembers({ pageSize: 500 }),
|
queryFn: () => membersService.getMembers({ pageSize: 500 }),
|
||||||
@@ -75,20 +81,23 @@ export default function PersoenlicheAusruestungNeu() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
const bezeichnung = typeof formBezeichnung === 'string'
|
const bezeichnung = formArtikel?.bezeichnung ?? '';
|
||||||
? formBezeichnung
|
|
||||||
: formBezeichnung?.bezeichnung ?? '';
|
|
||||||
if (!bezeichnung.trim()) return;
|
if (!bezeichnung.trim()) return;
|
||||||
|
|
||||||
const payload: CreatePersoenlicheAusruestungPayload = {
|
const payload: CreatePersoenlicheAusruestungPayload = {
|
||||||
bezeichnung: bezeichnung.trim(),
|
bezeichnung: bezeichnung.trim(),
|
||||||
kategorie: formKategorie || undefined,
|
artikel_id: formArtikel?.id,
|
||||||
artikel_id: typeof formBezeichnung === 'object' && formBezeichnung ? formBezeichnung.id : undefined,
|
|
||||||
user_id: formUserId?.id || undefined,
|
user_id: formUserId?.id || undefined,
|
||||||
benutzer_name: formBenutzerName || undefined,
|
benutzer_name: formBenutzerName || undefined,
|
||||||
groesse: formGroesse || undefined,
|
|
||||||
zustand: formZustand,
|
zustand: formZustand,
|
||||||
notizen: formNotizen || undefined,
|
notizen: formNotizen || undefined,
|
||||||
|
eigenschaften: Object.entries(eigenschaftValues)
|
||||||
|
.filter(([, v]) => v.trim())
|
||||||
|
.map(([id, wert]) => ({
|
||||||
|
eigenschaft_id: Number(id),
|
||||||
|
name: artikelEigenschaften.find(e => e.id === Number(id))?.name ?? '',
|
||||||
|
wert,
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
createMutation.mutate(payload);
|
createMutation.mutate(payload);
|
||||||
};
|
};
|
||||||
@@ -103,20 +112,12 @@ export default function PersoenlicheAusruestungNeu() {
|
|||||||
|
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
freeSolo
|
|
||||||
options={catalogItems ?? []}
|
options={catalogItems ?? []}
|
||||||
getOptionLabel={(o) => typeof o === 'string' ? o : o.bezeichnung}
|
getOptionLabel={(o) => o.bezeichnung}
|
||||||
value={formBezeichnung}
|
value={formArtikel}
|
||||||
onChange={(_e, v) => {
|
onChange={(_e, v) => {
|
||||||
setFormBezeichnung(v);
|
setFormArtikel(v);
|
||||||
if (v && typeof v !== 'string' && v.kategorie) {
|
setEigenschaftValues({});
|
||||||
setFormKategorie(v.kategorie);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onInputChange={(_e, v) => {
|
|
||||||
if (typeof formBezeichnung === 'string' || formBezeichnung === null) {
|
|
||||||
setFormBezeichnung(v);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField {...params} label="Bezeichnung" required size="small" />
|
<TextField {...params} label="Bezeichnung" required size="small" />
|
||||||
@@ -124,6 +125,23 @@ export default function PersoenlicheAusruestungNeu() {
|
|||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{artikelEigenschaften.map(e =>
|
||||||
|
e.typ === 'options' && e.optionen?.length ? (
|
||||||
|
<TextField key={e.id} select size="small" label={e.name} required={e.pflicht}
|
||||||
|
value={eigenschaftValues[e.id] ?? ''}
|
||||||
|
onChange={ev => setEigenschaftValues(prev => ({ ...prev, [e.id]: ev.target.value }))}
|
||||||
|
>
|
||||||
|
<MenuItem value="">—</MenuItem>
|
||||||
|
{e.optionen.map(opt => <MenuItem key={opt} value={opt}>{opt}</MenuItem>)}
|
||||||
|
</TextField>
|
||||||
|
) : (
|
||||||
|
<TextField key={e.id} size="small" label={e.name} required={e.pflicht}
|
||||||
|
value={eigenschaftValues[e.id] ?? ''}
|
||||||
|
onChange={ev => setEigenschaftValues(prev => ({ ...prev, [e.id]: ev.target.value }))}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
{canViewAll && (
|
{canViewAll && (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
options={memberOptions}
|
options={memberOptions}
|
||||||
@@ -146,20 +164,6 @@ export default function PersoenlicheAusruestungNeu() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<TextField
|
|
||||||
label="Kategorie"
|
|
||||||
size="small"
|
|
||||||
value={formKategorie}
|
|
||||||
onChange={(e) => setFormKategorie(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextField
|
|
||||||
label="Größe"
|
|
||||||
size="small"
|
|
||||||
value={formGroesse}
|
|
||||||
onChange={(e) => setFormGroesse(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
label="Zustand"
|
label="Zustand"
|
||||||
select
|
select
|
||||||
@@ -188,7 +192,7 @@ export default function PersoenlicheAusruestungNeu() {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handleCreate}
|
onClick={handleCreate}
|
||||||
disabled={createMutation.isPending || !(typeof formBezeichnung === 'string' ? formBezeichnung.trim() : formBezeichnung?.bezeichnung?.trim())}
|
disabled={createMutation.isPending || !formArtikel}
|
||||||
>
|
>
|
||||||
Erstellen
|
Erstellen
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export interface AusruestungAnfrage {
|
|||||||
positionen_count?: number;
|
positionen_count?: number;
|
||||||
geliefert_count?: number;
|
geliefert_count?: number;
|
||||||
items_count?: number;
|
items_count?: number;
|
||||||
|
im_haus?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AusruestungAnfragePosition {
|
export interface AusruestungAnfragePosition {
|
||||||
|
|||||||
Reference in New Issue
Block a user