summaryrefslogtreecommitdiff
path: root/lib/welding
diff options
context:
space:
mode:
Diffstat (limited to 'lib/welding')
-rw-r--r--lib/welding/service.ts45
-rw-r--r--lib/welding/table/delete-ocr-rows-dialog.tsx151
-rw-r--r--lib/welding/table/exporft-ocr-data.ts4
-rw-r--r--lib/welding/table/ocr-table-columns.tsx4
-rw-r--r--lib/welding/table/ocr-table-toolbar-actions.tsx22
-rw-r--r--lib/welding/table/ocr-table.tsx12
6 files changed, 234 insertions, 4 deletions
diff --git a/lib/welding/service.ts b/lib/welding/service.ts
index feb6272b..424c4666 100644
--- a/lib/welding/service.ts
+++ b/lib/welding/service.ts
@@ -1,6 +1,6 @@
"use server";
-import { revalidateTag, unstable_noStore } from "next/cache";
+import { revalidatePath, revalidateTag, unstable_noStore } from "next/cache";
import db from "@/db/db";
import { unstable_cache } from "@/lib/unstable-cache";
import { filterColumns } from "@/lib/filter-columns";
@@ -11,6 +11,7 @@ import { OcrRow, ocrRows, users } from "@/db/schema";
import { countOcrRows, selectOcrRows } from "./repository";
import { getServerSession } from "next-auth/next"
import { authOptions } from "@/app/api/auth/[...nextauth]/route"
+import { z } from "zod";
@@ -166,6 +167,8 @@ export async function getOcrAllRows(): Promise<OcrRow[]> {
sessionId: ocrRows.sessionId,
rowIndex: ocrRows.rowIndex,
reportNo: ocrRows.reportNo,
+ fileName: ocrRows.fileName,
+ inspectionDate: ocrRows.inspectionDate,
no: ocrRows.no,
identificationNo: ocrRows.identificationNo,
tagNo: ocrRows.tagNo,
@@ -186,10 +189,50 @@ export async function getOcrAllRows(): Promise<OcrRow[]> {
.leftJoin(users, eq(ocrRows.userId, users.id))
.orderBy(desc(ocrRows.createdAt))
+ console.log(allRows.length)
+
return allRows
} catch (error) {
console.error("Error fetching all OCR rows:", error)
throw new Error("Failed to fetch all OCR data")
}
+}
+
+
+const removeOcrRowsSchema = z.object({
+ ids: z.array(z.string().uuid()),
+})
+
+export async function removeOcrRows(
+ input: z.infer<typeof removeOcrRowsSchema>
+) {
+ try {
+ const { ids } = removeOcrRowsSchema.parse(input)
+
+ if (ids.length === 0) {
+ return {
+ data: null,
+ error: "삭제할 데이터를 선택해주세요.",
+ }
+ }
+
+ // OCR 행들을 삭제
+ await db
+ .delete(ocrRows)
+ .where(inArray(ocrRows.id, ids))
+
+ revalidatePath("/partners/ocr")
+
+ return {
+ data: null,
+ error: null,
+ }
+ } catch (error) {
+ console.error("OCR 행 삭제 중 오류 발생:", error)
+ return {
+ data: null,
+ error: error instanceof Error ? error.message : "삭제 중 오류가 발생했습니다.",
+ }
+ }
} \ No newline at end of file
diff --git a/lib/welding/table/delete-ocr-rows-dialog.tsx b/lib/welding/table/delete-ocr-rows-dialog.tsx
new file mode 100644
index 00000000..8e67eea3
--- /dev/null
+++ b/lib/welding/table/delete-ocr-rows-dialog.tsx
@@ -0,0 +1,151 @@
+"use client"
+
+import * as React from "react"
+import { type OcrRow } from "@/db/schema"
+import { type Row } from "@tanstack/react-table"
+import { Loader, Trash } from "lucide-react"
+import { toast } from "sonner"
+
+import { useMediaQuery } from "@/hooks/use-media-query"
+import { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog"
+import {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/components/ui/drawer"
+
+import { removeOcrRows } from "../service"
+
+interface DeleteOcrRowsDialogProps
+ extends React.ComponentPropsWithoutRef<typeof Dialog> {
+ ocrRows: Row<OcrRow>["original"][]
+ showTrigger?: boolean
+ onSuccess?: () => void
+}
+
+export function DeleteOcrRowsDialog({
+ ocrRows,
+ showTrigger = true,
+ onSuccess,
+ ...props
+}: DeleteOcrRowsDialogProps) {
+ const [isDeletePending, startDeleteTransition] = React.useTransition()
+ const isDesktop = useMediaQuery("(min-width: 640px)")
+
+ function onDelete() {
+ startDeleteTransition(async () => {
+ const { error } = await removeOcrRows({
+ ids: ocrRows.map((row) => row.id),
+ })
+
+ if (error) {
+ toast.error(error)
+ return
+ }
+
+ props.onOpenChange?.(false)
+ toast.success(
+ `${ocrRows.length}개의 OCR 데이터가 성공적으로 삭제되었습니다.`
+ )
+ onSuccess?.()
+ })
+ }
+
+ if (isDesktop) {
+ return (
+ <Dialog {...props}>
+ {showTrigger ? (
+ <DialogTrigger asChild>
+ <Button variant="destructive" size="sm">
+ <Trash className="mr-2 size-4" aria-hidden="true" />
+ 삭제 ({ocrRows.length})
+ </Button>
+ </DialogTrigger>
+ ) : null}
+ <DialogContent>
+ <DialogHeader>
+ <DialogTitle>정말로 삭제하시겠습니까?</DialogTitle>
+ <DialogDescription>
+ 이 작업은 되돌릴 수 없습니다. 선택한{" "}
+ <span className="font-medium">{ocrRows.length}</span>개의 OCR 데이터가
+ 서버에서 영구적으로 삭제됩니다.
+ </DialogDescription>
+ </DialogHeader>
+ <DialogFooter className="gap-2 sm:space-x-0">
+ <DialogClose asChild>
+ <Button variant="outline">취소</Button>
+ </DialogClose>
+ <Button
+ aria-label="선택된 행 삭제"
+ variant="destructive"
+ onClick={onDelete}
+ disabled={isDeletePending}
+ >
+ {isDeletePending && (
+ <Loader
+ className="mr-2 size-4 animate-spin"
+ aria-hidden="true"
+ />
+ )}
+ 삭제
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+ }
+
+ return (
+ <Drawer {...props}>
+ {showTrigger ? (
+ <DrawerTrigger asChild>
+ <Button variant="destructive" size="sm">
+ <Trash className="mr-2 size-4" aria-hidden="true" />
+ 삭제 ({ocrRows.length})
+ </Button>
+ </DrawerTrigger>
+ ) : null}
+ <DrawerContent>
+ <DrawerHeader>
+ <DrawerTitle>정말로 삭제하시겠습니까?</DrawerTitle>
+ <DrawerDescription>
+ 이 작업은 되돌릴 수 없습니다. 선택한{" "}
+ <span className="font-medium">{ocrRows.length}</span>개의 OCR 데이터가
+ 서버에서 영구적으로 삭제됩니다.
+ </DrawerDescription>
+ </DrawerHeader>
+ <DrawerFooter className="gap-2 sm:space-x-0">
+ <DrawerClose asChild>
+ <Button variant="outline">취소</Button>
+ </DrawerClose>
+ <Button
+ aria-label="선택된 행 삭제"
+ variant="destructive"
+ onClick={onDelete}
+ disabled={isDeletePending}
+ >
+ {isDeletePending && (
+ <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
+ )}
+ 삭제
+ </Button>
+ </DrawerFooter>
+ </DrawerContent>
+ </Drawer>
+ )
+} \ No newline at end of file
diff --git a/lib/welding/table/exporft-ocr-data.ts b/lib/welding/table/exporft-ocr-data.ts
index 76856808..729b1beb 100644
--- a/lib/welding/table/exporft-ocr-data.ts
+++ b/lib/welding/table/exporft-ocr-data.ts
@@ -19,6 +19,8 @@ export async function exportOcrDataToExcel(
// 컬럼 정의 (OCR 데이터에 맞게 설정)
const columns = [
+ { key: "fileName", header: "file Name", width: 20 },
+ { key: "inspectionDate", header: "inspection Date", width: 20 },
{ key: "reportNo", header: "Report No", width: 15 },
{ key: "no", header: "No", width: 10 },
{ key: "identificationNo", header: "Identification No", width: 20 },
@@ -52,6 +54,8 @@ export async function exportOcrDataToExcel(
reportNo: row.reportNo || "",
no: row.no || "",
identificationNo: row.identificationNo || "",
+ inspectionDate: row.inspectionDate ? new Date(row.inspectionDate).toLocaleDateString() : "",
+ fileName: row.fileName || "",
tagNo: row.tagNo || "",
jointNo: row.jointNo || "",
jointType: row.jointType || "",
diff --git a/lib/welding/table/ocr-table-columns.tsx b/lib/welding/table/ocr-table-columns.tsx
index d4ca9f5f..6413010d 100644
--- a/lib/welding/table/ocr-table-columns.tsx
+++ b/lib/welding/table/ocr-table-columns.tsx
@@ -346,14 +346,14 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<OcrRow>
<DropdownMenuSeparator />
- <DropdownMenuItem
+ {/* <DropdownMenuItem
onClick={() => {
setRowAction({ type: "delete", row })
}}
className="text-destructive focus:text-destructive"
>
Delete
- </DropdownMenuItem>
+ </DropdownMenuItem> */}
</DropdownMenuContent>
</DropdownMenu>
),
diff --git a/lib/welding/table/ocr-table-toolbar-actions.tsx b/lib/welding/table/ocr-table-toolbar-actions.tsx
index 03d8cab0..a6a38adc 100644
--- a/lib/welding/table/ocr-table-toolbar-actions.tsx
+++ b/lib/welding/table/ocr-table-toolbar-actions.tsx
@@ -2,7 +2,7 @@
import * as React from "react"
import { type Table } from "@tanstack/react-table"
-import { Download, RefreshCcw, Upload, FileText, Loader2, ChevronDown, X, Play, Pause, RotateCcw } from "lucide-react"
+import { Download, RefreshCcw, Upload, FileText, Loader2, ChevronDown, X, Play, Pause, RotateCcw, Trash } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
@@ -48,6 +48,7 @@ import {
} from "@/components/ui/file-list"
import { getOcrAllRows } from "../service"
import { exportOcrDataToExcel } from "./exporft-ocr-data"
+import { DeleteOcrRowsDialog } from "./delete-ocr-rows-dialog"
interface OcrTableToolbarActionsProps {
table: Table<OcrRow>
@@ -96,6 +97,9 @@ export function OcrTableToolbarActions({ table }: OcrTableToolbarActionsProps) {
const [isPaused, setIsPaused] = React.useState(false)
const batchControllerRef = React.useRef<AbortController | null>(null)
+ // 선택된 행들
+ const selectedRows = table.getFilteredSelectedRowModel().rows
+
// 단일 파일 업로드 다이얼로그 닫기 핸들러
const handleDialogOpenChange = (open: boolean) => {
if (!open) {
@@ -522,6 +526,14 @@ export function OcrTableToolbarActions({ table }: OcrTableToolbarActionsProps) {
}
}
+ // 삭제 후 콜백 - 테이블 새로고침
+ const handleDeleteSuccess = () => {
+ // 선택 해제
+ table.resetRowSelection()
+ // 페이지 새로고침
+ window.location.reload()
+ }
+
const getStatusBadgeVariant = (status: FileUploadItem['status']) => {
switch (status) {
case 'pending': return 'secondary'
@@ -554,6 +566,14 @@ export function OcrTableToolbarActions({ table }: OcrTableToolbarActionsProps) {
return (
<div className="flex items-center gap-2">
+ {/* 선택된 행이 있을 때만 삭제 버튼 표시 */}
+ {selectedRows.length > 0 && (
+ <DeleteOcrRowsDialog
+ ocrRows={selectedRows}
+ onSuccess={handleDeleteSuccess}
+ />
+ )}
+
{/* 단일 파일 OCR 업로드 다이얼로그 */}
<Dialog open={isUploadDialogOpen} onOpenChange={handleDialogOpenChange}>
<DialogTrigger asChild>
diff --git a/lib/welding/table/ocr-table.tsx b/lib/welding/table/ocr-table.tsx
index e14c53d1..443f5a6b 100644
--- a/lib/welding/table/ocr-table.tsx
+++ b/lib/welding/table/ocr-table.tsx
@@ -70,6 +70,18 @@ export function OcrTable({ promises }: ItemsTableProps) {
// group: "Basic Info",
},
{
+ id: "fileName",
+ label: "file Name",
+ type: "text",
+ // group: "Basic Info",
+ },
+ {
+ id: "reportNo",
+ label: "report No",
+ type: "text",
+ // group: "Basic Info",
+ },
+ {
id: "no",
label: "No",
type: "text",