1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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>
)
}
|