Skip to content
Brok UI

Loading…

No results

Map Blocks

Open source

Interactive MapLibre maps — analytics overview, logistics network, geographic heatmap and live delivery tracking.

Preview

resources/views/blocks/maps-delivery-tracker.blade.php Blade
@props([
    // Order identity + live status.
    'orderId' => '#FD-4821',
    'status' => 'Out for delivery',
    'etaMinutes' => 12,
    // Route progress 0–100 (restaurant => home).
    'progress' => 64,
    // Courier details.
    'courierName' => 'Maya R.',
    'courierRole' => 'Your courier',
    'courierRating' => '4.9',
    'courierInitials' => 'MR',
    // Endpoints: label + [longitude, latitude]. Central San Francisco.
    'restaurantName' => 'Golden Gate Kitchen',
    'restaurantAddress' => '450 Market St',
    'restaurantCoords' => [-122.4014, 37.7912],
    'customerName' => 'Home',
    'customerAddress' => '1100 Howard St',
    'customerCoords' => [-122.4119, 37.7765],
    // Order line items: qty, item label, price.
    'items' => null,
    'total' => '$38.40',
])

@php
    $restaurant = $restaurantCoords;
    $customer = $customerCoords;

    // A gently-curved route so the polyline reads like real streets, not a ruler line.
    $route = [
        $restaurant,
        [-122.4060, 37.7886],
        [-122.4092, 37.7848],
        [-122.4108, 37.7802],
        $customer,
    ];

    // Courier sits partway along the route, driven by the progress value.
    $courierIndex = (int) round((count($route) - 1) * (max(0, min(100, (int) $progress)) / 100));
    $courier = $route[$courierIndex] ?? $route[1];

    $items ??= [
        ['qty' => 1, 'label' => __('Truffle mushroom pizza'), 'price' => '$18.00'],
        ['qty' => 2, 'label' => __('Garlic focaccia'), 'price' => '$12.00'],
        ['qty' => 1, 'label' => __('Sparkling lemonade'), 'price' => '$5.50'],
    ];
@endphp

