diff --git a/assets/admin/pages/RepresentationDetailPage.tsx b/assets/admin/pages/RepresentationDetailPage.tsx index 88709991..9c3c9905 100644 --- a/assets/admin/pages/RepresentationDetailPage.tsx +++ b/assets/admin/pages/RepresentationDetailPage.tsx @@ -5,7 +5,7 @@ import { PencilIcon, TrashIcon, CheckCircleIcon, XCircleIcon } from '@heroicons/ import { toast } from 'sonner'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; -import type { Representation } from '../types'; +import type { Representation, Category } from '../types'; import { formatDate, formatNumber } from '../lib/utils'; import PageHeader from '../components/ui/PageHeader'; import { ActiveBadge } from '../components/ui/StatusBadge'; @@ -27,7 +27,14 @@ export default function RepresentationDetailPage() { const qc = useQueryClient(); const [deleteOpen, setDeleteOpen] = useState(false); const [editOpen, setEditOpen] = useState(false); - const [formData, setFormData] = useState({ full_name: '', city: '', mobile_number: '', commission_percent: '' }); + const [formData, setFormData] = useState({ full_name: '', city_id: '', mobile_number: '', commission_percent: '' }); + + const citiesQuery = useQuery({ + queryKey: ['categories', 'city'], + queryFn: () => api.get>('/api/v1/categorys/city'), + staleTime: 5 * 60_000, + }); + const cities: Category[] = (citiesQuery.data?.data as any)?.data ?? []; const { data, isLoading } = useQuery({ queryKey: ['representation', uuid], @@ -74,7 +81,7 @@ export default function RepresentationDetailPage() { if (!rep) return; setFormData({ full_name: rep.full_name ?? rep.domain ?? '', - city: rep.city ?? '', + city_id: rep.city_id ? String(rep.city_id) : '', mobile_number: rep.mobile_number ?? '', commission_percent: String(rep.commission_percent), }); @@ -177,7 +184,7 @@ export default function RepresentationDetailPage() { ? {rep.mobile_number} : null } /> - + } /> @@ -218,10 +225,10 @@ export default function RepresentationDetailPage() { + )} + + columns={columns} data={items} loading={isLoading} searchValue={search} onSearchChange={(v) => { setSearch(v); setPage(1); }} - searchPlaceholder="جستجو بر اساس دامنه یا شهر..." + searchPlaceholder="جستجو بر اساس نام یا موبایل..." emptyMessage="هیچ نماینده‌ای یافت نشد" actions={(rep) => ( <> @@ -127,10 +167,10 @@ export default function RepresentationsPage() { - setAddOpen(false)} + { setAddOpen(false); reset(); }} footer={ <> - @@ -143,20 +183,30 @@ export default function RepresentationsPage() { >
createMutation.mutate(d))} className="space-y-4">
- - نام کامل + - {errors.domain &&

{errors.domain.message}

} + {errors.full_name &&

{errors.full_name.message}

} +
+
+ + + {errors.mobile_number &&

{errors.mobile_number.message}

}
- - {errors.city &&

{errors.city.message}

} +
- {errors.commission_percent &&

{errors.commission_percent.message}

}
@@ -166,7 +216,7 @@ export default function RepresentationsPage() { addSql('ALTER TABLE representations ADD city_id INT DEFAULT NULL, DROP city'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE representations ADD city VARCHAR(255) DEFAULT NULL, DROP city_id'); + } +} diff --git a/src/Admin/Controller/AdminApiController.php b/src/Admin/Controller/AdminApiController.php index 39c8d59d..b670e790 100644 --- a/src/Admin/Controller/AdminApiController.php +++ b/src/Admin/Controller/AdminApiController.php @@ -4,6 +4,7 @@ namespace App\Admin\Controller; use App\Appointment\Entity\Appointment; use App\Auth\Entity\User; +use App\Category\Entity\Category; use App\Clinic\Entity\Clinic; use App\Doctor\Entity\Doctor; use App\Payment\Entity\Payment; @@ -169,17 +170,24 @@ class AdminApiController extends BaseController $page = max(1, (int) $request->query->get('page', 1)); $limit = min(100, max(1, (int) $request->query->get('limit', 15))); $search = trim((string) $request->query->get('search', '')); + $cityId = $request->query->get('city_id'); $qb = $this->em->createQueryBuilder() - ->select('r.uuid, r.fullName, r.mobileNumber, r.city, r.commissionPercent, r.active, r.createdAt') + ->select('r.uuid, r.fullName, r.mobileNumber, r.cityId, r.commissionPercent, r.active, r.createdAt, c.label as city_name') ->from(Representation::class, 'r') + ->leftJoin(Category::class, 'c', 'WITH', 'c.id = r.cityId') ->orderBy('r.createdAt', 'DESC'); if ($search !== '') { - $qb->andWhere('r.fullName LIKE :s OR r.city LIKE :s') + $qb->andWhere('r.fullName LIKE :s OR r.mobileNumber LIKE :s') ->setParameter('s', '%' . $search . '%'); } + if ($cityId !== null && $cityId !== '') { + $qb->andWhere('r.cityId = :cityId') + ->setParameter('cityId', (int) $cityId); + } + $total = (clone $qb)->select('COUNT(r.id)')->getQuery()->getSingleScalarResult(); $rows = $qb->setFirstResult(($page - 1) * $limit)->setMaxResults($limit) @@ -190,7 +198,8 @@ class AdminApiController extends BaseController 'domain' => $r['fullName'], 'full_name' => $r['fullName'], 'mobile_number' => $r['mobileNumber'], - 'city' => $r['city'] ?? '', + 'city_id' => $r['cityId'], + 'city' => $r['city_name'] ?? null, 'commission_percent' => (float) $r['commissionPercent'], 'wallet_balance' => 0, 'is_active' => (bool) $r['active'], diff --git a/src/Representation/Controller/RepresentationController.php b/src/Representation/Controller/RepresentationController.php index 9250aef7..8f96dcb9 100644 --- a/src/Representation/Controller/RepresentationController.php +++ b/src/Representation/Controller/RepresentationController.php @@ -53,7 +53,7 @@ class RepresentationController extends BaseController } $rep = new Representation($user, $fullName); - if (!empty($data['city'])) $rep->setCity($data['city']); + if (isset($data['city_id'])) $rep->setCityId($data['city_id'] ? (int)$data['city_id'] : null); if (!empty($data['commission_percent'])) $rep->setCommissionPercent((string)$data['commission_percent']); if (!empty($data['bank_account'])) $rep->setBankAccount($data['bank_account']); @@ -91,7 +91,7 @@ class RepresentationController extends BaseController $data = json_decode($request->getContent(), true) ?? []; if (array_key_exists('full_name', $data)) $rep->setFullName($data['full_name']); - if (array_key_exists('city', $data)) $rep->setCity($data['city']); + if (array_key_exists('city_id', $data)) $rep->setCityId($data['city_id'] ? (int)$data['city_id'] : null); if (array_key_exists('bank_account', $data)) $rep->setBankAccount($data['bank_account']); if (array_key_exists('commission_percent', $data)) $rep->setCommissionPercent((string)$data['commission_percent']); if (array_key_exists('active', $data)) $rep->setActive((bool)$data['active']); diff --git a/src/Representation/Entity/Representation.php b/src/Representation/Entity/Representation.php index 72c2027a..2869523c 100644 --- a/src/Representation/Entity/Representation.php +++ b/src/Representation/Entity/Representation.php @@ -28,8 +28,8 @@ class Representation #[ORM\Column(type: 'string', length: 20, nullable: true)] private ?string $mobileNumber = null; - #[ORM\Column(type: 'string', length: 255, nullable: true)] - private ?string $city = null; + #[ORM\Column(name: 'city_id', type: 'integer', nullable: true)] + private ?int $cityId = null; #[ORM\Column(name: 'commission_percent', type: 'decimal', precision: 5, scale: 2)] private string $commissionPercent = '10.00'; @@ -55,19 +55,19 @@ class Representation $this->updatedAt = time(); } - public function getId(): ?int { return $this->id; } - public function getUuid(): string { return $this->uuid; } - public function getUser(): User { return $this->user; } - public function getFullName(): string { return $this->fullName; } + public function getId(): ?int { return $this->id; } + public function getUuid(): string { return $this->uuid; } + public function getUser(): User { return $this->user; } + public function getFullName(): string { return $this->fullName; } public function getMobileNumber(): ?string { return $this->mobileNumber; } - public function getCity(): ?string { return $this->city; } + public function getCityId(): ?int { return $this->cityId; } public function getCommissionPercent(): string { return $this->commissionPercent; } - public function getBankAccount(): ?array { return $this->bankAccount; } - public function isActive(): bool { return $this->active; } + public function getBankAccount(): ?array { return $this->bankAccount; } + public function isActive(): bool { return $this->active; } public function setFullName(string $v): self { $this->fullName = $v; $this->touch(); return $this; } public function setMobileNumber(?string $v): self { $this->mobileNumber = $v; $this->touch(); return $this; } - public function setCity(?string $v): self { $this->city = $v; $this->touch(); return $this; } + public function setCityId(?int $v): self { $this->cityId = $v; $this->touch(); return $this; } public function setCommissionPercent(string $v): self { $this->commissionPercent = $v; $this->touch(); return $this; } public function setBankAccount(?array $v): self { $this->bankAccount = $v; $this->touch(); return $this; } public function setActive(bool $v): self { $this->active = $v; $this->touch(); return $this; } @@ -80,7 +80,7 @@ class Representation 'uuid' => $this->uuid, 'full_name' => $this->fullName, 'mobile_number' => $this->mobileNumber, - 'city' => $this->city, + 'city_id' => $this->cityId, 'commission_percent' => $this->commissionPercent, 'bank_account' => $this->bankAccount, 'active' => $this->active,