import React, { useEffect, useMemo, useState } from 'react'; import { PlusIcon } from '@heroicons/react/24/outline'; import PageHeader from '../components/ui/PageHeader'; import DataTable, { type Column } from '../components/ui/DataTable'; import Modal from '../components/ui/Modal'; import PriceInput from '../components/ui/PriceInput'; import SearchableSelect from '../components/ui/SearchableSelect'; import { ActiveBadge } from '../components/ui/StatusBadge'; import { formatRial } from '../lib/utils'; import { useUrlState } from '../hooks/useUrlState'; import { usePermissions } from '../hooks/usePermissions'; import { usePackages } from '../hooks/usePackages'; import { api, type ApiResponse } from '../lib/api'; import { useQuery } from '@tanstack/react-query'; import type { PackageDefinition, ServiceItem } from '../types'; interface Draft { uuid?: string; name: string; session_count: number; price_rials: number; validity_days: number | ''; service_uuids: string[]; } const EMPTY: Draft = { name: '', session_count: 6, price_rials: 0, validity_days: '', service_uuids: [] }; /** * تعریف پکیج‌ها. * * ماندهٔ بیمار اینجا نیست — آن در پروندهٔ بیمار است. اینجا فقط «چه می‌فروشیم». */ export default function PackagesPage() { const { packages, loading, create, update, deactivate } = usePackages(); const { can } = usePermissions(); const canManage = can('appointment_settings', 'update'); const [urlState, setUrlState] = useUrlState({ search: '' }); const [draft, setDraft] = useState(null); const { data: servicesData } = useQuery({ queryKey: ['service-items-for-packages'], queryFn: () => api.get>('/api/v1/service-items'), staleTime: 60_000, }); const services = servicesData?.data ?? []; const rows = useMemo(() => { const q = urlState.search.trim(); return packages.filter((p) => q === '' || p.name.includes(q)); }, [packages, urlState.search]); const columns: Column[] = [ { key: 'name', header: 'پکیج', render: (p) => (
{p.name} {p.services.map((s) => s.name).join('، ') || 'بدون سرویس'}
), }, { key: 'session_count', header: 'تعداد جلسه', render: (p) => {p.session_count}, }, { key: 'price_rials', header: 'قیمت', render: (p) => {formatRial(p.price_rials)}, }, { key: 'validity_days', header: 'اعتبار', render: (p) => ( {p.validity_days === null ? 'بی‌پایان' : `${p.validity_days} روز`} ), }, { key: 'active', header: 'وضعیت', render: (p) => , }, ]; const save = async () => { if (!draft) return; const body = { name: draft.name, session_count: draft.session_count, price_rials: draft.price_rials, validity_days: draft.validity_days === '' ? null : draft.validity_days, service_uuids: draft.service_uuids, }; if (draft.uuid) { await update.mutateAsync({ uuid: draft.uuid, body }); } else { await create.mutateAsync(body); } setDraft(null); }; return (
setDraft({ ...EMPTY })}> پکیج تازه ) : undefined } /> setUrlState({ search: v })} searchPlaceholder="جستجو در پکیج‌ها..." emptyMessage="هنوز پکیجی تعریف نشده است" actions={(p) => canManage ? (
{p.active && ( )}
) : null } /> setDraft(null)} footer={ <> } > {draft && (
setDraft({ ...draft, name: e.target.value })} placeholder="۶ جلسه لیزر فول‌بادی" />
setDraft({ ...draft, session_count: Number(e.target.value) })} />
setDraft({ ...draft, price_rials: v })} suffix="ریال" />
setDraft({ ...draft, validity_days: e.target.value === '' ? '' : Number(e.target.value) }) } placeholder="خالی یعنی بی‌پایان" />
{ const uuid = String(v ?? ''); if (uuid && !draft.service_uuids.includes(uuid)) { setDraft({ ...draft, service_uuids: [...draft.service_uuids, uuid] }); } }} options={services .filter((s) => !draft.service_uuids.includes(s.uuid)) .map((s) => ({ value: s.uuid, label: s.name }))} placeholder="افزودن سرویس" />
{draft.service_uuids.map((uuid) => ( ))}
{draft.service_uuids.length === 0 && ( پکیج بدون سرویس قابل مصرف نیست. )}
)}
); }