feat: port secretaries tab from tauri to admin my-secretaries page

- Redesign MySecretariesPage pixel-perfect to clinic-pro-tauri (active/previous
  tabs, desktop table, mobile cards, add/edit/view modal with permission
  accordions, deactivate confirm)
- Permission sections based on existing admin pages (appointments, patients,
  payments, insurances, addresses, clinic_info)
- Extend DoctorSecretary with national_code + address columns (+migration);
  wire create/update in SecretaryController; add patients/payments to
  DEFAULT_PERMISSIONS
- Extend Secretary/SecretaryPermissions types; update admin SecretariesPage
- Backend + frontend tests; update docs/api/secretary.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-15 12:23:17 +03:30
co-authored by Claude Opus 4.8
parent 57c5383baf
commit 89e23a2c0d
9 changed files with 1146 additions and 709 deletions
@@ -108,6 +108,13 @@ class SecretaryController extends BaseController
$secretary = new DoctorSecretary($doctor, $secretaryUser, $ownerType, $ownerClinic);
if (array_key_exists('national_code', $data)) {
$secretary->setNationalCode($data['national_code'] !== null ? trim((string) $data['national_code']) : null);
}
if (array_key_exists('address', $data)) {
$secretary->setAddress($data['address'] !== null ? trim((string) $data['address']) : null);
}
if (!empty($data['permissions'])) {
$secretary->mergePermissions($data['permissions']);
}
@@ -168,6 +175,19 @@ class SecretaryController extends BaseController
$secretary->setActive((bool) $data['active']);
}
if (!empty($data['name'])) {
$secretaryUser = $secretary->getSecretary();
$secretaryUser->setRealName(trim((string) $data['name']));
$this->userRepo->save($secretaryUser);
}
if (array_key_exists('national_code', $data)) {
$secretary->setNationalCode($data['national_code'] !== null ? trim((string) $data['national_code']) : null);
}
if (array_key_exists('address', $data)) {
$secretary->setAddress($data['address'] !== null ? trim((string) $data['address']) : null);
}
if (!empty($data['permissions'])) {
$secretary->mergePermissions($data['permissions']);
}
+15 -1
View File
@@ -21,9 +21,11 @@ class DoctorSecretary
'version' => 1,
'resources' => [
'appointments' => ['view' => true, 'create' => true, 'cancel' => false, 'update_status' => true],
'patients' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
'payments' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
'insurances' => ['view' => true, 'create' => false, 'update' => false, 'delete' => 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],
],
];
@@ -53,6 +55,12 @@ class DoctorSecretary
#[ORM\Column(name: 'permission', type: 'json', nullable: true)]
private ?array $permissions = null;
#[ORM\Column(name: 'national_code', type: 'string', length: 20, nullable: true)]
private ?string $nationalCode = null;
#[ORM\Column(name: 'address', type: 'text', nullable: true)]
private ?string $address = null;
#[ORM\Column(type: 'boolean')]
private bool $active = true;
@@ -81,12 +89,16 @@ class DoctorSecretary
public function getOwnerType(): string { return $this->ownerType; }
public function getClinic(): ?Clinic { return $this->clinic; }
public function getPermissions(): array { return $this->permissions ?? self::DEFAULT_PERMISSIONS; }
public function getNationalCode(): ?string { return $this->nationalCode; }
public function getAddress(): ?string { return $this->address; }
public function isActive(): bool { return $this->active; }
public function getCreatedAt(): int { return $this->createdAt; }
public function getUpdatedAt(): int { return $this->updatedAt; }
public function setActive(bool $v): self { $this->active = $v; $this->touch(); return $this; }
public function setPermissions(array $v): self { $this->permissions = $v; $this->touch(); return $this; }
public function setNationalCode(?string $v): self { $this->nationalCode = $v; $this->touch(); return $this; }
public function setAddress(?string $v): self { $this->address = $v; $this->touch(); return $this; }
/** Deep merge: only provided resources/actions are updated */
public function mergePermissions(array $patch): void
@@ -122,6 +134,8 @@ class DoctorSecretary
'owner_type' => $this->ownerType,
'clinic_uuid' => $this->clinic?->getUuid(),
'is_active' => $this->active,
'national_code' => $this->nationalCode,
'address' => $this->address,
'permissions' => $this->getPermissions()['resources'] ?? $this->getPermissions(),
'created_at' => $this->createdAt,
];