feat(representation): update representation deactivation logic and enhance API documentation

This commit is contained in:
hamed
2026-06-19 00:46:34 +03:30
parent 0f3bf68fdd
commit 2943e29663
3 changed files with 41 additions and 19 deletions
+16 -13
View File
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { EyeIcon, TrashIcon, PlusIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { EyeIcon, NoSymbolIcon, PlusIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
@@ -75,10 +75,11 @@ export default function RepresentationsPage() {
onError: (err: Error) => toast.error(err.message),
});
const deleteMutation = useMutation({
mutationFn: (r: Representation) => api.delete<ApiResponse<null>>(`/api/v1/representation/${r.uuid}`),
const deactivateMutation = useMutation({
mutationFn: (r: Representation) =>
api.patch<ApiResponse<Representation>>(`/api/v1/representation/${r.uuid}`, { active: false }),
onSuccess: () => {
toast.success('نماینده حذف شد');
toast.success('نماینده غیرفعال شد');
setDeleteTarget(null);
qc.invalidateQueries({ queryKey: ['representations'] });
},
@@ -146,10 +147,12 @@ export default function RepresentationsPage() {
className="mini-btn" title="مشاهده">
<EyeIcon style={{ width: 15, height: 15 }} />
</button>
<button onClick={() => setDeleteTarget(rep)}
className="mini-btn danger" title="حذف">
<TrashIcon style={{ width: 15, height: 15 }} />
</button>
{(rep.is_active ?? rep.active) && (
<button onClick={() => setDeleteTarget(rep)}
className="mini-btn danger" title="غیرفعال‌سازی">
<NoSymbolIcon style={{ width: 15, height: 15 }} />
</button>
)}
</>
)}
/>
@@ -206,12 +209,12 @@ export default function RepresentationsPage() {
<ConfirmDialog
open={!!deleteTarget}
title="حذف نماینده"
message={`آیا از حذف نماینده "${deleteTarget?.full_name}" اطمینان دارید؟`}
confirmLabel="حذف"
title="غیرفعال‌سازی نماینده"
message={`آیا از غیرفعال‌سازی نماینده "${deleteTarget?.full_name}" اطمینان دارید؟ این نماینده دیگر فعال نخواهد بود.`}
confirmLabel="غیرفعال‌سازی"
danger
loading={deleteMutation.isPending}
onConfirm={() => deleteTarget && deleteMutation.mutate(deleteTarget)}
loading={deactivateMutation.isPending}
onConfirm={() => deleteTarget && deactivateMutation.mutate(deleteTarget)}
onCancel={() => setDeleteTarget(null)}
/>
</div>
+21 -3
View File
@@ -621,12 +621,30 @@ List all representations.
| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `page` | integer | ❌ | Default: 1 |
| `limit` | integer | ❌ | Default: 20 |
| `search` | string | ❌ | Search by name |
| `limit` | integer | ❌ | Default: 15 |
| `search` | string | ❌ | Search by name or mobile (representation's or linked user's) |
| `city_id` | integer | ❌ | Filter by city |
### Response `200`
Paginated representation list.
Paginated representation list. Each item:
```json
{
"id": 3,
"uuid": "...",
"full_name": "حامد حسینی",
"mobile_number": "09120671756",
"city_id": 132,
"city": "یزد",
"commission_percent": 10.0,
"wallet_balance": 0,
"is_active": true,
"created_at": "2026-06-18T..."
}
```
> `mobile_number` falls back to the linked user's mobile when the representation's own `mobile_number` column is empty.
> **Deactivation, not deletion:** the admin panel deactivates a representation via `PATCH /api/v1/representation/{uuid}` with `{ "active": false }` rather than calling `DELETE`.
---
+4 -3
View File
@@ -921,13 +921,14 @@ class AdminApiController extends BaseController
$cityId = $request->query->get('city_id');
$qb = $this->em->createQueryBuilder()
->select('r.id, r.uuid, r.fullName, r.mobileNumber, r.cityId, r.commissionPercent, r.active, r.createdAt, c.name as city_name')
->select('r.id, r.uuid, r.fullName, r.mobileNumber, u.mobileNumber as user_mobile, r.cityId, r.commissionPercent, r.active, r.createdAt, c.name as city_name')
->from(Representation::class, 'r')
->join('r.user', 'u')
->leftJoin(City::class, 'c', 'WITH', 'c.id = r.cityId')
->orderBy('r.createdAt', 'DESC');
if ($search !== '') {
$qb->andWhere('r.fullName LIKE :s OR r.mobileNumber LIKE :s')
$qb->andWhere('r.fullName LIKE :s OR r.mobileNumber LIKE :s OR u.mobileNumber LIKE :s')
->setParameter('s', '%' . $search . '%');
}
@@ -946,7 +947,7 @@ class AdminApiController extends BaseController
'uuid' => $r['uuid'],
'domain' => $r['fullName'],
'full_name' => $r['fullName'],
'mobile_number' => $r['mobileNumber'],
'mobile_number' => $r['mobileNumber'] ?: ($r['user_mobile'] ?? null),
'city_id' => $r['cityId'],
'city' => $r['city_name'] ?? null,
'commission_percent' => (float) $r['commissionPercent'],