<section {{ $attributes->merge(['class' => 'bg-background py-10 text-foreground sm:py-16']) }}>
    <div class="mx-auto max-w-5xl px-4">
        <x-ui.card class="overflow-hidden">
            <div class="grid gap-0 lg:grid-cols-2">
                {{-- MAP: restaurant => courier => home along a routed polyline. --}}
                <div class="relative">
                    <x-ui.map
                        :center="$courier"
                        :zoom="13"
                        height="420px"
                        :label="__('Live delivery route from the restaurant to your address')"
                    >
                        <x-ui.map.controls position="top-right" show-zoom />

                        <x-ui.map.route
                            :coordinates="$route"
                            color="primary"
                            :width="4"
                            :opacity="0.9"
                        />

                        <x-ui.map.marker
                            :lng="$restaurant[0]"
                            :lat="$restaurant[1]"
                            color="muted"
                            :label="__('Restaurant')"
                        >
                            <x-slot:popup>
                                <p class="text-sm font-semibold text-foreground">{{ __($restaurantName) }}</p>
                                <p class="text-sm text-muted-foreground">{{ __('Picked up') }}</p>
                            </x-slot:popup>
                        </x-ui.map.marker>

                        <x-ui.map.marker
                            :lng="$courier[0]"
                            :lat="$courier[1]"
                            color="primary"
                            pulse
                            :label="__('Courier')"
                        >
                            <x-slot:popup>
                                <p class="text-sm font-semibold text-foreground">{{ __($courierName) }}</p>
                                <p class="text-sm text-muted-foreground">{{ __('On the way') }}</p>
                            </x-slot:popup>
                        </x-ui.map.marker>

                        <x-ui.map.marker
                            :lng="$customer[0]"
                            :lat="$customer[1]"
                            color="success"
                            :label="__('Destination')"
                        >
                            <x-slot:popup>
                                <p class="text-sm font-semibold text-foreground">{{ __($customerName) }}</p>
                                <p class="text-sm text-muted-foreground">{{ __($customerAddress) }}</p>
                            </x-slot:popup>
                        </x-ui.map.marker>
                    </x-ui.map>
                </div>

                {{-- DETAILS: status, ETA, progress, courier, order summary. --}}
                <div class="flex flex-col">
                    <x-ui.card.header>
                        <div class="flex flex-wrap items-start justify-between gap-4">
                            <div class="space-y-2">
                                <x-ui.card.description>{{ __('Order') }} {{ $orderId }}</x-ui.card.description>
                                <x-ui.card.title>{{ __('Arriving soon') }}</x-ui.card.title>
                            </div>
                            <x-ui.badge variant="success" class="gap-2">
                                <span class="relative flex size-2" aria-hidden="true">
                                    <span class="absolute inline-flex size-2 rounded-full bg-success-foreground/70 motion-safe:animate-ping"></span>
                                    <span class="relative inline-flex size-2 rounded-full bg-success-foreground"></span>
                                </span>
                                {{ __($status) }}
                            </x-ui.badge>
                        </div>
                    </x-ui.card.header>

                    <x-ui.card.content class="space-y-6">
                        <div>
                            <p class="text-sm text-muted-foreground">{{ __('Estimated arrival') }}</p>
                            <p class="mt-2 text-4xl font-bold tracking-tight tabular-nums text-foreground">
                                {{ $etaMinutes }} <span class="text-2xl font-semibold text-muted-foreground">{{ __('min') }}</span>
                            </p>
                        </div>

                        <div class="space-y-2">
                            <x-ui.progress :value="$progress" />
                            <div class="flex items-center justify-between text-sm text-muted-foreground">
                                <span class="font-medium text-foreground">{{ __($restaurantName) }}</span>
                                <span class="font-medium text-foreground">{{ __($customerName) }}</span>
                            </div>
                        </div>

                        <x-ui.separator />

                        {{-- Courier row. --}}
                        <div class="flex items-center gap-4">
                            <x-ui.avatar :fallback="$courierInitials" />
                            <div class="min-w-0 flex-1">
                                <p class="truncate text-sm font-semibold text-foreground">{{ __($courierName) }}</p>
                                <p class="text-sm text-muted-foreground">{{ __($courierRole) }}</p>
                            </div>
                            <span class="inline-flex items-center gap-2 text-sm font-medium text-foreground">
                                <svg class="size-4 text-warning" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                                    <path d="M12 2.5l2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 17.8 6.2 20.9l1.1-6.5L2.6 9.3l6.5-.9L12 2.5z" />
                                </svg>
                                <span class="tabular-nums">{{ $courierRating }}</span>
                            </span>
                        </div>

                        <x-ui.separator />

                        {{-- Order summary. --}}
                        <div>
                            <h3 class="text-sm font-semibold text-foreground">{{ __('Your order') }}</h3>
                            <dl class="mt-4 space-y-2">
                                @foreach ($items as $item)
                                    <div class="flex items-center justify-between gap-4 text-sm">
                                        <dt class="flex min-w-0 items-center gap-2 text-foreground">
                                            <span class="inline-flex h-5 min-w-5 items-center justify-center rounded bg-muted px-2 text-sm font-medium tabular-nums text-muted-foreground">{{ $item['qty'] }}</span>
                                            <span class="truncate">{{ $item['label'] }}</span>
                                        </dt>
                                        <dd class="flex-none tabular-nums text-muted-foreground">{{ $item['price'] }}</dd>
                                    </div>
                                @endforeach
                            </dl>
                            <div class="mt-4 flex items-center justify-between border-t border-border pt-4 text-sm">
                                <span class="font-semibold text-foreground">{{ __('Total') }}</span>
                                <span class="font-semibold tabular-nums text-foreground">{{ $total }}</span>
                            </div>
                        </div>
                    </x-ui.card.content>

                    <x-ui.card.footer class="mt-auto">
                        <x-ui.button variant="outline" class="w-full">
                            <svg class="me-2 size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z" />
                            </svg>
                            {{ __('Contact courier') }}
                        </x-ui.button>
                    </x-ui.card.footer>
                </div>
            </div>
        </x-ui.card>
    </div>
