{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table-12",
  "type": "registry:component",
  "title": "DataTable 12",
  "description": "DataTable 12. A component for displaying and managing tabular data.",
  "dependencies": [
    "lucide-react",
    "papaparse",
    "write-excel-file"
  ],
  "registryDependencies": [
    "badge",
    "checkbox",
    "dropdown-menu",
    "input",
    "table"
  ],
  "files": [
    {
      "path": "components/watermelon/data-table-12.tsx",
      "type": "registry:component",
      "content": "'use client'\n\nimport { useMemo, useState } from 'react'\n\nimport { DownloadIcon, FileSpreadsheetIcon, FileTextIcon } from 'lucide-react'\n\nimport Papa from 'papaparse'\nimport writeExcelFile from 'write-excel-file/browser'\n\nimport { Badge } from '@/components/base-ui/badge'\nimport { Checkbox } from '@/components/base-ui/checkbox'\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger\n} from '@/components/base-ui/dropdown-menu'\nimport { Input } from '@/components/base-ui/input'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/base-ui/table'\n\ntype PaymentStatus = 'failed' | 'processing' | 'success'\n\ntype Payment = {\n  amount: number\n  email: string\n  id: string\n  name: string\n  status: PaymentStatus\n}\n\nconst data: readonly Payment[] = [\n  {\n    id: 'PAY-101',\n    name: 'Shang Chain',\n    amount: 699,\n    status: 'success',\n    email: 'shang07@yahoo.com'\n  },\n  {\n    id: 'PAY-102',\n    name: 'Kevin Lincoln',\n    amount: 242,\n    status: 'success',\n    email: 'kevinli09@gmail.com'\n  },\n  {\n    id: 'PAY-103',\n    name: 'Milton Rose',\n    amount: 655,\n    status: 'processing',\n    email: 'rose96@gmail.com'\n  },\n  {\n    id: 'PAY-104',\n    name: 'Silas Ryan',\n    amount: 874,\n    status: 'success',\n    email: 'silas22@gmail.com'\n  },\n  {\n    id: 'PAY-105',\n    name: 'Ben Tenison',\n    amount: 541,\n    status: 'failed',\n    email: 'bent@hotmail.com'\n  },\n  {\n    id: 'PAY-106',\n    name: 'Alice Cooper',\n    amount: 321,\n    status: 'processing',\n    email: 'alice@email.com'\n  },\n  {\n    id: 'PAY-107',\n    name: 'Bob Johnson',\n    amount: 789,\n    status: 'success',\n    email: 'bob.j@company.com'\n  },\n  {\n    id: 'PAY-108',\n    name: 'Carol Williams',\n    amount: 456,\n    status: 'processing',\n    email: 'carol.w@domain.org'\n  }\n] as const\n\nconst availabilityBadgeClass: Record<PaymentStatus, string> = {\n  success: 'border-none bg-green-600/10 text-green-600 dark:bg-green-400/10 dark:text-green-400',\n  failed: 'border-none bg-destructive/10 text-destructive dark:bg-destructive/20',\n  processing: 'border-none bg-amber-600/10 text-amber-600 dark:bg-amber-400/10 dark:text-amber-400'\n}\n\nconst formatCurrency = (amount: number) =>\n  new Intl.NumberFormat('en-US', {\n    style: 'currency',\n    currency: 'USD'\n  }).format(amount)\n\nconst DataTable12 = () => {\n  const [searchQuery, setSearchQuery] = useState('')\n  const [selectedIds, setSelectedIds] = useState<string[]>([])\n\n  const filteredData = useMemo(() => {\n    const query = searchQuery.trim().toLowerCase()\n\n    if (!query) {\n      return data\n    }\n\n    return data.filter((item) =>\n      [item.name, item.email, item.status, String(item.amount)].some((value) => value.toLowerCase().includes(query))\n    )\n  }, [searchQuery])\n\n  const allSelected = filteredData.length > 0 && filteredData.every((item) => selectedIds.includes(item.id))\n  const someSelected = filteredData.some((item) => selectedIds.includes(item.id)) && !allSelected\n\n  const selectedRows = data.filter((item) => selectedIds.includes(item.id))\n\n  const exportRows = (): Payment[] => [...(selectedRows.length > 0 ? selectedRows : filteredData)]\n\n  const toggleAll = (checked: boolean) => {\n    if (checked) {\n      setSelectedIds((current) => Array.from(new Set([...current, ...filteredData.map((item) => item.id)])))\n      return\n    }\n\n    setSelectedIds((current) => current.filter((id) => !filteredData.some((item) => item.id === id)))\n  }\n\n  const toggleRow = (id: string, checked: boolean) => {\n    setSelectedIds((current) => {\n      if (checked) {\n        return current.includes(id) ? current : [...current, id]\n      }\n\n      return current.filter((item) => item !== id)\n    })\n  }\n\n  const downloadBlob = (blob: Blob, filename: string) => {\n    const link = document.createElement('a')\n    const url = URL.createObjectURL(blob)\n\n    link.setAttribute('href', url)\n    link.setAttribute('download', filename)\n    link.style.visibility = 'hidden'\n    document.body.appendChild(link)\n    link.click()\n    document.body.removeChild(link)\n    URL.revokeObjectURL(url)\n  }\n\n  const exportToCSV = () => {\n    const csv = Papa.unparse(exportRows(), { header: true })\n    downloadBlob(new Blob([csv], { type: 'text/csv;charset=utf-8;' }), `payments-export-${new Date().toISOString().split('T')[0]}.csv`)\n  }\n\n  const exportToExcel = async () => {\n    const rows = exportRows()\n    const sheetData = [\n      [\n        { value: 'ID', fontWeight: 'bold' as const },\n        { value: 'Name', fontWeight: 'bold' as const },\n        { value: 'Status', fontWeight: 'bold' as const },\n        { value: 'Email', fontWeight: 'bold' as const },\n        { value: 'Amount', fontWeight: 'bold' as const }\n      ],\n      ...rows.map((payment) => [\n        payment.id,\n        payment.name,\n        payment.status,\n        payment.email,\n        { value: payment.amount, format: '$#,##0.00' }\n      ])\n    ]\n\n    await writeExcelFile(sheetData, {\n      columns: [\n        { width: 14 },\n        { width: 22 },\n        { width: 16 },\n        { width: 30 },\n        { width: 16 }\n      ],\n      sheet: 'Payments'\n    }).toFile(`payments-export-${new Date().toISOString().split('T')[0]}.xlsx`)\n  }\n\n  const exportToJSON = () => {\n    const json = JSON.stringify(exportRows(), null, 2)\n    downloadBlob(new Blob([json], { type: 'application/json' }), `payments-export-${new Date().toISOString().split('T')[0]}.json`)\n  }\n\n  return (\n    <div className='w-60 sm:w-100 md:w-120 lg:w-160 2xl:w-220 mx-auto'>\n      <div className='flex justify-between gap-2 pb-4 max-sm:flex-col sm:items-center'>\n        <div className='flex items-center space-x-2'>\n          <Input\n            placeholder='Search all columns...'\n            value={searchQuery}\n            onChange={(event) => setSearchQuery(event.target.value)}\n            className='h-10 max-w-sm rounded-lg border-border/60 bg-muted/20 px-4'\n          />\n        </div>\n        <div className='flex items-center sm:space-x-2'>\n          <div className='text-muted-foreground text-sm'>\n            {selectedRows.length > 0 ? (\n              <span className='mr-2'>\n                {selectedRows.length} of {filteredData.length} row(s) selected\n              </span>\n            ) : null}\n          </div>\n          <DropdownMenu>\n            <DropdownMenuTrigger className='inline-flex h-10 min-w-28 items-center justify-center rounded-md border border-border/60 bg-background px-3 py-2 text-sm font-medium text-foreground outline-none transition-colors hover:bg-muted/20'>\n              <DownloadIcon className='mr-2 size-4' />\n              Export\n            </DropdownMenuTrigger>\n            <DropdownMenuContent align='end' className='w-52'>\n              <DropdownMenuItem onClick={exportToCSV} className='whitespace-nowrap'>\n                <FileTextIcon className='mr-2 size-4' />\n                Export as CSV\n              </DropdownMenuItem>\n              <DropdownMenuItem onClick={exportToExcel} className='whitespace-nowrap'>\n                <FileSpreadsheetIcon className='mr-2 size-4' />\n                Export as Excel\n              </DropdownMenuItem>\n              <DropdownMenuSeparator />\n              <DropdownMenuItem onClick={exportToJSON} className='whitespace-nowrap'>\n                <FileTextIcon className='mr-2 size-4' />\n                Export as JSON\n              </DropdownMenuItem>\n            </DropdownMenuContent>\n          </DropdownMenu>\n        </div>\n      </div>\n      <div className='overflow-hidden rounded-xl border border-border/60 bg-background shadow-sm'>\n        <Table className='w-60 sm:w-100 md:w-120 lg:w-160 2xl:w-220 mx-auto'>\n          <TableHeader>\n            <TableRow>\n              <TableHead className='h-12 w-10 bg-muted/20 font-medium'>\n                <Checkbox\n                  checked={allSelected}\n                  aria-checked={someSelected ? 'mixed' : allSelected}\n                  onCheckedChange={(value) => toggleAll(!!value)}\n                  aria-label='Select all filtered rows'\n                  className='after:hidden data-checked:border-sky-600 data-checked:bg-sky-600 data-checked:text-white dark:data-checked:border-sky-500 dark:data-checked:bg-sky-500 dark:data-checked:text-white'\n                />\n              </TableHead>\n              <TableHead className='h-12 bg-muted/20 text-[13px] font-medium tracking-[0.08em] text-muted-foreground'>\n                Name\n              </TableHead>\n              <TableHead className='h-12 bg-muted/20 text-[13px] font-medium tracking-[0.08em] text-muted-foreground'>\n                Status\n              </TableHead>\n              <TableHead className='h-12 bg-muted/20 text-[13px] font-medium tracking-[0.08em] text-muted-foreground'>\n                Email\n              </TableHead>\n              <TableHead className='h-12 bg-muted/20 text-right text-[13px] font-medium tracking-[0.08em] text-muted-foreground'>\n                Amount\n              </TableHead>\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            {filteredData.length > 0 ? (\n              filteredData.map((item) => {\n                const isSelected = selectedIds.includes(item.id)\n\n                return (\n                  <TableRow\n                    key={item.id}\n                    data-state={isSelected ? 'selected' : undefined}\n                    className='hover:bg-muted/10 data-[state=selected]:bg-muted/20'\n                  >\n                    <TableCell className='py-3.5'>\n                      <Checkbox\n                        checked={isSelected}\n                        onCheckedChange={(value) => toggleRow(item.id, !!value)}\n                        aria-label={`Select ${item.name}`}\n                        className='after:hidden data-checked:border-sky-600 data-checked:bg-sky-600 data-checked:text-white dark:data-checked:border-sky-500 dark:data-checked:bg-sky-500 dark:data-checked:text-white'\n                      />\n                    </TableCell>\n                    <TableCell className='py-3.5'>\n                      <div className='font-medium'>{item.name}</div>\n                    </TableCell>\n                    <TableCell className='py-3.5'>\n                      <Badge className={availabilityBadgeClass[item.status]}>{item.status}</Badge>\n                    </TableCell>\n                    <TableCell className='py-3.5'>\n                      <div className='lowercase text-muted-foreground'>{item.email}</div>\n                    </TableCell>\n                    <TableCell className='py-3.5 text-right'>\n                      <div className='font-medium'>{formatCurrency(item.amount)}</div>\n                    </TableCell>\n                  </TableRow>\n                )\n              })\n            ) : (\n              <TableRow>\n                <TableCell colSpan={5} className='h-24 text-center'>\n                  No results.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n    </div>\n  )\n}\n\nexport default DataTable12\n"
    }
  ]
}
