refine: feedback and stage actions via right-click menu only

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
EENE Dashboard
2026-06-05 23:08:20 +09:00
parent b8975098ae
commit 4a90ea096e

View File

@@ -508,54 +508,25 @@ function DetailView({ task }: { task: TaskWithRelations }) {
+ +
</button> </button>
</div> </div>
<div <div className="flex min-h-0 flex-1 flex-col overflow-y-auto pr-1">
className="flex min-h-0 flex-1 flex-col overflow-y-auto pr-1"
onContextMenu={(e) => {
if (!selectedId) return;
e.preventDefault();
setFeedbackCtx({ x: e.clientX, y: e.clientY });
}}
>
{sortedFeedbacks.length === 0 ? ( {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"> <div className="mb-2 min-h-0 flex-1 space-y-2">
{sortedFeedbacks.map((f) => ( {sortedFeedbacks.map((f) => (
<div <div
key={f.id} key={f.id}
className="flex items-start justify-between gap-2 rounded-lg bg-slate-50 px-3 py-2" className="rounded-lg bg-slate-50 px-3 py-2"
onContextMenu={(e) => { onContextMenu={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setFeedbackCtx({ x: e.clientX, y: e.clientY, detailId: f.id }); setFeedbackCtx({ x: e.clientX, y: e.clientY, detailId: f.id });
}} }}
> >
<p className="min-w-0 flex-1 truncate text-2xl font-black leading-snug text-slate-700"> <p className="truncate text-2xl font-black leading-snug text-slate-700">
{f.content} {f.content}
<span className="font-bold text-slate-400"> {feedbackAuthorName(f)}</span> <span className="font-bold text-slate-400"> {feedbackAuthorName(f)}</span>
</p> </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>
))} ))}
</div> </div>
@@ -659,7 +630,7 @@ function DetailView({ task }: { task: TaskWithRelations }) {
onClose={() => setCtxMenu(null)} onClose={() => setCtxMenu(null)}
items={[ items={[
{ {
label: '단계 수정', label: '수정',
icon: '✏️', icon: '✏️',
onClick: () => { onClick: () => {
const ms = milestones.find((m) => m.id === ctxMenu.stageId); const ms = milestones.find((m) => m.id === ctxMenu.stageId);
@@ -667,7 +638,7 @@ function DetailView({ task }: { task: TaskWithRelations }) {
}, },
}, },
{ {
label: '단계 삭제', label: '삭제',
icon: '🗑', icon: '🗑',
danger: true, danger: true,
onClick: () => { onClick: () => {
@@ -696,41 +667,31 @@ function DetailView({ task }: { task: TaskWithRelations }) {
/> />
)} )}
{feedbackCtx && ( {feedbackCtx?.detailId && (
<ContextMenu <ContextMenu
x={feedbackCtx.x} x={feedbackCtx.x}
y={feedbackCtx.y} y={feedbackCtx.y}
onClose={() => setFeedbackCtx(null)} onClose={() => setFeedbackCtx(null)}
items={ items={[
feedbackCtx.detailId {
? [ label: '수정',
{ icon: '✏️',
label: '피드백 수정', onClick: () => {
icon: '✏️', const d = details.find((item) => item.id === feedbackCtx.detailId);
onClick: () => { if (d) setFeedbackModal({ mode: 'edit', detail: d });
const d = details.find((item) => item.id === feedbackCtx.detailId); },
if (d) setFeedbackModal({ mode: 'edit', detail: d }); },
}, {
}, label: '삭제',
{ icon: '🗑',
label: '피드백 삭제', danger: true,
icon: '🗑', onClick: () => {
danger: true, if (window.confirm('이 피드백을 삭제하시겠습니까?')) {
onClick: () => { deleteFeedback.mutate(feedbackCtx.detailId!);
if (window.confirm('이 피드백을 삭제하시겠습니까?')) { }
deleteFeedback.mutate(feedbackCtx.detailId!); },
} },
}, ]}
},
]
: [
{
label: '피드백 추가',
icon: '',
onClick: () => setFeedbackModal({ mode: 'add' }),
},
]
}
/> />
)} )}
</div> </div>