diff options
Diffstat (limited to 'components/pq-input')
| -rw-r--r-- | components/pq-input/pq-input-tabs.tsx | 92 | ||||
| -rw-r--r-- | components/pq-input/pq-review-wrapper.tsx | 36 |
2 files changed, 116 insertions, 12 deletions
diff --git a/components/pq-input/pq-input-tabs.tsx b/components/pq-input/pq-input-tabs.tsx index 3f7e1718..4e6b7ed2 100644 --- a/components/pq-input/pq-input-tabs.tsx +++ b/components/pq-input/pq-input-tabs.tsx @@ -15,6 +15,13 @@ import { import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" import { X, Save, CheckCircle2, AlertTriangle, ChevronsUpDown, Download, Loader2 } from "lucide-react" import prettyBytes from "pretty-bytes" import { useToast } from "@/hooks/use-toast" @@ -129,6 +136,12 @@ const pqFormSchema = z.object({ type PQFormValues = z.infer<typeof pqFormSchema> +// 통화 단위 옵션 +const currencyUnits = [ + "USD", "EUR", "GBP", "JPY", "CNY", "KRW", "AUD", "CAD", "CHF", "HKD", + "SGD", "THB", "PHP", "IDR", "MYR", "VND", "INR", "BRL", "MXN", "RUB" +] + // ---------------------------------------------------------------------- // 3) Main Component: PQInputTabs // ---------------------------------------------------------------------- @@ -369,12 +382,12 @@ export function PQInputTabs({ break case "PHONE": case "FAX": - // 전화번호/팩스번호는 숫자만 허용 - const phoneRegex = /^\d+$/ + // 전화번호/팩스번호는 숫자와 하이픈 허용 + const phoneRegex = /^[\d-]+$/ if (!phoneRegex.test(answerData.answer)) { toast({ title: `${inputFormat === "PHONE" ? "전화번호" : "팩스번호"} 형식 오류`, - description: `숫자만 입력해주세요.`, + description: `숫자와 하이픈(-)만 입력해주세요.`, variant: "destructive", }) return @@ -391,6 +404,9 @@ export function PQInputTabs({ return } break + case "NUMBER_WITH_UNIT": + // 숫자+단위는 별도 검증 없음 (숫자와 단위가 분리되어 있음) + break case "TEXT": case "TEXT_FILE": case "FILE": @@ -767,7 +783,7 @@ export function PQInputTabs({ {data.map((group) => ( <TabsContent key={group.groupName} value={group.groupName}> {/* 2-column grid */} - <div className="grid grid-cols-1 md:grid-cols-2 gap-4 pb-4"> + <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 pb-4"> {sortByCode(group.items).map((item) => { const { criteriaId, code, checkPoint, remarks, description, contractInfo, additionalRequirement } = item const answerIndex = getAnswerIndex(criteriaId) @@ -789,7 +805,7 @@ export function PQInputTabs({ return ( <Collapsible key={criteriaId} defaultOpen={isReadOnly || !isSaved} className="w-full"> - <Card className={isSaved ? "border-green-200" : ""}> + <Card className={`${isSaved ? "border-green-200" : ""} h-fit min-h-[400px]`}> <CardHeader className="pb-1"> <div className="flex justify-between"> <div className="flex-1"> @@ -849,7 +865,7 @@ export function PQInputTabs({ </CardHeader> <CollapsibleContent> - <CardContent className="pt-3 space-y-3"> + <CardContent className="pt-3 space-y-3 h-full flex flex-col"> {/* 프로젝트별 추가 필드 (contractInfo, additionalRequirement) */} {projectId && contractInfo && ( <div className="space-y-1"> @@ -884,8 +900,12 @@ export function PQInputTabs({ return "이메일 주소"; case "PHONE": return "전화번호"; + case "FAX": + return "팩스번호"; case "NUMBER": return "숫자 값"; + case "NUMBER_WITH_UNIT": + return "숫자+단위"; case "TEXT_FILE": return "텍스트 답변"; default: @@ -905,6 +925,7 @@ export function PQInputTabs({ type="email" disabled={isDisabled} placeholder="example@company.com" + className="h-12" onChange={(e) => { field.onChange(e) form.setValue( @@ -923,6 +944,7 @@ export function PQInputTabs({ type="tel" disabled={isDisabled} placeholder="02-1234-5678" + className="h-12" onChange={(e) => { // 전화번호 형식만 허용 (숫자, -, +, 공백) const value = e.target.value; @@ -943,6 +965,7 @@ export function PQInputTabs({ type="text" disabled={isDisabled} placeholder="숫자를 입력하세요" + className="h-12" onChange={(e) => { // 숫자만 허용 const value = e.target.value; @@ -957,13 +980,60 @@ export function PQInputTabs({ }} /> ); + case "NUMBER_WITH_UNIT": + return ( + <div className="flex gap-2"> + <Input + type="text" + disabled={isDisabled} + placeholder="숫자 입력" + className="h-12 flex-1" + value={field.value?.split(' ')[0] || ''} + onChange={(e) => { + const unit = field.value?.split(' ')[1] || '' + const newValue = e.target.value + (unit ? ` ${unit}` : '') + field.onChange(newValue) + form.setValue( + `answers.${answerIndex}.saved`, + false, + { shouldDirty: true } + ) + }} + /> + <Select + disabled={isDisabled} + value={field.value?.split(' ')[1] || ''} + onValueChange={(unit) => { + const number = field.value?.split(' ')[0] || '' + const newValue = number + (unit ? ` ${unit}` : '') + field.onChange(newValue) + form.setValue( + `answers.${answerIndex}.saved`, + false, + { shouldDirty: true } + ) + }} + > + <SelectTrigger className="h-12 w-24"> + <SelectValue placeholder="단위" /> + </SelectTrigger> + <SelectContent> + {currencyUnits.map((unit) => ( + <SelectItem key={unit} value={unit}> + {unit} + </SelectItem> + ))} + </SelectContent> + </Select> + </div> + ); case "TEXT_FILE": return ( <div className="space-y-2"> <Textarea {...field} disabled={isDisabled} - className="min-h-24" + className="min-h-32 h-32" placeholder="텍스트 답변을 입력하세요" onChange={(e) => { field.onChange(e) @@ -984,7 +1054,7 @@ export function PQInputTabs({ <Textarea {...field} disabled={isDisabled} - className="min-h-24" + className="min-h-32 h-32" placeholder="답변을 입력해주세요." onChange={(e) => { field.onChange(e) @@ -1029,7 +1099,7 @@ export function PQInputTabs({ > {() => ( <FormItem> - <DropzoneZone className="flex justify-center h-24"> + <DropzoneZone className="flex justify-center h-32"> <FormControl> <DropzoneInput /> </FormControl> @@ -1171,7 +1241,7 @@ export function PQInputTabs({ <Textarea {...field} disabled={true} - className="min-h-20 bg-muted/50" + className="min-h-24 h-24 bg-muted/50" placeholder="SHI 코멘트가 없습니다." /> </FormControl> @@ -1192,7 +1262,7 @@ export function PQInputTabs({ <Textarea {...field} disabled={isDisabled} - className="min-h-20 bg-muted/50" + className="min-h-24 h-24 bg-muted/50" placeholder="벤더 Reply를 입력하세요." onChange={(e) => { field.onChange(e) diff --git a/components/pq-input/pq-review-wrapper.tsx b/components/pq-input/pq-review-wrapper.tsx index 44916dce..1545314c 100644 --- a/components/pq-input/pq-review-wrapper.tsx +++ b/components/pq-input/pq-review-wrapper.tsx @@ -366,7 +366,7 @@ export function PQReviewWrapper({ <div key={group.groupName} className="space-y-4"> <h3 className="text-lg font-medium">{group.groupName}</h3> - <div className="grid grid-cols-1 gap-4"> + <div className="grid grid-cols-2 gap-4"> {sortByCode(group.items).map((item) => ( <Card key={item.criteriaId}> <CardHeader> @@ -439,7 +439,9 @@ export function PQReviewWrapper({ {item.inputFormat === "TEXT" && "텍스트"} {item.inputFormat === "EMAIL" && "이메일"} {item.inputFormat === "PHONE" && "전화번호"} + {item.inputFormat === "FAX" && "팩스번호"} {item.inputFormat === "NUMBER" && "숫자"} + {item.inputFormat === "NUMBER_WITH_UNIT" && "숫자+단위"} {item.inputFormat === "FILE" && "파일"} {item.inputFormat === "TEXT_FILE" && "텍스트+파일"} </Badge> @@ -468,6 +470,15 @@ export function PQReviewWrapper({ </div> </div> ); + case "FAX": + return ( + <div className="space-y-2"> + <div className="text-sm font-medium text-muted-foreground">팩스번호:</div> + <div className="whitespace-pre-wrap"> + {item.answer || <span className="text-muted-foreground">답변 없음</span>} + </div> + </div> + ); case "NUMBER": return ( <div className="space-y-2"> @@ -477,6 +488,29 @@ export function PQReviewWrapper({ </div> </div> ); + case "NUMBER_WITH_UNIT": + const numberWithUnit = item.answer || ""; + const [number, unit] = numberWithUnit.split(' '); + return ( + <div className="space-y-2"> + <div className="text-sm font-medium text-muted-foreground">숫자+단위:</div> + <div className="flex items-center gap-2"> + {number && ( + <span className="font-mono text-lg font-semibold text-blue-600"> + {number} + </span> + )} + {unit && ( + <Badge variant="outline" className="text-xs"> + {unit} + </Badge> + )} + {!numberWithUnit && ( + <span className="text-muted-foreground">답변 없음</span> + )} + </div> + </div> + ); case "FILE": return ( <div className="space-y-2"> |