</section>

Installation

terminal
php artisan ui:add blocks/maps-delivery-tracker

Installs these dependencies automatically:

Registry contract

Install confidence

php artisan ui:add blocks/maps-delivery-tracker writes only the generated targets below. The CLI validates each file hash before writing and prompts before replacing local changes unless --force is used.

Version
1.0.4
License
open
Stability
stable
Contract
v1
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/maps-delivery-tracker.blade.php

Registry dependencies

card badge button progress separator avatar map

Package dependencies

composer: jml/brok:^0.2

npm: maplibre-gl

Use with AI

A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.

maps-delivery-tracker.md
# Brok UI block: Delivery Tracker (`maps-delivery-tracker`)

Live delivery tracking card with a routed MapLibre map, courier ETA, progress, and an itemised order summary.

Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.

## Install

```bash
php artisan ui:add blocks/maps-delivery-tracker
```

## Render it

```blade
<x-blocks.maps-delivery-tracker />
```

## Props

- `order-id` (string, default `#FD-4821`) — Order identifier shown above the title.
- `status` (string, default `Out for delivery`) — Live delivery status shown in the badge.
- `eta-minutes` (int, default `12`) — Estimated minutes until arrival, shown as the headline number.
- `progress` (int, default `64`) — Route completion 0-100; also positions the courier marker along the route.
- `courier-name` (string, default `Maya R.`) — Courier's display name.
- `courier-role` (string, default `Your courier`) — Label shown under the courier's name.
- `courier-rating` (string, default `4.9`) — Courier rating shown next to the star icon.
- `courier-initials` (string, default `MR`) — Fallback initials for the courier avatar.
- `restaurant-name` (string, default `Golden Gate Kitchen`) — Pickup location name shown in the map popup.
- `restaurant-address` (string, default `450 Market St`) — Pickup street address for the restaurant location.
- `restaurant-coords` (array, default `[-122.4014, 37.7912]`) — [longitude, latitude] of the pickup location.
- `customer-name` (string, default `Home`) — Delivery destination label, e.g. 'Home'.
- `customer-address` (string, default `1100 Howard St`) — Delivery street address shown in the destination popup.
- `customer-coords` (array, default `[-122.4119, 37.7765]`) — [longitude, latitude] of the delivery destination.
- `items` (mixed|null, default `null`) — Order line items: quantity, label, and price; null falls back to a sample order.
- `total` (string, default `$38.40`) — Order total shown at the bottom of the summary.

## Use when

- Tracking a single food or parcel delivery order with a routed map from restaurant to courier to customer.
- Pairing the map with ETA, progress, courier details, and an itemised order summary.

## Avoid when

- Needing a network-wide view of multiple hubs and lanes rather than one order's route; use maps-logistics for logistics, or maps-analytics for global analytics.

## Rules

