Custom cards
Custom cards
Any package (or the app itself) can contribute pages and cards. A card is a Livewire component: PHP + Blade, no JavaScript build, using whatever PromQL/TraceQL/LogQL fits — including metrics and spans the core UI knows nothing about.
// e.g. in cboxdk/queue-autoscale's service provider
use Cbox\TelemetryUi\Facades\TelemetryUi;
public function boot(): void
{
if (class_exists(TelemetryUi::class)) {
TelemetryUi::page('autoscale', 'Autoscale', group: 'Activity');
TelemetryUi::card(\Cbox\QueueAutoscale\Ui\ScalingDecisions::class, page: 'autoscale');
TelemetryUi::card(\Cbox\QueueAutoscale\Ui\WorkerFleet::class, page: 'autoscale');
}
}
That's all the wiring you need — no routes to register. The dashboard's
catch-all route resolves any registered page slug to a generic page view that
renders the page's cards, so /telemetry-ui/autoscale works the moment you
page('autoscale', …). Register the cards on that slug and they appear.
page() also takes an optional detectMetric: name pattern: the page only
shows when a matching metric exists in the backend — the same autodetection
the built-in Statamic group uses. group: places it under a sidebar heading,
icon: sets its glyph.
use Cbox\TelemetryUi\Cards\Card;
final class ScalingDecisions extends Card
{
public function render(): View
{
[$start, $end] = $this->range();
return view('queue-autoscale::cards.scaling-decisions', [
'series' => $this->toChartSeries($this->metrics()->queryRange(
'sum by (queue) (rate(autoscale_scaling_events_total[5m])) * 60',
$start, $end,
), label: 'queue'),
]);
}
}
<x-telemetry-ui::card title="Scaling decisions" span="2">
<x-telemetry-ui::chart :series="$series" type="bar" unit="events/min" />
</x-telemetry-ui::card>
Interactive cards
Cards are ordinary Livewire components, so actions and forms just work:
final class ExceptionActions extends Card
{
public function createTicket(string $exceptionClass): void
{
// call Linear/GitHub, dispatch a job, open a modal…
}
}
Long-running/AI output can stream with wire:stream. This is the intended
home for ticket-creation and AI-resolve flows on the roadmap.
Conventions
- Query through
$this->metrics()/$this->traces()/$this->logs()so named connections and tenancy keep working. - Catch
SourceExceptionand render an inline error state; a broken backend must not take the page down. - Respect
$this->range(); don't hardcode time windows.
Add, replace, remove
The registry supports more than appending — useful for white-labelling or embedding, where you want to reshape the built-in dashboard:
use Cbox\TelemetryUi\Facades\TelemetryUi;
// Add — append a card to any page (default: the dashboard).
TelemetryUi::card(MyCard::class, page: 'requests');
// Replace — swap a page's whole card list for your own (a branded dashboard).
TelemetryUi::setCards('dashboard', [MyHeadline::class, MyChart::class]);
// Remove — drop a single built-in card…
TelemetryUi::removeCard(\Cbox\TelemetryUi\Cards\Builtin\JobsOverview::class, 'dashboard');
// …or a whole page/section from the sidebar + routing.
TelemetryUi::removePage('users');
Re-registering a page slug with TelemetryUi::page(...) overwrites it, so you
can relabel or regroup a built-in page too. To extend a built-in card instead
of replacing it, subclass it (the built-in overview cards are not final — the
detail pages do exactly this) and register your subclass.