summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/notice/notice-client.tsx2
-rw-r--r--components/pq-input/pq-input-tabs.tsx92
-rw-r--r--components/pq-input/pq-review-wrapper.tsx36
3 files changed, 117 insertions, 13 deletions
diff --git a/components/notice/notice-client.tsx b/components/notice/notice-client.tsx
index ae8ccebc..c68add0b 100644
--- a/components/notice/notice-client.tsx
+++ b/components/notice/notice-client.tsx
@@ -296,7 +296,7 @@ export function NoticeClient({ initialData = [], currentUserId }: NoticeClientPr
</button>
</TableHead>
<TableHead>팝업</TableHead>
- <TableHead>게시기간</TableHead>
+ <TableHead>팝업게시기간</TableHead>
{/* <TableHead>다시보지않기</TableHead> */}
<TableHead>작성자</TableHead>
<TableHead>상태</TableHead>
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">