Skip to content

Several panels

An application can have several panels: one for each group of users with its own purpose, look and rules.

public array $panels = [
\App\Panels\AdminPanel::class, // /admin – back office
\App\Panels\PortalPanel::class, // /portal – self service for all employees
];
final class AdminPanel extends Panel
{
public function id(): string { return 'admin'; }
public function brand(): string { return 'ACME Back Office'; }
public function primaryColor(): ?string { return '#0f3d76'; }
public function permission(): ?string { return 'admin.access'; }
public function requiresMfa(): bool { return true; }
public function resources(): array
{
return [PersonResource::class, RequestResource::class, ...Access::resources()];
}
}
final class PortalPanel extends Panel
{
public function id(): string { return 'portal'; }
public function brand(): string { return 'ACME Portal'; }
public function primaryColor(): ?string { return '#15803d'; }
public function resources(): array
{
return [MyRequestResource::class];
}
public function pages(): array
{
return [Dashboard::class, ...Access::pages()];
}
}
  • The back office is closed to users without admin.access and needs a second factor.
  • The portal is open to every user who logged in; MyRequestResource restricts its query to the requests of the user (scopeQuery()).
Shared Per panel
login, users, roles, tenants path, brand, colour, assets
models, data, audit log resources, pages, navigation, widgets
permissions of resources (invoices.view) permissions of pages (admin.page.reports) and of the panel
notifications component tokens (a token of one panel is not valid in another)
saved views and widget layouts of the users

Permissions of a resource are named by its slug, not by the panel: a role with requests.view may list requests in every panel that registers a resource with that slug. Use different slugs when the panels must differ. Files of records belong to the resource class, so two resource classes never share attachments.

A resource class can be registered in several panels. It is instantiated per panel, builds its URLs from the panel and applies the same permissions and policies. Restrict it per panel with a subclass:

final class MyRequestResource extends RequestResource
{
protected ?string $slug = 'my-requests';
public function scopeQuery(Model $model): void
{
$model->where('requester_id', auth()->id());
}
}

The API publishes the resources of the panels listed in Config\Anvil::$apiPanels under /api/v1/<resource slug>.