import { useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { BanknotesIcon, ShieldCheckIcon, ClockIcon, UsersIcon, PencilIcon, CalendarDaysIcon, WrenchScrewdriverIcon, CubeIcon, } from '@heroicons/react/24/outline'; import { toast } from 'sonner'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import type { ServiceItem } from '../types'; import { formatRial, formatNumber, formatYear, formatDateTime } from '../lib/utils'; import PageHeader from '../components/ui/PageHeader'; import FeatureGate from '../components/ui/FeatureGate'; import ConfirmDialog from '../components/ui/ConfirmDialog'; import ServiceTariffModal from '../components/ServiceTariffModal'; import ServiceInsuranceModal from '../components/ServiceInsuranceModal'; import ServiceItemFormModal from '../components/ServiceItemFormModal'; interface Tariff { uuid: string; year: number; price_rials: number; is_active: boolean; } interface TariffList { current_year: number; default_price_rials: number; data: Tariff[]; } interface TenantInsurance { uuid: string; insurance_name: string | null; insurance_kind: 'basic' | 'supplementary' | null; coverage_percent: number; } interface CoverageRow { service_item_uuid: string | null; covered: boolean; coverage_percent: number | null; franchise_rials: number | null; ceiling_rials: number | null; } const TABS = [ { id: 'info', label: 'اطلاعات سرویس' }, { id: 'tariffs', label: 'تعرفه‌ها' }, { id: 'insurance', label: 'بیمه‌ها' }, ] as const; type TabId = typeof TABS[number]['id']; const KIND = { basic: { label: 'پایه', cls: 'blue' }, supplementary: { label: 'تکمیلی', cls: 'violet' }, } as const; function Row({ icon, label, children }: { icon: React.ReactNode; label: string; children: React.ReactNode }) { return (
{icon} {label} {children}
); } function InfoTab({ item }: { item: ServiceItem }) { const members = item.staff_members ?? (item.staff ? [item.staff] : []); return (
} label="قیمت پایه"> {formatRial(item.price_rials)} } label="بخش"> {item.section_name ?? '—'} } label="زمان متوسط"> {item.duration_minutes ? {formatNumber(Number(item.duration_minutes))} دقیقه : '—'} } label="نمایش در نوبت‌دهی"> {item.bookable ? فعال : غیرفعال} } label="پوشش بیمه"> {item.insurance_covered ? تحت پوشش : '—'} } label="پرسنل مسئول"> {members.length > 0 ? ( {members.map((m) => ( {m.full_name} ))} ) : '—'} } label="تاریخ ایجاد"> {item.created_at ? formatDateTime(item.created_at) : '—'}
آخرین ویرایش {item.updated_at ? formatDateTime(item.updated_at) : '—'}
); } function TariffsTab({ item, onManage }: { item: ServiceItem; onManage: () => void }) { const { data, isLoading } = useQuery>({ queryKey: ['service-tariffs', item.uuid], queryFn: () => api.get(`/api/v1/service-items/${item.uuid}/tariffs`), }); const list = data?.data; const tariffs = list?.data ?? []; return (
تعرفه‌های سالانه
قیمت هر سال شمسی؛ قیمت سال جاری مبنای صورتحساب است.
{isLoading ? (
در حال بارگذاری...
) : tariffs.length === 0 ? (

تعرفه‌ای ثبت نشده است

) : (
{tariffs.map((t) => (
{formatYear(t.year)} {t.year === list?.current_year && سال جاری} {!t.is_active && غیرفعال} {formatRial(t.price_rials)}
))}
)}
); } function InsuranceTab({ item, onManage }: { item: ServiceItem; onManage: () => void }) { const { data: contractsData, isLoading } = useQuery<{ data: { data: TenantInsurance[] } }>({ queryKey: ['tenant-insurances'], queryFn: () => api.get('/api/v1/billing/tenant-insurances'), }); const contracts = (contractsData as any)?.data?.data as TenantInsurance[] | undefined ?? []; return (
بیمه‌های مرتبط
درصد پوشش، فرانشیز و سقف هر بیمه‌گر برای این خدمت.
{isLoading ? (
در حال بارگذاری...
) : contracts.length === 0 ? (

قرارداد بیمه‌ی فعالی ندارید

) : (
{contracts.map((c) => ( ))}
)}
); } function ContractCoverageRow({ contract, itemUuid }: { contract: TenantInsurance; itemUuid: string }) { const { data } = useQuery<{ data: { data: CoverageRow[] } }>({ queryKey: ['service-coverage', contract.uuid], queryFn: () => api.get(`/api/v1/billing/tenant-insurances/${contract.uuid}/service-coverage`), }); const row = ((data as any)?.data?.data as CoverageRow[] | undefined) ?.find((r) => r.service_item_uuid === itemUuid); const kind = contract.insurance_kind ? KIND[contract.insurance_kind] : null; const percent = row?.coverage_percent ?? contract.coverage_percent; return (
{contract.insurance_name ?? 'بیمه'} {kind && {kind.label}} {row && !row.covered ? ( بدون پوشش ) : ( پوشش {formatNumber(percent)}٪ {row?.franchise_rials ? فرانشیز {formatRial(row.franchise_rials)} : null} {row?.ceiling_rials ? سقف {formatRial(row.ceiling_rials)} : null} {!row && (ارث از قرارداد)} )}
); } function ServiceDetailPageInner() { const { uuid } = useParams<{ uuid: string }>(); const navigate = useNavigate(); const qc = useQueryClient(); const [tab, setTab] = useState('info'); const [editOpen, setEditOpen] = useState(false); const [tariffOpen, setTariffOpen] = useState(false); const [insuranceOpen, setInsuranceOpen] = useState(false); const [toggleOpen, setToggleOpen] = useState(false); const { data, isLoading, isError } = useQuery>({ queryKey: ['service-item', uuid], queryFn: () => api.get(`/api/v1/service-item/${uuid}`), enabled: !!uuid, }); const item = data?.data; const toggleActive = useMutation({ mutationFn: () => api.patch(`/api/v1/service-item/${uuid}`, { active: !item!.active }), onSuccess: () => { qc.invalidateQueries({ queryKey: ['service-item', uuid] }); qc.invalidateQueries({ queryKey: ['service-items'] }); setToggleOpen(false); toast.success(item!.active ? 'سرویس غیرفعال شد' : 'سرویس فعال شد'); }, onError: (e: Error) => { toast.error(e.message); setToggleOpen(false); }, }); if (isLoading) { return
در حال بارگذاری...
; } if (isError || !item) { return (
سرویس یافت نشد
); } return (
} />
{item.active ? 'فعال' : 'غیرفعال'}
{TABS.map((t) => ( ))}
{tab === 'info' && } {tab === 'tariffs' && setTariffOpen(true)} />} {tab === 'insurance' && setInsuranceOpen(true)} />} setEditOpen(false)} onManageInsurance={() => setInsuranceOpen(true)} /> setTariffOpen(false)} /> setInsuranceOpen(false)} /> toggleActive.mutate()} onCancel={() => setToggleOpen(false)} loading={toggleActive.isPending} /> ); } export default function ServiceDetailPage() { return ( ); }