GenBlocks
Menu
Browse
  • Blocks
  • Images
Agents
  • Connect
Categories
  • All
  • Banners
  • Blog
  • Cards
  • Charts
  • Contact
  • CTA
  • Dashboard
  • FAQ
  • Features
  • Footer
  • Forms & Auth
  • Gallery
  • Hero
  • Integrations
  • Navbar
  • Newsletter & Waitlist
  • Onboarding
  • Pricing
  • Social proof
  • States
  • Stats
  • Tables
  • Team
  • Testimonials
  • Timeline
Source
GenBlocks
Log in
Log inSign up
  1. Blocks

© 2026 GenBlocks. All rights reserved.

AboutImpressumPrivacyTerms

Interactive preview, sandboxed: nothing you type leaves this page.

Files
import { useMemo, useState } from "react";type ShipmentStatus = "on time" | "delayed" | "held at customs" | "delivered";type Shipment = { id: string; vessel: string; eta: string; weight: string; status: ShipmentStatus; scan: string; port: string };const shipments: Shipment[] = [  { id: "KELU 482190-6", vessel: "Maersk Eland", eta: "18 Jun, 09:20", weight: "24.8 t", status: "on time", scan: "Gate-in confirmed · 17 Jun, 16:42", port: "Rotterdam, NL" },  { id: "KELU 391742-1", vessel: "MSC Algeciras", eta: "18 Jun, 13:00", weight: "18.2 t", status: "delayed", scan: "Berth departure · 17 Jun, 21:08", port: "Algeciras, ES" },  { id: "KELU 740156-3", vessel: "CMA CGM Andromeda", eta: "19 Jun, 06:45", weight: "26.1 t", status: "on time", scan: "Loaded to vessel · 17 Jun, 11:26", port: "Rotterdam, NL" },  { id: "KELU 528304-9", vessel: "Ever Given", eta: "19 Jun, 15:30", weight: "21.7 t", status: "held at customs", scan: "Customs inspection · 17 Jun, 08:15", port: "Singapore, SG" },  { id: "KELU 164980-2", vessel: "ONE Innovation", eta: "20 Jun, 02:10", weight: "19.4 t", status: "on time", scan: "Discharged · 17 Jun, 19:50", port: "Algeciras, ES" },  { id: "KELU 863215-7", vessel: "HMM Rotterdam", eta: "20 Jun, 11:55", weight: "27.3 t", status: "delivered", scan: "Container released · 17 Jun, 14:03", port: "Singapore, SG" },  { id: "KELU 275641-8", vessel: "MSC Oscar", eta: "21 Jun, 04:30", weight: "23.6 t", status: "on time", scan: "Vessel departed · 17 Jun, 22:31", port: "Rotterdam, NL" },  { id: "KELU 609372-4", vessel: "CMA CGM Jacques Saadé", eta: "21 Jun, 17:15", weight: "20.9 t", status: "delayed", scan: "Anchored · 17 Jun, 06:40", port: "Algeciras, ES" },  { id: "KELU 415806-0", vessel: "Ever Ace", eta: "22 Jun, 08:05", weight: "25.2 t", status: "on time", scan: "Transshipment complete · 17 Jun, 18:22", port: "Singapore, SG" },  { id: "KELU 937204-5", vessel: "Maersk Mc-Kinney Møller", eta: "22 Jun, 20:40", weight: "17.8 t", status: "held at customs", scan: "Documents under review · 17 Jun, 12:14", port: "Rotterdam, NL" },  { id: "KELU 351768-2", vessel: "MSC Gülsün", eta: "23 Jun, 07:25", weight: "22.4 t", status: "on time", scan: "Loaded to vessel · 17 Jun, 09:57", port: "Algeciras, ES" },  { id: "KELU 702493-6", vessel: "ONE Apus", eta: "24 Jun, 14:50", weight: "24.1 t", status: "delivered", scan: "Delivery confirmed · 17 Jun, 15:36", port: "Singapore, SG" },];const statusMark: Record<ShipmentStatus, string> = {  "on time": "bg-emerald-600 dark:bg-emerald-400",  delayed: "bg-amber-600 dark:bg-amber-400",  "held at customs": "bg-rose-700 dark:bg-rose-400",  delivered: "bg-sky-700 dark:bg-sky-400",};function etaValue(value: string) {  const [day, month, time] = value.replace(",", "").split(" ");  const months: Record<string, number> = { Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11 };  return new Date(2025, months[month] ?? 0, Number(day), Number(time.slice(0, 2)), Number(time.slice(3))).getTime();}export default function Block() {  const [sortAscending, setSortAscending] = useState(true);  const [expanded, setExpanded] = useState<Set<string>>(() => new Set());  const orderedShipments = useMemo(    () => [...shipments].sort((a, b) => (sortAscending ? 1 : -1) * (etaValue(a.eta) - etaValue(b.eta))),    [sortAscending],  );  function toggleExpanded(id: string) {    setExpanded((current) => {      const next = new Set(current);      if (next.has(id)) next.delete(id);      else next.add(id);      return next;    });  }  return (    <section className="w-full bg-white px-4 py-8 text-neutral-900 dark:bg-neutral-950 dark:text-neutral-100 sm:px-6 sm:py-10">      <div className="mx-auto w-full max-w-7xl">        <header className="mb-5 flex flex-wrap items-end justify-between gap-x-6 gap-y-3 border-b border-neutral-200 pb-4 dark:border-neutral-800 sm:mb-6 sm:pb-5">          <div>            <p className="mb-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-neutral-600 dark:text-neutral-400">Kestrel Line <span className="px-1 text-neutral-400">/</span> Freight movements</p>            <h1 className="text-[25px] font-semibold leading-tight tracking-[-0.035em] sm:text-[30px]">Shipments</h1>          </div>          <p className="pb-0.5 text-[13px] font-medium text-neutral-700 dark:text-neutral-300"><span className="font-mono tabular-nums">12</span> consignments <span className="mx-1.5 text-neutral-400" aria-hidden="true">·</span> Rotterdam / Algeciras / Singapore</p>        </header>        <table className="shipment-table w-full table-fixed border-collapse text-left">          <caption className="sr-only">Kestrel Line shipment schedule with container, vessel, estimated arrival, weight and status.</caption>          <thead className="sticky top-0 z-10 bg-neutral-100 text-[11px] font-semibold uppercase tracking-[0.1em] text-neutral-700 dark:bg-neutral-900 dark:text-neutral-300">            <tr className="border-y border-neutral-200 dark:border-neutral-800">              <th scope="col" className="w-[22%] px-3 py-3 sm:px-4">Container</th>              <th scope="col" className="w-[25%] px-3 py-3 sm:px-4">Vessel</th>              <th scope="col" aria-sort={sortAscending ? "ascending" : "descending"} className="w-[20%] px-3 py-3 sm:px-4">                <button type="button" onClick={() => setSortAscending((value) => !value)} aria-label={`Sort ETA ${sortAscending ? "descending" : "ascending"}`} className="inline-flex min-h-6 items-center gap-1.5 rounded-sm text-left hover:text-neutral-950 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-700 dark:hover:text-white dark:focus-visible:outline-amber-300">ETA <span aria-hidden="true" className="text-[13px] leading-none">{sortAscending ? "↑" : "↓"}</span></button>              </th>              <th scope="col" className="w-[13%] px-3 py-3 sm:px-4">Weight</th>              <th scope="col" className="w-[20%] px-3 py-3 sm:px-4">Status</th>            </tr>          </thead>          <tbody>            {orderedShipments.map((shipment) => {              const isExpanded = expanded.has(shipment.id);              const detailId = `scan-${shipment.id.replace(/[^a-zA-Z0-9]/g, "")}`;              return <ShipmentRows key={shipment.id} shipment={shipment} isExpanded={isExpanded} detailId={detailId} onToggle={() => toggleExpanded(shipment.id)} />;            })}          </tbody>        </table>      </div>      <style>{`        .shipment-table th { position: sticky; top: 0; }        @media (max-width: 639px) {          .shipment-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }          .shipment-table, .shipment-table tbody { display: block; width: 100%; }          .shipment-table tbody > tr.shipment-record { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; column-gap: 9px; row-gap: 6px; padding: 10px 0 11px; border-bottom: 1px solid rgb(229 229 229); }          .dark .shipment-table tbody > tr.shipment-record { border-color: rgb(64 64 64); }          .shipment-table tbody > tr.shipment-record > td { display: block; width: auto; padding: 0; }          .shipment-table tbody > tr.shipment-record > td:first-child { grid-column: 1 / 3; grid-row: 1; }          .shipment-table tbody > tr.shipment-record > td:nth-child(2) { grid-column: 1; grid-row: 2; }          .shipment-table tbody > tr.shipment-record > td:nth-child(3) { grid-column: 2; grid-row: 2; }          .shipment-table tbody > tr.shipment-record > td:nth-child(4) { grid-column: 3; grid-row: 2; }          .shipment-table tbody > tr.shipment-record > td:nth-child(5) { grid-column: 3; grid-row: 1; }          .shipment-table tbody > tr.shipment-detail { display: block; border-bottom: 1px solid rgb(229 229 229); }          .dark .shipment-table tbody > tr.shipment-detail { border-color: rgb(64 64 64); }          .shipment-table tbody > tr.shipment-detail > td { display: block; padding: 10px 0 12px; }        }      `}</style>    </section>  );}type ShipmentRowsProps = { shipment: Shipment; isExpanded: boolean; detailId: string; onToggle: () => void };function ShipmentRows({ shipment, isExpanded, detailId, onToggle }: ShipmentRowsProps) {  return (    <>      <tr className="shipment-record border-b border-neutral-200 text-[13px] dark:border-neutral-800">        <td className="px-3 py-3.5 sm:px-4 sm:py-4">          <button type="button" onClick={onToggle} aria-expanded={isExpanded} aria-controls={detailId} aria-label={`${isExpanded ? "Hide" : "Show"} last scan for ${shipment.id}`} className="group inline-flex min-h-6 max-w-full items-center gap-2 rounded-sm text-left focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-700 dark:focus-visible:outline-amber-300">            <span aria-hidden="true" className={`h-0 w-0 shrink-0 border-y-[4px] border-l-[5px] border-y-transparent border-l-neutral-500 transition-transform dark:border-l-neutral-400 ${isExpanded ? "rotate-90" : ""}`} />            <span className="font-mono text-[12px] font-semibold tabular-nums tracking-[-0.02em] sm:text-[13px]">{shipment.id}</span>          </button>        </td>        <td className="truncate px-3 py-3.5 text-neutral-800 dark:text-neutral-200 sm:px-4 sm:py-4">{shipment.vessel}</td>        <td className="whitespace-nowrap px-3 py-3.5 font-mono tabular-nums text-neutral-800 dark:text-neutral-200 sm:px-4 sm:py-4">{shipment.eta}</td>        <td className="whitespace-nowrap px-3 py-3.5 font-mono tabular-nums text-neutral-800 dark:text-neutral-200 sm:px-4 sm:py-4">{shipment.weight}</td>        <td className="px-3 py-3.5 sm:px-4 sm:py-4"><span className="inline-flex items-center gap-2 whitespace-nowrap text-[12px] font-medium text-neutral-800 dark:text-neutral-200"><span aria-hidden="true" className={`h-1.5 w-1.5 shrink-0 rounded-full ${statusMark[shipment.status]}`} />{shipment.status}</span></td>      </tr>      {isExpanded && <tr id={detailId} className="shipment-detail bg-neutral-50 text-[12px] text-neutral-700 dark:bg-neutral-900 dark:text-neutral-300"><td colSpan={5} className="px-4 py-3 pl-9"><div className="flex flex-wrap gap-x-8 gap-y-1"><p><span className="mr-2 font-semibold text-neutral-900 dark:text-neutral-100">Last scan</span>{shipment.scan}</p><p><span className="mr-2 font-semibold text-neutral-900 dark:text-neutral-100">Port</span>{shipment.port}</p></div></td></tr>}    </>  );}

Design a standalone shipment-management table block for

Category
Tables
Date
23 Oct 2026
Model
gpt-6-luna
Source
AI-generated
Trace
7e58fcffa9f6364d1f45891c97106801

Judge

Render
94
Accessibility
94
Visual
87
Copy
91
Compliance
99

Harness

assets
ok
safety
ok
imports
ok
ast
ok
Types
ok
Lint> genblocks-sandbox@0.0.0 lint > eslint . /tmp/tmpudcfway8/scripts/inline.mjs 25:24 error 'process' is not defined no-undef 190:3 error 'process' is not defined no-undef 190:47 error 'Buffer' is not defined no-undef /tmp/tmpudcfway8/scripts/scan.mjs 23:14 error 'process' is not defined no-undef 24:14 error 'process' is not defined no-undef 264:21 error 'URL' is not defined no-undef 353:3 error 'co
failed
Build
ok
bundle
ok
Render
ok
Desktop
ok
Laptop
ok
Tablet
ok
Phone
ok
Touch targets
ok
Keyboard focus
ok
Reduced motion
ok
Accessibility
ok
Accessibility, dark
ok
preview
ok
Sign in to report