feat: add issue kanban/attachments/deadlines, dashboard widget DnD, and checklisten system

This commit is contained in:
Matthias Hochmeister
2026-03-28 15:19:41 +01:00
parent a1cda5be51
commit 0c2ea829aa
42 changed files with 4804 additions and 201 deletions

View File

@@ -0,0 +1,131 @@
import { api } from './api';
import type {
ChecklistVorlage,
ChecklistVorlageItem,
FahrzeugChecklistItem,
ChecklistAusfuehrung,
ChecklistFaelligkeit,
ChecklistVorlageFilter,
ChecklistAusfuehrungFilter,
CreateVorlagePayload,
UpdateVorlagePayload,
CreateVorlageItemPayload,
UpdateVorlageItemPayload,
CreateFahrzeugItemPayload,
UpdateFahrzeugItemPayload,
SubmitAusfuehrungPayload,
} from '../types/checklist.types';
export const checklistenApi = {
// ── Vorlagen (Templates) ──
getVorlagen: async (filter?: ChecklistVorlageFilter): Promise<ChecklistVorlage[]> => {
const params = new URLSearchParams();
if (filter?.fahrzeug_typ_id != null) params.set('fahrzeug_typ_id', String(filter.fahrzeug_typ_id));
if (filter?.aktiv != null) params.set('aktiv', String(filter.aktiv));
const qs = params.toString();
const r = await api.get(`/api/checklisten/vorlagen${qs ? `?${qs}` : ''}`);
return r.data.data;
},
getVorlage: async (id: number): Promise<ChecklistVorlage> => {
const r = await api.get(`/api/checklisten/vorlagen/${id}`);
return r.data.data;
},
createVorlage: async (data: CreateVorlagePayload): Promise<ChecklistVorlage> => {
const r = await api.post('/api/checklisten/vorlagen', data);
return r.data.data;
},
updateVorlage: async (id: number, data: UpdateVorlagePayload): Promise<ChecklistVorlage> => {
const r = await api.put(`/api/checklisten/vorlagen/${id}`, data);
return r.data.data;
},
deleteVorlage: async (id: number): Promise<void> => {
await api.delete(`/api/checklisten/vorlagen/${id}`);
},
// ── Vorlage Items ──
getVorlageItems: async (vorlageId: number): Promise<ChecklistVorlageItem[]> => {
const r = await api.get(`/api/checklisten/vorlagen/${vorlageId}/items`);
return r.data.data;
},
addVorlageItem: async (vorlageId: number, data: CreateVorlageItemPayload): Promise<ChecklistVorlageItem> => {
const r = await api.post(`/api/checklisten/vorlagen/${vorlageId}/items`, data);
return r.data.data;
},
updateVorlageItem: async (id: number, data: UpdateVorlageItemPayload): Promise<ChecklistVorlageItem> => {
const r = await api.put(`/api/checklisten/items/${id}`, data);
return r.data.data;
},
deleteVorlageItem: async (id: number): Promise<void> => {
await api.delete(`/api/checklisten/items/${id}`);
},
// ── Vehicle-specific Items ──
getVehicleItems: async (fahrzeugId: string): Promise<FahrzeugChecklistItem[]> => {
const r = await api.get(`/api/checklisten/fahrzeug/${fahrzeugId}/items`);
return r.data.data;
},
addVehicleItem: async (fahrzeugId: string, data: CreateFahrzeugItemPayload): Promise<FahrzeugChecklistItem> => {
const r = await api.post(`/api/checklisten/fahrzeug/${fahrzeugId}/items`, data);
return r.data.data;
},
updateVehicleItem: async (id: number, data: UpdateFahrzeugItemPayload): Promise<FahrzeugChecklistItem> => {
const r = await api.put(`/api/checklisten/fahrzeug-items/${id}`, data);
return r.data.data;
},
deleteVehicleItem: async (id: number): Promise<void> => {
await api.delete(`/api/checklisten/fahrzeug-items/${id}`);
},
// ── Checklists for a Vehicle ──
getChecklistenForVehicle: async (fahrzeugId: string): Promise<ChecklistVorlage[]> => {
const r = await api.get(`/api/checklisten/fahrzeug/${fahrzeugId}/checklisten`);
return r.data.data;
},
// ── Executions ──
startExecution: async (fahrzeugId: string, vorlageId: number): Promise<ChecklistAusfuehrung> => {
const r = await api.post('/api/checklisten/ausfuehrungen', { fahrzeug_id: fahrzeugId, vorlage_id: vorlageId });
return r.data.data;
},
submitExecution: async (id: string, data: SubmitAusfuehrungPayload): Promise<ChecklistAusfuehrung> => {
const r = await api.put(`/api/checklisten/ausfuehrungen/${id}`, data);
return r.data.data;
},
approveExecution: async (id: string): Promise<ChecklistAusfuehrung> => {
const r = await api.post(`/api/checklisten/ausfuehrungen/${id}/freigabe`);
return r.data.data;
},
getExecution: async (id: string): Promise<ChecklistAusfuehrung> => {
const r = await api.get(`/api/checklisten/ausfuehrungen/${id}`);
return r.data.data;
},
getExecutions: async (filter?: ChecklistAusfuehrungFilter): Promise<ChecklistAusfuehrung[]> => {
const params = new URLSearchParams();
if (filter?.fahrzeug_id) params.set('fahrzeug_id', filter.fahrzeug_id);
if (filter?.vorlage_id != null) params.set('vorlage_id', String(filter.vorlage_id));
if (filter?.status?.length) params.set('status', filter.status.join(','));
const qs = params.toString();
const r = await api.get(`/api/checklisten/ausfuehrungen${qs ? `?${qs}` : ''}`);
return r.data.data;
},
// ── Overdue / Due ──
getOverdue: async (): Promise<ChecklistFaelligkeit[]> => {
const r = await api.get('/api/checklisten/faellig');
return r.data.data;
},
};

