Mapcn
blocklogistics-networksource: ok · hit
Logistics Network
Domestic logistics map with a sidebar of stats.
npx shadcn add "https://mapcn.dev/r/logistics-network.json"free
Where it goes
statssidebardashboard-shell
Interaction
animated-counter
Use cases
dashboard
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lighticonslucide-react
Registry dependencies
@mapcn/mapsidebarseparator
Files4 · source cached 10/8/2026, 4:05:38 PM
src/registry/blocks/logistics-network/page.tsx
"use client";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { hubs, routes } from "./data";
import { FilterSidebar } from "./components/filter-sidebar";
import { NetworkMap } from "./components/network-map";
export default function Page() {
return (
<SidebarProvider>
<FilterSidebar hubs={hubs} routes={routes} />
<SidebarInset>
<NetworkMap hubs={hubs} routes={routes} />
</SidebarInset>
</SidebarProvider>
);
}
src/registry/blocks/logistics-network/data.ts
export interface Hub {
id: string;
city: string;
lng: number;
lat: number;
type: "primary" | "secondary";
shipments: number;
region: "west" | "midwest" | "south" | "northeast";
}
export interface Route {
from: string;
to: string;
mode: "air" | "ground";
shipments: number;
status: "active" | "delayed";
}
export const hubs: Hub[] = [
{
id: "ord",
city: "Chicago",
lng: -87.6298,
lat: 41.8781,
type: "primary",
shipments: 1247,
region: "midwest",
},
{
id: "lax",
city: "Los Angeles",
lng: -118.2437,
lat: 34.0522,
type: "primary",
shipments: 1102,
region: "west",
},
{
id: "jfk",
city: "New York",
lng: -73.9352,
lat: 40.6413,
type: "primary",
shipments: 983,
region: "northeast",
},
{
id: "dfw",
city: "Dallas",
lng: -96.797,
lat: 32.8968,
type: "primary",
shipments: 856,
region: "south",
},
{
id: "atl",
city: "Atlanta",
lng: -84.4281,
lat: 33.6407,
type: "primary",
shipments: 914,
region: "south",
},
{
id: "den",
city: "Denver",
lng: -104.6731,
lat: 39.8617,
type: "secondary",
shipments: 634,
region: "west",
},
{
id: "sea",
city: "Seattle",
lng: -122.3321,
lat: 47.6062,
type: "secondary",
shipments: 723,
region: "west",
},
{
id: "mia",
city: "Miami",
lng: -80.1918,
lat: 25.7617,
type: "secondary",
shipments: 478,
region: "south",
},
{
id: "phx",
city: "Phoenix",
lng: -112.074,
lat: 33.4484,
type: "secondary",
shipments: 512,
region: "west",
},
{
id: "iah",
city: "Houston",
lng: -95.3698,
lat: 29.9844,
type: "secondary",
shipments: 698,
region: "south",
},
{
id: "bos",
city: "Boston",
lng: -71.0054,
lat: 42.3643,
type: "secondary",
shipments: 534,
region: "northeast",
},
{
id: "sfo",
city: "San Francisco",
lng: -122.4194,
lat: 37.7749,
type: "secondary",
shipments: 789,
region: "west",
},
{
id: "msp",
city: "Minneapolis",
lng: -93.2219,
lat: 44.8848,
type: "secondary",
shipments: 423,
region: "midwest",
},
{
id: "dtw",
city: "Detroit",
lng: -83.0458,
lat: 42.2162,
type: "secondary",
shipments: 456,
region: "midwest",
},
{
id: "slc",
city: "Salt Lake City",
lng: -111.978,
lat: 40.758,
type: "secondary",
shipments: 342,
region: "west",
},
];
export const routes: Route[] = [
{ from: "ord", to: "lax", mode: "air", shipments: 234, status: "active" },
{ from: "ord", to: "jfk", mode: "ground", shipments: 312, status: "active" },
{ from: "ord", to: "dfw", mode: "air", shipments: 189, status: "active" },
{ from: "ord", to: "atl", mode: "air", shipments: 213, status: "active" },
{ from: "ord", to: "den", mode: "ground", shipments: 156, status: "active" },
{ from: "ord", to: "msp", mode: "ground", shipments: 198, status: "active" },
{ from: "ord", to: "dtw", mode: "ground", shipments: 167, status: "delayed" },
{ from: "lax", to: "sfo", mode: "ground", shipments: 245, status: "active" },
{ from: "lax", to: "sea", mode: "air", shipments: 178, status: "active" },
{ from: "lax", to: "den", mode: "air", shipments: 198, status: "active" },
{ from: "lax", to: "phx", mode: "ground", shipments: 212, status: "active" },
{ from: "lax", to: "dfw", mode: "air", shipments: 223, status: "active" },
{ from: "jfk", to: "atl", mode: "air", shipments: 267, status: "active" },
{ from: "jfk", to: "mia", mode: "air", shipments: 234, status: "active" },
{ from: "jfk", to: "bos", mode: "ground", shipments: 189, status: "active" },
{ from: "jfk", to: "ord", mode: "air", shipments: 278, status: "active" },
{ from: "dfw", to: "atl", mode: "air", shipments: 198, status: "active" },
{ from: "dfw", to: "iah", mode: "ground", shipments: 245, status: "active" },
{ from: "dfw", to: "den", mode: "air", shipments: 167, status: "active" },
{ from: "dfw", to: "phx", mode: "ground", shipments: 156, status: "delayed" },
{ from: "atl", to: "mia", mode: "ground", shipments: 234, status: "active" },
{ from: "atl", to: "iah", mode: "air", shipments: 189, status: "active" },
{ from: "den", to: "sea", mode: "air", shipments: 178, status: "active" },
{ from: "den", to: "slc", mode: "ground", shipments: 198, status: "active" },
{ from: "den", to: "phx", mode: "air", shipments: 167, status: "active" },
{ from: "sea", to: "sfo", mode: "air", shipments: 212, status: "active" },
{ from: "sfo", to: "phx", mode: "air", shipments: 156, status: "active" },
{ from: "mia", to: "iah", mode: "air", shipments: 198, status: "active" },
{ from: "msp", to: "ord", mode: "ground", shipments: 189, status: "active" },
{ from: "msp", to: "den", mode: "air", shipments: 145, status: "active" },
{ from: "dtw", to: "ord", mode: "ground", shipments: 167, status: "active" },
{ from: "dtw", to: "jfk", mode: "air", shipments: 178, status: "active" },
{ from: "bos", to: "jfk", mode: "ground", shipments: 156, status: "active" },
{ from: "slc", to: "den", mode: "ground", shipments: 134, status: "active" },
{ from: "slc", to: "phx", mode: "air", shipments: 123, status: "delayed" },
{ from: "ord", to: "sfo", mode: "air", shipments: 198, status: "active" },
{ from: "ord", to: "mia", mode: "air", shipments: 245, status: "active" },
{ from: "lax", to: "atl", mode: "air", shipments: 289, status: "active" },
{ from: "jfk", to: "dfw", mode: "air", shipments: 234, status: "active" },
{ from: "atl", to: "ord", mode: "air", shipments: 267, status: "active" },
{ from: "sfo", to: "den", mode: "air", shipments: 189, status: "active" },
];
export const modeConfig = {
air: { color: "#3b82f6", label: "Air" },
ground: { color: "#22c55e", label: "Ground" },
} as const;
export const statusConfig = {
active: { color: "#10b981", label: "Active" },
delayed: { color: "#f59e0b", label: "Delayed" },
} as const;
export const regionLabels: Record<Hub["region"], string> = {
west: "West",
midwest: "Midwest",
south: "South",
northeast: "Northeast",
};
src/registry/blocks/logistics-network/components/filter-sidebar.tsx
"use client";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarSeparator,
} from "@/components/ui/sidebar";
import { Network, Plane, Truck } from "lucide-react";
import { regionLabels, statusConfig, type Hub, type Route } from "../data";
const regionIcons: Record<Hub["region"], string> = {
west: "W",
midwest: "MW",
south: "S",
northeast: "NE",
};
interface FilterSidebarProps {
hubs: Hub[];
routes: Route[];
}
export function FilterSidebar({ hubs, routes }: FilterSidebarProps) {
const totalShipments = routes.reduce((s, r) => s + r.shipments, 0);
const activeCount = routes.filter((r) => r.status === "active").length;
const delayedCount = routes.filter((r) => r.status === "delayed").length;
const airRouteCount = routes.filter((r) => r.mode === "air").length;
const groundRouteCount = routes.filter((r) => r.mode === "ground").length;
return (
<Sidebar collapsible="offcanvas">
<SidebarHeader className="p-4">
<div className="flex items-center gap-2">
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
<Network className="size-4" />
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="text-sm font-medium">Logistics Network</span>
<span className="text-muted-foreground text-xs">
Domestic Routes
</span>
</div>
</div>
<div className="mt-3 grid grid-cols-3 gap-2">
<div className="bg-background rounded-md border px-2.5 py-2 text-center">
<p className="text-lg leading-none font-bold tabular-nums">
{hubs.length}
</p>
<p className="text-muted-foreground mt-1 text-[10px]">Hubs</p>
</div>
<div className="bg-background rounded-md border px-2.5 py-2 text-center">
<p className="text-lg leading-none font-bold tabular-nums">
{activeCount}
</p>
<p className="text-muted-foreground mt-1 text-[10px]">Active</p>
</div>
<div className="bg-background rounded-md border px-2.5 py-2 text-center">
<p className="text-lg leading-none font-bold tabular-nums">
{delayedCount}
</p>
<p className="text-muted-foreground mt-1 text-[10px]">Delayed</p>
</div>
</div>
</SidebarHeader>
<SidebarSeparator className="mx-0" />
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Transport Mode</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
tooltip="Air"
className="pointer-events-none"
>
<Plane className="size-4" />
<span>Air Freight</span>
</SidebarMenuButton>
<SidebarMenuBadge>{airRouteCount}</SidebarMenuBadge>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton
tooltip="Ground"
className="pointer-events-none"
>
<Truck className="size-4" />
<span>Ground</span>
</SidebarMenuButton>
<SidebarMenuBadge>{groundRouteCount}</SidebarMenuBadge>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>Status</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
tooltip={statusConfig.active.label}
className="pointer-events-none"
>
<span className="flex size-4 items-center justify-center">
<span
className="size-2 rounded-full"
style={{ backgroundColor: statusConfig.active.color }}
/>
</span>
<span>{statusConfig.active.label}</span>
</SidebarMenuButton>
<SidebarMenuBadge>{activeCount}</SidebarMenuBadge>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton
tooltip={statusConfig.delayed.label}
className="pointer-events-none"
>
<span className="flex size-4 items-center justify-center">
<span
className="size-2 rounded-full"
style={{ backgroundColor: statusConfig.delayed.color }}
/>
</span>
<span>{statusConfig.delayed.label}</span>
</SidebarMenuButton>
<SidebarMenuBadge>{delayedCount}</SidebarMenuBadge>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>Region</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{(["west", "midwest", "south", "northeast"] as const).map(
(region) => {
const hubsInRegion = hubs.filter((h) => h.region === region);
return (
<SidebarMenuItem key={region}>
<SidebarMenuButton
tooltip={regionLabels[region]}
className="pointer-events-none"
>
<span className="bg-muted text-muted-foreground flex size-4 items-center justify-center rounded text-[9px] font-bold">
{regionIcons[region]}
</span>
<span>{regionLabels[region]}</span>
</SidebarMenuButton>
<SidebarMenuBadge>{hubsInRegion.length}</SidebarMenuBadge>
</SidebarMenuItem>
);
},
)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter className="p-4 pt-0">
<p className="text-muted-foreground mb-2 text-[11px] font-medium tracking-wider uppercase">
Summary
</p>
<div className="bg-background space-y-1.5 rounded-md border px-3 py-2 text-xs">
<div className="flex justify-between">
<span className="text-muted-foreground">Shipments</span>
<span className="font-medium tabular-nums">
{totalShipments.toLocaleString()}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Routes</span>
<span className="font-medium tabular-nums">{routes.length}</span>
</div>
</div>
</SidebarFooter>
</Sidebar>
);
}
src/registry/blocks/logistics-network/components/network-map.tsx
"use client";
import { useMemo } from "react";
import {
Map,
MapArc,
MapControls,
MapMarker,
MarkerContent,
MarkerTooltip,
} from "@/components/ui/map";
import { SidebarTrigger } from "@/components/ui/sidebar";
import {
modeConfig,
regionLabels,
statusConfig,
type Hub,
type Route,
} from "../data";
import { Separator } from "@/components/ui/separator";
interface NetworkMapProps {
hubs: Hub[];
routes: Route[];
}
function MapControlsCard() {
return (
<div className="border-border/40 bg-background/70 absolute top-4 left-4 z-20 flex items-center gap-3 rounded-lg border px-2.5 py-1.5 backdrop-blur-sm">
<SidebarTrigger />
<Separator orientation="vertical" className="h-4!" />
<div className="flex items-center gap-3 text-xs">
<div className="flex items-center gap-1.5">
<span
className="h-0.5 w-4 shrink-0 rounded-full"
style={{ backgroundColor: modeConfig.air.color }}
/>
<span>{modeConfig.air.label}</span>
</div>
<div className="flex items-center gap-1.5">
<span
className="h-0.5 w-4 shrink-0 rounded-full"
style={{ backgroundColor: modeConfig.ground.color }}
/>
<span>{modeConfig.ground.label}</span>
</div>
<div className="flex items-center gap-1.5">
<span
className="h-0.5 w-4 shrink-0 rounded-full"
style={{ backgroundColor: statusConfig.delayed.color }}
/>
<span>{statusConfig.delayed.label}</span>
</div>
<Separator orientation="vertical" className="h-4!" />
<div className="flex items-center gap-1.5">
<div className="size-2.5 shrink-0 rounded-full border border-white bg-blue-500 shadow-sm" />
<span>Hub</span>
</div>
</div>
</div>
);
}
export function NetworkMap({ hubs, routes }: NetworkMapProps) {
const arcs = useMemo(() => {
const hubById: Record<string, Hub> = Object.fromEntries(
hubs.map((hub) => [hub.id, hub]),
);
return routes.flatMap((route) => {
const fromHub = hubById[route.from];
const toHub = hubById[route.to];
if (!fromHub || !toHub) return [];
return [
{
id: `${route.from}-${route.to}`,
from: [fromHub.lng, fromHub.lat] as [number, number],
to: [toHub.lng, toHub.lat] as [number, number],
color:
route.status === "delayed"
? statusConfig.delayed.color
: modeConfig[route.mode].color,
},
];
});
}, [hubs, routes]);
return (
<div className="relative h-full">
<MapControlsCard />
<Map center={[-98, 39]} zoom={4} projection={{ type: "globe" }}>
<MapControls />
<MapArc
data={arcs}
curvature={0.3}
paint={{
"line-color": ["get", "color"],
"line-width": 2,
"line-opacity": 0.65,
}}
interactive={false}
/>
{hubs.map((hub) => (
<MapMarker key={hub.id} longitude={hub.lng} latitude={hub.lat}>
<MarkerContent>
<div className="size-3 rounded-full border-2 border-white bg-blue-500 shadow-md" />
</MarkerContent>
<MarkerTooltip
offset={16}
className="bg-background text-foreground border px-2.5 py-1.5"
>
<p className="font-medium">{hub.city}</p>
<p className="text-muted-foreground mt-1">
{hub.shipments.toLocaleString()} shipments
<span className="mx-1">•</span>
{regionLabels[hub.region]}
</p>
</MarkerTooltip>
</MapMarker>
))}
</Map>
</div>
);
}