PlatformWebhooksDeliveryDetailsSheet.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import { Copy, RotateCcw } from 'lucide-react'
  2. import {
  3. Badge,
  4. Button,
  5. Card,
  6. CardContent,
  7. Separator,
  8. Sheet,
  9. SheetContent,
  10. SheetFooter,
  11. SheetHeader,
  12. SheetSection,
  13. SheetTitle,
  14. Tabs_Shadcn_ as Tabs,
  15. TabsContent_Shadcn_ as TabsContent,
  16. TabsList_Shadcn_ as TabsList,
  17. TabsTrigger_Shadcn_ as TabsTrigger,
  18. } from 'ui'
  19. import { TimestampInfo } from 'ui-patterns'
  20. import type { WebhookDelivery } from './PlatformWebhooks.types'
  21. import { formatDeliveryStatus, statusBadgeVariant } from './PlatformWebhooksView.utils'
  22. import { getStatusLevel } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.utils'
  23. import { DataTableColumnStatusCode } from '@/components/ui/DataTable/DataTableColumn/DataTableColumnStatusCode'
  24. interface PlatformWebhooksDeliveryDetailsSheetProps {
  25. deliveryAttempt: number | null
  26. deliveryDetailsTab: 'event' | 'response'
  27. deliveryEventPayload: string
  28. deliveryResponsePayload: string
  29. open: boolean
  30. selectedDelivery: WebhookDelivery | null
  31. onCopy: (value: string, label: string) => void
  32. onOpenChange: (open: boolean) => void
  33. onRetryDelivery: (deliveryId: string) => void
  34. onTabChange: (tab: 'event' | 'response') => void
  35. }
  36. export const PlatformWebhooksDeliveryDetailsSheet = ({
  37. deliveryAttempt,
  38. deliveryDetailsTab,
  39. deliveryEventPayload,
  40. deliveryResponsePayload,
  41. open,
  42. selectedDelivery,
  43. onCopy,
  44. onOpenChange,
  45. onRetryDelivery,
  46. onTabChange,
  47. }: PlatformWebhooksDeliveryDetailsSheetProps) => {
  48. const retryableDelivery =
  49. selectedDelivery && selectedDelivery.status !== 'success' ? selectedDelivery : null
  50. return (
  51. <Sheet open={open} onOpenChange={onOpenChange}>
  52. <SheetContent size="default" className="flex flex-col gap-0">
  53. <SheetHeader>
  54. <div className="flex items-center gap-2">
  55. <SheetTitle>Delivery details</SheetTitle>
  56. {selectedDelivery && (
  57. <Badge variant={statusBadgeVariant[selectedDelivery.status]}>
  58. {formatDeliveryStatus(selectedDelivery.status)}
  59. </Badge>
  60. )}
  61. </div>
  62. </SheetHeader>
  63. <Separator />
  64. {selectedDelivery && (
  65. <SheetSection className="overflow-auto grow px-0 py-0">
  66. <div className="space-y-6 p-5">
  67. <Card>
  68. <CardContent className="grid grid-cols-1 gap-4 p-4 @md:grid-cols-2">
  69. <div className="space-y-1">
  70. <p className="text-sm text-foreground-light">Delivery ID</p>
  71. <code className="text-code-inline">{selectedDelivery.id}</code>
  72. </div>
  73. <div className="space-y-1">
  74. <p className="text-sm text-foreground-light">Attempt</p>
  75. <p className="text-sm text-foreground">
  76. {deliveryAttempt ? `#${deliveryAttempt}` : '-'}
  77. </p>
  78. </div>
  79. <div className="space-y-1">
  80. <p className="text-sm text-foreground-light">Timestamp</p>
  81. <TimestampInfo className="text-sm" utcTimestamp={selectedDelivery.attemptAt} />
  82. </div>
  83. <div className="space-y-1">
  84. <p className="text-sm text-foreground-light">Response code</p>
  85. {selectedDelivery.responseCode ? (
  86. <DataTableColumnStatusCode
  87. value={selectedDelivery.responseCode}
  88. level={getStatusLevel(selectedDelivery.responseCode)}
  89. className="text-xs"
  90. />
  91. ) : (
  92. <span className="text-xs text-foreground-muted">–</span>
  93. )}
  94. </div>
  95. </CardContent>
  96. </Card>
  97. <Tabs
  98. value={deliveryDetailsTab}
  99. onValueChange={(value) => onTabChange(value as 'event' | 'response')}
  100. >
  101. <TabsList className="w-full">
  102. <TabsTrigger value="event" className="flex-1">
  103. Event
  104. </TabsTrigger>
  105. <TabsTrigger value="response" className="flex-1">
  106. Response
  107. </TabsTrigger>
  108. </TabsList>
  109. <TabsContent value="event" className="space-y-4 pt-4">
  110. <div className="space-y-1">
  111. <p className="text-sm text-foreground-light">Event type</p>
  112. <code className="text-code-inline">{selectedDelivery.eventType}</code>
  113. </div>
  114. <div className="space-y-1">
  115. <p className="text-sm text-foreground-light">Event ID</p>
  116. <code className="text-code-inline">{selectedDelivery.id}</code>
  117. </div>
  118. <div className="space-y-1">
  119. <p className="text-sm text-foreground-light">Event timestamp</p>
  120. <TimestampInfo className="text-sm" utcTimestamp={selectedDelivery.attemptAt} />
  121. </div>
  122. <div className="space-y-2">
  123. <div className="flex items-center justify-between gap-2">
  124. <p className="text-sm text-foreground-light">Payload</p>
  125. <Button
  126. type="text"
  127. icon={<Copy size={14} />}
  128. onClick={() => onCopy(deliveryEventPayload, 'event payload')}
  129. >
  130. Copy
  131. </Button>
  132. </div>
  133. <div className="rounded-md border border-default bg-surface-200 p-3">
  134. <pre className="whitespace-pre-wrap text-xs text-foreground">
  135. {deliveryEventPayload}
  136. </pre>
  137. </div>
  138. </div>
  139. </TabsContent>
  140. <TabsContent value="response" className="space-y-4 pt-4">
  141. <div className="space-y-1">
  142. <p className="text-sm text-foreground-light">Status</p>
  143. <Badge variant={statusBadgeVariant[selectedDelivery.status]}>
  144. {formatDeliveryStatus(selectedDelivery.status)}
  145. </Badge>
  146. </div>
  147. <div className="space-y-1">
  148. <p className="text-sm text-foreground-light">Response code</p>
  149. {selectedDelivery.responseCode ? (
  150. <DataTableColumnStatusCode
  151. value={selectedDelivery.responseCode}
  152. level={getStatusLevel(selectedDelivery.responseCode)}
  153. className="text-xs"
  154. />
  155. ) : (
  156. <span className="text-xs text-foreground-muted">–</span>
  157. )}
  158. </div>
  159. <div className="space-y-2">
  160. <div className="flex items-center justify-between gap-2">
  161. <p className="text-sm text-foreground-light">Response payload</p>
  162. <Button
  163. type="text"
  164. icon={<Copy size={14} />}
  165. onClick={() => onCopy(deliveryResponsePayload, 'response payload')}
  166. >
  167. Copy
  168. </Button>
  169. </div>
  170. <div className="rounded-md border border-default bg-surface-200 p-3">
  171. <pre className="whitespace-pre-wrap text-xs text-foreground">
  172. {deliveryResponsePayload}
  173. </pre>
  174. </div>
  175. </div>
  176. </TabsContent>
  177. </Tabs>
  178. </div>
  179. </SheetSection>
  180. )}
  181. {retryableDelivery && (
  182. <SheetFooter className="shrink-0">
  183. <Button
  184. type="default"
  185. icon={<RotateCcw />}
  186. onClick={() => onRetryDelivery(retryableDelivery.id)}
  187. >
  188. Retry delivery
  189. </Button>
  190. </SheetFooter>
  191. )}
  192. </SheetContent>
  193. </Sheet>
  194. )
  195. }