shadimage
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 · light
iconslucide-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>
  );
}

More from Mapcn

Browse all components in this library →