feat: feedback edit/delete buttons and author name on edit

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
EENE Dashboard
2026-06-05 23:04:55 +09:00
parent c93df1ae40
commit b8975098ae
3 changed files with 43 additions and 17 deletions

View File

@@ -44,7 +44,7 @@ router.post('/:taskId', async (req, res, next) => {
// PATCH /api/details/item/:id
router.patch('/item/:id', async (req, res, next) => {
try {
const { content } = req.body as Record<string, string>;
const { content, authorName } = 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 } });
@@ -52,7 +52,10 @@ router.patch('/item/:id', async (req, res, next) => {
const detail = await prisma.taskDetail.update({
where: { id: req.params.id },
data: { content: content.toString().trim() },
data: {
content: content.toString().trim(),
...(authorName !== undefined && { authorName: authorName?.toString().trim() || null }),
},
include: { author: { select: { id: true, name: true } } },
});

View File

@@ -66,18 +66,18 @@ export function FeedbackModal({
/>
</label>
{mode === 'add' && (
<label className="block">
<span className="mb-1 block text-sm font-bold text-slate-500"> *</span>
<span className="mb-1 block text-sm font-bold text-slate-500">
{mode === 'add' ? '*' : ''}
</span>
<input
required
required={mode === 'add'}
value={form.authorName}
onChange={(e) => setForm((p) => ({ ...p, authorName: e.target.value }))}
className="w-full rounded-lg border border-slate-200 px-3 py-2 text-base focus:border-emerald-400 focus:outline-none"
placeholder="작성자 이름"
/>
</label>
)}
</div>
<div className="flex justify-end gap-2 border-t border-slate-100 px-6 py-4">

View File

@@ -373,6 +373,7 @@ function DetailView({ task }: { task: TaskWithRelations }) {
} else if (feedbackModal?.detail) {
await apiClient.patch(`/details/item/${feedbackModal.detail.id}`, {
content: data.content.trim(),
authorName: data.authorName.trim() || null,
});
}
await qc.invalidateQueries({ queryKey: ['task', task.id] });
@@ -516,23 +517,45 @@ function DetailView({ task }: { task: TaskWithRelations }) {
}}
>
{sortedFeedbacks.length === 0 ? (
<p className="text-lg text-slate-400"> + .</p>
<p className="text-lg text-slate-400">+ .</p>
) : (
<div className="mb-2 min-h-0 flex-1 space-y-2">
{sortedFeedbacks.map((f) => (
<div
key={f.id}
className="rounded-lg bg-slate-50 px-3 py-2"
className="flex items-start justify-between gap-2 rounded-lg bg-slate-50 px-3 py-2"
onContextMenu={(e) => {
e.preventDefault();
e.stopPropagation();
setFeedbackCtx({ x: e.clientX, y: e.clientY, detailId: f.id });
}}
>
<p className="truncate text-2xl font-black leading-snug text-slate-700">
<p className="min-w-0 flex-1 truncate text-2xl font-black leading-snug text-slate-700">
{f.content}
<span className="font-bold text-slate-400"> {feedbackAuthorName(f)}</span>
</p>
<div className="flex shrink-0 items-center gap-1 pt-0.5">
<button
type="button"
title="피드백 수정"
onClick={() => setFeedbackModal({ mode: 'edit', detail: f })}
className="rounded px-2 py-0.5 text-xs font-bold text-slate-500 hover:bg-slate-100"
>
</button>
<button
type="button"
title="피드백 삭제"
onClick={() => {
if (window.confirm('이 피드백을 삭제하시겠습니까?')) {
deleteFeedback.mutate(f.id);
}
}}
className="rounded px-2 py-0.5 text-xs font-bold text-red-400 hover:bg-red-50 hover:text-red-600"
>
🗑
</button>
</div>
</div>
))}
</div>