# Admin API

Admins manage the platform, locations, and owner verification.

## Role requirements

- Most admin routes require the `admin` or `super_admin` role.
- Creating another admin requires the `super_admin` role.

## Dashboard

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/dashboard` | Revenue and user-growth summary with filters. |
| GET | `/api/v1/admin/system-health` | Operational health snapshot (API, DB, queue, storage). See [`admin-system-health.md`](../admin-system-health.md). |

### Chart granularity

The dashboard charts (`charts.revenue` and `charts.user_growth`) now support `day`, `week`, and `month` buckets.

| Query param | Type | Default | Description |
|-------------|------|---------|-------------|
| `granularity` | `day` \| `week` \| `month` | `month` | Bucket size for chart series. |
| `from` | date (`Y-m-d`) | — | Inclusive start date. Required with `to`. |
| `to` | date (`Y-m-d`) | — | Inclusive end date. Required with `from`. |
| `months` | integer (1–12) | 6 | Month-bucket shorthand when `from`/`to` are omitted. |

Max ranges for explicit `from`/`to`:

- `day`: 365 days
- `week`: 52 weeks (364 days)
- `month`: 12 months

When `from`/`to` are omitted, `months` behaves as before and returns monthly buckets (`YYYY-MM-01`).

### Date formats per granularity

| Granularity | `date` value | Notes |
|-------------|--------------|-------|
| `day` | `YYYY-MM-DD` | One point per calendar day. |
| `week` | `YYYY-MM-DD` | ISO week start (Monday). |
| `month` | `YYYY-MM-01` | First day of the month. |

### Sample request

```http
GET /api/v1/admin/dashboard?granularity=day&from=2026-07-01&to=2026-07-07
```

### Sample response (day granularity)

```json
{
  "data": {
    "summary": {
      "users": { "value": 42, "change": 5.0 },
      "owners": { "value": 12, "change": 2.0 },
      "buildings": { "value": 8, "change": 1.0 },
      "pending_approvals": { "value": 3, "change": 0.0 },
      "collected": 1250.0,
      "refunds": 50.0,
      "outstanding": 300.0
    },
    "charts": {
      "revenue": [
        { "date": "2026-07-01", "revenue": 0, "bookings": 0 },
        { "date": "2026-07-02", "revenue": 100, "bookings": 1 },
        { "date": "2026-07-03", "revenue": 0, "bookings": 0 },
        { "date": "2026-07-04", "revenue": 0, "bookings": 0 },
        { "date": "2026-07-05", "revenue": 200, "bookings": 2 },
        { "date": "2026-07-06", "revenue": 0, "bookings": 0 },
        { "date": "2026-07-07", "revenue": 0, "bookings": 0 }
      ],
      "user_growth": [
        { "date": "2026-07-01", "value": 40 },
        { "date": "2026-07-02", "value": 41 },
        { "date": "2026-07-03", "value": 41 },
        { "date": "2026-07-04", "value": 41 },
        { "date": "2026-07-05", "value": 42 },
        { "date": "2026-07-06", "value": 42 },
        { "date": "2026-07-07", "value": 42 }
      ]
    }
  }
}
```

### Sample request (month shorthand)

```http
GET /api/v1/admin/dashboard?months=3
```

Returns three monthly points:

```json
{
  "data": {
    "charts": {
      "revenue": [
        { "date": "2026-05-01", "revenue": 5000, "bookings": 10 },
        { "date": "2026-06-01", "revenue": 6200, "bookings": 12 },
        { "date": "2026-07-01", "revenue": 3100, "bookings": 6 }
      ],
      "user_growth": [
        { "date": "2026-05-01", "value": 38 },
        { "date": "2026-06-01", "value": 40 },
        { "date": "2026-07-01", "value": 42 }
      ]
    }
  }
}
```

## Reservations & buildings

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/reservations` | List all reservations. |
| GET | `/api/v1/admin/buildings` | List all buildings. |

## Locations

Admins have full CRUD over locations. Public read-only endpoints are documented in [`customer.md`](customer.md).

| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/admin/cities` | Create a city. |
| PUT | `/api/v1/admin/cities/{city}` | Update a city. |
| DELETE | `/api/v1/admin/cities/{city}` | Delete a city. |
| POST | `/api/v1/admin/countries` | Create a country. |
| PUT | `/api/v1/admin/countries/{country}` | Update a country. |
| DELETE | `/api/v1/admin/countries/{country}` | Delete a country. |
| POST | `/api/v1/admin/currencies` | Create a currency. |
| PUT | `/api/v1/admin/currencies/{currency}` | Update a currency. |
| DELETE | `/api/v1/admin/currencies/{currency}` | Delete a currency. |
| POST | `/api/v1/admin/regions` | Create a region. |
| PUT | `/api/v1/admin/regions/{region}` | Update a region. |
| DELETE | `/api/v1/admin/regions/{region}` | Delete a region. |

## Users & owners

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/owners` | List owners. |
| GET | `/api/v1/admin/owners/{owner}` | Owner detail page (profile, buildings, employees, revenue, audits). |
| POST | `/api/v1/owners/{owner}/verify` | Verify, suspend, or reject an owner profile. |
| GET | `/api/v1/admin/employees` | List all employees. Supports `?owner_id={id}`. |
| GET | `/api/v1/admin/notifications` | List all notifications. |
| POST | `/api/v1/admins` | Create a new admin (`super_admin` only). |
| GET | `/api/v1/customers` | List customers (legacy thin response). |
| DELETE | `/api/v1/customers/{customer}` | Delete a customer. |