View File

@@ -0,0 +1,28 @@
import { api } from './api';
import type { FahrzeugTyp } from '../types/checklist.types';
export const fahrzeugTypenApi = {
getAll: async (): Promise<FahrzeugTyp[]> => {
const r = await api.get('/api/fahrzeug-typen');
return r.data.data;
},
getById: async (id: number): Promise<FahrzeugTyp> => {
const r = await api.get(`/api/fahrzeug-typen/${id}`);
return r.data.data;
},
create: async (data: Partial<FahrzeugTyp>): Promise<FahrzeugTyp> => {
const r = await api.post('/api/fahrzeug-typen', data);
return r.data.data;
},
update: async (id: number, data: Partial<FahrzeugTyp>): Promise<FahrzeugTyp> => {
const r = await api.put(`/api/fahrzeug-typen/${id}`, data);
return r.data.data;
},
delete: async (id: number): Promise<void> => {
await api.delete(`/api/fahrzeug-typen/${id}`);
},
};

View File

@@ -1,5 +1,5 @@
import { api } from './api';
import type { Issue, IssueComment, CreateIssuePayload, UpdateIssuePayload, IssueTyp, IssueFilters, AssignableMember, IssueStatusDef, IssuePriorityDef, IssueWidgetSummary, IssueHistorie } from '../types/issue.types';
import type { Issue, IssueComment, CreateIssuePayload, UpdateIssuePayload, IssueTyp, IssueFilters, AssignableMember, IssueStatusDef, IssuePriorityDef, IssueWidgetSummary, IssueHistorie, IssueDatei } from '../types/issue.types';
export const issuesApi = {
getIssues: async (filters?: IssueFilters): Promise<Issue[]> => {
@@ -98,4 +98,20 @@ export const issuesApi = {
deletePriority: async (id: number): Promise<void> => {
await api.delete(`/api/issues/priorities/${id}`);
},
// Files
uploadFile: async (issueId: number, file: File): Promise<IssueDatei> => {
const formData = new FormData();
formData.append('file', file);
const r = await api.post(`/api/issues/${issueId}/files`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return r.data.data;
},
getFiles: async (issueId: number): Promise<IssueDatei[]> => {
const r = await api.get(`/api/issues/${issueId}/files`);
return r.data.data;
},
deleteFile: async (fileId: string): Promise<void> => {
await api.delete(`/api/issues/files/${fileId}`);
},
};