diff options
Diffstat (limited to 'lib/pq')
| -rw-r--r-- | lib/pq/pq-criteria/pq-table-column.tsx | 11 | ||||
| -rw-r--r-- | lib/pq/service.ts | 133 | ||||
| -rw-r--r-- | lib/pq/table/add-pq-list-dialog.tsx | 34 | ||||
| -rw-r--r-- | lib/pq/table/copy-pq-list-dialog.tsx | 80 | ||||
| -rw-r--r-- | lib/pq/table/pq-lists-columns.tsx | 127 | ||||
| -rw-r--r-- | lib/pq/table/pq-lists-table.tsx | 45 | ||||
| -rw-r--r-- | lib/pq/table/pq-lists-toolbar.tsx | 4 | ||||
| -rw-r--r-- | lib/pq/validations.ts | 16 |
8 files changed, 344 insertions, 106 deletions
diff --git a/lib/pq/pq-criteria/pq-table-column.tsx b/lib/pq/pq-criteria/pq-table-column.tsx index de7396bf..32d6cc32 100644 --- a/lib/pq/pq-criteria/pq-table-column.tsx +++ b/lib/pq/pq-criteria/pq-table-column.tsx @@ -1,6 +1,7 @@ "use client" import * as React from "react" +import { useMemo } from "react" import { ColumnDef } from "@tanstack/react-table" import { formatDate } from "@/lib/utils" import { Checkbox } from "@/components/ui/checkbox" @@ -179,7 +180,10 @@ export function getColumns({ header: ({ column }) => ( <DataTableColumnHeaderSimple column={column} title="생성일" /> ), - cell: ({ cell }) => formatDate(cell.getValue() as Date, "ko-KR"), + cell: ({ cell }) => { + const dateValue = cell.getValue() as Date + return useMemo(() => formatDate(dateValue, "ko-KR"), [dateValue]) + }, enableResizing: true, minSize: 180, size: 180, @@ -189,7 +193,10 @@ export function getColumns({ header: ({ column }) => ( <DataTableColumnHeaderSimple column={column} title="수정일" /> ), - cell: ({ cell }) => formatDate(cell.getValue() as Date, "ko-KR"), + cell: ({ cell }) => { + const dateValue = cell.getValue() as Date + return useMemo(() => formatDate(dateValue, "ko-KR"), [dateValue]) + }, enableResizing: true, minSize: 180, size: 180, diff --git a/lib/pq/service.ts b/lib/pq/service.ts index 7aa80dfa..67be5398 100644 --- a/lib/pq/service.ts +++ b/lib/pq/service.ts @@ -1,7 +1,7 @@ "use server"
import db from "@/db/db"
-import { CopyPqListInput, CreatePqListInput, copyPqListSchema, createPqListSchema, GetPqListsSchema, GetPQSchema, GetPQSubmissionsSchema } from "./validations"
+import { CopyPqListInput, CreatePqListInput, UpdatePqValidToInput, copyPqListSchema, createPqListSchema, updatePqValidToSchema, GetPqListsSchema, GetPQSchema, GetPQSubmissionsSchema } from "./validations"
import { unstable_cache } from "@/lib/unstable-cache";
import { filterColumns } from "@/lib/filter-columns";
import { getErrorMessage } from "@/lib/handle-error";
@@ -2977,7 +2977,9 @@ export async function getPQLists(input: GetPqListsSchema) { const s = `%${input.search}%`;
globalWhere = or(
ilike(pqLists.name, s),
- ilike(pqLists.type, s)
+ ilike(pqLists.type, s),
+ ilike(projects.code, s),
+ ilike(projects.name, s)
);
}
@@ -3125,6 +3127,17 @@ export async function createPQListAction(input: CreatePqListInput) { // 프로젝트 PQ인 경우 중복 체크
if (validated.type === "PROJECT" && validated.projectId) {
+ // 프로젝트 정보 조회 (이름과 코드 포함)
+ const projectInfo = await db
+ .select({
+ code: projects.code,
+ name: projects.name
+ })
+ .from(projects)
+ .where(eq(projects.id, validated.projectId))
+ .limit(1)
+ .then(rows => rows[0]);
+
const existingPQ = await db
.select()
.from(pqLists)
@@ -3136,11 +3149,12 @@ export async function createPQListAction(input: CreatePqListInput) { )
)
.limit(1);
-
+
if (existingPQ.length > 0) {
+ const projectDisplayName = projectInfo ? `${projectInfo.code} - ${projectInfo.name}` : "알 수 없는 프로젝트";
return {
success: false,
- error: "해당 프로젝트에 대한 PQ가 이미 존재합니다"
+ error: `${projectDisplayName} 프로젝트에 대한 PQ가 이미 존재합니다`
};
}
}
@@ -3315,24 +3329,38 @@ export async function copyPQListAction(input: CopyPqListInput) { };
}
- // 2. 대상 프로젝트에 이미 PQ가 존재하는지 확인
- const existingProjectPQ = await tx
- .select()
- .from(pqLists)
- .where(
- and(
- eq(pqLists.projectId, validated.targetProjectId),
- eq(pqLists.type, "PROJECT"),
- eq(pqLists.isDeleted, false)
- )
- )
- .limit(1);
+ // 2. 프로젝트 PQ인 경우에만 대상 프로젝트에 이미 PQ가 존재하는지 확인
+ if (sourcePqList.type === "PROJECT" && validated.targetProjectId) {
+ // 프로젝트 정보 조회 (이름과 코드 포함)
+ const projectInfo = await tx
+ .select({
+ code: projects.code,
+ name: projects.name
+ })
+ .from(projects)
+ .where(eq(projects.id, validated.targetProjectId))
+ .limit(1)
+ .then(rows => rows[0]);
- if (existingProjectPQ.length > 0) {
- return {
- success: false,
- error: "해당 프로젝트에 대한 PQ가 이미 존재합니다"
- };
+ const existingProjectPQ = await tx
+ .select()
+ .from(pqLists)
+ .where(
+ and(
+ eq(pqLists.projectId, validated.targetProjectId),
+ eq(pqLists.type, "PROJECT"),
+ eq(pqLists.isDeleted, false)
+ )
+ )
+ .limit(1);
+
+ if (existingProjectPQ.length > 0) {
+ const projectDisplayName = projectInfo ? `${projectInfo.code} - ${projectInfo.name}` : "알 수 없는 프로젝트";
+ return {
+ success: false,
+ error: `${projectDisplayName} 프로젝트에 대한 PQ가 이미 존재합니다`
+ };
+ }
}
// 3. 새 PQ 목록 생성
@@ -3344,7 +3372,7 @@ export async function copyPQListAction(input: CopyPqListInput) { .values({
name: newName || sourcePqList.name,
type: sourcePqList.type,
- projectId: validated.targetProjectId,
+ projectId: sourcePqList.type === "PROJECT" ? validated.targetProjectId : null,
isDeleted: false,
createdAt: now,
updatedAt: now,
@@ -3957,6 +3985,67 @@ export async function autoDeactivateExpiredPQLists() { }
}
+// PQ 유효일 수정 서버액션
+export async function updatePqValidToAction(input: UpdatePqValidToInput) {
+ try {
+ const validated = updatePqValidToSchema.parse(input);
+ const session = await getServerSession(authOptions);
+ const userId = session?.user?.id;
+
+ if (!userId) {
+ return {
+ success: false,
+ error: "인증이 필요합니다"
+ };
+ }
+
+ // PQ 목록 존재 확인
+ const existingPqList = await db
+ .select()
+ .from(pqLists)
+ .where(eq(pqLists.id, validated.pqListId))
+ .limit(1)
+ .then(rows => rows[0]);
+
+ if (!existingPqList) {
+ return {
+ success: false,
+ error: "PQ 목록을 찾을 수 없습니다"
+ };
+ }
+
+ // 유효일 업데이트
+ await db
+ .update(pqLists)
+ .set({
+ validTo: validated.validTo,
+ updatedAt: new Date(),
+ updatedBy: userId,
+ })
+ .where(eq(pqLists.id, validated.pqListId));
+
+ // 캐시 재검증
+ revalidateTag("pq-lists");
+
+ return {
+ success: true,
+ message: "유효일이 성공적으로 수정되었습니다"
+ };
+ } catch (error) {
+ console.error("Error updating PQ valid to:", error);
+ if (error instanceof z.ZodError) {
+ return {
+ success: false,
+ error: "입력 데이터가 올바르지 않습니다"
+ };
+ }
+ return {
+ success: false,
+ error: "유효일 수정에 실패했습니다"
+ };
+ }
+}
+
// SHI 참석자 총 인원수 계산 함수
export async function getTotalShiAttendees(shiAttendees: Record<string, unknown> | null): Promise<number> {
diff --git a/lib/pq/table/add-pq-list-dialog.tsx b/lib/pq/table/add-pq-list-dialog.tsx index c1899a29..472a1b3d 100644 --- a/lib/pq/table/add-pq-list-dialog.tsx +++ b/lib/pq/table/add-pq-list-dialog.tsx @@ -10,13 +10,8 @@ import { DatePicker } from "@/components/ui/date-picker" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Loader2, Plus } from "lucide-react"
+import { ProjectSelector } from "@/components/ProjectSelector"
-// 프로젝트 목록을 위한 임시 타입 (실제로는 projects에서 가져와야 함)
-interface Project {
- id: number
- name: string
- code: string
-}
const pqListFormSchema = z.object({
name: z.string().min(1, "PQ 목록 명을 입력해주세요"),
@@ -42,7 +37,6 @@ interface PqListFormProps { open: boolean
onOpenChange: (open: boolean) => void
initialData?: Partial<PqListFormData> & { id?: number }
- projects?: Project[]
onSubmit: (data: PqListFormData & { id?: number }) => Promise<void>
isLoading?: boolean
}
@@ -57,7 +51,6 @@ export function AddPqDialog({ open,
onOpenChange,
initialData,
- projects = [],
onSubmit,
isLoading = false
}: PqListFormProps) {
@@ -162,23 +155,13 @@ export function AddPqDialog({ <FormLabel className="flex items-center gap-1">
프로젝트 <span className="text-red-500">*</span>
</FormLabel>
- <Select
- onValueChange={(value) => field.onChange(parseInt(value))}
- defaultValue={field.value?.toString()}
- >
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="프로젝트를 선택하세요" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {projects.map((project) => (
- <SelectItem key={project.id} value={project.id.toString()}>
- {project.code} - {project.name}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
+ <FormControl>
+ <ProjectSelector
+ selectedProjectId={field.value}
+ onProjectSelect={(project) => field.onChange(project.id)}
+ placeholder="프로젝트를 선택하세요"
+ />
+ </FormControl>
<FormMessage />
</FormItem>
)}
@@ -200,6 +183,7 @@ export function AddPqDialog({ date={field.value ?? undefined}
onSelect={(date) => field.onChange(date ?? null)}
placeholder="유효일 선택"
+ minDate={new Date()}
/>
</FormControl>
<FormMessage />
diff --git a/lib/pq/table/copy-pq-list-dialog.tsx b/lib/pq/table/copy-pq-list-dialog.tsx index 647ab1a3..51b7eed1 100644 --- a/lib/pq/table/copy-pq-list-dialog.tsx +++ b/lib/pq/table/copy-pq-list-dialog.tsx @@ -33,11 +33,17 @@ const copyPqSchema = z.object({ sourcePqListId: z.number({
required_error: "복사할 PQ 목록을 선택해주세요"
}),
- targetProjectId: z.number({
- required_error: "대상 프로젝트를 선택해주세요"
- }),
+ targetProjectId: z.number().optional(),
validTo: z.date(),
newName: z.string(),
+}).refine((data) => {
+ // 미실사 PQ가 아닌 경우에만 targetProjectId 필수
+ if (data.targetProjectId !== undefined) return true
+ // 미실사 PQ인 경우 targetProjectId는 선택사항
+ return true
+}, {
+ message: "프로젝트 PQ인 경우 대상 프로젝트를 선택해야 합니다",
+ path: ["targetProjectId"]
})
type CopyPqFormData = z.infer<typeof copyPqSchema>
@@ -106,36 +112,6 @@ export function CopyPqDialog({ <Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6">
- {/* 대상 프로젝트 선택 */}
- <FormField
- control={form.control}
- name="targetProjectId"
- render={({ field }) => (
- <FormItem>
- <FormLabel className="flex items-center gap-1">
- 대상 프로젝트 <span className="text-red-500">*</span>
- </FormLabel>
- <Select
- onValueChange={(value) => field.onChange(parseInt(value))}
- defaultValue={field.value?.toString()}
- >
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="PQ를 적용할 프로젝트를 선택하세요" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {projects.map((project) => (
- <SelectItem key={project.id} value={project.id.toString()}>
- {project.code} - {project.name}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- <FormMessage />
- </FormItem>
- )}
- />
{/* 복사할 PQ 목록 선택 */}
<FormField
control={form.control}
@@ -155,7 +131,9 @@ export function CopyPqDialog({ </SelectTrigger>
</FormControl>
<SelectContent>
- {pqLists.map((pqList) => (
+ {pqLists
+ .filter(pqList => pqList.type !== "GENERAL") // 일반 PQ 제외
+ .map((pqList) => (
<SelectItem key={pqList.id} value={pqList.id.toString()}>
<div className="flex items-center gap-2">
<Badge className={typeColors[pqList.type]}>
@@ -184,6 +162,39 @@ export function CopyPqDialog({ </FormItem>
)}
/>
+ {/* 대상 프로젝트 선택 (미실사 PQ가 아닌 경우에만) */}
+ {selectedPqList?.type !== "NON_INSPECTION" && (
+ <FormField
+ control={form.control}
+ name="targetProjectId"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel className="flex items-center gap-1">
+ 대상 프로젝트 <span className="text-red-500">*</span>
+ </FormLabel>
+ <Select
+ onValueChange={(value) => field.onChange(parseInt(value))}
+ defaultValue={field.value?.toString()}
+ >
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="PQ를 적용할 프로젝트를 선택하세요" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {projects.map((project) => (
+ <SelectItem key={project.id} value={project.id.toString()}>
+ {project.code} - {project.name}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ )}
+
{/* 새 PQ 목록 명 */}
<FormField
control={form.control}
@@ -214,6 +225,7 @@ export function CopyPqDialog({ date={field.value ?? undefined}
onSelect={(date) => field.onChange(date ?? null)}
placeholder="유효기간 선택"
+ minDate={new Date()}
/>
</FormControl>
<FormMessage />
diff --git a/lib/pq/table/pq-lists-columns.tsx b/lib/pq/table/pq-lists-columns.tsx index 1c401fac..a9262a12 100644 --- a/lib/pq/table/pq-lists-columns.tsx +++ b/lib/pq/table/pq-lists-columns.tsx @@ -13,9 +13,19 @@ import { DropdownMenuTrigger
} from "@/components/ui/dropdown-menu"
import { Button } from "@/components/ui/button"
-import React from "react"
+import React, { useMemo, useState } from "react"
import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
import { Checkbox } from "@/components/ui/checkbox"
+import {
+ Sheet,
+ SheetContent,
+ SheetDescription,
+ SheetFooter,
+ SheetHeader,
+ SheetTitle,
+} from "@/components/ui/sheet"
+import { DatePicker } from "@/components/ui/date-picker"
+import { toast } from "sonner"
export interface PQList {
id: number
@@ -48,6 +58,84 @@ const typeColors = { interface GetColumnsProps {
setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<PQList> | null>>
}
+
+// 유효일 수정 시트 컴포넌트
+interface EditValidToSheetProps {
+ pqList: PQList | null
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ onUpdate: (pqListId: number, newValidTo: Date | null) => Promise<void>
+}
+
+export function EditValidToSheet({ pqList, open, onOpenChange, onUpdate }: EditValidToSheetProps) {
+ const [newValidTo, setNewValidTo] = useState<Date | null>(pqList?.validTo || null)
+ const [isLoading, setIsLoading] = useState(false)
+
+ const handleSave = async () => {
+ if (!pqList) return
+
+ setIsLoading(true)
+ try {
+ await onUpdate(pqList.id, newValidTo)
+ onOpenChange(false)
+ } catch (error) {
+ console.error("유효일 수정 실패:", error)
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ return (
+ <Sheet open={open} onOpenChange={onOpenChange}>
+ <SheetContent>
+ <SheetHeader>
+ <SheetTitle>유효일 수정</SheetTitle>
+ <SheetDescription>
+ {pqList && (
+ <>
+ <strong>{pqList.name}</strong>의 유효일을 수정합니다.
+ </>
+ )}
+ </SheetDescription>
+ </SheetHeader>
+
+ <div className="py-6">
+ <div className="space-y-4">
+ <div>
+ <label className="text-sm font-medium">현재 유효일</label>
+ <div className="mt-1 p-2 bg-muted rounded-md">
+ {pqList?.validTo ? formatDate(pqList.validTo, "ko-KR") : "설정되지 않음"}
+ </div>
+ </div>
+
+ <div>
+ <label className="text-sm font-medium">새 유효일</label>
+ <div className="mt-1">
+ <DatePicker
+ date={newValidTo ?? undefined}
+ onSelect={(date) => setNewValidTo(date ?? null)}
+ placeholder="새 유효일을 선택하세요"
+ minDate={new Date()}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <SheetFooter>
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
+ 취소
+ </Button>
+ <Button onClick={handleSave} disabled={isLoading}>
+ {isLoading && <div className="mr-2 h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />}
+ 저장
+ </Button>
+ </SheetFooter>
+ </SheetContent>
+ </Sheet>
+ )
+}
+
export function createPQListsColumns({
setRowAction
}: GetColumnsProps): ColumnDef<PQList>[] {
@@ -122,17 +210,23 @@ export function createPQListsColumns({ ),
cell: ({ row }) => {
const validTo = row.getValue("validTo") as Date | null
- const now = new Date()
- const isExpired = validTo && validTo < now
-
- const formattedDate = validTo ? formatDate(validTo, "ko-KR") : "-"
-
+
+ const dateInfo = useMemo(() => {
+ if (!validTo) return { formattedDate: "-", isExpired: false }
+
+ const now = new Date()
+ const isExpired = validTo < now
+ const formattedDate = formatDate(validTo, "ko-KR")
+
+ return { formattedDate, isExpired }
+ }, [validTo])
+
return (
<div className="text-sm">
- <span className={isExpired ? "text-red-600 font-medium" : ""}>
- {formattedDate}
+ <span className={dateInfo.isExpired ? "text-red-600 font-medium" : ""}>
+ {dateInfo.formattedDate}
</span>
- {isExpired && (
+ {dateInfo.isExpired && (
<Badge variant="destructive" className="ml-2 text-xs">
만료
</Badge>
@@ -168,14 +262,20 @@ export function createPQListsColumns({ header: ({ column }) => (
<DataTableColumnHeaderSimple column={column} title="생성일" />
),
- cell: ({ row }) => formatDate(row.getValue("createdAt"), "ko-KR"),
+ cell: ({ row }) => {
+ const createdAt = row.getValue("createdAt") as Date
+ return useMemo(() => formatDate(createdAt, "ko-KR"), [createdAt])
+ },
},
{
accessorKey: "updatedAt",
header: ({ column }) => (
<DataTableColumnHeaderSimple column={column} title="변경일" />
),
- cell: ({ row }) => formatDate(row.getValue("updatedAt"), "ko-KR"),
+ cell: ({ row }) => {
+ const updatedAt = row.getValue("updatedAt") as Date
+ return useMemo(() => formatDate(updatedAt, "ko-KR"), [updatedAt])
+ },
},
{
id: "actions",
@@ -196,6 +296,11 @@ export function createPQListsColumns({ >
상세보기
</DropdownMenuItem>
+ <DropdownMenuItem
+ onSelect={() => setRowAction({ row, type: "editValidTo" })}
+ >
+ 유효일 수정
+ </DropdownMenuItem>
{/* <DropdownMenuSeparator />
<DropdownMenuItem
onSelect={() => setRowAction({ row, type: "delete" })}
diff --git a/lib/pq/table/pq-lists-table.tsx b/lib/pq/table/pq-lists-table.tsx index c5fd82a5..1be0a1c7 100644 --- a/lib/pq/table/pq-lists-table.tsx +++ b/lib/pq/table/pq-lists-table.tsx @@ -13,10 +13,12 @@ import { deletePQListsAction,
copyPQListAction,
togglePQListsAction,
+ updatePqValidToAction,
} from "@/lib/pq/service"
import { CopyPqDialog } from "./copy-pq-list-dialog"
import { AddPqDialog } from "./add-pq-list-dialog"
import { PQListsToolbarActions } from "./pq-lists-toolbar"
+import { EditValidToSheet } from "./pq-lists-columns"
import type { DataTableRowAction } from "@/types/table"
interface Project {
@@ -34,10 +36,12 @@ export function PqListsTable({ promises }: PqListsTableProps) { const [rowAction, setRowAction] = React.useState<DataTableRowAction<PQList> | null>(null)
const [createDialogOpen, setCreateDialogOpen] = React.useState(false)
const [copyDialogOpen, setCopyDialogOpen] = React.useState(false)
+ const [editValidToSheetOpen, setEditValidToSheetOpen] = React.useState(false)
+ const [selectedPqList, setSelectedPqList] = React.useState<PQList | null>(null)
const [isPending, startTransition] = React.useTransition()
const [{ data, pageCount }, projects] = React.use(promises)
- const activePqLists = data.filter((item) => !item.isDeleted)
+ // const activePqLists = data.filter((item) => !item.isDeleted)
const columns = React.useMemo(() => createPQListsColumns({ setRowAction }), [setRowAction])
@@ -116,15 +120,38 @@ export function PqListsTable({ promises }: PqListsTableProps) { })
}
+ const handleUpdateValidTo = React.useCallback(async (pqListId: number, newValidTo: Date | null) => {
+ startTransition(async () => {
+ try {
+ const result = await updatePqValidToAction({ pqListId, validTo: newValidTo })
+ if (result.success) {
+ toast.success(result.message || "유효일이 성공적으로 수정되었습니다")
+ setEditValidToSheetOpen(false)
+ setSelectedPqList(null)
+ router.refresh()
+ } else {
+ toast.error(`유효일 수정 실패: ${result.error}`)
+ }
+ } catch (error) {
+ console.error("유효일 수정 실패:", error)
+ toast.error("유효일 수정 실패")
+ }
+ })
+ }, [])
+
React.useEffect(() => {
if (!rowAction) return
- const id = rowAction.row.original.id
+ const pqList = rowAction.row.original
switch (rowAction.type) {
case "view":
- router.push(`/evcp/pq-criteria/${id}`)
+ router.push(`/evcp/pq-criteria/${pqList.id}`)
break
case "delete":
- handleDelete([id])
+ handleDelete([pqList.id])
+ break
+ case "editValidTo":
+ setSelectedPqList(pqList)
+ setEditValidToSheetOpen(true)
break
}
setRowAction(null)
@@ -153,18 +180,24 @@ export function PqListsTable({ promises }: PqListsTableProps) { open={createDialogOpen}
onOpenChange={setCreateDialogOpen}
onSubmit={handleCreate}
- projects={projects}
isLoading={isPending}
/>
<CopyPqDialog
open={copyDialogOpen}
onOpenChange={setCopyDialogOpen}
- pqLists={activePqLists}
+ pqLists={data}
projects={projects}
onCopy={handleCopy}
isLoading={isPending}
/>
+
+ <EditValidToSheet
+ pqList={selectedPqList}
+ open={editValidToSheetOpen}
+ onOpenChange={setEditValidToSheetOpen}
+ onUpdate={handleUpdateValidTo}
+ />
</>
)
}
diff --git a/lib/pq/table/pq-lists-toolbar.tsx b/lib/pq/table/pq-lists-toolbar.tsx index 3a85327d..1feb9a1a 100644 --- a/lib/pq/table/pq-lists-toolbar.tsx +++ b/lib/pq/table/pq-lists-toolbar.tsx @@ -2,7 +2,7 @@ import * as React from "react"
import { Button } from "@/components/ui/button"
-import { Trash, CopyPlus, Plus } from "lucide-react"
+import { Trash, CopyPlus, Plus, RefreshCw } from "lucide-react"
import { type Table } from "@tanstack/react-table"
import type { PQList } from "./pq-lists-columns"
// import { PqListForm } from "./add-pq-list-dialog"
@@ -44,7 +44,7 @@ export function PQListsToolbarActions({ size="sm"
onClick={() => onToggleActive(selected, newState!)}
>
- <Trash className="mr-2 h-4 w-4" />
+ <RefreshCw className="mr-2 h-4 w-4" />
{toggleLabel}
</Button>
)}
diff --git a/lib/pq/validations.ts b/lib/pq/validations.ts index 93daf460..c8a9d8e5 100644 --- a/lib/pq/validations.ts +++ b/lib/pq/validations.ts @@ -107,9 +107,7 @@ export const copyPqListSchema = z.object({ sourcePqListId: z.number({
required_error: "복사할 PQ 목록을 선택해주세요"
}),
- targetProjectId: z.number({
- required_error: "대상 프로젝트를 선택해주세요"
- }),
+ targetProjectId: z.number().optional(),
newName: z.string().min(1, "새 PQ 목록 명을 입력해주세요").optional(),
validTo: z.date().optional().nullable(),
});
@@ -124,4 +122,14 @@ export type CreatePqListInput = z.infer<typeof createPqListSchema>; export type CopyPqListInput = z.infer<typeof copyPqListSchema>;
export type GetPqListsSchema = z.infer<typeof getPqListsSchema>;
-export type GetPQSubmissionsSchema = Awaited<ReturnType<typeof searchParamsPQReviewCache.parse>>
\ No newline at end of file +export type GetPQSubmissionsSchema = Awaited<ReturnType<typeof searchParamsPQReviewCache.parse>>
+
+// PQ 유효일 수정
+export const updatePqValidToSchema = z.object({
+ pqListId: z.number({
+ required_error: "PQ 목록 ID가 필요합니다"
+ }),
+ validTo: z.date().nullable(),
+});
+
+export type UpdatePqValidToInput = z.infer<typeof updatePqValidToSchema>;
\ No newline at end of file |
