268 lines
6.1 KiB
Markdown
268 lines
6.1 KiB
Markdown
# Location API (Province & City)
|
|
|
|
> **Prefix:** `/api/v1/provinces`, `/api/v1/cities`, `/api/v1/admin/province`, `/api/v1/admin/city`
|
|
|
|
---
|
|
|
|
## GET `/api/v1/provinces`
|
|
|
|
List all active provinces.
|
|
|
|
**Permission:** `PUBLIC`
|
|
|
|
### Response `200`
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{ "id": 1, "name": "تهران", "status": "active", "weight": 1 },
|
|
{ "id": 2, "name": "اصفهان", "status": "active", "weight": 2 }
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## GET `/api/v1/cities`
|
|
|
|
List cities, optionally filtered by province.
|
|
|
|
**Permission:** `PUBLIC`
|
|
|
|
### Query Parameters
|
|
| Param | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `province_id` | integer | ❌ | Filter cities by province |
|
|
|
|
### Response `200`
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{ "id": 10, "name": "تهران", "province_id": 1, "status": "active", "weight": 1 },
|
|
{ "id": 11, "name": "کرج", "province_id": 1, "status": "active", "weight": 2 }
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## GET `/api/v1/admin/provinces`
|
|
|
|
List all provinces with pagination (admin view — includes inactive).
|
|
|
|
**Permission:** `ROLE_ADMIN`
|
|
|
|
### Query Parameters
|
|
| Param | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `page` | integer | ❌ | Default: 1 |
|
|
| `limit` | integer | ❌ | Default: 20 |
|
|
| `search` | string | ❌ | Search in name |
|
|
|
|
### Response `200`
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [ ... ],
|
|
"meta": { "totalRecords": 31, "totalPages": 2, "currentPage": 1 }
|
|
}
|
|
```
|
|
|
|
### Errors
|
|
| Code | HTTP | Description |
|
|
|------|------|-------------|
|
|
| `ERR_AUTH_001` | 401 | Missing token |
|
|
| `ERR_AUTH_006` | 403 | Not admin |
|
|
|
|
---
|
|
|
|
## GET `/api/v1/admin/cities`
|
|
|
|
List all cities with pagination (admin view).
|
|
|
|
**Permission:** `ROLE_ADMIN`
|
|
|
|
### Query Parameters
|
|
| Param | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `page` | integer | ❌ | Default: 1 |
|
|
| `limit` | integer | ❌ | Default: 20 |
|
|
| `search` | string | ❌ | Matches `name`, `site_name`, `domain`, **or the parent province's name** (case-insensitive `LIKE`) |
|
|
| `province_id` | integer | ❌ | Filter by province |
|
|
|
|
### Response `200`
|
|
Paginated city list. Each row already carries `province_name` alongside `province_id`.
|
|
|
|
---
|
|
|
|
## POST `/api/v1/admin/province`
|
|
|
|
Create a new province.
|
|
|
|
**Permission:** `ROLE_ADMIN`
|
|
|
|
### Request Body (`application/json`)
|
|
```json
|
|
{
|
|
"name": "تهران",
|
|
"status": "active",
|
|
"weight": 1
|
|
}
|
|
```
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `name` | string | ✅ | Province name |
|
|
| `status` | string | ❌ | `"active"` (default) or `"inactive"` |
|
|
| `weight` | integer | ❌ | Sort weight |
|
|
|
|
### Response `201`
|
|
Province object.
|
|
|
|
### Errors
|
|
| Code | HTTP | Description |
|
|
|------|------|-------------|
|
|
| `ERR_AUTH_001` | 401 | Missing token |
|
|
| `ERR_AUTH_006` | 403 | Not admin |
|
|
| `ERR_VALIDATION_002` | 422 | Missing name |
|
|
|
|
---
|
|
|
|
## PATCH `/api/v1/admin/province/{id}`
|
|
|
|
Update a province.
|
|
|
|
**Permission:** `ROLE_ADMIN`
|
|
|
|
### Path Parameters
|
|
| Param | Type | Description |
|
|
|-------|------|-------------|
|
|
| `id` | integer | Province ID |
|
|
|
|
All fields optional.
|
|
|
|
### Response `200`
|
|
Updated province object.
|
|
|
|
### Errors
|
|
| Code | HTTP | Description |
|
|
|------|------|-------------|
|
|
| `ERR_AUTH_001` | 401 | Missing token |
|
|
| `ERR_AUTH_006` | 403 | Not admin |
|
|
| `ERR_NOT_FOUND_001` | 404 | Province not found |
|
|
|
|
---
|
|
|
|
## DELETE `/api/v1/admin/province/{id}`
|
|
|
|
Delete a province.
|
|
|
|
**Permission:** `ROLE_ADMIN`
|
|
|
|
### Response `200`
|
|
```json
|
|
{ "success": true, "data": { "message": "استان حذف شد" } }
|
|
```
|
|
|
|
---
|
|
|
|
## POST `/api/v1/admin/city`
|
|
|
|
Create a new city.
|
|
|
|
**Permission:** `ROLE_ADMIN`
|
|
|
|
### Request Body (`application/json`)
|
|
```json
|
|
{
|
|
"name": "تهران",
|
|
"site_name": "تهران نوبت",
|
|
"province_id": 1,
|
|
"status": "active",
|
|
"weight": 1
|
|
}
|
|
```
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `name` | string | ✅ | City name |
|
|
| `site_name` | string | ❌ | Public site name shown on the city's booking website (e.g. `"یزد نوبت"`) |
|
|
| `title` | string | ❌ | SEO meta title for the city's booking website (e.g. `"نوبتدهی آنلاین پزشکان یزد | یزد نوبت"`) |
|
|
| `province_id` | integer | ❌ | Parent province ID |
|
|
| `status` | string | ❌ | `"active"` or `"inactive"` |
|
|
| `weight` | integer | ❌ | Sort weight |
|
|
| `contact_phone`, `email`, `description`, `slogan`, `domain`, `keywords`, `footer_description`, `social_media`, `logo_url` | mixed | ❌ | SEO / branding fields returned in the city object |
|
|
|
|
### Response `201`
|
|
City object.
|
|
|
|
### Errors
|
|
| Code | HTTP | Description |
|
|
|------|------|-------------|
|
|
| `ERR_AUTH_001` | 401 | Missing token |
|
|
| `ERR_AUTH_006` | 403 | Not admin |
|
|
| `ERR_VALIDATION_002` | 422 | Missing name |
|
|
|
|
---
|
|
|
|
## PATCH `/api/v1/admin/city/{id}`
|
|
|
|
Update a city.
|
|
|
|
**Permission:** `ROLE_ADMIN`
|
|
|
|
All body fields optional.
|
|
|
|
### Response `200`
|
|
Updated city object.
|
|
|
|
---
|
|
|
|
## DELETE `/api/v1/admin/city/{id}`
|
|
|
|
Delete a city.
|
|
|
|
**Permission:** `ROLE_ADMIN`
|
|
|
|
### Response `200`
|
|
```json
|
|
{ "success": true, "data": { "message": "شهر حذف شد" } }
|
|
```
|
|
|
|
---
|
|
|
|
## GET `/api/v1/categorys/{bundle}` *(Legacy)*
|
|
|
|
Legacy endpoint that proxies to the new endpoints.
|
|
|
|
**Permission:** `PUBLIC`
|
|
|
|
### Path Parameters
|
|
| Bundle | Maps to |
|
|
|--------|---------|
|
|
| `state` | `/api/v1/provinces` |
|
|
| `city` | `/api/v1/cities` |
|
|
| `specially_doctor` | `/api/v1/specialties` |
|
|
| `doctor_services` | `/api/v1/doctor-services` |
|
|
| `insurance_type` | `/api/v1/insurances?type=basic` |
|
|
| `supplementary_insurance` | `/api/v1/insurances?type=supplementary` |
|
|
| `tag` | `/api/v1/tags` |
|
|
|
|
> ⚠️ Response is **triple-nested**: `data?.data?.data ?? []`
|
|
> Note: `categorys` (not `categories`) is intentional — legacy route name.
|
|
|
|
---
|
|
|
|
## Bulk import / export
|
|
|
|
Full-table JSON export and strict wipe+replace import for this category live under
|
|
`/api/v1/admin/categories/{bundle}/{export|import}` — see [category-import.md](category-import.md).
|
|
|
|
|
|
### Sorting by id
|
|
|
|
The admin list endpoint accepts `sort=id&order=asc|desc` to order by `id`
|
|
(used by the admin «دستهبندیها» page when clicking the «شناسه» column).
|
|
Without `sort`, the default ordering (weight/name) is unchanged.
|