{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "onboarding-07",
  "type": "registry:block",
  "title": "Onboarding Deploy Pipeline",
  "description": "A onboarding deploy pipeline block.",
  "author": "ephraim duncan <https://ephraimduncan.com>",
  "registryDependencies": [
    "accordion",
    "button",
    "progress"
  ],
  "dependencies": [
    "@tabler/icons-react"
  ],
  "files": [
    {
      "path": "content/components/onboarding/onboarding-07.tsx",
      "type": "registry:component",
      "target": "components/onboarding-07.tsx",
      "content": "'use client';\n\nimport {\n  IconCircleCheckFilled,\n  IconLoader2,\n  IconRefresh,\n} from '@tabler/icons-react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n  Accordion,\n  AccordionContent,\n  AccordionItem,\n  AccordionTrigger,\n} from '@/components/ui/accordion';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\nimport { cn } from '@/lib/utils';\n\nconst initialSteps = [\n  {\n    id: 1,\n    type: 'created' as const,\n    description: 'Export records',\n    value: 100,\n    createdOn: '2024-03-10 09:32',\n    runTime: '12min 18s',\n  },\n  {\n    id: 2,\n    type: 'created' as const,\n    description: 'Transform schema',\n    value: 100,\n    createdOn: '2024-03-10 10:03',\n    runTime: '18min 45s',\n  },\n  {\n    id: 3,\n    type: 'in progress' as const,\n    description: 'Import to new system',\n    value: 45,\n    createdOn: null as string | null,\n    runTime: null as string | null,\n  },\n];\n\nexport function Onboarding07() {\n  const [steps, setSteps] = useState(initialSteps);\n  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n  const allComplete = steps.every((s) => s.value === 100);\n\n  const startAnimation = useCallback(() => {\n    if (intervalRef.current) {\n      clearInterval(intervalRef.current);\n    }\n    intervalRef.current = setInterval(() => {\n      setSteps((prev) => {\n        const next = prev.map((step) => {\n          if (step.id !== 3 || step.value >= 100) {\n            return step;\n          }\n          const increment = Math.random() * 0.8 + 0.3;\n          const newValue = Math.min(step.value + increment, 100);\n          if (newValue >= 100) {\n            return {\n              ...step,\n              value: 100,\n              type: 'created' as const,\n              createdOn: '2024-03-10 10:47',\n              runTime: '6min 32s',\n            };\n          }\n          return { ...step, value: newValue };\n        });\n        if (next[2].value >= 100 && intervalRef.current) {\n          clearInterval(intervalRef.current);\n          intervalRef.current = null;\n        }\n        return next;\n      });\n    }, 50);\n  }, []);\n\n  useEffect(() => {\n    startAnimation();\n    return () => {\n      if (intervalRef.current) {\n        clearInterval(intervalRef.current);\n      }\n    };\n  }, [startAnimation]);\n\n  const handleRunAgain = () => {\n    setSteps(\n      initialSteps.map((step) =>\n        step.id === 3\n          ? {\n              ...step,\n              value: 45,\n              type: 'in progress' as const,\n              createdOn: null,\n              runTime: null,\n            }\n          : step\n      )\n    );\n    startAnimation();\n  };\n\n  return (\n    <div className=\"flex min-h-dvh items-center justify-center bg-background p-4\">\n      <div className=\"w-full max-w-xl\">\n        <div className=\"flex items-center justify-between\">\n          <h3 className=\"font-semibold text-foreground\">Data Migration</h3>\n          <Button\n            className={\n              allComplete ? 'opacity-100' : 'pointer-events-none opacity-0'\n            }\n            onClick={handleRunAgain}\n            size=\"icon\"\n            variant=\"secondary\"\n          >\n            <IconRefresh aria-hidden=\"true\" className=\"size-4\" />\n          </Button>\n        </div>\n        <p className=\"mt-1 text-muted-foreground text-sm leading-6\">\n          Migrating records from your legacy system\n        </p>\n        <div className=\"mt-6 flex items-center space-x-2\">\n          {steps.map((step) => (\n            <div className=\"w-full truncate\" key={step.id}>\n              <Progress className=\"h-1.5\" value={step.value} />\n              <div className=\"mt-2 flex items-center space-x-1 truncate\">\n                {step.value === 100 ? (\n                  <IconCircleCheckFilled\n                    aria-hidden={true}\n                    className=\"size-4 shrink-0 text-primary\"\n                  />\n                ) : (\n                  <IconLoader2\n                    aria-hidden={true}\n                    className=\"size-4 shrink-0 animate-spin text-primary\"\n                  />\n                )}\n                <p className=\"truncate text-muted-foreground text-xs\">\n                  {step.description}\n                </p>\n              </div>\n            </div>\n          ))}\n        </div>\n        <Accordion className=\"mt-8\" defaultValue={['logs']}>\n          <AccordionItem className=\"rounded-sm border-b-0\" value=\"logs\">\n            <AccordionTrigger className=\"font-medium text-foreground text-sm\">\n              Logs overview ({steps.length})\n            </AccordionTrigger>\n            <AccordionContent>\n              <ul className=\"mt-2 space-y-6 pb-2\">\n                {steps.map((step, stepIdx) => (\n                  <li className=\"relative flex gap-x-3\" key={step.id}>\n                    <div\n                      className={cn(\n                        'absolute top-0 left-0 flex w-6 justify-center',\n                        stepIdx === steps.length - 1 ? 'h-6' : '-bottom-6'\n                      )}\n                    >\n                      <span aria-hidden={true} className=\"w-px bg-border\" />\n                    </div>\n                    <div className=\"flex items-start space-x-2.5\">\n                      <div className=\"relative flex size-6 flex-none items-center justify-center bg-background\">\n                        {step.type === 'created' ? (\n                          <div className=\"size-3 rounded-full border border-border bg-muted/50 ring-4 ring-background\" />\n                        ) : (\n                          <div className=\"size-3 rounded-full border border-border bg-background ring-4 ring-background\" />\n                        )}\n                      </div>\n                      <div>\n                        <p className=\"mt-0.5 font-medium text-foreground text-sm\">\n                          {step.description}\n                        </p>\n                        {step.type === 'created' ? (\n                          <p className=\"text-muted-foreground text-sm leading-6\">\n                            Created on {step.createdOn}, Runtime: {step.runTime}\n                          </p>\n                        ) : (\n                          <p className=\"text-muted-foreground text-sm leading-6\">\n                            12,847 of 28,500 records processed...\n                          </p>\n                        )}\n                      </div>\n                    </div>\n                  </li>\n                ))}\n              </ul>\n            </AccordionContent>\n          </AccordionItem>\n        </Accordion>\n      </div>\n    </div>\n  );\n}\n"
    }
  ],
  "categories": [
    "onboarding"
  ]
}