Files
dashboard/frontend/src/pages/BestellungNeu.tsx

221 lines
8.8 KiB
TypeScript

import { useState } from 'react';
import {
Box,
Typography,
Paper,
Button,
TextField,
IconButton,
Autocomplete,
Tooltip,
} from '@mui/material';
import {
Add as AddIcon,
RemoveCircleOutline as RemoveIcon,
} from '@mui/icons-material';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import DashboardLayout from '../components/dashboard/DashboardLayout';
import { PageHeader, FormLayout } from '../components/templates';
import { useNotification } from '../contexts/NotificationContext';
import { bestellungApi } from '../services/bestellung';
import type { BestellungFormData, BestellpositionFormData, LieferantFormData, Lieferant } from '../types/bestellung.types';
const emptyOrderForm: BestellungFormData = { bezeichnung: '', lieferant_id: undefined, besteller_id: '', notizen: '', positionen: [] };
const emptyVendorForm: LieferantFormData = { name: '', kontakt_name: '', email: '', telefon: '', adresse: '', website: '', notizen: '' };
const emptyPosition: BestellpositionFormData = { bezeichnung: '', menge: 1, einheit: 'Stk' };
export default function BestellungNeu() {
const navigate = useNavigate();
const queryClient = useQueryClient();
const { showSuccess, showError } = useNotification();
const [orderForm, setOrderForm] = useState<BestellungFormData>({ ...emptyOrderForm });
const [inlineVendorOpen, setInlineVendorOpen] = useState(false);
const [inlineVendorForm, setInlineVendorForm] = useState<LieferantFormData>({ ...emptyVendorForm });
// ── Queries ──
const { data: vendors = [] } = useQuery({
queryKey: ['lieferanten'],
queryFn: bestellungApi.getVendors,
});
const { data: orderUsers = [] } = useQuery({
queryKey: ['bestellungen', 'order-users'],
queryFn: bestellungApi.getOrderUsers,
});
// ── Mutations ──
const createOrder = useMutation({
mutationFn: (data: BestellungFormData) => bestellungApi.createOrder(data),
onSuccess: (created) => {
queryClient.invalidateQueries({ queryKey: ['bestellungen'] });
showSuccess('Bestellung erstellt');
navigate(`/bestellungen/${created.id}`);
},
onError: (error: any) => showError(error?.response?.data?.message || 'Fehler beim Erstellen der Bestellung'),
});
const createVendor = useMutation({
mutationFn: (data: LieferantFormData) => bestellungApi.createVendor(data),
onSuccess: (newVendor: Lieferant) => {
queryClient.invalidateQueries({ queryKey: ['lieferanten'] });
showSuccess('Lieferant erstellt');
setOrderForm((f) => ({ ...f, lieferant_id: newVendor.id }));
setInlineVendorOpen(false);
setInlineVendorForm({ ...emptyVendorForm });
},
onError: () => showError('Fehler beim Erstellen des Lieferanten'),
});
const handleSubmit = () => {
if (!orderForm.bezeichnung.trim()) return;
createOrder.mutate(orderForm);
};
return (
<DashboardLayout>
<PageHeader
title="Neue Bestellung"
backTo="/bestellungen"
/>
<FormLayout
actions={<>
<Button onClick={() => navigate('/bestellungen')}>Abbrechen</Button>
<Button
variant="contained"
onClick={handleSubmit}
disabled={!orderForm.bezeichnung.trim() || createOrder.isPending}
>
Erstellen
</Button>
</>}
>
<TextField
label="Bezeichnung"
required
InputLabelProps={{ shrink: true }}
value={orderForm.bezeichnung}
onChange={(e) => setOrderForm((f) => ({ ...f, bezeichnung: e.target.value }))}
/>
{/* Lieferant + inline create */}
<Box sx={{ display: 'flex', gap: 1, alignItems: 'flex-start' }}>
<Autocomplete
options={vendors}
getOptionLabel={(o) => o.name}
value={vendors.find((v) => v.id === orderForm.lieferant_id) || null}
onChange={(_e, v) => setOrderForm((f) => ({ ...f, lieferant_id: v?.id }))}
renderInput={(params) => <TextField {...params} label="Lieferant" />}
sx={{ flexGrow: 1 }}
/>
<Tooltip title="Neuen Lieferant anlegen">
<IconButton
onClick={() => setInlineVendorOpen(!inlineVendorOpen)}
color={inlineVendorOpen ? 'primary' : 'default'}
sx={{ mt: 1 }}
>
<AddIcon />
</IconButton>
</Tooltip>
</Box>
{inlineVendorOpen && (
<Paper variant="outlined" sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 1.5 }}>
<Typography variant="subtitle2">Neuer Lieferant</Typography>
<TextField size="small" label="Name *" value={inlineVendorForm.name} onChange={(e) => setInlineVendorForm((f) => ({ ...f, name: e.target.value }))} />
<TextField size="small" label="Kontakt-Name" value={inlineVendorForm.kontakt_name || ''} onChange={(e) => setInlineVendorForm((f) => ({ ...f, kontakt_name: e.target.value }))} />
<TextField size="small" label="E-Mail" value={inlineVendorForm.email || ''} onChange={(e) => setInlineVendorForm((f) => ({ ...f, email: e.target.value }))} />
<TextField size="small" label="Telefon" value={inlineVendorForm.telefon || ''} onChange={(e) => setInlineVendorForm((f) => ({ ...f, telefon: e.target.value }))} />
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'flex-end' }}>
<Button size="small" onClick={() => { setInlineVendorOpen(false); setInlineVendorForm({ ...emptyVendorForm }); }}>Abbrechen</Button>
<Button size="small" variant="contained" onClick={() => createVendor.mutate(inlineVendorForm)} disabled={!inlineVendorForm.name.trim() || createVendor.isPending}>Anlegen</Button>
</Box>
</Paper>
)}
<Autocomplete
options={orderUsers}
getOptionLabel={(o) => o.name}
value={orderUsers.find((u) => u.id === orderForm.besteller_id) || null}
onChange={(_e, v) => setOrderForm((f) => ({ ...f, besteller_id: v?.id || '' }))}
renderInput={(params) => <TextField {...params} label="Besteller" />}
/>
<TextField
label="Notizen"
multiline
rows={3}
value={orderForm.notizen || ''}
onChange={(e) => setOrderForm((f) => ({ ...f, notizen: e.target.value }))}
/>
{/* ── Positionen ── */}
<Typography variant="subtitle2" sx={{ mt: 1 }}>Positionen</Typography>
{(orderForm.positionen || []).map((pos, idx) => (
<Box key={idx} sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<TextField
label="Bezeichnung"
size="small"
required
InputLabelProps={{ shrink: true }}
value={pos.bezeichnung}
onChange={(e) => {
const next = [...(orderForm.positionen || [])];
next[idx] = { ...next[idx], bezeichnung: e.target.value };
setOrderForm((f) => ({ ...f, positionen: next }));
}}
sx={{ flexGrow: 1 }}
/>
<TextField
label="Menge"
size="small"
type="number"
InputLabelProps={{ shrink: true }}
value={pos.menge}
onChange={(e) => {
const next = [...(orderForm.positionen || [])];
next[idx] = { ...next[idx], menge: Math.max(1, Number(e.target.value) || 1) };
setOrderForm((f) => ({ ...f, positionen: next }));
}}
sx={{ width: 90 }}
inputProps={{ min: 1 }}
/>
<TextField
label="Einheit"
size="small"
InputLabelProps={{ shrink: true }}
value={pos.einheit || 'Stk'}
onChange={(e) => {
const next = [...(orderForm.positionen || [])];
next[idx] = { ...next[idx], einheit: e.target.value };
setOrderForm((f) => ({ ...f, positionen: next }));
}}
sx={{ width: 100 }}
/>
<IconButton
size="small"
color="error"
onClick={() => {
const next = (orderForm.positionen || []).filter((_, i) => i !== idx);
setOrderForm((f) => ({ ...f, positionen: next }));
}}
>
<RemoveIcon />
</IconButton>
</Box>
))}
<Button
size="small"
startIcon={<AddIcon />}
onClick={() => setOrderForm((f) => ({ ...f, positionen: [...(f.positionen || []), { ...emptyPosition }] }))}
>
Position hinzufügen
</Button>
{/* ── Submit ── */}
</FormLayout>
</DashboardLayout>
);
}