rework internal order system

This commit is contained in:
Matthias Hochmeister
2026-03-24 08:11:32 +01:00
parent 742c37b8de
commit 99f02b8425
11 changed files with 792 additions and 397 deletions

View File

@@ -101,7 +101,7 @@ const PERMISSION_SUB_GROUPS: Record<string, Record<string, string[]>> = {
},
ausruestungsanfrage: {
'Katalog': ['view', 'manage_catalog'],
'Anfragen': ['create_request', 'approve_requests', 'link_orders', 'view_overview', 'order_for_user'],
'Anfragen': ['create_request', 'approve', 'link_orders', 'view_all', 'order_for_user', 'edit'],
'Widget': ['widget'],
},
admin: {

View File

@@ -20,7 +20,7 @@ function AusruestungsanfrageWidget() {
return (
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>Ausrüstungsanfragen</Typography>
<Typography variant="h6" gutterBottom>Interne Bestellungen</Typography>
<Skeleton variant="rectangular" height={60} />
</CardContent>
</Card>
@@ -31,7 +31,7 @@ function AusruestungsanfrageWidget() {
return (
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>Ausrüstungsanfragen</Typography>
<Typography variant="h6" gutterBottom>Interne Bestellungen</Typography>
<Typography variant="body2" color="text.secondary">
Anfragen konnten nicht geladen werden.
</Typography>
@@ -46,7 +46,7 @@ function AusruestungsanfrageWidget() {
return (
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>Ausrüstungsanfragen</Typography>
<Typography variant="h6" gutterBottom>Interne Bestellungen</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, color: 'text.secondary' }}>
<Build fontSize="small" />
<Typography variant="body2">Keine offenen Anfragen</Typography>
@@ -60,7 +60,7 @@ function AusruestungsanfrageWidget() {
<Card>
<CardContent>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1 }}>
<Typography variant="h6">Ausrüstungsanfragen</Typography>
<Typography variant="h6">Interne Bestellungen</Typography>
<Chip label={`${pendingCount} offen`} size="small" color="warning" />
</Box>
<List dense disablePadding>

View File

@@ -121,7 +121,7 @@ const baseNavigationItems: NavigationItem[] = [
permission: 'bestellungen:view',
},
{
text: 'Ausrüstungsanfragen',
text: 'Interne Bestellungen',
icon: <Build />,
path: '/ausruestungsanfrage',
// subItems computed dynamically in navigationItems useMemo
@@ -189,8 +189,8 @@ function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
const ausruestungSubItems: SubItem[] = [];
let ausruestungTabIdx = 0;
if (hasPermission('ausruestungsanfrage:create_request')) { ausruestungSubItems.push({ text: 'Meine Anfragen', path: `/ausruestungsanfrage?tab=${ausruestungTabIdx}` }); ausruestungTabIdx++; }
if (hasPermission('ausruestungsanfrage:approve_requests')) { ausruestungSubItems.push({ text: 'Alle Anfragen', path: `/ausruestungsanfrage?tab=${ausruestungTabIdx}` }); ausruestungTabIdx++; }
if (hasPermission('ausruestungsanfrage:view_overview')) { ausruestungSubItems.push({ text: 'Übersicht', path: `/ausruestungsanfrage?tab=${ausruestungTabIdx}` }); ausruestungTabIdx++; }
if (hasPermission('ausruestungsanfrage:approve')) { ausruestungSubItems.push({ text: 'Alle Anfragen', path: `/ausruestungsanfrage?tab=${ausruestungTabIdx}` }); ausruestungTabIdx++; }
if (hasPermission('ausruestungsanfrage:view_all')) { ausruestungSubItems.push({ text: 'Übersicht', path: `/ausruestungsanfrage?tab=${ausruestungTabIdx}` }); ausruestungTabIdx++; }
ausruestungSubItems.push({ text: 'Katalog', path: `/ausruestungsanfrage?tab=${ausruestungTabIdx}` });
// Build Issues sub-items dynamically (tab order must match Issues.tsx)

View File

@@ -14,7 +14,7 @@ export const WIDGETS = [
{ key: 'adminStatus', label: 'Admin Status', defaultVisible: true },
{ key: 'links', label: 'Links', defaultVisible: true },
{ key: 'bestellungen', label: 'Bestellungen', defaultVisible: true },
{ key: 'ausruestungsanfragen', label: 'Ausrüstungsanfragen', defaultVisible: true },
{ key: 'ausruestungsanfragen', label: 'Interne Bestellungen', defaultVisible: true },
] as const;
export type WidgetKey = typeof WIDGETS[number]['key'];

File diff suppressed because it is too large Load Diff

View File

@@ -52,8 +52,20 @@ export const ausruestungsanfrageApi = {
const r = await api.get(`/api/ausruestungsanfragen/requests/${id}`);
return r.data.data;
},
createRequest: async (items: AusruestungAnfrageFormItem[], notizen?: string): Promise<AusruestungAnfrage> => {
const r = await api.post('/api/ausruestungsanfragen/requests', { items, notizen });
createRequest: async (
items: AusruestungAnfrageFormItem[],
notizen?: string,
bezeichnung?: string,
fuer_benutzer_id?: string,
): Promise<AusruestungAnfrage> => {
const r = await api.post('/api/ausruestungsanfragen/requests', { items, notizen, bezeichnung, fuer_benutzer_id });
return r.data.data;
},
updateRequest: async (
id: number,
data: { bezeichnung?: string; notizen?: string; items?: AusruestungAnfrageFormItem[] },
): Promise<AusruestungAnfrageDetailResponse> => {
const r = await api.patch(`/api/ausruestungsanfragen/requests/${id}`, data);
return r.data.data;
},
updateRequestStatus: async (id: number, status: string, admin_notizen?: string): Promise<AusruestungAnfrage> => {
@@ -77,4 +89,10 @@ export const ausruestungsanfrageApi = {
const r = await api.get('/api/ausruestungsanfragen/overview');
return r.data.data;
},
// ── Users ──
getOrderUsers: async (): Promise<Array<{ id: string; name: string }>> => {
const r = await api.get('/api/permissions/users-with', { params: { permission: 'ausruestungsanfrage:create_request' } });
return r.data.data;
},
};

View File

@@ -47,6 +47,7 @@ export interface AusruestungAnfrage {
id: number;
anfrager_id: string;
anfrager_name?: string;
bezeichnung?: string;
status: AusruestungAnfrageStatus;
notizen?: string;
admin_notizen?: string;