- Render the installed block with `<x-blocks.maps-delivery-tracker />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.

## Links

- Docs: https://brokui.dev/blocks/maps-delivery-tracker
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/maps-delivery-tracker.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Guidance

Use when

  • Tracking a single food or parcel delivery order with a routed map from restaurant to courier to customer.
  • Pairing the map with ETA, progress, courier details, and an itemised order summary.

Avoid when

  • Needing a network-wide view of multiple hubs and lanes rather than one order's route; use maps-logistics for logistics, or maps-analytics for global analytics.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
routed map with restaurant, courier, and destination markers status badge and ETA route progress bar courier row order summary contact courier button

Usage

Render the block as a component, passing data where useful:

resources/views/welcome.blade.php Blade
<x-blocks.maps-delivery-tracker />

Built from primitives

Composes <brok:*> Semantic tokens Re-themeable RTL-ready

This block composes installed <brok:*> primitives and semantic design tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by editing CSS variables; flip the preview to RTL to confirm it mirrors.

Source

The exact, editable file ui:add writes into your app. The preview above renders this same source — there are no preview-only blocks.

resources/views/blocks/maps-delivery-tracker.blade.php Blade
@props([
    // Order identity + live status.
    'orderId' => '#FD-4821',
    'status' => 'Out for delivery',
    'etaMinutes' => 12,
    // Route progress 0–100 (restaurant => home).
    'progress' => 64,
    // Courier details.
    'courierName' => 'Maya R.',
    'courierRole' => 'Your courier',
    'courierRating' => '4.9',
    'courierInitials' => 'MR',
    // Endpoints: label + [longitude, latitude]. Central San Francisco.
    'restaurantName' => 'Golden Gate Kitchen',
    'restaurantAddress' => '450 Market St',
    'restaurantCoords' => [-122.4014, 37.7912],
    'customerName' => 'Home',
    'customerAddress' => '1100 Howard St',
    'customerCoords' => [-122.4119, 37.7765],
    // Order line items: qty, item label, price.
    'items' => null,
    'total' => '$38.40',
])

@php
    $restaurant = $restaurantCoords;
    $customer = $customerCoords;

    // A gently-curved route so the polyline reads like real streets, not a ruler line.
    $route = [
        $restaurant,
        [-122.4060, 37.7886],
        [-122.4092, 37.7848],
        [-122.4108, 37.7802],
        $customer,
    ];

    // Courier sits partway along the route, driven by the progress value.
    $courierIndex = (int) round((count($route) - 1) * (max(0, min(100, (int) $progress)) / 100));
    $courier = $route[$courierIndex] ?? $route[1];

    $items ??= [
        ['qty' => 1, 'label' => __('Truffle mushroom pizza'), 'price' => '$18.00'],
        ['qty' => 2, 'label' => __('Garlic focaccia'), 'price' => '$12.00'],
        ['qty' => 1, 'label' => __('Sparkling lemonade'), 'price' => '$5.50'],
    ];
@endphp

<section {{ $attributes->merge(['class' => 'bg-background py-10 text-foreground sm:py-16']) }}>
    <div class="mx-auto max-w-5xl px-4">
        <x-ui.card class="overflow-hidden">
            <div class="grid gap-0 lg:grid-cols-2">
                {{-- MAP: restaurant => courier => home along a routed polyline. --}}
                <div class="relative">
                    <x-ui.map
                        :center="$courier"
                        :zoom="13"
                        height="420px"
                        :label="__('Live delivery route from the restaurant to your address')"
                    >
                        <x-ui.map.controls position="top-right" show-zoom />

                        <x-ui.map.route
                            :coordinates="$route"
                            color="primary"
                            :width="4"
                            :opacity="0.9"
                        />

                        <x-ui.map.marker
                            :lng="$restaurant[0]"
                            :lat="$restaurant[1]"
                            color="muted"
                            :label="__('Restaurant')"
                        >
                            <x-slot:popup>
                                <p class="text-sm font-semibold text-foreground">{{ __($restaurantName) }}</p>
                                <p class="text-sm text-muted-foreground">{{ __('Picked up') }}</p>
                            </x-slot:popup>
                        </x-ui.map.marker>

                        <x-ui.map.marker
                            :lng="$courier[0]"
                            :lat="$courier[1]"
                            color="primary"
                            pulse
                            :label="__('Courier')"
                        >
                            <x-slot:popup>
                                <p class="text-sm font-semibold text-foreground">{{ __($courierName) }}</p>
                                <p class="text-sm text-muted-foreground">{{ __('On the way') }}</p>
                            </x-slot:popup>
                        </x-ui.map.marker>

                        <x-ui.map.marker
                            :lng="$customer[0]"
                            :lat="$customer[1]"
                            color="success"
                            :label="__('Destination')"
                        >
                            <x-slot:popup>
                                <p class="text-sm font-semibold text-foreground">{{ __($customerName) }}</p>
                                <p class="text-sm text-muted-foreground">{{ __($customerAddress) }}</p>
                            </x-slot:popup>
                        </x-ui.map.marker>
                    </x-ui.map>
                </div>

                {{-- DETAILS: status, ETA, progress, courier, order summary. --}}
                <div class="flex flex-col">
                    <x-ui.card.header>
                        <div class="flex flex-wrap items-start justify-between gap-4">
                            <div class="space-y-2">
                                <x-ui.card.description>{{ __('Order') }} {{ $orderId }}</x-ui.card.description>
                                <x-ui.card.title>{{ __('Arriving soon') }}</x-ui.card.title>
                            </div>
                            <x-ui.badge variant="success" class="gap-2">
                                <span class="relative flex size-2" aria-hidden="true">
                                    <span class="absolute inline-flex size-2 rounded-full bg-success-foreground/70 motion-safe:animate-ping"></span>
                                    <span class="relative inline-flex size-2 rounded-full bg-success-foreground"></span>
                                </span>
                                {{ __($status) }}
                            </x-ui.badge>
                        </div>
                    </x-ui.card.header>

                    <x-ui.card.content class="space-y-6">
                        <div>
                            <p class="text-sm text-muted-foreground">{{ __('Estimated arrival') }}</p>
                            <p class="mt-2 text-4xl font-bold tracking-tight tabular-nums text-foreground">
                                {{ $etaMinutes }} <span class="text-2xl font-semibold text-muted-foreground">{{ __('min') }}</span>
                            </p>
                        </div>

                        <div class="space-y-2">
                            <x-ui.progress :value="$progress" />
                            <div class="flex items-center justify-between text-sm text-muted-foreground">
                                <span class="font-medium text-foreground">{{ __($restaurantName) }}</span>
                                <span class="font-medium text-foreground">{{ __($customerName) }}</span>
                            </div>
                        </div>

                        <x-ui.separator />

                        {{-- Courier row. --}}
                        <div class="flex items-center gap-4">
                            <x-ui.avatar :fallback="$courierInitials" />
                            <div class="min-w-0 flex-1">
                                <p class="truncate text-sm font-semibold text-foreground">{{ __($courierName) }}</p>
                                <p class="text-sm text-muted-foreground">{{ __($courierRole) }}</p>
                            </div>
                            <span class="inline-flex items-center gap-2 text-sm font-medium text-foreground">
                                <svg class="size-4 text-warning" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                                    <path d="M12 2.5l2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 17.8 6.2 20.9l1.1-6.5L2.6 9.3l6.5-.9L12 2.5z" />
                                </svg>
                                <span class="tabular-nums">{{ $courierRating }}</span>
                            </span>
                        </div>

                        <x-ui.separator />

                        {{-- Order summary. --}}
                        <div>
                            <h3 class="text-sm font-semibold text-foreground">{{ __('Your order') }}</h3>
                            <dl class="mt-4 space-y-2">
                                @foreach ($items as $item)
                                    <div class="flex items-center justify-between gap-4 text-sm">
                                        <dt class="flex min-w-0 items-center gap-2 text-foreground">
                                            <span class="inline-flex h-5 min-w-5 items-center justify-center rounded bg-muted px-2 text-sm font-medium tabular-nums text-muted-foreground">{{ $item['qty'] }}</span>
                                            <span class="truncate">{{ $item['label'] }}</span>
                                        </dt>
                                        <dd class="flex-none tabular-nums text-muted-foreground">{{ $item['price'] }}</dd>
                                    </div>
                                @endforeach
                            </dl>
                            <div class="mt-4 flex items-center justify-between border-t border-border pt-4 text-sm">
                                <span class="font-semibold text-foreground">{{ __('Total') }}</span>
                                <span class="font-semibold tabular-nums text-foreground">{{ $total }}</span>
                            </div>
                        </div>
                    </x-ui.card.content>

                    <x-ui.card.footer class="mt-auto">
                        <x-ui.button variant="outline" class="w-full">
                            <svg class="me-2 size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z" />
                            </svg>
                            {{ __('Contact courier') }}
                        </x-ui.button>
                    </x-ui.card.footer>
                </div>
            </div>
        </x-ui.card>
    </div>
</section>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.0.4
Status
Stable
License
open
Deprecation
Not deprecated
Contract
v1
Foundation
≥ 1.0.0