{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "approval-flow",
  "type": "registry:ui",
  "title": "Human approval flow",
  "description": "Carries recommendations, custom instructions, and explicit confirmation before an agent acts.",
  "dependencies": [
    "motion"
  ],
  "categories": [
    "agent"
  ],
  "docs": "https://motion-lexicon.pages.dev/en/components/approval-flow/",
  "meta": {
    "engines": [
      "motion"
    ],
    "runtimeCost": "light",
    "signature": "The selected path resolves in place into approval"
  },
  "files": [
    {
      "path": "src/registry/components/approval-flow.tsx",
      "type": "registry:ui",
      "target": "components/motion-lexicon/approval-flow.tsx",
      "content": "\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useState } from \"react\";\n\nexport type ApprovalOption = {\n  id: string;\n  label: string;\n  description?: string;\n  recommended?: boolean;\n};\nexport type ApprovalFlowProps = {\n  question: string;\n  options: readonly ApprovalOption[];\n  eyebrow?: string;\n  approveLabel?: string;\n  dismissLabel?: string;\n  customPlaceholder?: string;\n  approvedLabel?: string;\n  reviewAgainLabel?: string;\n  recommendedLabel?: string;\n  onApprove?: (value: string) => void;\n  onDismiss?: () => void;\n  className?: string;\n};\n\nexport function ApprovalFlow({\n  question,\n  options,\n  eyebrow = \"Approval required\",\n  approveLabel = \"Approve\",\n  dismissLabel = \"Dismiss\",\n  customPlaceholder = \"Add instructions\",\n  approvedLabel = \"Approved\",\n  reviewAgainLabel = \"Review again\",\n  recommendedLabel = \"Recommended\",\n  onApprove,\n  onDismiss,\n  className = \"\",\n}: ApprovalFlowProps) {\n  const [selected, setSelected] = useState(\n    options.find((option) => option.recommended)?.id ?? options[0]?.id ?? \"\",\n  );\n  const [custom, setCustom] = useState(\"\");\n  const [approved, setApproved] = useState(false);\n  const reduced = useReducedMotion();\n  const value = custom.trim() || selected;\n\n  return (\n    <section\n      className={`w-full rounded-[10px] border border-neutral-200 bg-white p-4 text-neutral-950 dark:border-white/10 dark:bg-[#1b1b1b] dark:text-neutral-50 ${className}`}\n    >\n      <AnimatePresence mode=\"wait\" initial={false}>\n        {approved ? (\n          <motion.div\n            key=\"approved\"\n            initial={reduced ? false : { opacity: 0, scale: 0.98 }}\n            animate={{ opacity: 1, scale: 1 }}\n            className=\"grid min-h-[220px] place-items-center text-center\"\n          >\n            <div>\n              <span className=\"mx-auto grid size-11 place-items-center rounded-full bg-emerald-500 text-lg text-white\">\n                ✓\n              </span>\n              <strong className=\"mt-4 block text-[14px]\">\n                {approvedLabel}\n              </strong>\n              <button\n                type=\"button\"\n                onClick={() => setApproved(false)}\n                className=\"mt-3 min-h-11 rounded-full px-3 text-[11px] text-neutral-500 hover:bg-neutral-100 dark:hover:bg-white/5\"\n              >\n                {reviewAgainLabel}\n              </button>\n            </div>\n          </motion.div>\n        ) : (\n          <motion.div\n            key=\"question\"\n            exit={reduced ? undefined : { opacity: 0, y: -6 }}\n          >\n            <span className=\"text-[11px] font-medium text-amber-700 dark:text-amber-300\">\n              {eyebrow}\n            </span>\n            <h3 className=\"mt-2 text-[16px] font-semibold tracking-[-.025em]\">\n              {question}\n            </h3>\n            <div className=\"mt-4 grid gap-2\" role=\"radiogroup\">\n              {options.map((option) => (\n                <button\n                  key={option.id}\n                  type=\"button\"\n                  role=\"radio\"\n                  aria-checked={selected === option.id}\n                  onClick={() => {\n                    setSelected(option.id);\n                    setCustom(\"\");\n                  }}\n                  className={`relative min-h-12 rounded-lg border px-3 py-2 text-left outline-none transition focus-visible:ring-2 focus-visible:ring-blue-600 ${selected === option.id ? \"border-neutral-950 bg-neutral-50 dark:border-neutral-50 dark:bg-white/5\" : \"border-neutral-200 hover:bg-neutral-50 dark:border-white/10 dark:hover:bg-white/5\"}`}\n                >\n                  <span className=\"flex items-center gap-2 text-[12px] font-medium\">\n                    <i\n                      className={`size-2 rounded-full ${selected === option.id ? \"bg-neutral-950 dark:bg-neutral-50\" : \"bg-neutral-200 dark:bg-white/15\"}`}\n                    />\n                    {option.label}\n                    {option.recommended ? (\n                      <small className=\"ml-auto rounded-md bg-neutral-100 px-2 py-0.5 text-[9px] text-neutral-600 dark:bg-white/8 dark:text-neutral-300\">\n                        {recommendedLabel}\n                      </small>\n                    ) : null}\n                  </span>\n                  {option.description ? (\n                    <span className=\"mt-1 block pl-4 text-[10px] text-neutral-500 dark:text-neutral-400\">\n                      {option.description}\n                    </span>\n                  ) : null}\n                </button>\n              ))}\n            </div>\n            <input\n              value={custom}\n              onChange={(event) => setCustom(event.target.value)}\n              placeholder={customPlaceholder}\n              className=\"mt-3 min-h-11 w-full rounded-lg border border-neutral-200 bg-transparent px-3 text-[12px] outline-none placeholder:text-neutral-500 focus:border-neutral-950 focus:ring-2 focus:ring-blue-600/20 dark:border-white/10 dark:focus:border-neutral-50\"\n            />\n            <div className=\"mt-4 flex justify-end gap-2\">\n              <button\n                type=\"button\"\n                onClick={onDismiss}\n                className=\"min-h-11 rounded-lg px-4 text-[11px] text-neutral-500 hover:bg-neutral-100 dark:hover:bg-white/5\"\n              >\n                {dismissLabel}\n              </button>\n              <button\n                type=\"button\"\n                disabled={!value}\n                onClick={() => {\n                  setApproved(true);\n                  onApprove?.(value);\n                }}\n                className=\"min-h-11 rounded-lg bg-neutral-950 px-4 text-[11px] font-semibold text-white outline-none focus-visible:ring-2 focus-visible:ring-blue-600 disabled:opacity-40 dark:bg-neutral-50 dark:text-neutral-950\"\n              >\n                {approveLabel}\n              </button>\n            </div>\n          </motion.div>\n        )}\n      </AnimatePresence>\n    </section>\n  );\n}\n"
    }
  ]
}
