Components
Registry
Components
"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-detailInstalls 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.
"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#
| Prop | Type | Description |
|---|---|---|
deliveryId | string | The aggregate's id; also resets inspector state per delivery. |
delivery | WebhookDeliverySummary | null | Mutable state row; null renders the trail without state/actions (aged out). |
attempts | WebhookAttempt[] | Ascending attempt trail. |
initialAttempt | { generation, attempt } | Pre-selects the clicked row's attempt. |
isLoading / isError | boolean | Loading and error states for the trail. |
onReplay / replayPending | — | Shows the Replay action; omit to hide (read-only portals). |
onRedeliver / redeliverPending | — | Optional Redeliver action (hidden in the portal). |