feat: add export functionality for JSON data in CategoriesPage and update TabActions component
This commit is contained in:
@@ -3,7 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
TrashIcon, PlusIcon, PencilIcon, TagIcon, MapPinIcon,
|
||||
BuildingOffice2Icon, HeartIcon, ShieldCheckIcon, WrenchScrewdriverIcon,
|
||||
PhotoIcon, XMarkIcon,
|
||||
PhotoIcon, XMarkIcon, ArrowDownTrayIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
@@ -115,11 +115,48 @@ function SBadge({ status }: { status: number }) {
|
||||
);
|
||||
}
|
||||
|
||||
// ── Export helper ──────────────────────────────────────────────────────────────
|
||||
|
||||
async function exportJson(url: string, filename: string, token: string | null) {
|
||||
const res = await fetch(url, token ? { headers: { Authorization: `Bearer ${token}` } } : {});
|
||||
const json = await res.json();
|
||||
const items = json?.data ?? json?.data?.data ?? [];
|
||||
const blob = new Blob([JSON.stringify(items, null, 2)], { type: 'application/json' });
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(a.href);
|
||||
}
|
||||
|
||||
// ── Tab sub-component wrapper ──────────────────────────────────────────────────
|
||||
|
||||
function TabActions({ label, onClick }: { label: string; onClick: () => void }) {
|
||||
function TabActions({ label, onClick, exportUrl, exportFile }: {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
exportUrl: string;
|
||||
exportFile: string;
|
||||
}) {
|
||||
const token = useAuthStore((s) => s.token);
|
||||
const [exporting, setExporting] = useState(false);
|
||||
|
||||
const handleExport = async () => {
|
||||
setExporting(true);
|
||||
try {
|
||||
await exportJson(exportUrl, exportFile, token);
|
||||
} catch {
|
||||
// silent
|
||||
} finally {
|
||||
setExporting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', padding: 'var(--card-pad)' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, padding: 'var(--card-pad)' }}>
|
||||
<button onClick={handleExport} disabled={exporting} className="btn ghost sm">
|
||||
<ArrowDownTrayIcon style={{ width: 15, height: 15 }} />
|
||||
{exporting ? 'در حال دانلود...' : 'خروجی JSON'}
|
||||
</button>
|
||||
<button onClick={onClick} className="btn primary sm">
|
||||
<PlusIcon style={{ width: 15, height: 15 }} /> {label}
|
||||
</button>
|
||||
@@ -191,7 +228,7 @@ function ProvincesTab() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabActions label="افزودن استان" onClick={() => { reset({ status: '1', weight: '0' }); setAddOpen(true); }} />
|
||||
<TabActions label="افزودن استان" onClick={() => { reset({ status: '1', weight: '0' }); setAddOpen(true); }} exportUrl="/api/v1/admin/provinces?limit=9999" exportFile="state.json" />
|
||||
<DataTable<Province> columns={columns} data={items} loading={isLoading} searchValue={search} onSearchChange={(v) => { setSearch(v); setPage(1); }} searchPlaceholder="جستجو در استانها..." emptyMessage="هیچ استانی یافت نشد"
|
||||
actions={(p) => (
|
||||
<>
|
||||
@@ -351,7 +388,7 @@ function CitiesTab() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabActions label="افزودن شهر" onClick={() => { reset({ status: '1', weight: '0' }); setAddOpen(true); }} />
|
||||
<TabActions label="افزودن شهر" onClick={() => { reset({ status: '1', weight: '0' }); setAddOpen(true); }} exportUrl="/api/v1/admin/cities?limit=9999" exportFile="city.json" />
|
||||
<DataTable<City> columns={columns} data={items} loading={isLoading} searchValue={search} onSearchChange={(v) => { setSearch(v); setPage(1); }} searchPlaceholder="جستجو در شهرها..." emptyMessage="هیچ شهری یافت نشد"
|
||||
actions={(c) => (
|
||||
<>
|
||||
@@ -511,7 +548,7 @@ function SpecialtiesTab() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabActions label="افزودن تخصص" onClick={() => { reset({ status: '1', weight: '0' }); setAddOpen(true); }} />
|
||||
<TabActions label="افزودن تخصص" onClick={() => { reset({ status: '1', weight: '0' }); setAddOpen(true); }} exportUrl="/api/v1/admin/specialties?limit=9999" exportFile="specialties.json" />
|
||||
<DataTable<SpecialtyFull> columns={columns} data={items} loading={isLoading} searchValue={search} onSearchChange={(v) => { setSearch(v); setPage(1); }} searchPlaceholder="جستجو در تخصصها..." emptyMessage="هیچ تخصصی یافت نشد"
|
||||
actions={(s) => (
|
||||
<>
|
||||
@@ -632,7 +669,7 @@ function DoctorServicesTab() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabActions label="افزودن خدمت" onClick={() => { reset({ status: '1', weight: '0' }); setAddOpen(true); }} />
|
||||
<TabActions label="افزودن خدمت" onClick={() => { reset({ status: '1', weight: '0' }); setAddOpen(true); }} exportUrl="/api/v1/admin/doctor-services?limit=9999" exportFile="doctor-services.json" />
|
||||
<DataTable<DoctorService> columns={columns} data={items} loading={isLoading} searchValue={search} onSearchChange={(v) => { setSearch(v); setPage(1); }} searchPlaceholder="جستجو در خدمات..." emptyMessage="هیچ خدمتی یافت نشد"
|
||||
actions={(s) => (
|
||||
<>
|
||||
@@ -758,7 +795,7 @@ function InsurancesTab() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabActions label="افزودن بیمه" onClick={() => { reset({ status: '1', type: 'basic' }); setLogoUrl(null); setUploadTarget(null); setAddOpen(true); }} />
|
||||
<TabActions label="افزودن بیمه" onClick={() => { reset({ status: '1', type: 'basic' }); setLogoUrl(null); setUploadTarget(null); setAddOpen(true); }} exportUrl="/api/v1/admin/insurances?limit=9999" exportFile="insurances.json" />
|
||||
<DataTable<Insurance> columns={columns} data={items} loading={isLoading} searchValue={search} onSearchChange={(v) => { setSearch(v); setPage(1); }} searchPlaceholder="جستجو در بیمهها..." emptyMessage="هیچ بیمهای یافت نشد"
|
||||
actions={(i) => (
|
||||
<>
|
||||
@@ -872,7 +909,7 @@ function TagsTab() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabActions label="افزودن تگ" onClick={() => { reset({ status: '1' }); setAddOpen(true); }} />
|
||||
<TabActions label="افزودن تگ" onClick={() => { reset({ status: '1' }); setAddOpen(true); }} exportUrl="/api/v1/admin/tags?limit=9999" exportFile="tags.json" />
|
||||
<DataTable<Tag> columns={columns} data={items} loading={isLoading} searchValue={search} onSearchChange={(v) => { setSearch(v); setPage(1); }} searchPlaceholder="جستجو در تگها..." emptyMessage="هیچ تگی یافت نشد"
|
||||
actions={(t) => (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user