{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "settings-webhooks",
  "title": "Settings Webhooks",
  "description": "Configure webhooks for event notifications and deliveries.",
  "registryDependencies": [
    "alert-dialog",
    "badge",
    "button",
    "card",
    "checkbox",
    "dialog",
    "field",
    "input-group",
    "separator"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/settings/settings-webhooks.tsx",
      "content": "\"use client\";\n\nimport { Check, Copy, Plus, RefreshCw, Send, Trash2, X } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n  AlertDialogTrigger,\n} from \"@/registry/new-york/ui/alert-dialog\";\nimport { Badge } from \"@/registry/new-york/ui/badge\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/registry/new-york/ui/card\";\nimport { Checkbox } from \"@/registry/new-york/ui/checkbox\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/registry/new-york/ui/dialog\";\nimport {\n  Field,\n  FieldContent,\n  FieldError,\n  FieldLabel,\n} from \"@/registry/new-york/ui/field\";\nimport {\n  InputGroup,\n  InputGroupInput,\n} from \"@/registry/new-york/ui/input-group\";\nimport { Separator } from \"@/registry/new-york/ui/separator\";\n\nexport interface Webhook {\n  id: string;\n  name: string;\n  url: string;\n  secret?: string;\n  events: string[];\n  status: \"active\" | \"paused\" | \"failed\";\n  createdAt: Date;\n  lastTriggered?: Date;\n  successCount: number;\n  failureCount: number;\n}\n\nexport interface WebhookDelivery {\n  id: string;\n  webhookId: string;\n  status: \"success\" | \"failed\";\n  responseCode?: number;\n  timestamp: Date;\n  payload: string;\n  response?: string;\n}\n\nexport interface SettingsWebhooksProps {\n  webhooks?: Webhook[];\n  deliveries?: Record<string, WebhookDelivery[]>;\n  onCreate?: (data: {\n    name: string;\n    url: string;\n    events: string[];\n  }) => Promise<Webhook>;\n  onUpdate?: (id: string, data: Partial<Webhook>) => Promise<void>;\n  onDelete?: (id: string) => Promise<void>;\n  onTest?: (id: string) => Promise<void>;\n  onToggleStatus?: (id: string) => Promise<void>;\n  className?: string;\n}\n\nconst availableEvents = [\n  { id: \"user.created\", label: \"User Created\" },\n  { id: \"user.updated\", label: \"User Updated\" },\n  { id: \"user.deleted\", label: \"User Deleted\" },\n  { id: \"payment.succeeded\", label: \"Payment Succeeded\" },\n  { id: \"payment.failed\", label: \"Payment Failed\" },\n  { id: \"subscription.created\", label: \"Subscription Created\" },\n  { id: \"subscription.updated\", label: \"Subscription Updated\" },\n  { id: \"subscription.cancelled\", label: \"Subscription Cancelled\" },\n];\n\nfunction formatDate(date: Date): string {\n  return new Intl.DateTimeFormat(\"en-US\", {\n    month: \"short\",\n    day: \"numeric\",\n    year: \"numeric\",\n    hour: \"numeric\",\n    minute: \"2-digit\",\n  }).format(date);\n}\n\nexport default function SettingsWebhooks({\n  webhooks = [],\n  deliveries = {},\n  onCreate,\n  onUpdate,\n  onDelete,\n  onTest,\n  onToggleStatus,\n  className,\n}: SettingsWebhooksProps) {\n  const [createDialogOpen, setCreateDialogOpen] = useState(false);\n  const [selectedWebhook, setSelectedWebhook] = useState<string | null>(null);\n  const [errors, setErrors] = useState<Record<string, string>>({});\n  const [copiedSecret, setCopiedSecret] = useState<string | null>(null);\n\n  const [webhookData, setWebhookData] = useState({\n    name: \"\",\n    url: \"\",\n    events: [] as string[],\n  });\n\n  const handleCreate = async () => {\n    setErrors({});\n\n    if (!webhookData.name.trim()) {\n      setErrors({ name: \"Name is required\" });\n      return;\n    }\n\n    if (!webhookData.url.trim()) {\n      setErrors({ url: \"URL is required\" });\n      return;\n    }\n\n    try {\n      new URL(webhookData.url);\n    } catch {\n      setErrors({ url: \"Please enter a valid URL\" });\n      return;\n    }\n\n    if (webhookData.events.length === 0) {\n      setErrors({ events: \"Select at least one event\" });\n      return;\n    }\n\n    try {\n      await onCreate?.(webhookData);\n      setWebhookData({ name: \"\", url: \"\", events: [] });\n      setCreateDialogOpen(false);\n    } catch (error) {\n      setErrors({\n        _general:\n          error instanceof Error ? error.message : \"Failed to create webhook\",\n      });\n    }\n  };\n\n  const toggleEvent = (eventId: string) => {\n    setWebhookData((prev) => ({\n      ...prev,\n      events: prev.events.includes(eventId)\n        ? prev.events.filter((e) => e !== eventId)\n        : [...prev.events, eventId],\n    }));\n  };\n\n  const copySecret = async (secret: string) => {\n    await navigator.clipboard.writeText(secret);\n    setCopiedSecret(secret);\n    setTimeout(() => setCopiedSecret(null), 2000);\n  };\n\n  const getStatusBadge = (status: Webhook[\"status\"]) => {\n    switch (status) {\n      case \"active\":\n        return (\n          <Badge className=\"text-xs\" variant=\"default\">\n            Active\n          </Badge>\n        );\n      case \"paused\":\n        return (\n          <Badge className=\"text-xs\" variant=\"secondary\">\n            Paused\n          </Badge>\n        );\n      case \"failed\":\n        return (\n          <Badge className=\"text-xs\" variant=\"destructive\">\n            Failed\n          </Badge>\n        );\n    }\n  };\n\n  return (\n    <Card className={cn(\"w-full shadow-xs\", className)}>\n      <CardHeader>\n        <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n          <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n            <CardTitle className=\"wrap-break-word\">Webhooks</CardTitle>\n            <CardDescription className=\"wrap-break-word\">\n              Configure webhooks to receive real-time event notifications\n            </CardDescription>\n          </div>\n          <Dialog onOpenChange={setCreateDialogOpen} open={createDialogOpen}>\n            <DialogTrigger asChild>\n              <Button className=\"w-full shrink-0 sm:w-auto\" type=\"button\">\n                <Plus className=\"size-4\" />\n                <span className=\"whitespace-nowrap\">Create Webhook</span>\n              </Button>\n            </DialogTrigger>\n            <DialogContent className=\"sm:max-w-lg\">\n              <DialogHeader>\n                <DialogTitle>Create Webhook</DialogTitle>\n                <DialogDescription>\n                  Set up a new webhook to receive event notifications\n                </DialogDescription>\n              </DialogHeader>\n              <div className=\"flex flex-col gap-4\">\n                {errors._general && (\n                  <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-3\">\n                    <p className=\"text-destructive text-sm\">\n                      {errors._general}\n                    </p>\n                  </div>\n                )}\n\n                <Field>\n                  <FieldLabel htmlFor=\"webhook-name\">\n                    Name <span className=\"text-destructive\">*</span>\n                  </FieldLabel>\n                  <FieldContent>\n                    <InputGroup>\n                      <InputGroupInput\n                        id=\"webhook-name\"\n                        onChange={(e) =>\n                          setWebhookData((prev) => ({\n                            ...prev,\n                            name: e.target.value,\n                          }))\n                        }\n                        placeholder=\"My Webhook\"\n                        value={webhookData.name}\n                      />\n                    </InputGroup>\n                    {errors.name && <FieldError>{errors.name}</FieldError>}\n                  </FieldContent>\n                </Field>\n\n                <Field>\n                  <FieldLabel htmlFor=\"webhook-url\">\n                    URL <span className=\"text-destructive\">*</span>\n                  </FieldLabel>\n                  <FieldContent>\n                    <InputGroup>\n                      <InputGroupInput\n                        id=\"webhook-url\"\n                        onChange={(e) =>\n                          setWebhookData((prev) => ({\n                            ...prev,\n                            url: e.target.value,\n                          }))\n                        }\n                        placeholder=\"https://example.com/webhook\"\n                        type=\"url\"\n                        value={webhookData.url}\n                      />\n                    </InputGroup>\n                    {errors.url && <FieldError>{errors.url}</FieldError>}\n                  </FieldContent>\n                </Field>\n\n                <Field>\n                  <FieldLabel>\n                    Events <span className=\"text-destructive\">*</span>\n                  </FieldLabel>\n                  <FieldContent>\n                    <div className=\"flex flex-col gap-2 rounded-lg border p-3\">\n                      {availableEvents.map((event) => (\n                        <div\n                          className=\"flex items-center gap-3 rounded-lg p-2 hover:bg-muted/50\"\n                          key={event.id}\n                        >\n                          <Checkbox\n                            checked={webhookData.events.includes(event.id)}\n                            id={`event-${event.id}`}\n                            onCheckedChange={() => toggleEvent(event.id)}\n                          />\n                          <label\n                            className=\"flex-1 cursor-pointer text-sm\"\n                            htmlFor={`event-${event.id}`}\n                          >\n                            {event.label}\n                          </label>\n                        </div>\n                      ))}\n                    </div>\n                    {errors.events && <FieldError>{errors.events}</FieldError>}\n                  </FieldContent>\n                </Field>\n              </div>\n              <DialogFooter>\n                <Button\n                  onClick={() => setCreateDialogOpen(false)}\n                  type=\"button\"\n                  variant=\"outline\"\n                >\n                  Cancel\n                </Button>\n                <Button onClick={handleCreate} type=\"button\">\n                  Create Webhook\n                </Button>\n              </DialogFooter>\n            </DialogContent>\n          </Dialog>\n        </div>\n      </CardHeader>\n      <CardContent>\n        {webhooks.length === 0 ? (\n          <div className=\"flex flex-col items-center justify-center gap-4 py-12 text-center\">\n            <div className=\"flex size-12 items-center justify-center rounded-full bg-muted\">\n              <Send className=\"size-6 text-muted-foreground\" />\n            </div>\n            <div className=\"flex flex-col gap-2\">\n              <p className=\"font-medium text-sm\">No webhooks</p>\n              <p className=\"text-muted-foreground text-sm\">\n                Create your first webhook to receive event notifications\n              </p>\n            </div>\n          </div>\n        ) : (\n          <div className=\"flex flex-col gap-4\">\n            {webhooks.map((webhook) => (\n              <div\n                className=\"flex flex-col gap-4 rounded-lg border p-4\"\n                key={webhook.id}\n              >\n                {/* Header */}\n                <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n                  <div className=\"flex flex-wrap items-center gap-2\">\n                    <h4 className=\"font-medium text-sm\">{webhook.name}</h4>\n                    {getStatusBadge(webhook.status)}\n                  </div>\n                  <div className=\"flex shrink-0 flex-wrap gap-2\">\n                    <Button\n                      className=\"w-full sm:w-auto\"\n                      onClick={() => onTest?.(webhook.id)}\n                      type=\"button\"\n                      variant=\"outline\"\n                    >\n                      <Send className=\"size-4\" />\n                      Test\n                    </Button>\n                    <Button\n                      className=\"w-full sm:w-auto\"\n                      onClick={() => onToggleStatus?.(webhook.id)}\n                      type=\"button\"\n                      variant=\"outline\"\n                    >\n                      {webhook.status === \"active\" ? (\n                        <div className=\"flex items-center gap-2\">\n                          <X className=\"size-4\" />\n                          <span>Pause</span>\n                        </div>\n                      ) : (\n                        <div className=\"flex items-center gap-2\">\n                          <RefreshCw className=\"size-4\" />\n                          <span>Resume</span>\n                        </div>\n                      )}\n                    </Button>\n                    <AlertDialog>\n                      <AlertDialogTrigger asChild>\n                        <Button\n                          className=\"w-full sm:w-auto\"\n                          type=\"button\"\n                          variant=\"destructive\"\n                        >\n                          <Trash2 className=\"size-4\" />\n                          Delete\n                        </Button>\n                      </AlertDialogTrigger>\n                      <AlertDialogContent>\n                        <AlertDialogHeader>\n                          <AlertDialogTitle>Delete Webhook?</AlertDialogTitle>\n                          <AlertDialogDescription>\n                            This will permanently delete the webhook{\" \"}\n                            <strong>{webhook.name}</strong>. This action cannot\n                            be undone.\n                          </AlertDialogDescription>\n                        </AlertDialogHeader>\n                        <AlertDialogFooter>\n                          <AlertDialogCancel>Cancel</AlertDialogCancel>\n                          <AlertDialogAction\n                            onClick={() => onDelete?.(webhook.id)}\n                          >\n                            Delete Webhook\n                          </AlertDialogAction>\n                        </AlertDialogFooter>\n                      </AlertDialogContent>\n                    </AlertDialog>\n                  </div>\n                </div>\n\n                {/* Main Content */}\n                <div className=\"flex flex-col gap-4\">\n                  {/* URL and Secret */}\n                  <div className=\"flex flex-col gap-3 rounded-lg border bg-muted/30 p-4\">\n                    <Field>\n                      <FieldLabel className=\"mb-0\">URL</FieldLabel>\n                      <FieldContent>\n                        <div className=\"flex items-center gap-2\">\n                          <code className=\"flex-1 break-all rounded bg-background px-3 py-2 font-mono text-xs\">\n                            {webhook.url}\n                          </code>\n                          <Button\n                            aria-label=\"Copy URL\"\n                            onClick={() => copySecret(webhook.url)}\n                            size=\"icon-sm\"\n                            type=\"button\"\n                            variant=\"ghost\"\n                          >\n                            {copiedSecret === webhook.url ? (\n                              <Check className=\"size-4 text-green-600\" />\n                            ) : (\n                              <Copy className=\"size-4\" />\n                            )}\n                          </Button>\n                        </div>\n                      </FieldContent>\n                    </Field>\n                    {webhook.secret && (\n                      <Field>\n                        <FieldLabel className=\"mb-0\">Secret</FieldLabel>\n                        <FieldContent>\n                          <div className=\"flex items-center gap-2\">\n                            <code className=\"flex-1 rounded bg-background px-3 py-2 font-mono text-xs\">\n                              {webhook.secret.slice(0, 8)}...\n                            </code>\n                            <Button\n                              aria-label=\"Copy secret\"\n                              onClick={() => copySecret(webhook.secret!)}\n                              size=\"icon-sm\"\n                              type=\"button\"\n                              variant=\"ghost\"\n                            >\n                              {copiedSecret === webhook.secret ? (\n                                <Check className=\"size-4 text-green-600\" />\n                              ) : (\n                                <Copy className=\"size-4\" />\n                              )}\n                            </Button>\n                          </div>\n                        </FieldContent>\n                      </Field>\n                    )}\n                  </div>\n\n                  {/* Events */}\n                  <div className=\"flex flex-col gap-2\">\n                    <FieldLabel className=\"mb-0\">Events</FieldLabel>\n                    <div className=\"flex flex-wrap gap-2\">\n                      {webhook.events.map((event) => (\n                        <Badge\n                          className=\"text-xs\"\n                          key={event}\n                          variant=\"outline\"\n                        >\n                          {availableEvents.find((e) => e.id === event)?.label ||\n                            event}\n                        </Badge>\n                      ))}\n                    </div>\n                  </div>\n\n                  {/* Metadata */}\n                  <div className=\"flex flex-wrap items-center gap-3 text-muted-foreground text-xs\">\n                    <span>Created: {formatDate(webhook.createdAt)}</span>\n                    {webhook.lastTriggered && (\n                      <>\n                        <span>•</span>\n                        <span>\n                          Last triggered: {formatDate(webhook.lastTriggered)}\n                        </span>\n                      </>\n                    )}\n                    <span>•</span>\n                    <span className=\"text-green-600\">\n                      {webhook.successCount} successful\n                    </span>\n                    {webhook.failureCount > 0 && (\n                      <>\n                        <span>•</span>\n                        <span className=\"text-destructive\">\n                          {webhook.failureCount} failed\n                        </span>\n                      </>\n                    )}\n                  </div>\n\n                  {/* Recent Deliveries */}\n                  {deliveries[webhook.id] &&\n                    deliveries[webhook.id].length > 0 && (\n                      <>\n                        <Separator />\n                        <div className=\"flex flex-col gap-3\">\n                          <h5 className=\"font-medium text-sm\">\n                            Recent Deliveries\n                          </h5>\n                          <div className=\"flex flex-col gap-2\">\n                            {deliveries[webhook.id]\n                              .slice(0, 5)\n                              .map((delivery) => (\n                                <div\n                                  className=\"flex flex-col gap-2 rounded-lg border p-3 sm:flex-row sm:items-center sm:justify-between\"\n                                  key={delivery.id}\n                                >\n                                  <div className=\"flex flex-wrap items-center gap-2\">\n                                    <Badge\n                                      className=\"text-xs\"\n                                      variant={\n                                        delivery.status === \"success\"\n                                          ? \"default\"\n                                          : \"destructive\"\n                                      }\n                                    >\n                                      {delivery.status}\n                                    </Badge>\n                                    {delivery.responseCode && (\n                                      <span className=\"text-muted-foreground text-xs\">\n                                        {delivery.responseCode}\n                                      </span>\n                                    )}\n                                    <span className=\"text-muted-foreground text-xs\">\n                                      {formatDate(delivery.timestamp)}\n                                    </span>\n                                  </div>\n                                </div>\n                              ))}\n                          </div>\n                        </div>\n                      </>\n                    )}\n                </div>\n              </div>\n            ))}\n          </div>\n        )}\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "settings"
  ],
  "type": "registry:ui"
}