import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { EyeIcon, TrashIcon, PlusIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline'; import { useForm, Controller } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { toast } from 'sonner'; import { api } from '../lib/api'; import type { ApiResponse, PaginatedResponse } from '../lib/api'; import type { Representation, City } from '../types'; import { formatDate, formatRial, formatNumber } from '../lib/utils'; import DataTable, { Column } from '../components/ui/DataTable'; import { ActiveBadge } from '../components/ui/StatusBadge'; import Pagination from '../components/ui/Pagination'; import ConfirmDialog from '../components/ui/ConfirmDialog'; import Modal from '../components/ui/Modal'; import SearchableSelect from '../components/ui/SearchableSelect'; const schema = z.object({ full_name: z.string().min(2, 'نام الزامی است'), mobile_number: z.string().min(10, 'شماره موبایل معتبر نیست'), city_id: z.number().nullable().optional(), commission_percent: z.coerce.number().min(0).max(100), }); type FormData = z.infer; export default function RepresentationsPage() { const navigate = useNavigate(); const qc = useQueryClient(); const [page, setPage] = useState(1); const [search, setSearch] = useState(''); const [cityFilter, setCityFilter] = useState(null); const [addOpen, setAddOpen] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const limit = 15; const citiesQuery = useQuery({ queryKey: ['cities-select'], queryFn: () => api.get>('/api/v1/admin/cities?limit=200'), staleTime: 5 * 60_000, }); const cities: City[] = citiesQuery.data?.data ?? []; const cityOptions = cities.map((c) => ({ value: c.id, label: c.name })); const { data, isLoading } = useQuery({ queryKey: ['representations', page, search, cityFilter], queryFn: () => { const params = new URLSearchParams({ page: String(page), limit: String(limit) }); if (search) params.set('search', search); if (cityFilter !== null) params.set('city_id', String(cityFilter)); return api.get>(`/api/v1/admin/representations?${params}`); }, }); const { register, handleSubmit, reset, control, formState: { errors, isSubmitting } } = useForm({ resolver: zodResolver(schema), defaultValues: { commission_percent: 10 }, }); const createMutation = useMutation({ mutationFn: (d: FormData) => api.post>('/api/v1/representation', { full_name: d.full_name, mobile_number: d.mobile_number, city_id: d.city_id ?? null, commission_percent: d.commission_percent, }), onSuccess: () => { toast.success('نماینده اضافه شد'); setAddOpen(false); reset(); qc.invalidateQueries({ queryKey: ['representations'] }); }, onError: (err: Error) => toast.error(err.message), }); const deleteMutation = useMutation({ mutationFn: (r: Representation) => api.delete>(`/api/v1/representation/${r.uuid}`), onSuccess: () => { toast.success('نماینده حذف شد'); setDeleteTarget(null); qc.invalidateQueries({ queryKey: ['representations'] }); }, onError: (err: Error) => toast.error(err.message), }); const columns: Column[] = [ { key: 'full_name', header: 'نام', render: (r) => {r.full_name} }, { key: 'mobile_number', header: 'موبایل', render: (r) => {r.mobile_number ?? '—'} }, { key: 'city', header: 'شهر', render: (r) => r.city ?? '—' }, { key: 'commission_percent', header: 'کمیسیون', render: (r) => `${formatNumber(r.commission_percent)}٪` }, { key: 'wallet_balance', header: 'موجودی کیف‌پول', render: (r) => formatRial(r.wallet_balance ?? 0) }, { key: 'is_active', header: 'وضعیت', render: (r) => }, { key: 'created_at', header: 'تاریخ ثبت', render: (r) => formatDate(r.created_at) }, ]; const items = data?.data ?? []; const total = data?.meta?.totalRecords ?? 0; return (

نمایندگان

{total} نماینده ثبت‌شده
{ setSearch(e.target.value); setPage(1); }} placeholder="جستجو بر اساس نام یا موبایل..." />
{ setCityFilter(val as number | null); setPage(1); }} placeholder="فیلتر شهر..." isClearable isLoading={citiesQuery.isLoading} noOptionsMessage="هیچ شهری یافت نشد" />
columns={columns} data={items} loading={isLoading} emptyMessage="هیچ نماینده‌ای یافت نشد" actions={(rep) => ( <> )} />
{ setAddOpen(false); reset(); }} footer={ <> } >
createMutation.mutate(d))}>
{errors.full_name &&

{errors.full_name.message}

}
{errors.mobile_number &&

{errors.mobile_number.message}

}
( field.onChange(val as number | null)} placeholder="انتخاب شهر..." isClearable isLoading={citiesQuery.isLoading} noOptionsMessage="هیچ شهری یافت نشد" /> )} />
{errors.commission_percent &&

{errors.commission_percent.message}

}
deleteTarget && deleteMutation.mutate(deleteTarget)} onCancel={() => setDeleteTarget(null)} />
); }