summaryrefslogtreecommitdiff
path: root/lib/dolce
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dolce')
-rw-r--r--lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx415
-rw-r--r--lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx25
-rw-r--r--lib/dolce/dialogs/upload-files-to-detail-dialog.tsx252
-rw-r--r--lib/dolce/table/detail-drawing-columns.tsx2
-rw-r--r--lib/dolce/table/drawing-list-table-v2.tsx4
5 files changed, 419 insertions, 279 deletions
diff --git a/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx b/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx
index 673d48d6..0253228b 100644
--- a/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx
+++ b/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx
@@ -72,6 +72,8 @@ export function AddAndModifyDetailDrawingDialog({
const [comment, setComment] = useState<string>("");
const [isSubmitting, setIsSubmitting] = useState(false);
+ const [showConfirmation, setShowConfirmation] = useState(false);
+
// Edit 모드일 때 초기값 설정
useEffect(() => {
if (mode === "edit" && detailDrawing && open) {
@@ -155,9 +157,10 @@ export function AddAndModifyDetailDrawingDialog({
setRevisionError("");
setComment("");
clearFiles();
+ setShowConfirmation(false);
};
- // 제출
+ // 제출 (확인 단계 포함)
const handleSubmit = async () => {
// 유효성 검사
if (!registerKind) {
@@ -200,6 +203,12 @@ export function AddAndModifyDetailDrawingDialog({
return;
}
+ // 확인 단계가 아니면 확인 단계로 이동
+ if (!showConfirmation) {
+ setShowConfirmation(true);
+ return;
+ }
+
try {
setIsSubmitting(true);
@@ -212,7 +221,7 @@ export function AddAndModifyDetailDrawingDialog({
dwgList: [
{
Mode: "ADD",
- Status: "Submitted",
+ Status: "Standby",
RegisterId: 0,
ProjectNo: drawing.ProjectNo,
Discipline: drawing.Discipline,
@@ -329,8 +338,12 @@ export function AddAndModifyDetailDrawingDialog({
};
const handleCancel = () => {
- resetForm();
- onOpenChange(false);
+ if (showConfirmation) {
+ setShowConfirmation(false);
+ } else {
+ resetForm();
+ onOpenChange(false);
+ }
};
// DrawingUsage가 변경되면 RegisterKind 초기화
@@ -355,219 +368,279 @@ export function AddAndModifyDetailDrawingDialog({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
- <DialogContent className="max-w-2xl">
+ <DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>
- {mode === "edit" ? t("editDetailDialog.title") : t("addDetailDialog.title")}
+ {showConfirmation
+ ? t("addDetailDialog.confirmTitle", "확인")
+ : (mode === "edit" ? t("editDetailDialog.title") : t("addDetailDialog.title"))
+ }
</DialogTitle>
</DialogHeader>
- <div className="space-y-6">
- {/* 도면 정보 표시 */}
- {mode === "add" && drawing && (
+ {showConfirmation ? (
+ <div className="space-y-6">
<Alert>
<Info className="h-4 w-4" />
<AlertDescription>
- <div className="font-medium">{drawing.DrawingNo}</div>
- <div className="text-sm text-muted-foreground">{drawing.DrawingName}</div>
+ {t("addDetailDialog.confirmMessage", "아래 내용으로 제출하시겠습니까?")}
</AlertDescription>
</Alert>
- )}
- {mode === "edit" && detailDrawing && (
- <Alert>
- <Info className="h-4 w-4" />
- <AlertDescription>
- <div className="font-medium">{detailDrawing.DrawingNo} - Rev. {detailDrawing.DrawingRevNo}</div>
- <div className="text-sm text-muted-foreground">{detailDrawing.DrawingName}</div>
- </AlertDescription>
- </Alert>
- )}
+ <div className="grid grid-cols-2 gap-4 border rounded-lg p-4 bg-muted/20">
+ <div className="space-y-1">
+ <Label className="text-xs text-muted-foreground">{t("addDetailDialog.drawingUsageLabel")}</Label>
+ <p className="text-sm font-medium">
+ {drawingUsageOptions.find(opt => opt.value === drawingUsage)?.label || drawingUsage}
+ </p>
+ </div>
+ <div className="space-y-1">
+ <Label className="text-xs text-muted-foreground">{t("addDetailDialog.registerKindLabel")}</Label>
+ <p className="text-sm font-medium">
+ {registerKindOptions.find(opt => opt.value === registerKind)?.label || registerKind}
+ </p>
+ </div>
+ {drawingUsage !== "CMT" && (
+ <div className="space-y-1">
+ <Label className="text-xs text-muted-foreground">{t("addDetailDialog.revisionLabel")}</Label>
+ <p className="text-sm font-medium">{revision}</p>
+ </div>
+ )}
+ <div className="space-y-1 col-span-2">
+ <Label className="text-xs text-muted-foreground">{t("addDetailDialog.commentLabel")}</Label>
+ <p className="text-sm">{comment || "-"}</p>
+ </div>
+ </div>
+
+ {files.length > 0 && (
+ <div className="space-y-2">
+ <Label>{t("addDetailDialog.selectedFiles", { count: files.length })}</Label>
+ <div className="max-h-60 overflow-y-auto space-y-2 border rounded-lg p-2">
+ {isSubmitting ? (
+ <FileUploadProgressList fileProgresses={fileProgresses} />
+ ) : (
+ files.map((file, index) => (
+ <div key={index} className="flex items-center gap-2 p-2 rounded bg-muted/50 text-sm">
+ <FileIcon className="h-4 w-4 text-muted-foreground shrink-0" />
+ <span className="truncate flex-1">{file.name}</span>
+ <span className="text-xs text-muted-foreground whitespace-nowrap">
+ {(file.size / 1024 / 1024).toFixed(2)} MB
+ </span>
+ </div>
+ ))
+ )}
+ </div>
+ </div>
+ )}
+ </div>
+ ) : (
+ <div className="space-y-6">
+ {/* 도면 정보 표시 */}
+ {mode === "add" && drawing && (
+ <Alert>
+ <Info className="h-4 w-4" />
+ <AlertDescription>
+ <div className="font-medium">{drawing.DrawingNo}</div>
+ <div className="text-sm text-muted-foreground">{drawing.DrawingName}</div>
+ </AlertDescription>
+ </Alert>
+ )}
+
+ {mode === "edit" && detailDrawing && (
+ <Alert>
+ <Info className="h-4 w-4" />
+ <AlertDescription>
+ <div className="font-medium">{detailDrawing.DrawingNo} - Rev. {detailDrawing.DrawingRevNo}</div>
+ <div className="text-sm text-muted-foreground">{detailDrawing.DrawingName}</div>
+ </AlertDescription>
+ </Alert>
+ )}
- {/* 도면용도 선택 (Add 모드에서만 표시) */}
- {mode === "add" && (
+ {/* 도면용도 선택 (Add 모드에서만 표시) */}
+ {mode === "add" && (
+ <div className="space-y-2">
+ <Label>{t("addDetailDialog.drawingUsageLabel")}</Label>
+ <Select value={drawingUsage} onValueChange={handleDrawingUsageChange}>
+ <SelectTrigger>
+ <SelectValue placeholder={t("addDetailDialog.drawingUsagePlaceholder")} />
+ </SelectTrigger>
+ <SelectContent>
+ {drawingUsageOptions.map((option) => (
+ <SelectItem key={option.value} value={option.value}>
+ {option.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ </div>
+ )}
+
+ {/* 등록종류 선택 */}
<div className="space-y-2">
- <Label>{t("addDetailDialog.drawingUsageLabel")}</Label>
- <Select value={drawingUsage} onValueChange={handleDrawingUsageChange}>
+ <Label>{t("addDetailDialog.registerKindLabel")}</Label>
+ <Select
+ value={registerKind}
+ onValueChange={setRegisterKind}
+ disabled={mode === "add" && !drawingUsage}
+ >
<SelectTrigger>
- <SelectValue placeholder={t("addDetailDialog.drawingUsagePlaceholder")} />
+ <SelectValue placeholder={t("addDetailDialog.registerKindPlaceholder")} />
</SelectTrigger>
<SelectContent>
- {drawingUsageOptions.map((option) => (
+ {registerKindOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
+ {revisionRule && (
+ <p className="text-sm text-muted-foreground">
+ {t("addDetailDialog.revisionFormatPrefix")}{revisionRule}
+ </p>
+ )}
</div>
- )}
-
- {/* 등록종류 선택 */}
- <div className="space-y-2">
- <Label>{t("addDetailDialog.registerKindLabel")}</Label>
- <Select
- value={registerKind}
- onValueChange={setRegisterKind}
- disabled={mode === "add" && !drawingUsage}
- >
- <SelectTrigger>
- <SelectValue placeholder={t("addDetailDialog.registerKindPlaceholder")} />
- </SelectTrigger>
- <SelectContent>
- {registerKindOptions.map((option) => (
- <SelectItem key={option.value} value={option.value}>
- {option.label}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- {revisionRule && (
- <p className="text-sm text-muted-foreground">
- {t("addDetailDialog.revisionFormatPrefix")}{revisionRule}
- </p>
+
+ {/* Revision 입력 */}
+ {drawingUsage !== "CMT" && (
+ <div className="space-y-2">
+ <Label>{t("addDetailDialog.revisionLabel")}</Label>
+ <Input
+ value={revision}
+ onChange={(e) => handleRevisionChange(e.target.value)}
+ placeholder={t("addDetailDialog.revisionPlaceholder")}
+ disabled={!registerKind}
+ className={revisionError ? "border-red-500 focus-visible:ring-red-500" : ""}
+ />
+ {revisionError && (
+ <p className="text-sm text-red-500 flex items-center gap-1">
+ {revisionError}
+ </p>
+ )}
+ {!revisionError && revision && (
+ <p className="text-sm text-green-600 flex items-center gap-1">
+ {t("addDetailDialog.revisionValid")}
+ </p>
+ )}
+ </div>
)}
- </div>
- {/* Revision 입력 */}
- {drawingUsage !== "CMT" && (
+ {/* Comment 입력 */}
<div className="space-y-2">
- <Label>{t("addDetailDialog.revisionLabel")}</Label>
- <Input
- value={revision}
- onChange={(e) => handleRevisionChange(e.target.value)}
- placeholder={t("addDetailDialog.revisionPlaceholder")}
- disabled={!registerKind}
- className={revisionError ? "border-red-500 focus-visible:ring-red-500" : ""}
+ <Label>{t("addDetailDialog.commentLabel")}</Label>
+ <Textarea
+ value={comment}
+ onChange={(e) => setComment(e.target.value)}
+ placeholder={t("addDetailDialog.commentPlaceholder")}
+ rows={3}
+ className="resize-none"
/>
- {revisionError && (
- <p className="text-sm text-red-500 flex items-center gap-1">
- {revisionError}
- </p>
- )}
- {!revisionError && revision && (
- <p className="text-sm text-green-600 flex items-center gap-1">
- {t("addDetailDialog.revisionValid")}
- </p>
- )}
+ <p className="text-xs text-muted-foreground">
+ {t("addDetailDialog.commentMaxLength")}
+ </p>
</div>
- )}
-
- {/* Comment 입력 */}
- <div className="space-y-2">
- <Label>{t("addDetailDialog.commentLabel")}</Label>
- <Textarea
- value={comment}
- onChange={(e) => setComment(e.target.value)}
- placeholder={t("addDetailDialog.commentPlaceholder")}
- rows={3}
- className="resize-none"
- />
- <p className="text-xs text-muted-foreground">
- {t("addDetailDialog.commentMaxLength")}
- </p>
- </div>
- {/* 파일 업로드 (Add 모드에서만 표시) */}
- {mode === "add" && (
- <div className="space-y-2">
- <Label>{t("addDetailDialog.attachmentLabel")}</Label>
- <div
- {...getRootProps()}
- className={`
- border-2 border-dashed rounded-lg p-8 text-center cursor-pointer
- transition-colors
- ${isDragActive ? "border-primary bg-primary/5" : "border-muted-foreground/25"}
- ${files.length > 0 ? "py-4" : ""}
- `}
- >
- <input {...getInputProps()} />
- {files.length === 0 ? (
- <div className="space-y-2">
- <Upload className="h-8 w-8 mx-auto text-muted-foreground" />
- <div>
+ {/* 파일 업로드 (Add 모드에서만 표시) */}
+ {mode === "add" && (
+ <div className="space-y-2">
+ <Label>{t("addDetailDialog.attachmentLabel")}</Label>
+ <div
+ {...getRootProps()}
+ className={`
+ border-2 border-dashed rounded-lg p-8 text-center cursor-pointer
+ transition-colors
+ ${isDragActive ? "border-primary bg-primary/5" : "border-muted-foreground/25"}
+ ${files.length > 0 ? "py-4" : ""}
+ `}
+ >
+ <input {...getInputProps()} />
+ {files.length === 0 ? (
+ <div className="space-y-2">
+ <Upload className="h-8 w-8 mx-auto text-muted-foreground" />
+ <div>
+ <p className="text-sm font-medium">
+ {t("addDetailDialog.dragDropText")}
+ </p>
+ <p className="text-xs text-muted-foreground">
+ {t("addDetailDialog.fileInfo")}
+ </p>
+ </div>
+ </div>
+ ) : (
+ <div className="space-y-2">
<p className="text-sm font-medium">
- {t("addDetailDialog.dragDropText")}
+ {t("addDetailDialog.filesSelected", { count: files.length })}
</p>
<p className="text-xs text-muted-foreground">
- {t("addDetailDialog.fileInfo")}
+ {t("addDetailDialog.addMoreFiles")}
</p>
</div>
- </div>
- ) : (
- <div className="space-y-2">
- <p className="text-sm font-medium">
- {t("addDetailDialog.filesSelected", { count: files.length })}
- </p>
- <p className="text-xs text-muted-foreground">
- {t("addDetailDialog.addMoreFiles")}
- </p>
- </div>
- )}
- </div>
+ )}
+ </div>
- {/* 선택된 파일 목록 */}
- {files.length > 0 && (
- <div className="space-y-2 mt-4">
- {isSubmitting ? (
- // 업로드 중: 진행도 표시
- <FileUploadProgressList fileProgresses={fileProgresses} />
- ) : (
- // 대기 중: 삭제 버튼 표시
- <>
- <div className="flex items-center justify-between mb-2">
- <h4 className="text-sm font-medium">
- {t("addDetailDialog.selectedFiles", { count: files.length })}
- </h4>
- <Button
- variant="ghost"
- size="sm"
- onClick={clearFiles}
- >
- {t("addDetailDialog.removeAll")}
- </Button>
- </div>
- <div className="max-h-60 overflow-y-auto space-y-2">
- {files.map((file, index) => (
- <div
- key={index}
- className="flex items-center gap-2 p-2 border rounded-lg bg-muted/50"
+ {/* 선택된 파일 목록 */}
+ {files.length > 0 && (
+ <div className="space-y-2 mt-4">
+ {isSubmitting ? (
+ // 업로드 중: 진행도 표시
+ <FileUploadProgressList fileProgresses={fileProgresses} />
+ ) : (
+ // 대기 중: 삭제 버튼 표시
+ <>
+ <div className="flex items-center justify-between mb-2">
+ <h4 className="text-sm font-medium">
+ {t("addDetailDialog.selectedFiles", { count: files.length })}
+ </h4>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={clearFiles}
>
- <FileIcon className="h-4 w-4 text-muted-foreground shrink-0" />
- <div className="flex-1 min-w-0">
- <p className="text-sm truncate">{file.name}</p>
- <p className="text-xs text-muted-foreground">
- {(file.size / 1024 / 1024).toFixed(2)} MB
- </p>
- </div>
- <Button
- variant="ghost"
- size="sm"
- onClick={() => removeFile(index)}
+ {t("addDetailDialog.removeAll")}
+ </Button>
+ </div>
+ <div className="max-h-60 overflow-y-auto space-y-2">
+ {files.map((file, index) => (
+ <div
+ key={index}
+ className="flex items-center gap-2 p-2 border rounded-lg bg-muted/50"
>
- <X className="h-4 w-4" />
- </Button>
- </div>
- ))}
- </div>
- </>
- )}
+ <FileIcon className="h-4 w-4 text-muted-foreground shrink-0" />
+ <div className="flex-1 min-w-0">
+ <p className="text-sm truncate">{file.name}</p>
+ <p className="text-xs text-muted-foreground">
+ {(file.size / 1024 / 1024).toFixed(2)} MB
+ </p>
+ </div>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => removeFile(index)}
+ >
+ <X className="h-4 w-4" />
+ </Button>
+ </div>
+ ))}
+ </div>
+ </>
+ )}
+ </div>
+ )}
</div>
)}
- </div>
- )}
- </div>
+ </div>
+ )}
<DialogFooter>
<Button variant="outline" onClick={handleCancel} disabled={isSubmitting}>
- {t("addDetailDialog.cancelButton")}
+ {showConfirmation ? t("addDetailDialog.backButton", "뒤로") : t("addDetailDialog.cancelButton")}
</Button>
<Button onClick={handleSubmit} disabled={isSubmitting || !isFormValid}>
{isSubmitting
? t("addDetailDialog.processingButton")
- : mode === "edit"
- ? t("editDetailDialog.updateButton")
- : t("addDetailDialog.addButton")
+ : showConfirmation
+ ? t("addDetailDialog.confirmSubmit", "제출")
+ : t("addDetailDialog.nextButton", "다음")
}
</Button>
</DialogFooter>
diff --git a/lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx b/lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx
index ea955420..8bb5dd42 100644
--- a/lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx
+++ b/lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx
@@ -301,7 +301,7 @@ export function B4BulkUploadDialogV3({
// Reuse UploadId from the first item's mapping data if available, else generate new
const firstItemMapping = groupItems[0].mappingData;
// Reuse existing UploadId if present in API response, otherwise generate new one
- // The prompt says: "UploadId는 있으면 재활용하고, 없으면 UUID로 만들어줌"
+ // UploadId는 있으면 재활용하고, 없으면 UUID로 만들어서 사용
const uploadId = firstItemMapping.UploadId || uuidv4();
console.log(`[V3 Dialog] Processing group ${groupKey}, UploadId: ${uploadId}`);
@@ -371,30 +371,31 @@ export function B4BulkUploadDialogV3({
const m = item.mappingData;
return {
CGbn: m.CGbn,
- Category: "TS", // Hardcoded as per prompt
- CheckBox: "0",
+ Category: "TS", // Hardcoded fixed value is required!
+ CheckBox: m.CheckBox,
DGbn: m.DGbn,
DegreeGbn: m.DegreeGbn,
DeptGbn: m.DeptGbn,
Discipline: m.Discipline,
- DrawingKind: "B4",
- DrawingMoveGbn: "도면입수",
+ DrawingKind: m.DrawingKind,
+ DrawingMoveGbn: m.DrawingMoveGbn,
DrawingName: m.DrawingName,
DrawingNo: m.DrawingNo,
- DrawingUsage: "입수용",
+ DrawingUsage: m.DrawingUsage,
FileNm: item.file.name,
JGbn: m.JGbn,
- Manager: m.Manager || "970043", // Fallback/Default
- MappingYN: "Y",
- NewOrNot: "N",
+ Manager: m.Manager,
+ MappingYN: m.MappingYN,
+ NewOrNot: m.NewOrNot,
ProjectNo: projectNo,
- RegisterGroup: 0,
+ RegisterGroup: m.RegisterGroup,
RegisterGroupId: m.RegisterGroupId,
RegisterKindCode: m.RegisterKindCode,
RegisterSerialNo: m.RegisterSerialNo,
RevNo: m.RevNo,
SGbn: m.SGbn,
- UploadId: uploadId // Used for all files in this group
+ UploadId: uploadId, // Used for all files in this group
+ status: "Standby", // Hardcoded fixed value is required!
};
});
@@ -473,7 +474,7 @@ export function B4BulkUploadDialogV3({
return (
<>
<Dialog open={open} onOpenChange={onOpenChange}>
- <DialogContent className="max-w-2xl">
+ <DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>{t("bulkUpload.title")} (V3)</DialogTitle>
<DialogDescription>
diff --git a/lib/dolce/dialogs/upload-files-to-detail-dialog.tsx b/lib/dolce/dialogs/upload-files-to-detail-dialog.tsx
index e8d82129..f21ccc70 100644
--- a/lib/dolce/dialogs/upload-files-to-detail-dialog.tsx
+++ b/lib/dolce/dialogs/upload-files-to-detail-dialog.tsx
@@ -55,20 +55,29 @@ export function UploadFilesToDetailDialog({
isDragActive,
} = useFileUploadWithProgress();
+ const [showConfirmation, setShowConfirmation] = useState(false);
+
// 다이얼로그 닫을 때 초기화
React.useEffect(() => {
if (!open) {
clearFiles();
+ setShowConfirmation(false);
}
}, [open, clearFiles]);
- // 업로드 처리
+ // 업로드 처리 (확인 단계 포함)
const handleUpload = async () => {
if (selectedFiles.length === 0) {
toast.error(t("uploadFilesDialog.selectFilesError"));
return;
}
+ // 확인 단계가 아니면 확인 단계로 이동
+ if (!showConfirmation) {
+ setShowConfirmation(true);
+ return;
+ }
+
setIsUploading(true);
try {
@@ -112,117 +121,169 @@ export function UploadFilesToDetailDialog({
}
};
+ const handleCancel = () => {
+ if (showConfirmation) {
+ setShowConfirmation(false);
+ } else {
+ onOpenChange(false);
+ }
+ };
+
return (
<Dialog open={open} onOpenChange={onOpenChange}>
- <DialogContent className="max-w-2xl">
+ <DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto">
<DialogHeader>
- <DialogTitle>{t("uploadFilesDialog.title")}</DialogTitle>
+ <DialogTitle>
+ {showConfirmation
+ ? t("uploadFilesDialog.confirmTitle", "파일 업로드 확인")
+ : t("uploadFilesDialog.title")
+ }
+ </DialogTitle>
<DialogDescription>
{t("uploadFilesDialog.description", { drawingNo, revNo })}
</DialogDescription>
</DialogHeader>
- <div className="space-y-4">
- {/* 안내 메시지 */}
- <Alert>
- <AlertCircle className="h-4 w-4" />
- <AlertDescription>
- {t("uploadFilesDialog.alertMessage")}
- </AlertDescription>
- </Alert>
-
- {/* 파일 선택 영역 */}
- <div
- {...getRootProps()}
- className={`border-2 border-dashed rounded-lg p-8 transition-all duration-200 cursor-pointer ${
- isDragActive
- ? "border-primary bg-primary/5 scale-[1.02]"
- : "border-muted-foreground/30 hover:border-muted-foreground/50"
- }`}
- >
- <input {...getInputProps()} />
- <div className="flex flex-col items-center justify-center">
- <FolderOpen
- className={`h-12 w-12 mb-3 transition-colors ${
- isDragActive ? "text-primary" : "text-muted-foreground"
- }`}
- />
- <p
- className={`text-sm transition-colors ${
- isDragActive
- ? "text-primary font-medium"
- : "text-muted-foreground"
- }`}
- >
- {isDragActive
- ? t("uploadFilesDialog.dropHereText")
- : t("uploadFilesDialog.dragDropText")}
- </p>
- <p className="text-xs text-muted-foreground mt-1">
- {t("uploadFilesDialog.fileInfo")}
- </p>
- </div>
- </div>
+ {showConfirmation ? (
+ <div className="space-y-4">
+ <Alert>
+ <AlertCircle className="h-4 w-4" />
+ <AlertDescription>
+ {t("uploadFilesDialog.confirmMessage", "선택한 파일을 업로드하시겠습니까?")}
+ </AlertDescription>
+ </Alert>
- {/* 선택된 파일 목록 */}
- {selectedFiles.length > 0 && (
<div className="border rounded-lg p-4">
- {isUploading ? (
- // 업로드 중: 진행도 표시
- <FileUploadProgressList fileProgresses={fileProgresses} />
- ) : (
- // 대기 중: 삭제 버튼 표시
- <>
- <div className="flex items-center justify-between mb-3">
- <h4 className="text-sm font-medium">
- {t("uploadFilesDialog.selectedFiles", { count: selectedFiles.length })}
- </h4>
- <Button
- variant="ghost"
- size="sm"
- onClick={clearFiles}
+ <h4 className="text-sm font-medium mb-3">
+ {t("uploadFilesDialog.selectedFiles", { count: selectedFiles.length })}
+ </h4>
+ <div className="max-h-60 overflow-y-auto space-y-2">
+ {isUploading ? (
+ <FileUploadProgressList fileProgresses={fileProgresses} />
+ ) : (
+ selectedFiles.map((file, index) => (
+ <div
+ key={index}
+ className="flex items-center justify-between p-2 rounded bg-muted/50"
>
- {t("uploadFilesDialog.removeAll")}
- </Button>
- </div>
- <div className="max-h-60 overflow-y-auto space-y-2">
- {selectedFiles.map((file, index) => (
- <div
- key={index}
- className="flex items-center justify-between p-2 rounded bg-muted/50"
- >
- <div className="flex items-center gap-2 flex-1 min-w-0">
- <FileText className="h-4 w-4 text-muted-foreground shrink-0" />
- <div className="flex-1 min-w-0">
- <p className="text-sm truncate">{file.name}</p>
- <p className="text-xs text-muted-foreground">
- {(file.size / 1024 / 1024).toFixed(2)} MB
- </p>
- </div>
+ <div className="flex items-center gap-2 flex-1 min-w-0">
+ <FileText className="h-4 w-4 text-muted-foreground shrink-0" />
+ <div className="flex-1 min-w-0">
+ <p className="text-sm truncate">{file.name}</p>
+ <p className="text-xs text-muted-foreground">
+ {(file.size / 1024 / 1024).toFixed(2)} MB
+ </p>
</div>
- <Button
- variant="ghost"
- size="sm"
- onClick={() => removeFile(index)}
- >
- <X className="h-4 w-4" />
- </Button>
</div>
- ))}
- </div>
- </>
- )}
+ </div>
+ ))
+ )}
+ </div>
+ </div>
+ </div>
+ ) : (
+ <div className="space-y-4">
+ {/* 안내 메시지 */}
+ <Alert>
+ <AlertCircle className="h-4 w-4" />
+ <AlertDescription>
+ {t("uploadFilesDialog.alertMessage")}
+ </AlertDescription>
+ </Alert>
+
+ {/* 파일 선택 영역 */}
+ <div
+ {...getRootProps()}
+ className={`border-2 border-dashed rounded-lg p-8 transition-all duration-200 cursor-pointer ${
+ isDragActive
+ ? "border-primary bg-primary/5 scale-[1.02]"
+ : "border-muted-foreground/30 hover:border-muted-foreground/50"
+ }`}
+ >
+ <input {...getInputProps()} />
+ <div className="flex flex-col items-center justify-center">
+ <FolderOpen
+ className={`h-12 w-12 mb-3 transition-colors ${
+ isDragActive ? "text-primary" : "text-muted-foreground"
+ }`}
+ />
+ <p
+ className={`text-sm transition-colors ${
+ isDragActive
+ ? "text-primary font-medium"
+ : "text-muted-foreground"
+ }`}
+ >
+ {isDragActive
+ ? t("uploadFilesDialog.dropHereText")
+ : t("uploadFilesDialog.dragDropText")}
+ </p>
+ <p className="text-xs text-muted-foreground mt-1">
+ {t("uploadFilesDialog.fileInfo")}
+ </p>
+ </div>
</div>
- )}
- </div>
+
+ {/* 선택된 파일 목록 */}
+ {selectedFiles.length > 0 && (
+ <div className="border rounded-lg p-4">
+ {isUploading ? (
+ // 업로드 중: 진행도 표시
+ <FileUploadProgressList fileProgresses={fileProgresses} />
+ ) : (
+ // 대기 중: 삭제 버튼 표시
+ <>
+ <div className="flex items-center justify-between mb-3">
+ <h4 className="text-sm font-medium">
+ {t("uploadFilesDialog.selectedFiles", { count: selectedFiles.length })}
+ </h4>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={clearFiles}
+ >
+ {t("uploadFilesDialog.removeAll")}
+ </Button>
+ </div>
+ <div className="max-h-60 overflow-y-auto space-y-2">
+ {selectedFiles.map((file, index) => (
+ <div
+ key={index}
+ className="flex items-center justify-between p-2 rounded bg-muted/50"
+ >
+ <div className="flex items-center gap-2 flex-1 min-w-0">
+ <FileText className="h-4 w-4 text-muted-foreground shrink-0" />
+ <div className="flex-1 min-w-0">
+ <p className="text-sm truncate">{file.name}</p>
+ <p className="text-xs text-muted-foreground">
+ {(file.size / 1024 / 1024).toFixed(2)} MB
+ </p>
+ </div>
+ </div>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => removeFile(index)}
+ >
+ <X className="h-4 w-4" />
+ </Button>
+ </div>
+ ))}
+ </div>
+ </>
+ )}
+ </div>
+ )}
+ </div>
+ )}
<DialogFooter>
<Button
variant="outline"
- onClick={() => onOpenChange(false)}
+ onClick={handleCancel}
disabled={isUploading}
>
- {t("uploadFilesDialog.cancelButton")}
+ {showConfirmation ? t("uploadFilesDialog.backButton", "뒤로") : t("uploadFilesDialog.cancelButton")}
</Button>
<Button
onClick={handleUpload}
@@ -233,10 +294,15 @@ export function UploadFilesToDetailDialog({
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
{t("uploadFilesDialog.uploadingButton")}
</>
+ ) : showConfirmation ? (
+ <>
+ <Upload className="mr-2 h-4 w-4" />
+ {t("uploadFilesDialog.confirmUpload", "업로드")}
+ </>
) : (
<>
<Upload className="mr-2 h-4 w-4" />
- {t("uploadFilesDialog.uploadButton", { count: selectedFiles.length })}
+ {t("uploadFilesDialog.nextButton", "다음")}
</>
)}
</Button>
diff --git a/lib/dolce/table/detail-drawing-columns.tsx b/lib/dolce/table/detail-drawing-columns.tsx
index 215a0cff..6127a7b6 100644
--- a/lib/dolce/table/detail-drawing-columns.tsx
+++ b/lib/dolce/table/detail-drawing-columns.tsx
@@ -148,7 +148,7 @@ export function createDetailDrawingColumns(
maxSize: 100,
cell: ({ row }) => {
const status = row.getValue("Status") as string;
- const isEditable = status === "Submitted";
+ const isEditable = status === "Standby";
return (
<div
diff --git a/lib/dolce/table/drawing-list-table-v2.tsx b/lib/dolce/table/drawing-list-table-v2.tsx
index 420ed672..e546fa79 100644
--- a/lib/dolce/table/drawing-list-table-v2.tsx
+++ b/lib/dolce/table/drawing-list-table-v2.tsx
@@ -53,7 +53,7 @@ export function DrawingListTableV2<TData extends DrawingData, TValue>({
onRowClick,
selectedRow,
getRowId,
- maxHeight = "45vh",
+ maxHeight,
minHeight = "400px",
defaultPageSize = 10,
}: DrawingListTableV2Props<TData, TValue>) {
@@ -120,7 +120,7 @@ export function DrawingListTableV2<TData extends DrawingData, TValue>({
minHeight: data.length === 0 ? minHeight : undefined,
}}
>
- <Table className="min-w-max">
+ <Table className="w-full">
<TableHeader className="sticky top-0 z-10 bg-background">
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>