import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { PlusIcon, PencilIcon, TrashIcon, IdentificationIcon, PhoneIcon } from '@heroicons/react/24/outline'; import { useForm } 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 } from '../lib/api'; import type { Secretary, SecretaryPermissions } from '../types'; import { formatDate, maskMobile } from '../lib/utils'; import { ActiveBadge } from '../components/ui/StatusBadge'; import DataTable, { type Column } from '../components/ui/DataTable'; import Modal from '../components/ui/Modal'; import ConfirmDialog from '../components/ui/ConfirmDialog'; import PageHeader from '../components/ui/PageHeader'; import { useAuthStore } from '../stores/authStore'; import { useSubscription } from '../hooks/useSubscription'; import { Link } from 'react-router-dom'; // ── Default permissions & labels ────────────────────────────────────────── const DEFAULT_PERMISSIONS: SecretaryPermissions = { appointments: { view: true, create: false, cancel: false, update_status: false }, addresses: { view: true, create: false, update: false, delete: false }, clinic_info: { view: true, update: false }, insurances: { view: true, create: false, update: false, delete: false }, }; type PermSection = keyof SecretaryPermissions; const PERMISSION_LABELS: Record = { appointments: { label: 'نوبت‌ها', actions: [ { key: 'view', label: 'مشاهده' }, { key: 'create', label: 'ایجاد' }, { key: 'cancel', label: 'لغو' }, { key: 'update_status', label: 'تغییر وضعیت' }, ], }, addresses: { label: 'آدرس‌ها', actions: [ { key: 'view', label: 'مشاهده' }, { key: 'create', label: 'ایجاد' }, { key: 'update', label: 'ویرایش' }, { key: 'delete', label: 'حذف' }, ], }, clinic_info: { label: 'اطلاعات کلینیک', actions: [ { key: 'view', label: 'مشاهده' }, { key: 'update', label: 'ویرایش' }, ], }, insurances: { label: 'بیمه‌ها', actions: [ { key: 'view', label: 'مشاهده' }, { key: 'create', label: 'ایجاد' }, { key: 'update', label: 'ویرایش' }, { key: 'delete', label: 'حذف' }, ], }, }; const ALL_ACTIONS = ['view', 'create', 'update', 'delete', 'cancel', 'update_status']; const ACTION_HEADERS = ['مشاهده', 'ایجاد', 'ویرایش', 'حذف', 'لغو', 'تغییر وضعیت']; function PermissionsMatrix({ permissions, onChange, }: { permissions: SecretaryPermissions; onChange: (p: SecretaryPermissions) => void; }) { const toggle = (section: PermSection, action: string) => { const cur = (permissions[section] as Record)[action]; onChange({ ...permissions, [section]: { ...(permissions[section] as Record), [action]: !cur }, }); }; return (
{ACTION_HEADERS.map((h) => ( ))} {(Object.keys(PERMISSION_LABELS) as PermSection[]).map((section, idx) => { const config = PERMISSION_LABELS[section]; const sectionPrm = permissions[section] as Record; return ( {ALL_ACTIONS.map((action) => { const ac = config.actions.find((a) => a.key === action); if (!ac) return ( ); return ( ); })} ); })}
بخش{h}
{config.label} toggle(section, action)} style={{ width: 16, height: 16, accentColor: 'var(--primary)', cursor: 'pointer' }} />
); } // ── Create form schema ───────────────────────────────────────────────────── const createSchema = z.object({ mobile_number: z.string().min(10, 'شماره موبایل معتبر نیست'), }); type CreateForm = z.infer; interface ClinicDoctor { uuid: string; name: string; } // ── Main component ───────────────────────────────────────────────────────── export default function MySecretariesPage() { const qc = useQueryClient(); const { doctorUuid, dbUuid, primaryRole } = useAuthStore(); const isClinic = primaryRole === 'clinic'; const { maxSecretaries } = useSubscription(); // for clinic: selected doctor to add secretary for const [selectedDoctorUuid, setSelectedDoctorUuid] = useState(''); const activeDoctorUuid = isClinic ? selectedDoctorUuid : (doctorUuid ?? ''); const [createOpen, setCreateOpen] = useState(false); const [editTarget, setEditTarget] = useState(null); const [editPerms, setEditPerms] = useState(DEFAULT_PERMISSIONS); const [deleteTarget, setDeleteTarget] = useState(null); // clinic: load clinic's doctors const { data: clinicDoctorsData, isLoading: clinicDoctorsLoading } = useQuery>({ queryKey: ['clinic-doctors', dbUuid], queryFn: () => api.get(`/api/v1/clinic/doctor-list/${dbUuid}`), enabled: isClinic && !!dbUuid, }); const clinicDoctors: ClinicDoctor[] = clinicDoctorsData?.data?.data ?? []; // clinic: load ALL secretaries across all its doctors const { data: clinicSecrData, isLoading: clinicSecrLoading } = useQuery>({ queryKey: ['my-secretaries-clinic', dbUuid], queryFn: () => api.get(`/api/v1/secretaries/clinic/${dbUuid}`), enabled: isClinic && !!dbUuid, }); // doctor: load secretaries for the doctor const { data: doctorSecrData, isLoading: doctorSecrLoading } = useQuery>({ queryKey: ['my-secretaries', activeDoctorUuid], queryFn: () => api.get(`/api/v1/secretaries/${activeDoctorUuid}`), enabled: !isClinic && !!activeDoctorUuid, }); const secretaries = isClinic ? (clinicSecrData?.data ?? []) : (doctorSecrData?.data ?? []); const isLoading = isClinic ? clinicSecrLoading : doctorSecrLoading; const createForm = useForm({ resolver: zodResolver(createSchema) }); const createMutation = useMutation({ mutationFn: (body: CreateForm) => api.post('/api/v1/secretary', { ...body, doctor_uuid: activeDoctorUuid }), onSuccess: () => { toast.success('منشی اضافه شد'); setCreateOpen(false); createForm.reset(); qc.invalidateQueries({ queryKey: ['my-secretaries'] }); qc.invalidateQueries({ queryKey: ['my-secretaries-clinic'] }); }, onError: (e: any) => toast.error(e.message), }); const updatePermsMutation = useMutation({ mutationFn: ({ uuid, permissions }: { uuid: string; permissions: SecretaryPermissions }) => api.patch(`/api/v1/secretary/${uuid}`, { permissions: { version: 1, resources: permissions } }), onSuccess: () => { toast.success('دسترسی‌ها بروزرسانی شد'); setEditTarget(null); qc.invalidateQueries({ queryKey: ['my-secretaries'] }); qc.invalidateQueries({ queryKey: ['my-secretaries-clinic'] }); }, onError: (e: any) => toast.error(e.message), }); const deleteMutation = useMutation({ mutationFn: (uuid: string) => api.delete(`/api/v1/secretary/${uuid}`), onSuccess: () => { toast.success('منشی غیرفعال شد'); setDeleteTarget(null); qc.invalidateQueries({ queryKey: ['my-secretaries'] }); qc.invalidateQueries({ queryKey: ['my-secretaries-clinic'] }); }, onError: (e: any) => toast.error(e.message), }); const openEdit = (s: Secretary) => { setEditTarget(s); setEditPerms(s.permissions ?? DEFAULT_PERMISSIONS); }; const handleCreateOpen = () => { if (isClinic && !selectedDoctorUuid) { toast.error('ابتدا یک پزشک را انتخاب کنید'); return; } setCreateOpen(true); }; const selectedDoctorName = clinicDoctors.find(d => d.uuid === selectedDoctorUuid)?.name ?? ''; // for clinic: show doctor column in the table const clinicColumns: Column[] = isClinic ? [ { key: 'doctor_name', header: 'پزشک', render: (s) => ( {s.doctor_name} ), }, ] : []; const allColumns: Column[] = [ { key: 'user_name', header: 'منشی', render: (s) => (
{(s.user_name ?? '?').charAt(0)}
{s.user_name}
), }, ...clinicColumns, { key: 'mobile_number', header: 'موبایل', render: (s) => (
{maskMobile(s.mobile_number)}
), }, { key: 'is_active', header: 'وضعیت', render: (s) => , }, { key: 'created_at', header: 'تاریخ ثبت', render: (s) => ( {formatDate(Number(s.created_at))} ), }, { key: 'uuid', header: 'عملیات', render: (s) => (
), }, ]; return ( <> = maxSecretaries ? ( ارتقاء پنل برای افزودن منشی ) : ( ) } /> {/* کلینیک: انتخاب پزشک برای افزودن منشی */} {isClinic && (
{clinicDoctorsLoading ? (

در حال بارگذاری...

) : clinicDoctors.length === 0 ? (

هیچ پزشکی در این کلینیک تعریف نشده است. ابتدا پزشک اضافه کنید.

) : ( )}
)} {!isClinic && !doctorUuid ? (
پروفایل پزشک یافت نشد
) : (
{secretaries.length === 0 && !isLoading ? (
هنوز منشی‌ای اضافه نشده
{isClinic ? 'برای افزودن منشی، ابتدا یک پزشک را از لیست بالا انتخاب کنید' : 'منشی می‌تواند نوبت‌ها و اطلاعات کلینیک را مدیریت کند'}
{!isClinic && ( )}
) : ( )}
)} {/* Modal افزودن منشی */} setCreateOpen(false)} title="افزودن منشی جدید"> {isClinic && selectedDoctorName && (
منشی برای دکتر {selectedDoctorName} اضافه می‌شود
)}
createMutation.mutate(d))}>
{createForm.formState.errors.mobile_number && ( {createForm.formState.errors.mobile_number.message} )}

کاربری با این شماره در سیستم جستجو شده و به عنوان منشی اضافه می‌شود.

{/* Modal ویرایش دسترسی‌ها */} setEditTarget(null)} title={`دسترسی‌های ${editTarget?.user_name ?? ''}`} size="lg" footer={ <> } > {/* Confirm غیرفعال‌سازی */} deleteTarget && deleteMutation.mutate(deleteTarget.uuid)} onCancel={() => setDeleteTarget(null)} /> ); }