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>
83 lines
2.5 KiB
TypeScript
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;
|