diff --git a/assets/admin/pages/MyPatientsPage.tsx b/assets/admin/pages/MyPatientsPage.tsx index 5a9a0ef2..7843e2b1 100644 --- a/assets/admin/pages/MyPatientsPage.tsx +++ b/assets/admin/pages/MyPatientsPage.tsx @@ -2,6 +2,7 @@ import { CheckCircleIcon, ChevronRightIcon, FolderOpenIcon, + IdentificationIcon, MagnifyingGlassIcon, PencilIcon, PhoneIcon, @@ -15,7 +16,6 @@ import React, { useCallback, useRef, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -import DataTable, { type Column } from "../components/ui/DataTable"; import FeatureGate from "../components/ui/FeatureGate"; import Modal from "../components/ui/Modal"; import PageHeader from "../components/ui/PageHeader"; @@ -65,13 +65,15 @@ const EMPTY_RECORDS: PatientRecord[] = []; const EMPTY_SESSIONS: PatientSession[] = []; function getPatientName(record?: PatientRecord | null) { - const user = record?.user; - return user?.fullName || user?.name || record?.user_name || "—"; + return record?.user_name || "—"; } function getPatientPhone(record?: PatientRecord | null) { - const user = record?.user; - return user?.phone || record?.user_mobile || "—"; + return record?.user_mobile || "—"; +} + +function getPatientNationalCode(record?: PatientRecord | null) { + return record?.user_national_code || null; } function calcFinalPrice( @@ -121,7 +123,9 @@ function MyPatientsPageInner() { uuid: string; name: string | null; mobile: string; + national_code?: string | null; } | null>(null); + const [recordNationalCode, setRecordNationalCode] = useState(""); const [searchError, setSearchError] = useState(""); const mobileInputRef = useRef(null); @@ -296,72 +300,6 @@ function MyPatientsPageInner() { setPage(1); }, []); - const recordColumns: Column[] = [ - { - key: "user", - header: "بیمار", - render: (r) => ( -
-
- {(r.user?.fullName ?? "?").charAt(0)} -
-
-
- {r.user?.fullName ?? "—"} -
-
- - {r.user?.phone ?? "—"} -
-
-
- ), - }, - { - key: "created_at", - header: "تاریخ ثبت", - render: (r) => ( - - {formatDate(r.created_at)} - - ), - }, - { - key: "uuid", - header: "", - render: (r) => ( - - ), - }, - ]; - if (!selectedRecord) { return ( <> @@ -408,65 +346,77 @@ function MyPatientsPageInner() { }} value={search} onChange={(e) => handleSearch(e.target.value)} - placeholder="جستجو بر اساس نام یا تلفن..." + placeholder="جستجو بر اساس نام، شماره موبایل یا کد ملی..." /> -
- {records.length === 0 && !isLoading ? ( + {records.length === 0 && !isLoading ? ( +
+
- -
- {search - ? "بیماری یافت نشد" - : "هنوز بیماری ثبت نشده است"} -
-
- {search - ? "عبارت جستجو را تغییر دهید" - : "پس از ثبت نوبت، پرونده بیمار به طور خودکار ایجاد می‌شود"} -
+ {search + ? "بیماری یافت نشد" + : "هنوز بیماری ثبت نشده است"}
- ) : ( - <> - -
- + {search + ? "عبارت جستجو را تغییر دهید" + : "پس از ثبت نوبت، پرونده بیمار به طور خودکار ایجاد می‌شود"} +
+
+ ) : ( + <> +
+ {records.map((r) => ( + { + setSelectedRecord(r); + setSessionPage(1); + }} /> -
- - )} -
+ ))} + +
+ +
+ + )} {/* Modal ایجاد پرونده دستی */} void; +}) { + const name = getPatientName(record); + const phone = getPatientPhone(record); + const nationalCode = getPatientNationalCode(record); + + return ( +
+
+
+ {(name === "—" ? "؟" : name).charAt(0)} +
+
+
+ {name} +
+
+ {formatDate(record.created_at)} +
+
+
+ +
+
+ + {phone} +
+
+ + + {nationalCode ?? ( + + کد ملی ثبت نشده + + )} + +
+
+ + +
+ ); +} + function SessionRow({ session, onEdit, diff --git a/assets/admin/types/index.ts b/assets/admin/types/index.ts index 0152b9e6..9f673ba7 100644 --- a/assets/admin/types/index.ts +++ b/assets/admin/types/index.ts @@ -426,15 +426,10 @@ export interface PatientRecord { uuid: string; entity_type: string; entity_id: number; - user?: { - uuid?: string; - fullName?: string; - name?: string; - phone?: string; - } | null; user_uuid?: string; user_name?: string | null; user_mobile?: string | null; + user_national_code?: string | null; created_at: number; } diff --git a/migrations/Version20260622160119.php b/migrations/Version20260622160119.php new file mode 100644 index 00000000..c2d03274 --- /dev/null +++ b/migrations/Version20260622160119.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE users ADD national_code VARCHAR(10) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE users DROP national_code'); + } +} diff --git a/src/Auth/Entity/User.php b/src/Auth/Entity/User.php index 6b6038a3..6a2f22db 100644 --- a/src/Auth/Entity/User.php +++ b/src/Auth/Entity/User.php @@ -32,6 +32,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\Column(name: 'real_name', type: 'string', length: 100, nullable: true)] private ?string $realName = null; + #[ORM\Column(name: 'national_code', type: 'string', length: 10, nullable: true)] + private ?string $nationalCode = null; + #[ORM\Column(type: 'json')] private array $roles = ['ROLE_USER']; @@ -57,6 +60,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface public function getMobileNumber(): string { return $this->mobileNumber; } public function getEmail(): ?string { return $this->email; } public function getRealName(): ?string { return $this->realName; } + public function getNationalCode(): ?string { return $this->nationalCode; } public function getStatus(): int { return $this->status; } public function getCreatedAt(): int { return $this->createdAt; } @@ -77,6 +81,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface public function setEmail(?string $email): self { $this->email = $email; return $this; } public function setRealName(?string $name): self { $this->realName = $name; $this->updatedAt = time(); return $this; } + public function setNationalCode(?string $code): self { $this->nationalCode = $code !== null && $code !== '' ? $code : null; $this->updatedAt = time(); return $this; } public function setPasswordHash(?string $hash): self { $this->passwordHash = $hash; $this->updatedAt = time(); return $this; } public function setRoles(array $roles): self { $this->roles = $roles; $this->updatedAt = time(); return $this; } public function setStatus(int $status): self { $this->status = $status; $this->updatedAt = time(); return $this; } diff --git a/src/Patient/Controller/PatientController.php b/src/Patient/Controller/PatientController.php index 9343aa39..a0d64518 100644 --- a/src/Patient/Controller/PatientController.php +++ b/src/Patient/Controller/PatientController.php @@ -59,6 +59,7 @@ class PatientController extends BaseController 'uuid' => $patient->getUuid(), 'name' => $patient->getRealName(), 'mobile' => $patient->getMobileNumber(), + 'national_code' => $patient->getNationalCode(), ]); } @@ -101,6 +102,15 @@ class PatientController extends BaseController return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'کاربر یافت نشد', 404); } + $nationalCode = trim((string) ($data['national_code'] ?? '')); + if ($nationalCode !== '' && $patient->getNationalCode() === null) { + if (!preg_match('/^\d{10}$/', $nationalCode)) { + return $this->error(ErrorCodes::ERR_VALIDATION_001, 'کد ملی باید ۱۰ رقم باشد', 422); + } + $patient->setNationalCode($nationalCode); + $this->userRepo->save($patient); + } + $existing = $this->recordRepo->findByEntityAndUser($entityType, $entityId, $patient); if ($existing !== null) { return $this->success($existing->toArray()); diff --git a/src/Patient/Entity/PatientRecord.php b/src/Patient/Entity/PatientRecord.php index e8a3f596..d3eeed23 100644 --- a/src/Patient/Entity/PatientRecord.php +++ b/src/Patient/Entity/PatientRecord.php @@ -72,8 +72,9 @@ class PatientRecord 'entity_type' => $this->entityType, 'entity_id' => $this->entityId, 'user_uuid' => $this->user->getUuid(), - 'user_name' => $this->user->getRealName(), - 'user_mobile' => $this->user->getMobileNumber(), + 'user_name' => $this->user->getRealName(), + 'user_mobile' => $this->user->getMobileNumber(), + 'user_national_code' => $this->user->getNationalCode(), 'created_by_type' => $this->createdByType, 'created_at' => $this->createdAt, ]; diff --git a/src/Patient/Repository/PatientRecordRepository.php b/src/Patient/Repository/PatientRecordRepository.php index 3d1a6170..9bffd73a 100644 --- a/src/Patient/Repository/PatientRecordRepository.php +++ b/src/Patient/Repository/PatientRecordRepository.php @@ -41,7 +41,7 @@ class PatientRecordRepository extends ServiceEntityRepository ->setMaxResults($limit); if ($search !== null && $search !== '') { - $qb->andWhere('u.realName LIKE :search OR u.mobileNumber LIKE :search') + $qb->andWhere('u.realName LIKE :search OR u.mobileNumber LIKE :search OR u.nationalCode LIKE :search') ->setParameter('search', '%' . $search . '%'); } @@ -59,7 +59,7 @@ class PatientRecordRepository extends ServiceEntityRepository ->setParameter('id', $entityId); if ($search !== null && $search !== '') { - $qb->andWhere('u.realName LIKE :search OR u.mobileNumber LIKE :search') + $qb->andWhere('u.realName LIKE :search OR u.mobileNumber LIKE :search OR u.nationalCode LIKE :search') ->setParameter('search', '%' . $search . '%'); }