# What the Existing Livewire Admin Panel Already Has

The current `app/Livewire/Pages/Restaurant/*` pages (`OrdersRestaurant`, `ProductsRestaurant`,
etc.) are **admin back-office CRUD** — an admin browsing into any one restaurant's data. They are
not restaurant-self-service, and not real-time. This is what already exists to be aware of, not a
template to copy.

## Directory layout

- `app/Livewire/Pages/{Domain}/{Action}{Domain}.php` — e.g. `Pages/Restaurant/OrdersRestaurant.php`
- `app/Livewire/Components/` — reusable pieces embedded in layouts/pages: `TopBar`, `SideBar`,
  `Flash`, `NotificationsDropDown`, `OrderOverviewCards`
- `app/Livewire/Forms/` — `Livewire\Form` objects for pages with real multi-field validation (e.g.
  `ProductForm`)
- Blade views under `resources/views/pages/{domain}/{kebab-case-action}.blade.php`

## Routing & permissions — two separate namespaces

`routes/web.php` gates every admin route with permission names like `view restaurant orders`,
`view restaurant products`, `view restaurant wallet`, `edit restaurants`. These are **admin-only**
permissions from `PermissionSeeder`, scoped to an admin browsing into one restaurant's back office.

They are a **different permission namespace** from what the `restaurant`/`staff` roles actually
hold (`view orders`, `accept orders`, `assign driver` — see
[06-tenancy-permissions-policies.md](06-tenancy-permissions-policies.md)). A restaurant owner or
staff member was never granted `view restaurant orders` — only `view orders`. Gating a
restaurant-facing route with the admin-style permission name locks the restaurant out of its own
page.

The existing admin routes also take `{restaurant}` as an open route parameter — an admin is
trusted to browse into any restaurant by id. A restaurant-facing route has no such trust: the
restaurant has to come from the logged-in user, not the URL.

## Flash messages

Two paths already exist:

- `session()->flash('success'|'failed', $message)` — rendered by the `Flash` component mounted in
  the layout.
- A browser-event path: Echo listeners in `resources/js/app.js` dispatch a `flash` browser event
  (`{type, title, message}`) that a JS toast helper (`flasher.js`) picks up directly. This is the
  path used when the trigger originates from a WebSocket event rather than a PHP action.

## What's already wired to real-time

`resources/js/app.js` subscribes to `App.Models.User.{id}` and, for several existing notification
types (new order, driver accepted an order, order delivered, a new restaurant/driver joining,
order auto-rejected, no driver accepted), dispatches a named browser event
(`refresh-index-orders`, `refresh-index-restaurants`, `refresh-index-drivers`) that the relevant
index page listens for to trigger a refresh. `NotificationsDropDown` listens for the same set of
events.

Nothing today subscribes to `order.{id}`, `restaurant.{id}.dispatch`, `driver.{id}`, or
`conversation.{id}` — those channels exist on the backend (see
[02-broadcast-reference.md](02-broadcast-reference.md)) but nothing in the Livewire panel consumes
them yet.

## What already exists to reuse rather than rebuild

- Order status badges/labels/options come from `OrderStatusEnum` directly — no separate lookup
  table or repeated `match()` logic elsewhere.
- The notification bell/unread count is `NotificationsDropDown`.
- PDF/Excel export of order/driver/restaurant lists already exists via `PrintService`/
  `ExportService`, wired into the existing admin pages.
- Pagination + search/status/date filtering on an order list already exists as a pattern in
  `OrdersRestaurant` — status options, date-range filtering, and a trashed/soft-deleted toggle are
  all already solved there.
