fix: stage save errors and add milestone progress field
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,6 +6,7 @@ export interface StageFormData {
|
||||
title: string;
|
||||
startDate: string;
|
||||
dueDate: string;
|
||||
progress: number;
|
||||
description: string;
|
||||
feedback: string;
|
||||
links: MilestoneLink[];
|
||||
@@ -39,6 +40,7 @@ export function StageModal({ mode, milestone, onSave, onClose, saving }: StageMo
|
||||
title: milestone?.title ?? '',
|
||||
startDate: toDateInput(milestone?.startDate),
|
||||
dueDate: toDateInput(milestone?.dueDate),
|
||||
progress: milestone?.progress ?? 0,
|
||||
description: milestone?.description ?? '',
|
||||
feedback: '',
|
||||
links: parseLinks(milestone?.links),
|
||||
@@ -92,6 +94,22 @@ export function StageModal({ mode, milestone, onSave, onClose, saving }: StageMo
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="block">
|
||||
<span className="mb-1 flex items-center justify-between text-sm font-bold text-slate-500">
|
||||
<span>진행률</span>
|
||||
<span className="text-lg font-black text-emerald-600">{form.progress}%</span>
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={100}
|
||||
step={5}
|
||||
value={form.progress}
|
||||
onChange={(e) => set('progress', Number(e.target.value))}
|
||||
className="w-full accent-emerald-600"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<label className="block">
|
||||
<span className="mb-1 block text-sm font-bold text-slate-500">시작일</span>
|
||||
|
||||
@@ -49,7 +49,9 @@ function sortByIsoDesc<T>(items: T[], pick: (item: T) => string) {
|
||||
}
|
||||
|
||||
function milestoneProgress(m: Milestone) {
|
||||
return m.completedAt ? 100 : 0;
|
||||
if (m.completedAt) return 100;
|
||||
const p = m.progress ?? 0;
|
||||
return Math.min(100, Math.max(0, p));
|
||||
}
|
||||
|
||||
function parseContentLines(text: string | null | undefined) {
|
||||
@@ -274,7 +276,6 @@ function DetailView({ task }: { task: TaskWithRelations }) {
|
||||
const form = new FormData();
|
||||
form.append('file', file);
|
||||
form.append('milestoneId', milestoneId);
|
||||
form.append('uploadedBy', task.creatorId);
|
||||
await apiClient.post(`/files/upload/${task.id}`, form, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
});
|
||||
@@ -289,21 +290,41 @@ function DetailView({ task }: { task: TaskWithRelations }) {
|
||||
description: data.description.trim() || undefined,
|
||||
startDate: data.startDate || undefined,
|
||||
dueDate: data.dueDate || undefined,
|
||||
progress: data.progress,
|
||||
feedback: data.feedback.trim() || undefined,
|
||||
links: JSON.stringify(data.links),
|
||||
links: data.links.length > 0 ? JSON.stringify(data.links) : undefined,
|
||||
};
|
||||
|
||||
let milestoneId: string;
|
||||
|
||||
if (stageModal?.mode === 'add') {
|
||||
const { data: created } = await apiClient.post<Milestone>(`/milestones/${task.id}`, payload);
|
||||
if (fileList.length) await uploadFiles(created.id, fileList);
|
||||
milestoneId = created.id;
|
||||
setSelectedId(created.id);
|
||||
} else if (stageModal?.milestone) {
|
||||
await apiClient.patch(`/milestones/item/${stageModal.milestone.id}`, payload);
|
||||
if (fileList.length) await uploadFiles(stageModal.milestone.id, fileList);
|
||||
const { data: updated } = await apiClient.patch<Milestone>(
|
||||
`/milestones/item/${stageModal.milestone.id}`,
|
||||
payload,
|
||||
);
|
||||
milestoneId = updated.id;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileList.length > 0) {
|
||||
try {
|
||||
await uploadFiles(milestoneId, fileList);
|
||||
} catch {
|
||||
alert('단계는 저장됐지만 파일 업로드에 실패했습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
await qc.invalidateQueries({ queryKey: ['task', task.id] });
|
||||
setStageModal(null);
|
||||
} catch (err: unknown) {
|
||||
const ax = err as { response?: { data?: { message?: string } }; message?: string };
|
||||
const msg = ax.response?.data?.message || ax.message || '단계 저장에 실패했습니다.';
|
||||
alert(msg);
|
||||
} finally {
|
||||
setStageSaving(false);
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ export interface Milestone {
|
||||
description: string | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
progress: number;
|
||||
links: string | null;
|
||||
completedAt: string | null;
|
||||
order: number;
|
||||
|
||||
Reference in New Issue
Block a user