### Owner detail

`GET /api/v1/admin/owners/{owner}` returns the full owner profile for review.

| Included data | Notes |
|---------------|-------|
| `user` | Name, email, WhatsApp number, and role. |
| `reviewed_by` | Admin who last changed the verification status. |
| `buildings` | Latest 15 buildings. |
| `employees` | Latest 15 employees. |
| `revenue_summary` | Collected, refunds, outstanding, net, transaction count, and currency code across all owner buildings. |
| `audits` | Last 10 audit records for the owner model. |

```json
{
  "data": {
    "id": 1,
    "status": "active",
    "is_verified": true,
    "phone": "+218910000000",
    "whatsapp_number": "+218910000000",
    "logo_url": "https://example.com/storage/1/logo.jpg",
    "buildings_count": 3,
    "employees_count": 2,
    "reviewed_by": { "id": 5, "name": "Admin User" },
    "approved_at": "2026-07-10T12:00:00+00:00",
    "suspension_reason": null,
    "user": {
      "id": 2,
      "name": "Owner Name",
      "email": "owner@example.ly",
      "whatsapp_number": "+218910000000",
      "roles": [{ "id": 3, "name": "owner" }]
    },
    "buildings": [...],
    "employees": [...],
    "revenue_summary": {
      "collected": 1250.0,
      "refunds": 50.0,
      "outstanding": 300.0,
      "net": 1200.0,
      "transactions_count": 15,
      "currency_code": "USD"
    },
    "audits": [
      {
        "id": 1,
        "event": "updated",
        "old_values": { "is_verified": false },
        "new_values": { "is_verified": true },
        "user": { "id": 5, "name": "Admin User" },
        "created_at": "2026-07-10T12:00:00+00:00"
      }
    ],
    "created_at": "2026-07-01T10:00:00+00:00"
  }
}
```

### Owner verification

`POST /api/v1/owners/{owner}/verify` sets the owner's verification status.

| Field | Type | Rules |
|-------|------|-------|
| `status` | string | required, `in:pending,active,suspended` |
| `suspension_reason` | string | optional; stored when `status=suspended` |

`active` marks the owner as verified and records `approved_at`; `suspended` sets `is_verified` to `false` and optionally records a `suspension_reason`; `pending` leaves the profile unverified.

## Customers

Admin customer DataTable and detail. All routes require `admin` or `super_admin`.

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/customers` | List customers with filters and counts. |
| GET | `/api/v1/admin/customers/{customer}` | Customer detail + last 15 reservations. |

### List filters

| Param | Type | Description |
|-------|------|-------------|
| `q` | string | Searches name, email, phone, and WhatsApp. |
| `is_verified` | `0` \| `1` | Filter by `users.is_verified`. |
| `has_wallet` | `0` \| `1` | `1` = wallet balance > 0; `0` = wallet balance = 0. |
| `created_from` | date (`Y-m-d`) | Inclusive registration start. |
| `created_to` | date (`Y-m-d`) | Inclusive registration end. |
| `per_page` | integer (1–100) | Page size; default 15. |

### List response item

```json
{
  "id": 1,
  "phone": "+15551234567",
  "whatsapp_number": "+15551234567",
  "wallet": "50.00",
  "reservations_count": 3,
  "user": {
    "id": 1,
    "name": "Customer A",
    "email": "a@example.com",
    "is_verified": true
  },
  "created_at": "2026-07-01T10:00:00+00:00"
}
```

> **Field-name note:** the API returns `wallet` (decimal string). The frontend may display it as `wallet_balance`.

### Detail response

`GET /api/v1/admin/customers/{customer}` returns the profile, the latest 15 reservations, and the latest 10 wallet transactions. For the full reservation history, use `GET /api/v1/admin/reservations?customer_id={id}`.

```json
{
  "data": {
    "id": 1,
    "phone": "+15551234567",
    "whatsapp_number": "+15551234567",
    "wallet": "50.00",
    "reservations_count": 3,
    "user": { "id": 1, "name": "Customer A", "email": "a@example.com", "is_verified": true },
    "reservations": [
      {
        "id": 10,
        "reservation_number": "RSV-001",
        "status": "checked_out",
        "payment_status": "paid",
        "total_price": 200.0,
        "check_in_date": "2026-07-01",
        "check_out_date": "2026-07-03",
        "unit": { "id": 2, "name_or_number": "A-101" },
        "building": { "id": 3, "name": "Building A" }
      }
    ],
    "wallet_transactions": [
      {
        "id": 1,
        "type": "credit",
        "amount": "50.00",
        "balance_after": "50.00",
        "description": "Refund to wallet",
        "created_at": "2026-07-01T12:00:00+00:00"
      }
    ],
    "created_at": "2026-07-01T10:00:00+00:00"
  }
}
```

### Wallet transactions

`GET /api/v1/admin/customers/{customer}/wallet-transactions` returns the full paginated wallet ledger.

| Field | Type | Notes |
|-------|------|-------|
| `type` | `credit` \| `debit` | — |
| `amount` | decimal string | Transaction amount. |
| `balance_after` | decimal string | Customer wallet balance after the transaction. |
| `description` | string \| null | — |
| `source` | object \| null | `{ type, id }` of the related payment/reservation, if recorded. |

Transactions are recorded automatically when wallet balance changes: refunds credit the wallet; wallet payments debit it.

## User management

Full user CRUD for platform accounts. Requires `admin` or `super_admin`. `super_admin` is required to assign the `super_admin` role.

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/users` | List users (paginated, with filters). |
| GET | `/api/v1/admin/users/{user}` | Show a single user. |
| POST | `/api/v1/admin/users` | Create a user. |
| PUT | `/api/v1/admin/users/{user}` | Update a user. |
| PATCH | `/api/v1/admin/users/{user}/status` | Suspend or activate a user. |
| DELETE | `/api/v1/admin/users/{user}` | Soft-delete a user. |

