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

@@ -175,6 +175,7 @@ class IssueController {
titel: 'Titel geändert',
beschreibung: 'Beschreibung geändert',
typ_id: 'Typ geändert',
faellig_am: 'Fälligkeitsdatum geändert',
};
for (const [field, label] of Object.entries(fieldLabels)) {
if (field in updateData && updateData[field] !== existing[field]) {
@@ -503,6 +504,67 @@ class IssueController {
res.status(500).json({ success: false, message: 'Priorität konnte nicht deaktiviert werden' });
}
}
// --- File management ---
async uploadFile(req: Request, res: Response): Promise<void> {
const issueId = parseInt(param(req, 'id'), 10);
if (isNaN(issueId)) {
res.status(400).json({ success: false, message: 'Ungültige Issue-ID' });
return;
}
const file = req.file as Express.Multer.File | undefined;
if (!file) {
res.status(400).json({ success: false, message: 'Keine Datei hochgeladen' });
return;
}
try {
const fileRecord = await issueService.addFile(issueId, {
dateiname: file.originalname,
dateipfad: file.path,
dateityp: file.mimetype,
dateigroesse: file.size,
}, req.user!.id);
res.status(201).json({ success: true, data: fileRecord });
} catch (error) {
logger.error('IssueController.uploadFile error', { error });
res.status(500).json({ success: false, message: 'Datei konnte nicht hochgeladen werden' });
}
}
async getFiles(req: Request, res: Response): Promise<void> {
const issueId = parseInt(param(req, 'id'), 10);
if (isNaN(issueId)) {
res.status(400).json({ success: false, message: 'Ungültige ID' });
return;
}
try {
const files = await issueService.getFiles(issueId);
res.status(200).json({ success: true, data: files });
} catch (error) {
logger.error('IssueController.getFiles error', { error });
res.status(500).json({ success: false, message: 'Dateien konnten nicht geladen werden' });
}
}
async deleteFile(req: Request, res: Response): Promise<void> {
const fileId = param(req, 'fileId');
if (!fileId) {
res.status(400).json({ success: false, message: 'Ungültige Datei-ID' });
return;
}
try {
const result = await issueService.deleteFile(fileId, req.user!.id);
if (!result) {
res.status(404).json({ success: false, message: 'Datei nicht gefunden' });
return;
}
res.status(200).json({ success: true, message: 'Datei gelöscht' });
} catch (error) {
logger.error('IssueController.deleteFile error', { error });
res.status(500).json({ success: false, message: 'Datei konnte nicht gelöscht werden' });
}
}
}
export default new IssueController();