feat(representation): update representation deactivation logic and enhance API documentation
This commit is contained in:
@@ -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
@@ -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`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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'],
|
||||
|
||||
Reference in New Issue
Block a user