Files
eene_dashboard/backend/src/routes/details.ts
EENE Dashboard 9abb58e5c8 feat: detail page attachments, preview, and file metadata
Add file displayName/sortOrder APIs, result preview with Excel/video support,
unified attachment/link editing, feedback modal, and AuthProvider fix.
Run prisma migrate deploy on Render builds.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-05 18:32:56 +09:00

83 lines
2.5 KiB
TypeScript

import { Router } from 'express';
import { prisma } from '../lib/prisma';
import { resolveTaskActorId } from '../lib/resolveUser';
import { AppError } from '../middleware/errorHandler';
const router = Router();
async function resolveAuthorId(taskId: string, authorName?: string): Promise<string> {
const name = authorName?.toString().trim();
if (name) {
const user = await prisma.user.findFirst({ where: { name } });
if (user) return user.id;
}
return resolveTaskActorId(taskId);
}
// POST /api/details/:taskId
router.post('/:taskId', async (req, res, next) => {
try {
const taskId = req.params.taskId;
const { content, milestoneId, authorName } = req.body as Record<string, string>;
if (!content?.toString().trim()) throw new AppError(400, '피드백 내용은 필수입니다.');
const task = await prisma.task.findUnique({ where: { id: taskId }, select: { id: true } });
if (!task) throw new AppError(404, '업무를 찾을 수 없습니다.');
if (milestoneId) {
const ms = await prisma.milestone.findFirst({ where: { id: milestoneId, taskId } });
if (!ms) throw new AppError(400, '유효하지 않은 업무 단계입니다.');
}
const updatedBy = await resolveAuthorId(taskId, authorName);
const detail = await prisma.taskDetail.create({
data: {
taskId,
milestoneId: milestoneId || null,
content: content.toString().trim(),
updatedBy,
},
include: { author: { select: { id: true, name: true } } },
});
res.status(201).json(detail);
} catch (err) {
next(err);
}
});
// PATCH /api/details/item/:id
router.patch('/item/:id', async (req, res, next) => {
try {
const { content } = req.body as Record<string, string>;
if (!content?.toString().trim()) throw new AppError(400, '피드백 내용은 필수입니다.');
const existing = await prisma.taskDetail.findUnique({ where: { id: req.params.id } });
if (!existing) throw new AppError(404, '피드백을 찾을 수 없습니다.');
const detail = await prisma.taskDetail.update({
where: { id: req.params.id },
data: { content: content.toString().trim() },
include: { author: { select: { id: true, name: true } } },
});
res.json(detail);
} catch (err) {
next(err);
}
});
// DELETE /api/details/item/:id
router.delete('/item/:id', async (req, res, next) => {
try {
await prisma.taskDetail.delete({ where: { id: req.params.id } });
res.status(204).send();
} catch (err) {
next(err);
}
});
export default router;