updates
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Button } from "@/shared/components/ui/button"
|
||||
import { Download, Loader2 } from "lucide-react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
type ExportDataButtonProps = {
|
||||
onExport: (filters?: Record<string, unknown>) => Promise<Blob>
|
||||
fileName?: string
|
||||
filters?: Record<string, unknown>
|
||||
label?: string
|
||||
}
|
||||
|
||||
export function ExportDataButton({
|
||||
onExport,
|
||||
fileName = "export",
|
||||
filters,
|
||||
label = "Export",
|
||||
}: ExportDataButtonProps) {
|
||||
const [isPending, setIsPending] = useState(false)
|
||||
|
||||
const handleExport = async () => {
|
||||
setIsPending(true)
|
||||
try {
|
||||
const blob = await onExport(filters)
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement("a")
|
||||
a.href = url
|
||||
a.download = `${fileName}.xlsx`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
toast.success("Data exported successfully")
|
||||
} catch (err: any) {
|
||||
toast.error(err?.message ?? "Failed to export data")
|
||||
} finally {
|
||||
setIsPending(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={isPending}
|
||||
onClick={handleExport}
|
||||
>
|
||||
{isPending ? <Loader2 className="animate-spin" /> : <Download />}
|
||||
{label}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
"use client"
|
||||
|
||||
import React, { useRef, useState } from "react"
|
||||
import { Button } from "@/shared/components/ui/button"
|
||||
import { Upload, Loader2 } from "lucide-react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
type ImportDataButtonProps = {
|
||||
onImport: (file: File) => Promise<{ message?: string }>
|
||||
onSuccess?: () => void
|
||||
accept?: string
|
||||
label?: string
|
||||
}
|
||||
|
||||
export function ImportDataButton({
|
||||
onImport,
|
||||
onSuccess,
|
||||
accept = ".xlsx,.xls,.csv",
|
||||
label = "Import",
|
||||
}: ImportDataButtonProps) {
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [isPending, setIsPending] = useState(false)
|
||||
|
||||
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0]
|
||||
if (!file) return
|
||||
|
||||
setIsPending(true)
|
||||
try {
|
||||
const result = await onImport(file)
|
||||
toast.success(result.message ?? "Data imported successfully")
|
||||
onSuccess?.()
|
||||
} catch (err: any) {
|
||||
toast.error(err?.message ?? "Failed to import data")
|
||||
} finally {
|
||||
setIsPending(false)
|
||||
if (inputRef.current) inputRef.current.value = ""
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="file"
|
||||
accept={accept}
|
||||
className="hidden"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={isPending}
|
||||
onClick={() => inputRef.current?.click()}
|
||||
>
|
||||
{isPending ? <Loader2 className="animate-spin" /> : <Upload />}
|
||||
{label}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user