summaryrefslogtreecommitdiff
path: root/lib/form-list
diff options
context:
space:
mode:
Diffstat (limited to 'lib/form-list')
-rw-r--r--lib/form-list/repository.ts2
-rw-r--r--lib/form-list/service.ts2
-rw-r--r--lib/form-list/table/formLists-table-toolbar-actions.tsx129
-rw-r--r--lib/form-list/table/formLists-table.tsx7
-rw-r--r--lib/form-list/table/meta-sheet.tsx85
5 files changed, 172 insertions, 53 deletions
diff --git a/lib/form-list/repository.ts b/lib/form-list/repository.ts
index d3c555bf..9c7f6891 100644
--- a/lib/form-list/repository.ts
+++ b/lib/form-list/repository.ts
@@ -36,6 +36,8 @@ export async function selectFormLists(
classLabel: tagTypeClassFormMappings.classLabel,
formCode: tagTypeClassFormMappings.formCode,
formName: tagTypeClassFormMappings.formName,
+ ep: tagTypeClassFormMappings.ep,
+ remark: tagTypeClassFormMappings.remark,
createdAt: tagTypeClassFormMappings.createdAt,
updatedAt: tagTypeClassFormMappings.updatedAt,
// 프로젝트 정보 추가
diff --git a/lib/form-list/service.ts b/lib/form-list/service.ts
index 310930be..9887609f 100644
--- a/lib/form-list/service.ts
+++ b/lib/form-list/service.ts
@@ -74,6 +74,8 @@ export async function getFormLists(input: GetFormListsSchema) {
offset,
limit: input.perPage,
});
+
+ console.log("dbdata")
const total = await countFormLists(tx, where);
return { data, total };
diff --git a/lib/form-list/table/formLists-table-toolbar-actions.tsx b/lib/form-list/table/formLists-table-toolbar-actions.tsx
index 96494607..cf874985 100644
--- a/lib/form-list/table/formLists-table-toolbar-actions.tsx
+++ b/lib/form-list/table/formLists-table-toolbar-actions.tsx
@@ -2,71 +2,150 @@
import * as React from "react"
import { type Table } from "@tanstack/react-table"
-import { Download, RefreshCcw, Upload } from "lucide-react"
+import { Download, RefreshCcw } from "lucide-react"
import { toast } from "sonner"
import { exportTableToExcel } from "@/lib/export"
import { Button } from "@/components/ui/button"
import { ExtendedFormMappings } from "../validation"
-
-
interface ItemsTableToolbarActionsProps {
table: Table<ExtendedFormMappings>
}
export function FormListsTableToolbarActions({ table }: ItemsTableToolbarActionsProps) {
const [isLoading, setIsLoading] = React.useState(false)
+ const [syncId, setSyncId] = React.useState<string | null>(null)
+ const pollingRef = React.useRef<NodeJS.Timeout | null>(null)
+
+ // Clean up polling on unmount
+ React.useEffect(() => {
+ return () => {
+ if (pollingRef.current) {
+ clearInterval(pollingRef.current)
+ }
+ }
+ }, [])
- const syncForms = async () => {
+ const startFormSync = async () => {
try {
setIsLoading(true)
-
- // API 엔드포인트 호출
- const response = await fetch('/api/cron/forms')
-
+
+ // API 엔드포인트 호출 - 작업 시작만 요청
+ const response = await fetch('/api/cron/forms/start', {
+ method: 'POST'
+ })
+
if (!response.ok) {
const errorData = await response.json()
- throw new Error(errorData.error || 'Failed to sync forms')
+ throw new Error(errorData.error || 'Failed to start form sync')
}
-
+
const data = await response.json()
-
- // 성공 메시지 표시
- toast.success(
- `Forms synced successfully! ${data.result.items} items processed.`
- )
-
- // 페이지 새로고침으로 테이블 데이터 업데이트
- window.location.reload()
+
+ // 작업 ID 저장
+ if (data.syncId) {
+ setSyncId(data.syncId)
+ toast.info('Form sync started. This may take a while...')
+
+ // 상태 확인을 위한 폴링 시작
+ startPolling(data.syncId)
+ } else {
+ throw new Error('No sync ID returned from server')
+ }
} catch (error) {
- console.error('Error syncing forms:', error)
+ console.error('Error starting form sync:', error)
toast.error(
error instanceof Error
? error.message
- : 'An error occurred while syncing forms'
+ : 'An error occurred while starting form sync'
)
- } finally {
setIsLoading(false)
}
}
-
+
+ const startPolling = (id: string) => {
+ // 이전 폴링이 있다면 제거
+ if (pollingRef.current) {
+ clearInterval(pollingRef.current)
+ }
+
+ // 5초마다 상태 확인
+ pollingRef.current = setInterval(async () => {
+ try {
+ const response = await fetch(`/api/cron/forms/status?id=${id}`)
+
+ if (!response.ok) {
+ throw new Error('Failed to get sync status')
+ }
+
+ const data = await response.json()
+
+ if (data.status === 'completed') {
+ // 폴링 중지
+ if (pollingRef.current) {
+ clearInterval(pollingRef.current)
+ pollingRef.current = null
+ }
+
+ // 상태 초기화
+ setIsLoading(false)
+ setSyncId(null)
+
+ // 성공 메시지 표시
+ toast.success(
+ `Forms synced successfully! ${data.result?.items || 0} items processed.`
+ )
+
+ // 테이블 데이터 업데이트 - 전체 페이지 새로고침 대신 데이터만 갱신
+ table.resetRowSelection()
+ // 여기서 테이블 데이터를 다시 불러오는 함수를 호출
+ // 예: refetchTableData()
+ // 또는 SWR/React Query 등을 사용하고 있다면 mutate() 호출
+
+ // 리로드가 꼭 필요하다면 아래 주석 해제
+ // window.location.reload()
+ } else if (data.status === 'failed') {
+ // 에러 처리
+ if (pollingRef.current) {
+ clearInterval(pollingRef.current)
+ pollingRef.current = null
+ }
+
+ setIsLoading(false)
+ setSyncId(null)
+ toast.error(data.error || 'Sync failed')
+ } else if (data.status === 'processing') {
+ // 진행 상태 업데이트 (선택적)
+ if (data.progress) {
+ toast.info(`Sync in progress: ${data.progress}%`, {
+ id: `sync-progress-${id}`,
+ })
+ }
+ }
+ } catch (error) {
+ console.error('Error checking sync status:', error)
+ }
+ }, 5000) // 5초마다 체크
+ }
return (
<div className="flex items-center gap-2">
- {/** 4) Export 버튼 */}
+ {/** Sync Forms 버튼 */}
<Button
variant="samsung"
size="sm"
className="gap-2"
+ onClick={startFormSync}
+ disabled={isLoading}
>
<RefreshCcw className={`size-4 ${isLoading ? 'animate-spin' : ''}`} aria-hidden="true" />
<span className="hidden sm:inline">
{isLoading ? 'Syncing...' : 'Get Forms'}
</span>
</Button>
-
- {/** 4) Export 버튼 */}
+
+ {/** Export 버튼 */}
<Button
variant="outline"
size="sm"
diff --git a/lib/form-list/table/formLists-table.tsx b/lib/form-list/table/formLists-table.tsx
index 58ac4671..aa5bfa09 100644
--- a/lib/form-list/table/formLists-table.tsx
+++ b/lib/form-list/table/formLists-table.tsx
@@ -32,7 +32,6 @@ export function FormListsTable({ promises }: ItemsTableProps) {
const [{ data, pageCount }] =
React.use(promises)
-
const [rowAction, setRowAction] =
React.useState<DataTableRowAction<ExtendedFormMappings> | null>(null)
@@ -100,6 +99,12 @@ export function FormListsTable({ promises }: ItemsTableProps) {
type: "text",
},
+ {
+ id: "remark",
+ label: "remark",
+ type: "text",
+
+ },
{
id: "createdAt",
diff --git a/lib/form-list/table/meta-sheet.tsx b/lib/form-list/table/meta-sheet.tsx
index 694ee845..7c15bdea 100644
--- a/lib/form-list/table/meta-sheet.tsx
+++ b/lib/form-list/table/meta-sheet.tsx
@@ -1,8 +1,9 @@
"use client"
import * as React from "react"
-import { useMemo } from "react"
+import { useState } from "react"
import { Badge } from "@/components/ui/badge"
+import { Button } from "@/components/ui/button"
import {
Sheet,
SheetContent,
@@ -34,6 +35,56 @@ import {
import type { TagTypeClassFormMappings } from "@/db/schema/vendorData" // or your actual type
import { fetchFormMetadata, FormColumn } from "@/lib/forms/services"
+// 옵션을 표시하기 위한 새로운 컴포넌트
+const CollapsibleOptions = ({ options }: { options?: string[] }) => {
+ const [isExpanded, setIsExpanded] = useState(false);
+
+ // 옵션이 없거나 5개 이하인 경우 모두 표시
+ if (!options || options.length <= 5) {
+ return (
+ <div className="flex flex-wrap gap-1">
+ {options?.map((option) => (
+ <Badge key={option} variant="outline" className="text-xs">
+ {option}
+ </Badge>
+ )) || "-"}
+ </div>
+ );
+ }
+
+ // 더 많은 옵션이 있는 경우 접었다 펼칠 수 있게 함
+ return (
+ <div>
+ <div className="flex flex-wrap gap-1 mb-1">
+ {isExpanded
+ ? options.map((option) => (
+ <Badge key={option} variant="outline" className="text-xs">
+ {option}
+ </Badge>
+ ))
+ : options.slice(0, 3).map((option) => (
+ <Badge key={option} variant="outline" className="text-xs">
+ {option}
+ </Badge>
+ ))
+ }
+ {!isExpanded && (
+ <Badge variant="outline" className="text-xs bg-muted">
+ +{options.length - 3} more
+ </Badge>
+ )}
+ </div>
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-6 text-xs px-2 py-0"
+ onClick={() => setIsExpanded(!isExpanded)}
+ >
+ {isExpanded ? "Show less" : "Show all"}
+ </Button>
+ </div>
+ );
+};
interface ViewMetasProps {
open: boolean
@@ -51,7 +102,7 @@ export function ViewMetas({ open, onOpenChange, form }: ViewMetasProps) {
const [loading, setLoading] = React.useState(false)
// Group columns by type for better organization
- const groupedColumns = useMemo(() => {
+ const groupedColumns = React.useMemo(() => {
if (!metadata?.columns) return {}
return metadata.columns.reduce((acc, column) => {
@@ -65,7 +116,7 @@ export function ViewMetas({ open, onOpenChange, form }: ViewMetasProps) {
}, [metadata])
// Types for the tabs
- const columnTypes = useMemo(() => {
+ const columnTypes = React.useMemo(() => {
return Object.keys(groupedColumns)
}, [groupedColumns])
@@ -165,17 +216,7 @@ export function ViewMetas({ open, onOpenChange, form }: ViewMetasProps) {
<Badge variant="secondary">{column.type}</Badge>
</TableCell>
<TableCell>
- {column.options ? (
- <div className="flex flex-wrap gap-1">
- {column.options.map((option) => (
- <Badge key={option} variant="outline" className="text-xs">
- {option}
- </Badge>
- ))}
- </div>
- ) : (
- "-"
- )}
+ <CollapsibleOptions options={column.options} />
</TableCell>
</TableRow>
))}
@@ -198,7 +239,7 @@ export function ViewMetas({ open, onOpenChange, form }: ViewMetasProps) {
<TableRow>
<TableHead>Key</TableHead>
<TableHead>Label</TableHead>
- {type === "select" && <TableHead>Options</TableHead>}
+ {type === "LIST" && <TableHead>Options</TableHead>}
</TableRow>
</TableHeader>
<TableBody>
@@ -206,19 +247,9 @@ export function ViewMetas({ open, onOpenChange, form }: ViewMetasProps) {
<TableRow key={column.key}>
<TableCell className="font-mono text-sm">{column.key}</TableCell>
<TableCell>{column.label}</TableCell>
- {type === "select" && (
+ {type === "LIST" && (
<TableCell>
- {column.options ? (
- <div className="flex flex-wrap gap-1">
- {column.options.map((option) => (
- <Badge key={option} variant="outline" className="text-xs">
- {option}
- </Badge>
- ))}
- </div>
- ) : (
- "-"
- )}
+ <CollapsibleOptions options={column.options} />
</TableCell>
)}
</TableRow>