Delivery Detail Drawer

The portal's golden drawer: delivery state header, selectable attempt trail, and Monaco request/response inspectors in a non-modal right-hand sheet.

"use client"

import { useState } from "react"

This is the drawer the Repost web app opens when you click a delivery, ported whole. RowDetailSheet is the non-modal right-hand sheet (clicking another table row swaps the detail in place instead of closing). DeliveryDetail is the aggregate inside it: mutable delivery state up top, the immutable attempt trail as a selectable table, and request/response inspectors for whichever attempt is selected, with headers/body toggles and timestamp annotations.

Installation

pnpm dlx shadcn@latest add @repost/delivery-detail

Installs this item, every component it depends on, and the npm packages listed below.

Usage

Presentational: feed it from useDeliveryAttempts and wire onReplay to useReplayDelivery (@repost/portal-react), or any data of the same shape. The webhook-dashboard block composes it exactly this way.

components/delivery-detail-demo.tsx
"use client"

import { useState } from "react"
import { Button } from "@/components/ui/button"
import { DeliveryDetail, RowDetailSheet, type WebhookDeliverySummary } from "@/components/delivery-detail"
import type { WebhookAttempt } from "@/lib/webhook-types"

const delivery: WebhookDeliverySummary = {
  id: "del_9k2mf1",
  messageId: "msg_7h1q9x",
  endpointId: "ep_primary",
  type: "invoice.paid",
  generation: 0,
  replayOf: null,
  status: "success",
  failureReason: null,
  attemptCount: 3,
  nextRetryAt: null,
  lastStatusCode: 200,
  lastErrorClass: null,
  createdAt: "2026-07-30T11:47:00.000Z",
  completedAt: "2026-07-30T11:57:21.000Z",
  superseded: false,
  data: { id: "inv_2041", amountCents: 12900, currency: "EUR", paidAt: "2026-07-27T07:03:41.000Z" },
}

const attempts: WebhookAttempt[] = [
  {
    attempt: 1,
    generation: 0,
    responseStatus: null,
    latencyMs: null,
    errorClass: "timeout",
    url: "https://api.acme.dev/webhooks/repost",
    responseBody: null,
    requestHeaders: { "content-type": "application/json", "webhook-id": "msg_7h1q9x" },
    responseHeaders: null,
    timestamp: "2026-07-30T11:47:00.000Z",
  },
  {
    attempt: 2,
    generation: 0,
    responseStatus: 503,
    latencyMs: 1240,
    errorClass: "endpoint_5xx",
    url: "https://api.acme.dev/webhooks/repost",
    responseBody: "upstream unavailable",
    requestHeaders: { "content-type": "application/json", "webhook-id": "msg_7h1q9x" },
    responseHeaders: { "content-type": "text/plain" },
    timestamp: "2026-07-30T11:52:11.000Z",
  },
  {
    attempt: 3,
    generation: 0,
    responseStatus: 200,
    latencyMs: 84,
    errorClass: null,
    url: "https://api.acme.dev/webhooks/repost",
    responseBody: '{"ok":true}',
    requestHeaders: { "content-type": "application/json", "webhook-id": "msg_7h1q9x" },
    responseHeaders: { "content-type": "application/json" },
    timestamp: "2026-07-30T11:57:21.000Z",
  },
]

export function DeliveryDetailDemo() {
  const [open, setOpen] = useState(false)

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        Open delivery details
      </Button>
      <RowDetailSheet open={open} onClose={() => setOpen(false)} title="Delivery details" description="Delivery attempt detail view">
        <DeliveryDetail
          deliveryId={delivery.id}
          delivery={delivery}
          attempts={attempts}
          isLoading={false}
          onReplay={() => setOpen(false)}
        />
      </RowDetailSheet>
    </>
  )
}

Props

PropTypeDescription
deliveryIdstringThe aggregate's id; also resets inspector state per delivery.
deliveryWebhookDeliverySummary | nullMutable state row; null renders the trail without state/actions (aged out).
attemptsWebhookAttempt[]Ascending attempt trail.
initialAttempt{ generation, attempt }Pre-selects the clicked row's attempt.
isLoading / isErrorbooleanLoading and error states for the trail.
onReplay / replayPendingShows the Replay action; omit to hide (read-only portals).
onRedeliver / redeliverPendingOptional Redeliver action (hidden in the portal).