diff options
Diffstat (limited to 'lib/email-template/table/template-table-toolbar-actions.tsx')
| -rw-r--r-- | lib/email-template/table/template-table-toolbar-actions.tsx | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/lib/email-template/table/template-table-toolbar-actions.tsx b/lib/email-template/table/template-table-toolbar-actions.tsx new file mode 100644 index 00000000..a7e107c6 --- /dev/null +++ b/lib/email-template/table/template-table-toolbar-actions.tsx @@ -0,0 +1,115 @@ +"use client" + +import * as React from "react" +import { type Table } from "@tanstack/react-table" +import { Download, Plus, Trash } from "lucide-react" + +import { Button } from "@/components/ui/button" +import { type TemplateListView } from "@/db/schema/template-views" +import { DeleteTemplateDialog } from "./delete-template-dialog" +import { toast } from "sonner" + +interface TemplateTableToolbarActionsProps { + table: Table<TemplateListView> + onCreateTemplate: () => void +} + +export function TemplateTableToolbarActions({ + table, + onCreateTemplate, +}: TemplateTableToolbarActionsProps) { + const [showDeleteDialog, setShowDeleteDialog] = React.useState(false) + + const selectedRows = table.getFilteredSelectedRowModel().rows + const selectedTemplates = selectedRows.map(row => row.original) + + // CSV 내보내기 함수 + const exportToCsv = React.useCallback(() => { + const headers = ['이름', 'Slug', '카테고리', '변수 개수', '버전', '생성일', '수정일'] + const csvData = [ + headers, + ...table.getFilteredRowModel().rows.map(row => { + const template = row.original + return [ + template.name, + template.slug, + template.categoryDisplayName || '미분류', + template.variableCount.toString(), + template.version.toString(), + new Date(template.createdAt).toLocaleDateString('ko-KR'), + new Date(template.updatedAt).toLocaleDateString('ko-KR'), + ] + }) + ] + + const csvContent = csvData.map(row => + row.map(field => `"${field}"`).join(',') + ).join('\n') + + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }) + const link = document.createElement('a') + + if (link.download !== undefined) { + const url = URL.createObjectURL(blob) + link.setAttribute('href', url) + link.setAttribute('download', `templates_${new Date().toISOString().split('T')[0]}.csv`) + link.style.visibility = 'hidden' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + } + + toast.success('템플릿 목록이 CSV로 내보내졌습니다.') + }, [table]) + + return ( + <div className="flex items-center gap-2"> + {/* 새 템플릿 생성 버튼 */} + <Button + variant="default" + size="sm" + onClick={onCreateTemplate} + > + <Plus className="mr-2 size-4" aria-hidden="true" /> + 새 템플릿 + </Button> + + {/* CSV 내보내기 버튼 */} + <Button + variant="outline" + size="sm" + onClick={exportToCsv} + > + <Download className="mr-2 size-4" aria-hidden="true" /> + 내보내기 + </Button> + + {/* 선택된 항목 일괄 삭제 버튼 */} + {selectedTemplates.length > 0 && ( + <> + <Button + variant="outline" + size="sm" + onClick={() => setShowDeleteDialog(true)} + className="text-destructive hover:text-destructive" + > + <Trash className="mr-2 size-4" aria-hidden="true" /> + 삭제 ({selectedTemplates.length}) + </Button> + + {/* 일괄 삭제 Dialog */} + <DeleteTemplateDialog + open={showDeleteDialog} + onOpenChange={setShowDeleteDialog} + templates={selectedTemplates} + showTrigger={false} + onSuccess={() => { + table.toggleAllRowsSelected(false) + setShowDeleteDialog(false) + }} + /> + </> + )} + </div> + ) +}
\ No newline at end of file |
