diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-01 11:47:47 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-01 11:47:47 +0000 |
| commit | 4c15b99d9586aa48693213c78c02fba4639ebb85 (patch) | |
| tree | ef48f45deeb36f499b01851870f6077c8621f396 /lib/information/table | |
| parent | 795b4915069c44f500a91638e16ded67b9e16618 (diff) | |
(최겸) 인포메이션 기능 수정
Diffstat (limited to 'lib/information/table')
| -rw-r--r-- | lib/information/table/add-information-dialog.tsx | 329 | ||||
| -rw-r--r-- | lib/information/table/delete-information-dialog.tsx | 125 | ||||
| -rw-r--r-- | lib/information/table/information-table-columns.tsx | 248 | ||||
| -rw-r--r-- | lib/information/table/information-table-toolbar-actions.tsx | 25 | ||||
| -rw-r--r-- | lib/information/table/information-table.tsx | 148 | ||||
| -rw-r--r-- | lib/information/table/update-information-dialog.tsx | 124 |
6 files changed, 26 insertions, 973 deletions
diff --git a/lib/information/table/add-information-dialog.tsx b/lib/information/table/add-information-dialog.tsx deleted file mode 100644 index a879fbfe..00000000 --- a/lib/information/table/add-information-dialog.tsx +++ /dev/null @@ -1,329 +0,0 @@ -"use client"
-
-import * as React from "react"
-import { zodResolver } from "@hookform/resolvers/zod"
-import { useForm } from "react-hook-form"
-import { toast } from "sonner"
-import { Loader, Upload, X } from "lucide-react"
-import { useRouter } from "next/navigation"
-
-import { Button } from "@/components/ui/button"
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
-} from "@/components/ui/dialog"
-import {
- Form,
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "@/components/ui/form"
-import { Input } from "@/components/ui/input"
-import { Textarea } from "@/components/ui/textarea"
-import { Switch } from "@/components/ui/switch"
-import { createInformation } from "@/lib/information/service"
-import { createInformationSchema, type CreateInformationSchema } from "@/lib/information/validations"
-
-interface AddInformationDialogProps {
- open: boolean
- onOpenChange: (open: boolean) => void
-}
-
-export function AddInformationDialog({
- open,
- onOpenChange,
-}: AddInformationDialogProps) {
- const router = useRouter()
- const [isLoading, setIsLoading] = React.useState(false)
- const [uploadedFile, setUploadedFile] = React.useState<File | null>(null)
-
- const form = useForm<CreateInformationSchema>({
- resolver: zodResolver(createInformationSchema),
- defaultValues: {
- pageCode: "",
- pageName: "",
- title: "",
- description: "",
- noticeTitle: "",
- noticeContent: "",
- attachmentFileName: "",
- attachmentFilePath: "",
- attachmentFileSize: "",
- isActive: true,
- },
- })
-
- const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
- const file = event.target.files?.[0]
- if (file) {
- setUploadedFile(file)
- // 파일 크기를 MB 단위로 변환
- const sizeInMB = (file.size / (1024 * 1024)).toFixed(2)
- form.setValue("attachmentFileName", file.name)
- form.setValue("attachmentFileSize", `${sizeInMB} MB`)
- }
- }
-
- const removeFile = () => {
- setUploadedFile(null)
- form.setValue("attachmentFileName", "")
- form.setValue("attachmentFilePath", "")
- form.setValue("attachmentFileSize", "")
- }
-
- const uploadFile = async (file: File): Promise<string> => {
- const formData = new FormData()
- formData.append("file", file)
-
- const response = await fetch("/api/upload", {
- method: "POST",
- body: formData,
- })
-
- if (!response.ok) {
- throw new Error("파일 업로드에 실패했습니다.")
- }
-
- const result = await response.json()
- return result.url
- }
-
- const onSubmit = async (values: CreateInformationSchema) => {
- setIsLoading(true)
- try {
- const finalValues = { ...values }
-
- // 파일이 있으면 업로드
- if (uploadedFile) {
- const filePath = await uploadFile(uploadedFile)
- finalValues.attachmentFilePath = filePath
- }
-
- const result = await createInformation(finalValues)
-
- if (result.success) {
- toast.success(result.message)
- form.reset()
- setUploadedFile(null)
- onOpenChange(false)
- router.refresh()
- } else {
- toast.error(result.message)
- }
- } catch (error) {
- toast.error("인포메이션 생성에 실패했습니다.")
- console.error(error)
- } finally {
- setIsLoading(false)
- }
- }
-
- // 다이얼로그가 닫힐 때 폼 초기화
- React.useEffect(() => {
- if (!open) {
- form.reset()
- setUploadedFile(null)
- }
- }, [open, form])
-
- return (
- <Dialog open={open} onOpenChange={onOpenChange}>
- <DialogContent className="max-w-2xl">
- <DialogHeader>
- <DialogTitle>인포메이션 추가</DialogTitle>
- <DialogDescription>
- 새로운 페이지 인포메이션을 추가합니다.
- </DialogDescription>
- </DialogHeader>
-
- <Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
- <div className="grid grid-cols-2 gap-4">
- <FormField
- control={form.control}
- name="pageCode"
- render={({ field }) => (
- <FormItem>
- <FormLabel>페이지 코드</FormLabel>
- <FormControl>
- <Input placeholder="예: vendor-list" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="pageName"
- render={({ field }) => (
- <FormItem>
- <FormLabel>페이지명</FormLabel>
- <FormControl>
- <Input placeholder="예: 협력업체 목록" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- </div>
-
- <FormField
- control={form.control}
- name="title"
- render={({ field }) => (
- <FormItem>
- <FormLabel>제목</FormLabel>
- <FormControl>
- <Input placeholder="인포메이션 제목을 입력하세요" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="description"
- render={({ field }) => (
- <FormItem>
- <FormLabel>설명</FormLabel>
- <FormControl>
- <Textarea
- placeholder="페이지 설명을 입력하세요"
- rows={4}
- {...field}
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="noticeTitle"
- render={({ field }) => (
- <FormItem>
- <FormLabel>공지사항 제목 (선택사항)</FormLabel>
- <FormControl>
- <Input placeholder="공지사항 제목을 입력하세요" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="noticeContent"
- render={({ field }) => (
- <FormItem>
- <FormLabel>공지사항 내용 (선택사항)</FormLabel>
- <FormControl>
- <Textarea
- placeholder="공지사항 내용을 입력하세요"
- rows={3}
- {...field}
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <div>
- <FormLabel>첨부파일</FormLabel>
- <div className="mt-2">
- {uploadedFile ? (
- <div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
- <div className="flex items-center gap-2">
- <span className="text-sm font-medium">{uploadedFile.name}</span>
- <span className="text-xs text-gray-500">
- ({(uploadedFile.size / (1024 * 1024)).toFixed(2)} MB)
- </span>
- </div>
- <Button
- type="button"
- variant="ghost"
- size="sm"
- onClick={removeFile}
- >
- <X className="h-4 w-4" />
- </Button>
- </div>
- ) : (
- <div className="border-2 border-dashed border-gray-300 rounded-lg p-4">
- <div className="text-center">
- <Upload className="mx-auto h-8 w-8 text-gray-400" />
- <div className="mt-2">
- <label
- htmlFor="file-upload"
- className="cursor-pointer text-sm text-blue-600 hover:text-blue-500"
- >
- 파일을 선택하세요
- </label>
- <input
- id="file-upload"
- type="file"
- className="hidden"
- onChange={handleFileSelect}
- accept=".pdf,.doc,.docx,.xlsx,.ppt,.pptx,.txt,.zip"
- />
- </div>
- <p className="text-xs text-gray-500 mt-1">
- PDF, DOC, DOCX, XLSX, PPT, PPTX, TXT, ZIP 파일만 업로드 가능
- </p>
- </div>
- </div>
- )}
- </div>
- </div>
-
- <FormField
- control={form.control}
- name="isActive"
- render={({ field }) => (
- <FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
- <div className="space-y-0.5">
- <FormLabel className="text-base">활성 상태</FormLabel>
- <div className="text-sm text-muted-foreground">
- 활성화하면 해당 페이지에서 인포메이션 버튼이 표시됩니다.
- </div>
- </div>
- <FormControl>
- <Switch
- checked={field.value}
- onCheckedChange={field.onChange}
- />
- </FormControl>
- </FormItem>
- )}
- />
-
- <DialogFooter>
- <Button
- type="button"
- variant="outline"
- onClick={() => onOpenChange(false)}
- disabled={isLoading}
- >
- 취소
- </Button>
- <Button type="submit" disabled={isLoading}>
- {isLoading && <Loader className="mr-2 h-4 w-4 animate-spin" />}
- 생성
- </Button>
- </DialogFooter>
- </form>
- </Form>
- </DialogContent>
- </Dialog>
- )
-}
\ No newline at end of file diff --git a/lib/information/table/delete-information-dialog.tsx b/lib/information/table/delete-information-dialog.tsx deleted file mode 100644 index e36d948d..00000000 --- a/lib/information/table/delete-information-dialog.tsx +++ /dev/null @@ -1,125 +0,0 @@ -"use client"
-
-import * as React from "react"
-import { useRouter } from "next/navigation"
-import { toast } from "sonner"
-import { Loader, Trash2 } from "lucide-react"
-
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
-} from "@/components/ui/alert-dialog"
-import { deleteInformation } from "@/lib/information/service"
-import type { PageInformation } from "@/db/schema/information"
-
-interface DeleteInformationDialogProps {
- open: boolean
- onOpenChange: (open: boolean) => void
- information?: PageInformation
- onClose: () => void
-}
-
-export function DeleteInformationDialog({
- open,
- onOpenChange,
- information,
- onClose,
-}: DeleteInformationDialogProps) {
- const router = useRouter()
- const [isLoading, setIsLoading] = React.useState(false)
-
- const handleDelete = async () => {
- if (!information) return
-
- setIsLoading(true)
- try {
- const result = await deleteInformation(information.id)
-
- if (result.success) {
- toast.success(result.message)
- onClose()
- router.refresh()
- } else {
- toast.error(result.message)
- }
- } catch (error) {
- toast.error("인포메이션 삭제에 실패했습니다.")
- console.error(error)
- } finally {
- setIsLoading(false)
- }
- }
-
- return (
- <AlertDialog open={open} onOpenChange={onOpenChange}>
- <AlertDialogContent>
- <AlertDialogHeader>
- <AlertDialogTitle className="flex items-center gap-2">
- <Trash2 className="h-5 w-5 text-destructive" />
- 인포메이션 삭제
- </AlertDialogTitle>
- <AlertDialogDescription>
- 다음 인포메이션을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.
- </AlertDialogDescription>
- </AlertDialogHeader>
-
- {information && (
- <div className="bg-muted rounded-lg p-4 my-4">
- <div className="space-y-2">
- <div>
- <span className="font-medium text-sm">페이지 코드:</span>
- <span className="ml-2 font-mono text-sm">{information.pageCode}</span>
- </div>
- <div>
- <span className="font-medium text-sm">페이지명:</span>
- <span className="ml-2 text-sm">{information.pageName}</span>
- </div>
- <div>
- <span className="font-medium text-sm">제목:</span>
- <span className="ml-2 text-sm">{information.title}</span>
- </div>
- {information.noticeTitle && (
- <div>
- <span className="font-medium text-sm">공지사항 제목:</span>
- <span className="ml-2 text-sm">{information.noticeTitle}</span>
- </div>
- )}
- {information.noticeContent && (
- <div>
- <span className="font-medium text-sm">공지사항 내용:</span>
- <span className="ml-2 text-sm text-orange-600">{information.noticeContent}</span>
- </div>
- )}
- {information.attachmentFileName && (
- <div>
- <span className="font-medium text-sm">첨부파일:</span>
- <span className="ml-2 text-sm">{information.attachmentFileName}</span>
- </div>
- )}
- </div>
- </div>
- )}
-
- <AlertDialogFooter>
- <AlertDialogCancel onClick={onClose} disabled={isLoading}>
- 취소
- </AlertDialogCancel>
- <AlertDialogAction
- onClick={handleDelete}
- disabled={isLoading}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
- >
- {isLoading && <Loader className="mr-2 h-4 w-4 animate-spin" />}
- 삭제
- </AlertDialogAction>
- </AlertDialogFooter>
- </AlertDialogContent>
- </AlertDialog>
- )
-}
\ No newline at end of file diff --git a/lib/information/table/information-table-columns.tsx b/lib/information/table/information-table-columns.tsx deleted file mode 100644 index f84fd2f9..00000000 --- a/lib/information/table/information-table-columns.tsx +++ /dev/null @@ -1,248 +0,0 @@ -"use client" - -import * as React from "react" -import type { ColumnDef } from "@tanstack/react-table" -import { Badge } from "@/components/ui/badge" -import { Button } from "@/components/ui/button" -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" -import type { DataTableRowAction } from "@/types/table" -import type { PageInformation } from "@/db/schema/information" -import { formatDate } from "@/lib/utils" -import { Ellipsis, FileText, Download } from "lucide-react" -import { informationColumnsConfig } from "@/config/informationColumnsConfig" - -interface GetColumnsProps { - setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<PageInformation> | null>> -} - -/** - * tanstack table 컬럼 정의 (중첩 헤더 버전) - */ -export function getInformationColumns({ setRowAction }: GetColumnsProps): ColumnDef<PageInformation>[] { - // // ---------------------------------------------------------------- - // // 1) Select 컬럼 (체크박스) - // // ---------------------------------------------------------------- - // const selectColumn: ColumnDef<PageInformation> = { - // id: "select", - // header: ({ table }) => ( - // <Checkbox - // checked={ - // table.getIsAllPageRowsSelected() || - // (table.getIsSomePageRowsSelected() && "indeterminate") - // } - // onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)} - // aria-label="Select all" - // className="translate-y-0.5" - // /> - // ), - // cell: ({ row }) => ( - // <Checkbox - // checked={row.getIsSelected()} - // onCheckedChange={(value) => row.toggleSelected(!!value)} - // aria-label="Select row" - // className="translate-y-0.5" - // /> - // ), - // enableSorting: false, - // enableHiding: false, - // } - - // ---------------------------------------------------------------- - // 2) 일반 컬럼들을 "그룹"별로 묶어 중첩 columns 생성 - // ---------------------------------------------------------------- - const groupMap: Record<string, ColumnDef<PageInformation>[]> = {} - - informationColumnsConfig.forEach((cfg) => { - // 만약 group가 없으면 "_noGroup" 처리 - const groupName = cfg.group || "_noGroup" - - if (!groupMap[groupName]) { - groupMap[groupName] = [] - } - - // child column 정의 - const childCol: ColumnDef<PageInformation> = { - accessorKey: cfg.id, - enableResizing: cfg.id === "description" || cfg.id === "noticeContent" ? false : true, - size: cfg.id === "description" || cfg.id === "noticeContent" ? 200 : undefined, - minSize: cfg.id === "description" || cfg.id === "noticeContent" ? 200 : undefined, - maxSize: cfg.id === "description" || cfg.id === "noticeContent" ? 200 : undefined, - header: ({ column }) => ( - <DataTableColumnHeaderSimple column={column} title={cfg.label} /> - ), - meta: { - excelHeader: cfg.excelHeader, - group: cfg.group, - type: cfg.type, - }, - cell: ({ row }) => { - const value = row.getValue(cfg.id) - - if (cfg.id === "pageCode") { - return <div className=" text-sm">{value as string}</div> - } - - if (cfg.id === "pageName") { - return <div className="max-w-4 truncate font-medium">{value as string}</div> - } - - if (cfg.id === "title") { - return <div className="max-w-4 truncate">{value as string}</div> - } - - if (cfg.id === "description") { - return ( - <div className="truncate text-muted-foreground" style={{ width: '200px', maxWidth: '200px' }}> - {value as string} - </div> - ) - } - - if (cfg.id === "noticeTitle") { - const noticeTitle = value as string - if (!noticeTitle) { - return <span className="text-muted-foreground">-</span> - } - return <div className="max-w-xs truncate">{noticeTitle}</div> - } - - if (cfg.id === "noticeContent") { - const noticeContent = value as string - if (!noticeContent) { - return <span className="text-muted-foreground">-</span> - } - return ( - <div className="truncate text-muted-foreground" style={{ width: '200px', maxWidth: '200px' }}> - {noticeContent} - </div> - ) - } - - if (cfg.id === "attachmentFileName") { - const fileName = value as string - if (!fileName) { - return <span className="text-muted-foreground">-</span> - } - return ( - <div className="flex items-center gap-1"> - <FileText className="h-3 w-3" /> - <span className="text-sm truncate max-w-32" title={fileName}> - {fileName} - </span> - </div> - ) - } - - if (cfg.id === "isActive") { - return ( - <Badge variant={value ? "default" : "secondary"}> - {value ? "활성" : "비활성"} - </Badge> - ) - } - - if (cfg.id === "createdAt" || cfg.id === "updatedAt") { - const dateVal = value as Date - return formatDate(dateVal) - } - - return value ?? "" - }, - } - - groupMap[groupName].push(childCol) - }) - - // ---------------------------------------------------------------- - // 3) groupMap에서 실제 상위 컬럼(그룹)을 만들기 - // ---------------------------------------------------------------- - const nestedColumns: ColumnDef<PageInformation>[] = [] - - // 순서를 고정하고 싶다면 group 순서를 미리 정의하거나 sort해야 함 - // 여기서는 그냥 Object.entries 순서 - Object.entries(groupMap).forEach(([groupName, colDefs]) => { - if (groupName === "_noGroup") { - // 그룹 없음 → 그냥 최상위 레벨 컬럼 - nestedColumns.push(...colDefs) - } else { - // 상위 컬럼 - nestedColumns.push({ - id: groupName, - header: groupName, // "기본 정보", "공지사항" 등 - columns: colDefs, - }) - } - }) - - // ---------------------------------------------------------------- - // 4) Actions 컬럼 - // ---------------------------------------------------------------- - const actionsColumn: ColumnDef<PageInformation> = { - id: "actions", - cell: ({ row }) => ( - <DropdownMenu> - <DropdownMenuTrigger asChild> - <Button - aria-label="Open menu" - variant="ghost" - className="flex size-8 p-0 data-[state=open]:bg-muted" - > - <Ellipsis className="size-4" aria-hidden="true" /> - </Button> - </DropdownMenuTrigger> - <DropdownMenuContent align="end" className="w-40"> - <DropdownMenuItem - onSelect={() => setRowAction({ row, type: "update" })} - > - 수정 - </DropdownMenuItem> - {row.original.attachmentFileName && ( - <> - <DropdownMenuSeparator /> - <DropdownMenuItem - onSelect={() => { - if (row.original.attachmentFilePath) { - const link = document.createElement('a') - link.href = row.original.attachmentFilePath - link.download = row.original.attachmentFileName || '' - document.body.appendChild(link) - link.click() - document.body.removeChild(link) - } - }} - > - <Download className="mr-2 h-4 w-4" /> - 다운로드 - </DropdownMenuItem> - </> - )} - <DropdownMenuSeparator /> - <DropdownMenuItem - onSelect={() => setRowAction({ row, type: "delete" })} - className="text-destructive" - > - 삭제 - </DropdownMenuItem> - </DropdownMenuContent> - </DropdownMenu> - ), - enableSorting: false, - enableHiding: false, - } - - // ---------------------------------------------------------------- - // 5) 최종 컬럼 배열: select, nestedColumns, actions - // ---------------------------------------------------------------- - return [ - // selectColumn, - ...nestedColumns, - actionsColumn, - ] -}
\ No newline at end of file diff --git a/lib/information/table/information-table-toolbar-actions.tsx b/lib/information/table/information-table-toolbar-actions.tsx deleted file mode 100644 index 5d8fff3a..00000000 --- a/lib/information/table/information-table-toolbar-actions.tsx +++ /dev/null @@ -1,25 +0,0 @@ -"use client"
-
-import { type Table } from "@tanstack/react-table"
-import { Plus } from "lucide-react"
-
-import { Button } from "@/components/ui/button"
-import type { PageInformation } from "@/db/schema/information"
-
-interface InformationTableToolbarActionsProps {
- table: Table<PageInformation>
- onAdd: () => void
-}
-
-export function InformationTableToolbarActions({
- onAdd,
-}: InformationTableToolbarActionsProps) {
- return (
- <div className="flex items-center gap-2">
- <Button size="sm" onClick={onAdd}>
- <Plus className="mr-2 size-4" aria-hidden="true" />
- 인포메이션 추가
- </Button>
- </div>
- )
-}
\ No newline at end of file diff --git a/lib/information/table/information-table.tsx b/lib/information/table/information-table.tsx deleted file mode 100644 index 9fc4ec29..00000000 --- a/lib/information/table/information-table.tsx +++ /dev/null @@ -1,148 +0,0 @@ -"use client"
-
-import * as React from "react"
-import type {
- DataTableAdvancedFilterField,
- DataTableFilterField,
- DataTableRowAction,
-} from "@/types/table"
-
-import { useDataTable } from "@/hooks/use-data-table"
-import { DataTable } from "@/components/data-table/data-table"
-import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar"
-import type { PageInformation } from "@/db/schema/information"
-import { getInformationColumns } from "./information-table-columns"
-import { InformationTableToolbarActions } from "./information-table-toolbar-actions"
-import { AddInformationDialog } from "./add-information-dialog"
-import { UpdateInformationDialog } from "./update-information-dialog"
-import { DeleteInformationDialog } from "./delete-information-dialog"
-
-interface InformationTableProps {
- promises: Promise<{
- data: PageInformation[]
- pageCount: number
- total: number
- }>
-}
-
-export function InformationTable({ promises }: InformationTableProps) {
- const [rowAction, setRowAction] = React.useState<DataTableRowAction<PageInformation> | null>(null)
- const [showAddDialog, setShowAddDialog] = React.useState(false)
- const [showUpdateDialog, setShowUpdateDialog] = React.useState(false)
- const [showDeleteDialog, setShowDeleteDialog] = React.useState(false)
-
- const { data, pageCount } = React.use(promises)
-
- // 컬럼 설정
- const columns = React.useMemo(
- () => getInformationColumns({ setRowAction }),
- [setRowAction]
- )
-
- const filterFields: DataTableFilterField<PageInformation>[] = []
-
- // 고급 필터 필드 설정
- const advancedFilterFields: DataTableAdvancedFilterField<PageInformation>[] = [
- {
- id: "pageCode",
- label: "페이지 코드",
- type: "text",
- },
- {
- id: "pageName",
- label: "페이지명",
- type: "text",
- },
- {
- id: "title",
- label: "제목",
- type: "text",
- },
- {
- id: "isActive",
- label: "상태",
- type: "select",
- options: [
- { label: "활성", value: "true" },
- { label: "비활성", value: "false" },
- ],
- },
- {
- id: "createdAt",
- label: "생성일",
- type: "date",
- },
- {
- id: "updatedAt",
- label: "수정일",
- type: "date",
- },
- ]
-
- const { table } = useDataTable({
- data,
- columns,
- pageCount,
- filterFields,
- enablePinning: true,
- enableAdvancedFilter: true,
- initialState: {
- sorting: [{ id: "createdAt", desc: true }],
- columnPinning: { right: ["actions"] },
- },
- getRowId: (originalRow) => String(originalRow.id),
- shallow: false,
- clearOnDefault: true,
- })
-
- // 행 액션 처리
- React.useEffect(() => {
- if (rowAction?.type === "update") {
- setShowUpdateDialog(true)
- } else if (rowAction?.type === "delete") {
- setShowDeleteDialog(true)
- }
- }, [rowAction])
-
- return (
- <>
- <DataTable table={table}>
- <DataTableAdvancedToolbar
- table={table}
- filterFields={advancedFilterFields}
- shallow={false}
- >
- <InformationTableToolbarActions
- table={table}
- onAdd={() => setShowAddDialog(true)}
- />
- </DataTableAdvancedToolbar>
- </DataTable>
-
- <AddInformationDialog
- open={showAddDialog}
- onOpenChange={setShowAddDialog}
- />
-
- <UpdateInformationDialog
- open={showUpdateDialog}
- onOpenChange={setShowUpdateDialog}
- information={rowAction?.row.original}
- onClose={() => {
- setShowUpdateDialog(false)
- setRowAction(null)
- }}
- />
-
- <DeleteInformationDialog
- open={showDeleteDialog}
- onOpenChange={setShowDeleteDialog}
- information={rowAction?.row.original}
- onClose={() => {
- setShowDeleteDialog(false)
- setRowAction(null)
- }}
- />
- </>
- )
-}
\ No newline at end of file diff --git a/lib/information/table/update-information-dialog.tsx b/lib/information/table/update-information-dialog.tsx index afa7559b..ed749fe7 100644 --- a/lib/information/table/update-information-dialog.tsx +++ b/lib/information/table/update-information-dialog.tsx @@ -24,7 +24,7 @@ import { FormLabel,
FormMessage,
} from "@/components/ui/form"
-import { Input } from "@/components/ui/input"
+
import { Textarea } from "@/components/ui/textarea"
import { Switch } from "@/components/ui/switch"
import { updateInformationData } from "@/lib/information/service"
@@ -35,14 +35,14 @@ interface UpdateInformationDialogProps { open: boolean
onOpenChange: (open: boolean) => void
information?: PageInformation
- onClose: () => void
+ onSuccess?: () => void
}
export function UpdateInformationDialog({
open,
onOpenChange,
information,
- onClose,
+ onSuccess,
}: UpdateInformationDialogProps) {
const router = useRouter()
const [isLoading, setIsLoading] = React.useState(false)
@@ -52,12 +52,10 @@ export function UpdateInformationDialog({ resolver: zodResolver(updateInformationSchema),
defaultValues: {
id: 0,
- pageCode: "",
- pageName: "",
- title: "",
- description: "",
- noticeTitle: "",
- noticeContent: "",
+ informationContent: "",
+ attachmentFileName: "",
+ attachmentFilePath: "",
+ attachmentFileSize: "",
isActive: true,
},
})
@@ -67,12 +65,7 @@ export function UpdateInformationDialog({ if (information && open) {
form.reset({
id: information.id,
- pageCode: information.pageCode,
- pageName: information.pageName,
- title: information.title,
- description: information.description,
- noticeTitle: information.noticeTitle || "",
- noticeContent: information.noticeContent || "",
+ informationContent: information.informationContent || "",
attachmentFileName: information.attachmentFileName || "",
attachmentFilePath: information.attachmentFilePath || "",
attachmentFileSize: information.attachmentFileSize || "",
@@ -131,7 +124,8 @@ export function UpdateInformationDialog({ if (result.success) {
toast.success(result.message)
- onClose()
+ if (onSuccess) onSuccess()
+ onOpenChange(false)
router.refresh()
} else {
toast.error(result.message)
@@ -146,7 +140,7 @@ export function UpdateInformationDialog({ const handleClose = () => {
setUploadedFile(null)
- onClose()
+ onOpenChange(false)
}
const currentFileName = form.watch("attachmentFileName")
@@ -163,92 +157,26 @@ export function UpdateInformationDialog({ <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
- <div className="grid grid-cols-2 gap-4">
- <FormField
- control={form.control}
- name="pageCode"
- render={({ field }) => (
- <FormItem>
- <FormLabel>페이지 코드</FormLabel>
- <FormControl>
- <Input placeholder="예: vendor-list" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="pageName"
- render={({ field }) => (
- <FormItem>
- <FormLabel>페이지명</FormLabel>
- <FormControl>
- <Input placeholder="예: 협력업체 목록" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
+ <div className="bg-blue-50 p-4 rounded-lg">
+ <div className="flex items-center gap-2 mb-2">
+ <span className="font-medium text-blue-900">페이지 정보</span>
+ </div>
+ <div className="text-sm text-blue-700">
+ <div><strong>페이지명:</strong> {information?.pageName}</div>
+ <div><strong>경로:</strong> {information?.pagePath}</div>
+ </div>
</div>
<FormField
control={form.control}
- name="title"
- render={({ field }) => (
- <FormItem>
- <FormLabel>제목</FormLabel>
- <FormControl>
- <Input placeholder="인포메이션 제목을 입력하세요" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="description"
- render={({ field }) => (
- <FormItem>
- <FormLabel>설명</FormLabel>
- <FormControl>
- <Textarea
- placeholder="페이지 설명을 입력하세요"
- rows={4}
- {...field}
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="noticeTitle"
- render={({ field }) => (
- <FormItem>
- <FormLabel>공지사항 제목 (선택사항)</FormLabel>
- <FormControl>
- <Input placeholder="공지사항 제목을 입력하세요" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- <FormField
- control={form.control}
- name="noticeContent"
+ name="informationContent"
render={({ field }) => (
<FormItem>
- <FormLabel>공지사항 내용 (선택사항)</FormLabel>
+ <FormLabel>인포메이션 내용</FormLabel>
<FormControl>
<Textarea
- placeholder="공지사항 내용을 입력하세요"
- rows={3}
+ placeholder="인포메이션 내용을 입력하세요"
+ rows={6}
{...field}
/>
</FormControl>
@@ -267,7 +195,7 @@ export function UpdateInformationDialog({ <span className="text-xs text-gray-500">
({(uploadedFile.size / (1024 * 1024)).toFixed(2)} MB)
</span>
- <span className="text-xs text-blue-600">(새 파일)</span>
+ <span className="text-xs">(새 파일)</span>
</div>
<Button
type="button"
@@ -291,7 +219,7 @@ export function UpdateInformationDialog({ <div className="flex gap-2">
<label
htmlFor="file-upload-update"
- className="cursor-pointer text-sm text-blue-600 hover:text-blue-500"
+ className="cursor-pointer text-sm"
>
변경
</label>
@@ -312,7 +240,7 @@ export function UpdateInformationDialog({ <div className="mt-2">
<label
htmlFor="file-upload-update"
- className="cursor-pointer text-sm text-blue-600 hover:text-blue-500"
+ className="cursor-pointer text-sm"
>
파일을 선택하세요
</label>
|
