summaryrefslogtreecommitdiff
path: root/components/pq/pq-input-tabs.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/pq/pq-input-tabs.tsx')
-rw-r--r--components/pq/pq-input-tabs.tsx136
1 files changed, 119 insertions, 17 deletions
diff --git a/components/pq/pq-input-tabs.tsx b/components/pq/pq-input-tabs.tsx
index 743e1729..b84d9167 100644
--- a/components/pq/pq-input-tabs.tsx
+++ b/components/pq/pq-input-tabs.tsx
@@ -54,7 +54,7 @@ import {
FileListName,
} from "@/components/ui/file-list"
-// Dialog components from shadcn/ui
+// Dialog components
import {
Dialog,
DialogContent,
@@ -65,13 +65,15 @@ import {
} from "@/components/ui/dialog"
// Additional UI
-import { Separator } from "../ui/separator"
+import { Separator } from "@/components/ui/separator"
+import { Badge } from "@/components/ui/badge"
-// Server actions (adjust to your actual code)
+// Server actions
import {
uploadFileAction,
savePQAnswersAction,
submitPQAction,
+ ProjectPQ,
} from "@/lib/pq/service"
import { PQGroupData } from "@/lib/pq/service"
@@ -132,9 +134,13 @@ type PQFormValues = z.infer<typeof pqFormSchema>
export function PQInputTabs({
data,
vendorId,
+ projectId,
+ projectData,
}: {
data: PQGroupData[]
vendorId: number
+ projectId?: number
+ projectData?: ProjectPQ | null
}) {
const [isSaving, setIsSaving] = React.useState(false)
const [isSubmitting, setIsSubmitting] = React.useState(false)
@@ -152,7 +158,7 @@ export function PQInputTabs({
data.forEach((group) => {
group.items.forEach((item) => {
- // Check if the server item is already “complete”
+ // Check if the server item is already "complete"
const hasExistingAnswer = item.answer && item.answer.trim().length > 0
const hasExistingAttachments = item.attachments && item.attachments.length > 0
@@ -190,7 +196,7 @@ export function PQInputTabs({
// ----------------------------------------------------------------------
React.useEffect(() => {
const values = form.getValues()
- // We consider items “saved” if `saved===true` AND they have an answer or attachments
+ // We consider items "saved" if `saved===true` AND they have an answer or attachments
const allItemsSaved = values.answers.every(
(answer) => answer.saved && (answer.answer || answer.uploadedFiles.length > 0)
)
@@ -299,6 +305,7 @@ export function PQInputTabs({
const updatedAnswer = form.getValues(`answers.${answerIndex}`)
const saveResult = await savePQAnswersAction({
vendorId,
+ projectId, // 프로젝트 ID 전달
answers: [
{
criteriaId: updatedAnswer.criteriaId,
@@ -396,13 +403,18 @@ export function PQInputTabs({
setIsSubmitting(true)
setShowConfirmDialog(false)
- const result = await submitPQAction(vendorId)
+ const result = await submitPQAction({
+ vendorId,
+ projectId, // 프로젝트 ID 전달
+ })
+
if (result.ok) {
toast({
title: "PQ Submitted",
description: "Your PQ information has been submitted successfully",
})
- // Optionally redirect
+ // 제출 후 페이지 새로고침 또는 리디렉션 처리
+ window.location.reload()
} else {
toast({
title: "Submit Error",
@@ -421,6 +433,72 @@ export function PQInputTabs({
setIsSubmitting(false)
}
}
+
+ // 프로젝트 정보 표시 섹션
+ const renderProjectInfo = () => {
+ if (!projectData) return null;
+
+ return (
+ <div className="mb-6 bg-muted p-4 rounded-md">
+ <div className="flex items-center justify-between mb-2">
+ <h3 className="text-lg font-semibold">프로젝트 정보</h3>
+ <Badge variant={getStatusVariant(projectData.status)}>
+ {getStatusLabel(projectData.status)}
+ </Badge>
+ </div>
+
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <div>
+ <p className="text-sm font-medium text-muted-foreground">프로젝트 코드</p>
+ <p>{projectData.projectCode}</p>
+ </div>
+ <div>
+ <p className="text-sm font-medium text-muted-foreground">프로젝트명</p>
+ <p>{projectData.projectName}</p>
+ </div>
+ {projectData.submittedAt && (
+ <div className="col-span-1 md:col-span-2">
+ <p className="text-sm font-medium text-muted-foreground">제출일</p>
+ <p>{formatDate(projectData.submittedAt)}</p>
+ </div>
+ )}
+ </div>
+ </div>
+ );
+ };
+
+ // 상태 표시용 함수
+ const getStatusLabel = (status: string) => {
+ switch (status) {
+ case "REQUESTED": return "요청됨";
+ case "IN_PROGRESS": return "진행중";
+ case "SUBMITTED": return "제출됨";
+ case "APPROVED": return "승인됨";
+ case "REJECTED": return "반려됨";
+ default: return status;
+ }
+ };
+
+ const getStatusVariant = (status: string) => {
+ switch (status) {
+ case "REQUESTED": return "secondary";
+ case "IN_PROGRESS": return "default";
+ case "SUBMITTED": return "outline";
+ case "APPROVED": return "outline";
+ case "REJECTED": return "destructive";
+ default: return "secondary";
+ }
+ };
+
+ // 날짜 형식화 함수
+ const formatDate = (date: Date) => {
+ if (!date) return "-";
+ return new Date(date).toLocaleDateString("ko-KR", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ });
+ };
// ----------------------------------------------------------------------
// H) Render
@@ -428,6 +506,9 @@ export function PQInputTabs({
return (
<Form {...form}>
<form>
+ {/* 프로젝트 정보 섹션 */}
+ {renderProjectInfo()}
+
<Tabs defaultValue={data[0]?.groupName || ""} className="w-full">
{/* Top Controls */}
<div className="flex justify-between items-center mb-4">
@@ -485,7 +566,7 @@ export function PQInputTabs({
{/* 2-column grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pb-4">
{group.items.map((item) => {
- const { criteriaId, code, checkPoint, description } = item
+ const { criteriaId, code, checkPoint, description, contractInfo, additionalRequirement } = item
const answerIndex = getAnswerIndex(criteriaId)
if (answerIndex === -1) return null
@@ -498,7 +579,7 @@ export function PQInputTabs({
const hasNewUploads = newUploads.length > 0
const canSave = isItemDirty || hasNewUploads
- // For “Not Saved” vs. “Saved” status label
+ // For "Not Saved" vs. "Saved" status label
const hasUploads =
form.watch(`answers.${answerIndex}.uploadedFiles`).length > 0 ||
newUploads.length > 0
@@ -556,13 +637,32 @@ export function PQInputTabs({
</CardHeader>
<CollapsibleContent>
- {/* Answer Field */}
- <CardHeader className="pt-0 pb-3">
+ <CardContent className="pt-3 space-y-3">
+ {/* 프로젝트별 추가 필드 (contractInfo, additionalRequirement) */}
+ {projectId && contractInfo && (
+ <div className="space-y-1">
+ <FormLabel className="text-sm font-medium">계약 정보</FormLabel>
+ <div className="rounded-md bg-muted/30 p-3 text-sm whitespace-pre-wrap">
+ {contractInfo}
+ </div>
+ </div>
+ )}
+
+ {projectId && additionalRequirement && (
+ <div className="space-y-1">
+ <FormLabel className="text-sm font-medium">추가 요구사항</FormLabel>
+ <div className="rounded-md bg-muted/30 p-3 text-sm whitespace-pre-wrap">
+ {additionalRequirement}
+ </div>
+ </div>
+ )}
+
+ {/* Answer Field */}
<FormField
control={form.control}
name={`answers.${answerIndex}.answer`}
render={({ field }) => (
- <FormItem className="mt-3">
+ <FormItem className="mt-2">
<FormLabel>Answer</FormLabel>
<FormControl>
<Textarea
@@ -583,11 +683,10 @@ export function PQInputTabs({
</FormItem>
)}
/>
- </CardHeader>
+
- {/* Attachments / Dropzone */}
- <CardContent>
- <div className="grid gap-2">
+ {/* Attachments / Dropzone */}
+ <div className="grid gap-2 mt-3">
<FormLabel>Attachments</FormLabel>
<Dropzone
maxSize={6e8} // 600MB
@@ -708,7 +807,10 @@ export function PQInputTabs({
<DialogHeader>
<DialogTitle>Confirm Submission</DialogTitle>
<DialogDescription>
- Review your answers before final submission.
+ {projectId
+ ? `${projectData?.projectCode} 프로젝트의 PQ 응답을 제출하시겠습니까?`
+ : "일반 PQ 응답을 제출하시겠습니까?"
+ } 제출 후에는 수정이 불가능합니다.
</DialogDescription>
</DialogHeader>