205 lines
7.1 KiB
TypeScript
205 lines
7.1 KiB
TypeScript
import { useState, useMemo } from 'react';
|
|
import {
|
|
Autocomplete,
|
|
Box,
|
|
Container,
|
|
MenuItem,
|
|
Stack,
|
|
TextField,
|
|
Button,
|
|
} from '@mui/material';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
|
import { personalEquipmentApi } from '../services/personalEquipment';
|
|
import { ausruestungsanfrageApi } from '../services/ausruestungsanfrage';
|
|
import { membersService } from '../services/members';
|
|
import { usePermissionContext } from '../contexts/PermissionContext';
|
|
import { useNotification } from '../contexts/NotificationContext';
|
|
import { PageHeader } from '../components/templates';
|
|
import { ZUSTAND_LABELS } from '../types/personalEquipment.types';
|
|
import type {
|
|
PersoenlicheAusruestungZustand,
|
|
CreatePersoenlicheAusruestungPayload,
|
|
} from '../types/personalEquipment.types';
|
|
import type { AusruestungArtikel } from '../types/ausruestungsanfrage.types';
|
|
|
|
const ZUSTAND_OPTIONS = Object.entries(ZUSTAND_LABELS) as [PersoenlicheAusruestungZustand, string][];
|
|
|
|
export default function PersoenlicheAusruestungNeu() {
|
|
const navigate = useNavigate();
|
|
const queryClient = useQueryClient();
|
|
const { hasPermission } = usePermissionContext();
|
|
const { showSuccess, showError } = useNotification();
|
|
|
|
const canViewAll = hasPermission('persoenliche_ausruestung:view_all');
|
|
|
|
const [formArtikel, setFormArtikel] = useState<AusruestungArtikel | null>(null);
|
|
const [formUserId, setFormUserId] = useState<{ id: string; name: string } | null>(null);
|
|
const [formBenutzerName, setFormBenutzerName] = useState('');
|
|
const [formZustand, setFormZustand] = useState<PersoenlicheAusruestungZustand>('gut');
|
|
const [formNotizen, setFormNotizen] = useState('');
|
|
const [eigenschaftValues, setEigenschaftValues] = useState<Record<number, string>>({});
|
|
|
|
const { data: catalogItems } = useQuery({
|
|
queryKey: ['ausruestungsanfrage-items-catalog'],
|
|
queryFn: () => ausruestungsanfrageApi.getItems(),
|
|
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({
|
|
queryKey: ['members-list-compact'],
|
|
queryFn: () => membersService.getMembers({ pageSize: 500 }),
|
|
staleTime: 5 * 60 * 1000,
|
|
enabled: canViewAll,
|
|
});
|
|
|
|
const memberOptions = useMemo(() => {
|
|
return (membersList?.items ?? []).map((m) => ({
|
|
id: m.id,
|
|
name: [m.given_name, m.family_name].filter(Boolean).join(' ') || m.email,
|
|
}));
|
|
}, [membersList]);
|
|
|
|
const createMutation = useMutation({
|
|
mutationFn: (data: CreatePersoenlicheAusruestungPayload) => personalEquipmentApi.create(data),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['persoenliche-ausruestung'] });
|
|
showSuccess('Persönliche Ausrüstung erstellt');
|
|
navigate('/persoenliche-ausruestung');
|
|
},
|
|
onError: () => {
|
|
showError('Fehler beim Erstellen');
|
|
},
|
|
});
|
|
|
|
const handleCreate = () => {
|
|
const bezeichnung = formArtikel?.bezeichnung ?? '';
|
|
if (!bezeichnung.trim()) return;
|
|
|
|
const payload: CreatePersoenlicheAusruestungPayload = {
|
|
bezeichnung: bezeichnung.trim(),
|
|
artikel_id: formArtikel?.id,
|
|
user_id: formUserId?.id || undefined,
|
|
benutzer_name: formBenutzerName || undefined,
|
|
zustand: formZustand,
|
|
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);
|
|
};
|
|
|
|
return (
|
|
<DashboardLayout>
|
|
<Container maxWidth="sm">
|
|
<PageHeader
|
|
title="Neue Ausrüstung zuweisen"
|
|
backTo="/persoenliche-ausruestung"
|
|
/>
|
|
|
|
<Stack spacing={2}>
|
|
<Autocomplete
|
|
options={catalogItems ?? []}
|
|
getOptionLabel={(o) => o.bezeichnung}
|
|
value={formArtikel}
|
|
onChange={(_e, v) => {
|
|
setFormArtikel(v);
|
|
setEigenschaftValues({});
|
|
}}
|
|
renderInput={(params) => (
|
|
<TextField {...params} label="Bezeichnung" required 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 && (
|
|
<Autocomplete
|
|
options={memberOptions}
|
|
getOptionLabel={(o) => o.name}
|
|
value={formUserId}
|
|
onChange={(_e, v) => setFormUserId(v)}
|
|
renderInput={(params) => (
|
|
<TextField {...params} label="Benutzer" size="small" />
|
|
)}
|
|
size="small"
|
|
/>
|
|
)}
|
|
|
|
{!canViewAll && (
|
|
<TextField
|
|
label="Benutzer (Name)"
|
|
size="small"
|
|
value={formBenutzerName}
|
|
onChange={(e) => setFormBenutzerName(e.target.value)}
|
|
/>
|
|
)}
|
|
|
|
<TextField
|
|
label="Zustand"
|
|
select
|
|
size="small"
|
|
value={formZustand}
|
|
onChange={(e) => setFormZustand(e.target.value as PersoenlicheAusruestungZustand)}
|
|
>
|
|
{ZUSTAND_OPTIONS.map(([key, label]) => (
|
|
<MenuItem key={key} value={key}>{label}</MenuItem>
|
|
))}
|
|
</TextField>
|
|
|
|
<TextField
|
|
label="Notizen"
|
|
size="small"
|
|
multiline
|
|
rows={2}
|
|
value={formNotizen}
|
|
onChange={(e) => setFormNotizen(e.target.value)}
|
|
/>
|
|
|
|
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'flex-end', mt: 1 }}>
|
|
<Button onClick={() => navigate('/persoenliche-ausruestung')}>
|
|
Abbrechen
|
|
</Button>
|
|
<Button
|
|
variant="contained"
|
|
onClick={handleCreate}
|
|
disabled={createMutation.isPending || !formArtikel}
|
|
>
|
|
Erstellen
|
|
</Button>
|
|
</Box>
|
|
</Stack>
|
|
</Container>
|
|
</DashboardLayout>
|
|
);
|
|
}
|