summaryrefslogtreecommitdiff
path: root/lib/compliance/responses
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compliance/responses')
-rw-r--r--lib/compliance/responses/compliance-response-stats.tsx97
-rw-r--r--lib/compliance/responses/compliance-responses-columns.tsx189
-rw-r--r--lib/compliance/responses/compliance-responses-list.tsx141
-rw-r--r--lib/compliance/responses/compliance-responses-page-client.tsx62
-rw-r--r--lib/compliance/responses/compliance-responses-table.tsx141
-rw-r--r--lib/compliance/responses/compliance-responses-toolbar.tsx69
6 files changed, 699 insertions, 0 deletions
diff --git a/lib/compliance/responses/compliance-response-stats.tsx b/lib/compliance/responses/compliance-response-stats.tsx
new file mode 100644
index 00000000..dace0505
--- /dev/null
+++ b/lib/compliance/responses/compliance-response-stats.tsx
@@ -0,0 +1,97 @@
+"use client";
+
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Clock, CheckCircle, Eye, FileText } from "lucide-react";
+
+interface ComplianceResponseStatsProps {
+ stats: {
+ inProgress: number;
+ completed: number;
+ reviewed: number;
+ total: number;
+ };
+ onFilterChange?: (filter: 'all' | 'IN_PROGRESS' | 'COMPLETED' | 'REVIEWED') => void;
+ currentFilter?: string;
+}
+
+export function ComplianceResponseStats({ stats, onFilterChange, currentFilter }: ComplianceResponseStatsProps) {
+ return (
+ <div className="grid gap-4 md:grid-cols-4">
+ {/* 전체 응답 */}
+ <Card
+ className={`cursor-pointer hover:shadow-md transition-shadow ${
+ currentFilter === 'all' ? 'ring-2 ring-blue-500' : ''
+ }`}
+ onClick={() => onFilterChange?.('all')}
+ >
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
+ <CardTitle className="text-sm font-medium">전체 응답</CardTitle>
+ <FileText className="h-3 w-3 text-muted-foreground" />
+ </CardHeader>
+ <CardContent>
+ <div className="text-2xl font-bold">{stats.total}</div>
+ <p className="text-xs text-muted-foreground">
+ 총 {stats.total}개 응답
+ </p>
+ </CardContent>
+ </Card>
+
+ {/* 진행중 */}
+ <Card
+ className={`cursor-pointer hover:shadow-md transition-shadow ${
+ currentFilter === 'IN_PROGRESS' ? 'ring-2 ring-orange-500' : ''
+ }`}
+ onClick={() => onFilterChange?.('IN_PROGRESS')}
+ >
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
+ <CardTitle className="text-sm font-medium">진행중</CardTitle>
+ <Clock className="h-4 w-4 text-orange-500" />
+ </CardHeader>
+ <CardContent>
+ <div className="text-2xl font-bold text-orange-500">{stats.inProgress}</div>
+ <p className="text-xs text-muted-foreground">
+ 작성 중인 응답
+ </p>
+ </CardContent>
+ </Card>
+
+ {/* 제출완료 */}
+ <Card
+ className={`cursor-pointer hover:shadow-md transition-shadow ${
+ currentFilter === 'COMPLETED' ? 'ring-2 ring-green-500' : ''
+ }`}
+ onClick={() => onFilterChange?.('COMPLETED')}
+ >
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
+ <CardTitle className="text-sm font-medium">제출완료</CardTitle>
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ </CardHeader>
+ <CardContent>
+ <div className="text-2xl font-bold text-green-500">{stats.completed}</div>
+ <p className="text-xs text-muted-foreground">
+ 제출 완료된 응답
+ </p>
+ </CardContent>
+ </Card>
+
+ {/* 검토완료 */}
+ <Card
+ className={`cursor-pointer hover:shadow-md transition-shadow ${
+ currentFilter === 'REVIEWED' ? 'ring-2 ring-blue-500' : ''
+ }`}
+ onClick={() => onFilterChange?.('REVIEWED')}
+ >
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
+ <CardTitle className="text-sm font-medium">검토완료</CardTitle>
+ <Eye className="h-4 w-4 text-blue-500" />
+ </CardHeader>
+ <CardContent>
+ <div className="text-2xl font-bold text-blue-500">{stats.reviewed}</div>
+ <p className="text-xs text-muted-foreground">
+ 검토 완료된 응답
+ </p>
+ </CardContent>
+ </Card>
+ </div>
+ );
+}
diff --git a/lib/compliance/responses/compliance-responses-columns.tsx b/lib/compliance/responses/compliance-responses-columns.tsx
new file mode 100644
index 00000000..c9596ae5
--- /dev/null
+++ b/lib/compliance/responses/compliance-responses-columns.tsx
@@ -0,0 +1,189 @@
+"use client";
+
+import * as React from "react";
+import { type ColumnDef } from "@tanstack/react-table";
+import { format } from "date-fns";
+import { ko } from "date-fns/locale";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { Checkbox } from "@/components/ui/checkbox";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+ DropdownMenuShortcut,
+} from "@/components/ui/dropdown-menu";
+import { MoreHorizontal, Eye, Download, Trash2 } from "lucide-react";
+import type { DataTableRowAction } from "@/types/table";
+import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header";
+
+interface GetResponseColumnsProps {
+ setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<any> | null>>;
+}
+
+export function getResponseColumns({ setRowAction }: GetResponseColumnsProps): ColumnDef<any>[] {
+ // ----------------------------------------------------------------
+ // 1) select 컬럼 (체크박스)
+ // ----------------------------------------------------------------
+ const selectColumn: ColumnDef<any> = {
+ 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"
+ />
+ ),
+ size: 40,
+ enableSorting: false,
+ enableHiding: false,
+ };
+
+ // ----------------------------------------------------------------
+ // 2) actions 컬럼 (Dropdown 메뉴)
+ // ----------------------------------------------------------------
+ const actionsColumn: ColumnDef<any> = {
+ id: "actions",
+ header: "작업",
+ enableHiding: false,
+ cell: ({ row }) => {
+ const response = row.original;
+
+ return (
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Button variant="ghost" className="h-8 w-8 p-0">
+ <span className="sr-only">메뉴 열기</span>
+ <MoreHorizontal className="h-4 w-4" />
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent align="end">
+ <DropdownMenuItem onClick={() => window.location.href = `/evcp/compliance/${response.templateId}/responses/${response.id}`}>
+ <Eye className="mr-2 h-4 w-4" />
+ Detail
+ </DropdownMenuItem>
+ <DropdownMenuItem onClick={() => setRowAction({ type: 'delete', row: row })}>
+ <Trash2 className="mr-2 h-4 w-4" />
+ Delete
+ <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ );
+ },
+ size: 40,
+ };
+
+ // ----------------------------------------------------------------
+ // 3) 일반 컬럼들 (정렬 가능)
+ // ----------------------------------------------------------------
+ const dataColumns: ColumnDef<any>[] = [
+ {
+ accessorKey: "templateName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="템플릿명" />
+ ),
+ cell: ({ row }) => (
+ <div className="font-medium">{row.getValue("templateName")}</div>
+ ),
+ enableResizing: true,
+ },
+ {
+ accessorKey: "vendorId",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Vendor ID" />
+ ),
+ cell: ({ row }) => (
+ <div className="font-medium">{row.getValue("vendorId") || '-'}</div>
+ ),
+ enableResizing: true,
+ },
+ {
+ accessorKey: "vendorName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="업체명" />
+ ),
+ cell: ({ row }) => (
+ <div className="font-medium">{row.getValue("vendorName") || '-'}</div>
+ ),
+ enableResizing: true,
+ },
+ {
+ accessorKey: "contractName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="계약서명" />
+ ),
+ cell: ({ row }) => (
+ <div className="font-medium">{row.getValue("contractName") || '-'}</div>
+ ),
+ enableResizing: true,
+ },
+ {
+ accessorKey: "status",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="상태" />
+ ),
+ cell: ({ row }) => {
+ const status = row.getValue("status") as string;
+ const getStatusBadge = (status: string) => {
+ switch (status) {
+ case "COMPLETED":
+ return <Badge variant="default">제출완료</Badge>;
+ case "IN_PROGRESS":
+ return <Badge variant="secondary">진행중</Badge>;
+ case "REVIEWED":
+ return <Badge variant="outline">검토완료</Badge>;
+ default:
+ return <Badge variant="secondary">{status}</Badge>;
+ }
+ };
+ return getStatusBadge(status);
+ },
+ enableResizing: true,
+ },
+ {
+ accessorKey: "reviewerName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="검토자" />
+ ),
+ cell: ({ row }) => {
+ const reviewerName = row.getValue("reviewerName") as string;
+ return reviewerName || '-';
+ },
+ enableResizing: true,
+ },
+ {
+ accessorKey: "reviewedAt",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="검토일시" />
+ ),
+ cell: ({ row }) => {
+ const date = row.getValue("reviewedAt") as Date;
+ return date ? format(new Date(date), 'yyyy-MM-dd HH:mm', { locale: ko }) : '-';
+ },
+ enableResizing: true,
+ },
+ ];
+
+ // ----------------------------------------------------------------
+ // 4) 최종 컬럼 배열: select, dataColumns, actions
+ // ----------------------------------------------------------------
+ return [
+ selectColumn,
+ ...dataColumns,
+ actionsColumn,
+ ];
+}
diff --git a/lib/compliance/responses/compliance-responses-list.tsx b/lib/compliance/responses/compliance-responses-list.tsx
new file mode 100644
index 00000000..cfa934ec
--- /dev/null
+++ b/lib/compliance/responses/compliance-responses-list.tsx
@@ -0,0 +1,141 @@
+"use client";
+
+import { format } from "date-fns";
+import { ko } from "date-fns/locale";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { Eye, Download } from "lucide-react";
+import { useRouter } from "next/navigation";
+import { complianceSurveyTemplates } from "@/db/schema/compliance";
+
+interface ComplianceResponsesListProps {
+ template: typeof complianceSurveyTemplates.$inferSelect;
+ responses: Array<{
+ id: number;
+ basicContractId: number;
+ templateId: number;
+ status: string;
+ completedAt: Date | null;
+ reviewedBy: number | null;
+ reviewedAt: Date | null;
+ reviewNotes: string | null;
+ createdAt: Date;
+ updatedAt: Date;
+ answersCount: number;
+ }>;
+}
+
+export function ComplianceResponsesList({ template, responses }: ComplianceResponsesListProps) {
+ const router = useRouter();
+
+ const getStatusBadge = (status: string) => {
+ switch (status) {
+ case "COMPLETED":
+ return <Badge variant="default">완료</Badge>;
+ case "IN_PROGRESS":
+ return <Badge variant="secondary">진행중</Badge>;
+ case "REVIEWED":
+ return <Badge variant="outline">검토완료</Badge>;
+ default:
+ return <Badge variant="secondary">{status}</Badge>;
+ }
+ };
+
+ if (responses.length === 0) {
+ return (
+ <div className="text-center py-8">
+ <p className="text-muted-foreground">아직 응답이 없습니다.</p>
+ </div>
+ );
+ }
+
+ return (
+ <div className="space-y-4">
+ <div className="rounded-md border">
+ <Table>
+ <TableHeader>
+ <TableRow>
+ <TableHead>응답 ID</TableHead>
+ <TableHead>계약 ID</TableHead>
+ <TableHead>상태</TableHead>
+ <TableHead>답변 수</TableHead>
+ <TableHead>완료일</TableHead>
+ <TableHead>검토일</TableHead>
+ <TableHead>생성일</TableHead>
+ <TableHead>작업</TableHead>
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {responses.map((response) => (
+ <TableRow key={response.id}>
+ <TableCell className="font-medium">
+ #{response.id}
+ </TableCell>
+ <TableCell>
+ {response.basicContractId}
+ </TableCell>
+ <TableCell>
+ {getStatusBadge(response.status)}
+ </TableCell>
+ <TableCell>
+ <Badge variant="outline">{response.answersCount}개</Badge>
+ </TableCell>
+ <TableCell>
+ {response.completedAt
+ ? format(new Date(response.completedAt), 'yyyy-MM-dd HH:mm', { locale: ko })
+ : "-"
+ }
+ </TableCell>
+ <TableCell>
+ {response.reviewedAt
+ ? format(new Date(response.reviewedAt), 'yyyy-MM-dd HH:mm', { locale: ko })
+ : "-"
+ }
+ </TableCell>
+ <TableCell>
+ {response.createdAt
+ ? format(new Date(response.createdAt), 'yyyy-MM-dd HH:mm', { locale: ko })
+ : "-"
+ }
+ </TableCell>
+ <TableCell>
+ <div className="flex space-x-2">
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => router.push(`/evcp/compliance/${template.id}/responses/${response.id}`)}
+ >
+ <Eye className="h-4 w-4 mr-1" />
+ 상세보기
+ </Button>
+ {response.status === "COMPLETED" && (
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => {
+ // TODO: 응답 다운로드 기능 구현
+ console.log("Download response:", response.id);
+ }}
+ >
+ <Download className="h-4 w-4 mr-1" />
+ 다운로드
+ </Button>
+ )}
+ </div>
+ </TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+ );
+}
diff --git a/lib/compliance/responses/compliance-responses-page-client.tsx b/lib/compliance/responses/compliance-responses-page-client.tsx
new file mode 100644
index 00000000..758d9ed7
--- /dev/null
+++ b/lib/compliance/responses/compliance-responses-page-client.tsx
@@ -0,0 +1,62 @@
+"use client";
+
+import * as React from "react";
+import { getComplianceResponsesWithPagination } from "@/lib/compliance/services";
+import { ComplianceResponsesTable } from "./compliance-responses-table";
+import { ComplianceResponseStats } from "./compliance-response-stats";
+
+interface ComplianceResponsesPageClientProps {
+ templateId: number;
+ promises?: Promise<[{ data: any[]; pageCount: number }, any]>;
+ isInfiniteMode: boolean;
+}
+
+export function ComplianceResponsesPageClient({
+ templateId,
+ promises,
+ isInfiniteMode
+}: ComplianceResponsesPageClientProps) {
+ // 페이지네이션 모드 데이터
+ const paginationData = promises ? React.use(promises) : null;
+ const responses = paginationData ? paginationData[0] : { data: [], pageCount: 0 };
+ const stats = paginationData ? paginationData[1] : {
+ inProgress: 0,
+ completed: 0,
+ reviewed: 0,
+ total: 0,
+ };
+
+ const [statusFilter, setStatusFilter] = React.useState<'all' | 'IN_PROGRESS' | 'COMPLETED' | 'REVIEWED'>('all');
+
+ // 필터링된 데이터
+ const filteredData = React.useMemo(() => {
+ if (statusFilter === 'all') {
+ return responses.data;
+ }
+ return responses.data.filter(item => item.status === statusFilter);
+ }, [responses.data, statusFilter]);
+
+ // 통계 카드 클릭 핸들러
+ const handleFilterChange = (filter: 'all' | 'IN_PROGRESS' | 'COMPLETED' | 'REVIEWED') => {
+ setStatusFilter(filter);
+ };
+
+ return (
+ <>
+ {/* 응답 통계 카드 */}
+ <div className="mb-6">
+ <ComplianceResponseStats
+ stats={stats}
+ onFilterChange={handleFilterChange}
+ currentFilter={statusFilter}
+ />
+ </div>
+
+ {/* 응답 테이블 */}
+ <ComplianceResponsesTable
+ templateId={templateId}
+ promises={Promise.resolve([{ data: filteredData, pageCount: Math.ceil(filteredData.length / 10) }])}
+ />
+ </>
+ );
+}
diff --git a/lib/compliance/responses/compliance-responses-table.tsx b/lib/compliance/responses/compliance-responses-table.tsx
new file mode 100644
index 00000000..e4292719
--- /dev/null
+++ b/lib/compliance/responses/compliance-responses-table.tsx
@@ -0,0 +1,141 @@
+"use client";
+
+import * as React from "react";
+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 {
+ DataTableAdvancedFilterField,
+ DataTableRowAction,
+ DataTableFilterField,
+} from "@/types/table"
+import { getComplianceResponsesWithPagination } from "../services";
+import { getResponseColumns } from "./compliance-responses-columns";
+import { ComplianceResponsesToolbarActions } from "./compliance-responses-toolbar";
+
+interface ComplianceResponsesTableProps {
+ templateId: number;
+ promises?: Promise<[{ data: any[]; pageCount: number }]>;
+}
+
+export function ComplianceResponsesTable({ templateId, promises }: ComplianceResponsesTableProps) {
+ // 페이지네이션 모드 데이터
+ const paginationData = promises ? React.use(promises) : null;
+ const [{ data: initialData = [], pageCount = 0 }] = paginationData || [{ data: [], pageCount: 0 }];
+
+ const [rowAction, setRowAction] = React.useState<DataTableRowAction<any> | null>(null);
+ const [data, setData] = React.useState(initialData);
+ const [currentSorting, setCurrentSorting] = React.useState<{ id: string; desc: boolean }[]>([]);
+
+ // 초기 데이터가 변경되면 data 상태 업데이트
+ React.useEffect(() => {
+ setData(initialData);
+ }, [initialData]);
+
+ // 컬럼 설정 - 외부 파일에서 가져옴
+ const columns = React.useMemo(
+ () => getResponseColumns({ setRowAction }),
+ [setRowAction]
+ )
+
+ // 기본 필터 필드 설정
+ const filterFields: DataTableFilterField<any>[] = [
+ {
+ id: "status",
+ label: "상태",
+ options: [
+ { label: "진행중", value: "IN_PROGRESS" },
+ { label: "완료", value: "COMPLETED" },
+ { label: "검토완료", value: "REVIEWED" },
+ ],
+ },
+ ];
+
+ // 고급 필터 필드 설정
+ const advancedFilterFields: DataTableAdvancedFilterField<any>[] = [
+ { id: "vendorId", label: "Vendor ID", type: "text" },
+ { id: "vendorName", label: "업체명", type: "text" },
+ { id: "contractName", label: "계약서명", type: "text" },
+ {
+ id: "status", label: "상태", type: "select", options: [
+ { label: "진행중", value: "IN_PROGRESS" },
+ { label: "완료", value: "COMPLETED" },
+ { label: "검토완료", value: "REVIEWED" },
+ ]
+ },
+ { id: "answersCount", label: "답변 수", type: "text" },
+ { id: "createdAt", label: "생성일", type: "date" },
+ { id: "completedAt", label: "완료일", type: "date" },
+ ];
+
+ const { table } = useDataTable({
+ data,
+ columns,
+ pageCount,
+ filterFields,
+ enablePinning: true,
+ enableAdvancedFilter: true,
+ enableRowSelection: true,
+ initialState: {
+ sorting: [{ id: "createdAt", desc: true }],
+ columnPinning: { right: ["actions"] },
+ },
+ getRowId: (originalRow) => String(originalRow.id),
+ shallow: false,
+ clearOnDefault: true,
+ })
+
+ // 정렬 상태 변경 감지
+ React.useEffect(() => {
+ const newSorting = table.getState().sorting;
+ if (JSON.stringify(newSorting) !== JSON.stringify(currentSorting)) {
+ setCurrentSorting(newSorting);
+ }
+ }, [table.getState().sorting, currentSorting]);
+
+ // 정렬이 변경될 때 데이터 다시 로드 (응답 데이터는 클라이언트 사이드 정렬)
+ React.useEffect(() => {
+ if (currentSorting && currentSorting.length > 0) {
+ const sortedData = [...initialData].sort((a, b) => {
+ for (const sort of currentSorting) {
+ const aValue = a[sort.id];
+ const bValue = b[sort.id];
+
+ if (aValue === bValue) continue;
+
+ if (aValue === null || aValue === undefined) return 1;
+ if (bValue === null || bValue === undefined) return -1;
+
+ if (typeof aValue === 'string' && typeof bValue === 'string') {
+ return sort.desc ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue);
+ }
+
+ if (aValue instanceof Date && bValue instanceof Date) {
+ return sort.desc ? bValue.getTime() - aValue.getTime() : aValue.getTime() - bValue.getTime();
+ }
+
+ return sort.desc ? (bValue > aValue ? 1 : -1) : (aValue > bValue ? 1 : -1);
+ }
+ return 0;
+ });
+
+ setData(sortedData);
+ } else {
+ setData(initialData);
+ }
+ }, [currentSorting, initialData]);
+
+ return (
+ <>
+ <DataTable table={table}>
+ <DataTableAdvancedToolbar
+ table={table}
+ filterFields={advancedFilterFields}
+ shallow={false}
+ >
+ <ComplianceResponsesToolbarActions table={table} />
+ </DataTableAdvancedToolbar>
+ </DataTable>
+ </>
+ );
+}
diff --git a/lib/compliance/responses/compliance-responses-toolbar.tsx b/lib/compliance/responses/compliance-responses-toolbar.tsx
new file mode 100644
index 00000000..26755aee
--- /dev/null
+++ b/lib/compliance/responses/compliance-responses-toolbar.tsx
@@ -0,0 +1,69 @@
+"use client";
+
+import * as React from "react";
+import { Button } from "@/components/ui/button";
+import { Download, Trash2 } from "lucide-react";
+import { toast } from "sonner";
+import type { Table } from "@tanstack/react-table";
+
+interface ComplianceResponsesToolbarActionsProps<TData> {
+ table: Table<TData>;
+}
+
+export function ComplianceResponsesToolbarActions<TData>({
+ table,
+}: ComplianceResponsesToolbarActionsProps<TData>) {
+ const selectedRows = table.getFilteredSelectedRowModel().rows;
+
+ const handleDeleteSelected = async () => {
+ if (selectedRows.length === 0) {
+ toast.error("삭제할 응답을 선택해주세요.");
+ return;
+ }
+
+ // TODO: 선택된 응답들 삭제 기능 구현
+ console.log("Delete selected responses:", selectedRows.map(row => row.original));
+ toast.success(`${selectedRows.length}개의 응답이 삭제되었습니다.`);
+
+ // 페이지 새로고침으로 데이터 업데이트
+ window.location.reload();
+ };
+
+ const handleExport = () => {
+ if (selectedRows.length === 0) {
+ toast.error("내보낼 응답을 선택해주세요.");
+ return;
+ }
+
+ // TODO: 선택된 응답들 내보내기 기능 구현
+ console.log("Export selected responses:", selectedRows.map(row => row.original));
+ toast.success(`${selectedRows.length}개의 응답이 내보내졌습니다.`);
+ };
+
+ return (
+ <div className="flex items-center gap-2">
+ {selectedRows.length > 0 && (
+ <>
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleExport}
+ className="h-8"
+ >
+ <Download className="mr-2 h-4 w-4" />
+ 내보내기 ({selectedRows.length})
+ </Button>
+ <Button
+ variant="destructive"
+ size="sm"
+ onClick={handleDeleteSelected}
+ className="h-8"
+ >
+ <Trash2 className="mr-2 h-4 w-4" />
+ Delete ({selectedRows.length})
+ </Button>
+ </>
+ )}
+ </div>
+ );
+}