# Admin Sections

## Register Navigation and Access

PHP owns section IDs, labels, ordering, and capabilities. The toolbar and the localized `flowcrafterAdmin.sections` list use the same capability-filtered registry. IDs are unique lowercase slugs and stay in the URL hash, so existing bookmarks keep working.

```php
add_action( 'flowcrafter_register_admin_sections', static function ( string $registry ): void {
    $registry::register( 'my-inventory', __( 'My Inventory', 'my-plugin' ), 25, 'gravityforms_edit_forms' );
} );
```

Built-in orders are Workflows `0`, Pending Tasks `10`, Activity `20`, and Schedules `30`. Gravity Forms capabilities (`gravityforms_*`) honor GF's `gform_full_access` permission, matching the host page and REST controller. Other capabilities use WordPress's direct check and are not bypassed by GF full access. Register through the action, not before it. A capability controls navigation, not endpoint authorization: protect every REST route and ability independently.

## Enqueue and Register Content

Use `flowcrafter_admin_scripts` to enqueue a separate admin bundle with `flowcrafter-admin` as a script dependency. This runs after the host's localization callback on the inventory page, not on the global editor route. Do not depend on or load the editor bundle. Externalize React through WordPress's dependency-extraction plugin so extension hooks use the same React runtime as the host.

```tsx
const admin = window.flowcrafterAdminAPI;
admin.registerAdminSection('my-inventory', MyInventory);
```

`registerAdminSection(id, component)` supplies content only; it cannot add PHP navigation or bypass capabilities. Duplicate registrations throw instead of replacing another owner. Registration is reactive: bundles can register before or after the app mounts. If PHP advertised a section but its bundle is absent, the selected section shows an explicit load error. Late registration replaces that error without navigation or a reload. Unrecognized or unauthorized hashes fall back to the first allowed section.

## Reuse the Host List Table

`window.flowcrafterAdminAPI` exposes:

| Member | Contract |
|---|---|
| `registerAdminSection(id, component)` | Register a React component for a PHP section ID. |
| `ListTable<Row>` | Host list-table component, including search chrome, sort headers, loading skeletons, empty/no-match rows, pagination, row actions, selection, bulk actions, and mobile row toggles. |
| `useListTableState<Row>(options)` | Client-side search, filter tabs, sorting, pagination, and selection. Pass the full fetched row set, stable row IDs, columns, optional search values/filter predicates, initial sort, and per-page count. |
| `formatDateTime(date)` | The same timestamp formatting as built-in inventories. |

The canonical TypeScript contract is exported from `js/src/admin/api.ts`, including `AdminAPI`, `AdminSection`, list-table types, and `UseListTableStateOptions`. An independently built peer may mirror the part it consumes, but must keep its declarations aligned. Runtime imports from a sibling plugin's source are not supported.

An extension owns fetching, REST errors/retry UI, copy failures, and other product-specific interactions. Pass `isLoading` and an `emptyState` to the shared table; do not recreate a simplified table that loses search, sorting, pagination, or responsive behavior.

## Inventory and Ability Ownership

Extensions may subclass `Flowcrafter\Inventory_Controller` and reuse these protected helpers:

- `workflows_with_node_type(string $contract): array`: indexed lookup of workflows whose registered node handlers implement an interface or extend a class.
- `get_last_node_log_at(int $workflow_id, string $node_id): ?string`: latest recorded node execution timestamp across runs (UTC). Requests rejected before execution are not recorded.
- `build_editor_url(...)`, `register_route(...)`, and `check_admin_permission()` from `Abstract_Workflow_Controller`.

The extension registers its routes on `rest_api_init`, formats its own rows, and registers abilities on `wp_abilities_api_init`. It may reuse `Ability_Schemas` and `Ability_Permissions`; load these only when Flowcrafter is present. No product-specific registry is required in core.

API Alchemist owns the Webhooks section and its inventory route, ability, row types, and styles. These administrative APIs use API Alchemist's namespace, not Flowcrafter's; core does not carry provider-specific route typings, query keys, or mock endpoints. The public delivery URLs and saved graph shapes remain unchanged so existing senders keep working. Without API Alchemist there is no Webhooks inventory section or ability; core's advertised Webhook Trigger offer remains available in the editor.
