This commit is contained in:
2026-04-09 15:17:07 +03:00
parent c5f6d2f596
commit 29cc9c6f4e
23 changed files with 420 additions and 190 deletions
@@ -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>
</>
)
}