### Request body (create / update)

| Field | Type | Rules | Notes |
|-------|------|-------|-------|
| `name` | string | required (create), sometimes (update), max:255 | — |
| `email` | string | required / sometimes, email, unique | Update ignores the current user. |
| `whatsapp_number` | string | nullable / sometimes, unique | Update ignores the current user. |
| `password` | string | required (create), sometimes (update), `Password::defaults()` | Update only when provided. |
| `role` | string | required / sometimes, `in:customer,owner,employee,admin` | `super_admin` may also use `super_admin`. |
| `is_verified` | boolean | nullable / sometimes | Defaults to `false` on create. |

### Status endpoint

| Field | Type | Rules |
|-------|------|-------|
| `status` | string | required, `in:active,suspended` |

### Response shape

```json
{
  "data": {
    "id": 1,
    "name": "Ahmed Salah",
    "email": "ahmed@example.ly",
    "whatsapp_number": "+218910000000",
    "role": "owner",
    "is_verified": true,
    "status": "active",
    "wallet_balance": null,
    "employee_status": null,
    "created_at": "2026-07-14T00:00:00+00:00"
  }
}
```

### Suspend / activate semantics

Turista has three separate suspension concepts:

| Level | Field / mechanism | Endpoint | Notes |
|-------|-------------------|----------|-------|
| **User** | `users.status` (`active` \| `suspended`) | `PATCH /api/v1/admin/users/{user}/status` | Blocks the account from authenticated routes. |
| **Owner** | `owners.is_verified` | `POST /api/v1/owners/{owner}/verify` | Approval flow for owner profiles. |
| **Employee** | `employees.status` (`active` \| `suspended`) | Owner-scoped employee management | Controlled by the employee's owner. |

## Roles & permissions

Manage Spatie roles and the platform permission matrix. Requires `admin` or `super_admin`.

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/admin/roles` | List roles with permission/user counts. |
| GET | `/api/v1/admin/roles/{role}` | Show role + permissions. Add `?include=users` for up to 15 holders. |
| POST | `/api/v1/admin/roles` | Create a role. |
| PUT | `/api/v1/admin/roles/{role}` | Update role name and sync permissions. |
| DELETE | `/api/v1/admin/roles/{role}` | Delete a role. |
| GET | `/api/v1/admin/permissions` | Full permission catalog for the checkbox matrix. |
| GET | `/api/v1/admin/roles/{role}/users` | Paginated users holding the role. |

### Request body (create / update)

| Field | Type | Rules |
|-------|------|-------|
| `name` | string | required (create) / sometimes (update), max:255, unique per `guard_name=api` |
| `permissions` | string[] | nullable / sometimes; each must be an existing Spatie permission name |

`permissions` is a replace-all sync: omitted on update leaves permissions untouched; provided replaces the entire set.

### System role guards

These seeded roles cannot be renamed or deleted:

`super_admin`, `admin`, `owner`, `employee`, `customer`

### Permission catalog sample

```json
{
  "data": {
    "actions": ["view", "create", "update", "delete"],
    "resources": [
      {
        "key": "buildings",
        "label": "Buildings",
        "permissions": {
          "view": "buildings.view",
          "create": "buildings.create",
          "update": "buildings.update",
          "delete": "buildings.delete"
        }
      }
    ]
  }
}
```

### Role response sample

```json
{
  "data": {
    "id": 6,
    "name": "content_manager",
    "guard_name": "api",
    "permissions_count": 4,
    "users_count": 2,
    "permissions": ["buildings.view", "units.view", "reservations.view", "customers.view"],
    "permission_ids": [1, 5, 9, 13],
    "created_at": "2026-07-14T00:00:00+00:00"
  }
}
```

For detailed request/response schemas, generate Laravel Request Docs (see [`auto-generated.md`](../auto-generated.md)).
