diff --git a/assets/admin/pages/DoctorDetailPage.tsx b/assets/admin/pages/DoctorDetailPage.tsx index 9096c38e..34b5d913 100644 --- a/assets/admin/pages/DoctorDetailPage.tsx +++ b/assets/admin/pages/DoctorDetailPage.tsx @@ -2305,6 +2305,7 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil onSuccess: () => { toast.success('آدرس حذف شد'); setDeletingAddrId(null); qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] }); + qc.invalidateQueries({ queryKey: ['available-locations', uuid] }); }, onError: (e: Error) => toast.error(e.message), }); @@ -2764,7 +2765,10 @@ export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfil onClose={() => { setAddrModalOpen(false); setEditingAddr(null); }} existing={editingAddr} doctorUuid={uuid} - onSaved={() => qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] })} + onSaved={() => { + qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] }); + qc.invalidateQueries({ queryKey: ['available-locations', uuid] }); + }} /> )} diff --git a/assets/admin/pages/MyPatientsPage.tsx b/assets/admin/pages/MyPatientsPage.tsx index a758f3b9..adb93532 100644 --- a/assets/admin/pages/MyPatientsPage.tsx +++ b/assets/admin/pages/MyPatientsPage.tsx @@ -1,8 +1,8 @@ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useRef } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { MagnifyingGlassIcon, PlusIcon, ChevronRightIcon, PencilIcon, PhoneIcon, - FolderOpenIcon, UsersIcon, + FolderOpenIcon, UsersIcon, UserPlusIcon, CheckCircleIcon, } from '@heroicons/react/24/outline'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -66,6 +66,12 @@ function MyPatientsPageInner() { const [sectionUuid, setSectionUuid] = useState(''); const [itemUuid, setItemUuid] = useState(''); + const [createRecordOpen, setCreateRecordOpen] = useState(false); + const [searchMobile, setSearchMobile] = useState(''); + const [foundUser, setFoundUser] = useState<{ uuid: string; name: string | null; mobile: string } | null>(null); + const [searchError, setSearchError] = useState(''); + const mobileInputRef = useRef(null); + const servicesTotal = selectedServices.reduce((sum, s) => sum + s.price_rials, 0); const finalPrice = calcFinalPrice(Number(watchVisit), Number(watchBase), Number(watchSupp), servicesTotal); @@ -126,6 +132,40 @@ function MyPatientsPageInner() { onError: (e: any) => toast.error(e.message), }); + const searchUserMut = useMutation({ + mutationFn: (mobile: string) => api.get(`/api/v1/patient/search-user?mobile=${encodeURIComponent(mobile)}`), + onSuccess: (res: any) => { setFoundUser(res?.data); setSearchError(''); }, + onError: () => { setFoundUser(null); setSearchError('کاربری با این شماره در سیستم یافت نشد'); }, + }); + + const createRecordMut = useMutation({ + mutationFn: (userUuid: string) => api.post('/api/v1/patient', { user_uuid: userUuid }), + onSuccess: () => { + qc.invalidateQueries({ queryKey: ['patients'] }); + setCreateRecordOpen(false); + setSearchMobile(''); + setFoundUser(null); + setSearchError(''); + toast.success('پرونده بیمار ایجاد شد'); + }, + onError: (e: any) => toast.error(e.message), + }); + + const handleSearchMobile = () => { + const digits = searchMobile.replace(/\D/g, ''); + if (!/^09\d{9}$/.test(digits)) { setSearchError('شماره موبایل معتبر نیست'); return; } + setSearchError(''); + setFoundUser(null); + searchUserMut.mutate(digits); + }; + + const handleCreateRecordClose = () => { + setCreateRecordOpen(false); + setSearchMobile(''); + setFoundUser(null); + setSearchError(''); + }; + const handleAddService = () => { if (!itemUuid) return; const found = itemsData?.data?.find((i) => i.uuid === itemUuid); @@ -192,7 +232,16 @@ function MyPatientsPageInner() { if (!selectedRecord) { return ( <> - + setCreateRecordOpen(true)}> + + پرونده جدید + + } + />
)}
+ + {/* Modal ایجاد پرونده دستی */} + + + + + } + > +
+ +
+ { + const digits = e.target.value.replace(/\D/g, ''); + setSearchMobile(digits); + setFoundUser(null); + setSearchError(''); + }} + onKeyDown={(e) => { if (e.key === 'Enter') handleSearchMobile(); }} + /> + +
+ {searchError && {searchError}} +
+ + {foundUser && ( +
+ +
+
{foundUser.name ?? 'بدون نام'}
+
{foundUser.mobile}
+
+
+ )} + +

+ بیمار باید قبلاً در سیستم ثبت‌نام کرده باشد. با شماره موبایل جستجو کنید، سپس پرونده ایجاد کنید. +

+
); } diff --git a/src/Patient/Controller/PatientController.php b/src/Patient/Controller/PatientController.php index 4641365b..d7ddc250 100644 --- a/src/Patient/Controller/PatientController.php +++ b/src/Patient/Controller/PatientController.php @@ -37,6 +37,29 @@ class PatientController extends BaseController private readonly UserActiveContextRepository $contextRepo, ) {} + #[Route('/api/v1/patient/search-user', methods: ['GET'])] + public function searchUser(Request $request, #[CurrentUser] User $user): JsonResponse + { + [$entityType, $entityId] = $this->resolveEntity($user); + $this->assertPatientGate($entityType, $entityId); + + $mobile = trim($request->query->get('mobile', '')); + if ($mobile === '') { + return $this->error(ErrorCodes::ERR_VALIDATION_001, 'mobile الزامی است', 422); + } + + $patient = $this->userRepo->findByMobile($mobile); + if ($patient === null) { + return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'کاربری با این شماره یافت نشد', 404); + } + + return $this->success([ + 'uuid' => $patient->getUuid(), + 'name' => $patient->getRealName(), + 'mobile' => $patient->getMobileNumber(), + ]); + } + #[Route('/api/v1/patients', methods: ['GET'])] public function list(Request $request, #[CurrentUser] User $user): JsonResponse {