Find an office
Four Northwind offices in London. Pick one on the map or in the list.
office-finder.tsx
"use client";
import { useState } from "react";
import { LuArmchair, LuBike, LuClock, LuMapPin, LuTrainFront } from "react-icons/lu";
import {
Badge,
Button,
Drawer,
ListItem,
MapMarker,
MapMarkerPlace,
MapView,
} from "@nexera-ui/react";
/** Positions are [lng, lat]. */
const OFFICES = [
{
id: "hq",
name: "Northwind HQ",
area: "Islington",
address: "18 Canal Street, London N1 9GU",
people: 124,
desks: 18,
hours: "Mon–Fri, 07:00–20:00",
transit: "Angel, 4 min walk",
lngLat: [-0.1057, 51.5323],
},
{
id: "studio",
name: "King’s Cross studio",
area: "King’s Cross",
address: "4 Granary Square, London N1C 4AA",
people: 36,
desks: 6,
hours: "Mon–Fri, 08:00–19:00",
transit: "King’s Cross St Pancras, 6 min walk",
lngLat: [-0.1252, 51.5357],
},
{
id: "wharf",
name: "Docklands hub",
area: "Canary Wharf",
address: "1 Cabot Square, London E14 4QJ",
people: 58,
desks: 0,
hours: "Mon–Fri, 08:00–18:00",
transit: "Canary Wharf, 2 min walk",
lngLat: [-0.0235, 51.505],
},
{
id: "cowork",
name: "Southwark co-working",
area: "Bankside",
address: "90 Union Street, London SE1 0NW",
people: 12,
desks: 9,
hours: "Every day, 06:00–22:00",
transit: "Southwark, 3 min walk",
lngLat: [-0.097, 51.503],
},
] as const;
/** Office finder: Northwind's London offices on a map, with the details of one office in a drawer. */
export default function OfficeFinder() {
const [selectedId, setSelectedId] = useState<string>("hq");
const [open, setOpen] = useState(false);
const office = OFFICES.find((o) => o.id === selectedId) ?? OFFICES[0];
return (
<div className="@container bg-page px-4 py-8 @3xl:px-8">
<section aria-labelledby="offices-title" className="mx-auto grid w-full max-w-6xl gap-4">
<header className="grid gap-1">
<h2 id="offices-title" className="text-heading-h2 text-primary">
Find an office
</h2>
<p className="text-body-default text-secondary">
Four Northwind offices in London. Pick one on the map or in the list.
</p>
</header>
<div className="grid overflow-hidden rounded-xl border border-default bg-surface @3xl:grid-cols-[minmax(0,1fr)_20rem]">
<div className="h-80 @3xl:h-[28rem]">
<MapView
aria-label="Map of Northwind’s London offices"
defaultCenter={[-0.075, 51.52]}
defaultZoom={11.6}
selectedId={selectedId}
height="100%"
className="rounded-none border-0"
>
{OFFICES.map((o) => (
<MapMarker key={o.id} id={o.id} lngLat={o.lngLat}>
<MapMarkerPlace
name={o.name}
headcount={o.people}
isSelected={o.id === selectedId}
onPress={() => {
setSelectedId(o.id);
}}
/>
</MapMarker>
))}
</MapView>
</div>
<aside
aria-label="Offices"
className="flex min-w-0 flex-col gap-3 border-t border-default p-4 @3xl:border-s @3xl:border-t-0"
>
<ul className="m-0 grid list-none gap-1 p-0">
{OFFICES.map((o) => (
<li
key={o.id}
aria-current={o.id === selectedId ? "true" : undefined}
className={`rounded-lg ${o.id === selectedId ? "bg-nav-active-bg" : ""}`}
>
<ListItem
icon={<LuMapPin />}
title={o.name}
subtitle={`${o.area} · ${String(o.people)} people`}
onPress={() => {
setSelectedId(o.id);
}}
/>
</li>
))}
</ul>
<Drawer
title={office.name}
subtitle={office.address}
isOpen={open}
onOpenChange={setOpen}
trigger={<Button className="mt-auto">Details for {office.name}</Button>}
primaryAction={
<Button size="md" slot="close" isDisabled={office.desks === 0}>
Book a desk
</Button>
}
secondaryAction={
<Button size="md" variant="secondary" slot="close">
Close
</Button>
}
>
<div className="grid gap-4">
<div className="flex justify-center rounded-lg bg-map-land py-4">
<MapMarkerPlace name={office.name} headcount={office.people} isSelected />
</div>
<ul className="m-0 grid list-none p-0">
<li>
<ListItem
icon={<LuArmchair />}
title="Desks free today"
subtitle="Wednesday 7 October"
trailing={
<Badge
tone={office.desks > 0 ? "success" : "danger"}
label={office.desks > 0 ? `${String(office.desks)} free` : "Full"}
/>
}
/>
</li>
<li>
<ListItem icon={<LuClock />} title="Opening hours" subtitle={office.hours} />
</li>
<li>
<ListItem
icon={<LuTrainFront />}
title="Nearest station"
subtitle={office.transit}
/>
</li>
<li>
<ListItem
icon={<LuBike />}
title="Bike storage and showers"
subtitle="Ask reception for a locker key"
/>
</li>
</ul>
</div>
</Drawer>
</aside>
</div>
</section>
</div>
);
}