{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "appointment",
  "type": "registry:component",
  "title": "Appointment Booking",
  "description": "Service, professional, time slot, and confirmation.",
  "author": "Stepperize",
  "dependencies": [
    "@stepperize/react",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "label",
    "radio-group"
  ],
  "categories": [
    "commerce"
  ],
  "meta": {
    "capabilities": [
      "navigation"
    ],
    "level": "beginner",
    "tags": [
      "booking",
      "appointment",
      "calendar",
      "scheduling"
    ]
  },
  "files": [
    {
      "path": "components/stepperize/appointment.tsx",
      "type": "registry:component",
      "target": "components/stepperize/appointment.tsx",
      "content": "\"use client\";\n\nimport { defineStepper } from \"@stepperize/react\";\nimport { Check } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { Label } from \"@/components/ui/label\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\n\nconst { Stepper } = defineStepper([\n\t{ id: \"service\", title: \"Service\" },\n\t{ id: \"staff\", title: \"Professional\" },\n\t{ id: \"slot\", title: \"Date & time\" },\n\t{ id: \"confirm\", title: \"Confirm\" },\n]);\n\nconst SLOTS = [\"9:00\", \"10:30\", \"12:00\", \"14:00\", \"15:30\", \"17:00\"];\n\nexport function AppointmentBlock() {\n\tconst [booked, setBooked] = useState(false);\n\n\treturn (\n\t\t<Stepper.Root\n\t\t\tlinear\n\t\t\tclassName=\"w-full max-w-xl rounded-xl border bg-background p-6 shadow-sm\"\n\t\t>\n\t\t\t{({ stepper }) => (\n\t\t\t\t<div className=\"grid gap-6 sm:grid-cols-[160px_1fr]\">\n\t\t\t\t\t<Stepper.List className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t<Stepper.Items>\n\t\t\t\t\t\t\t{(step, index) => (\n\t\t\t\t\t\t\t\t<Stepper.Item key={step.id} step={step.id}>\n\t\t\t\t\t\t\t\t\t<Stepper.Trigger className=\"flex w-full items-center gap-2.5 rounded-lg px-2 py-1.5 text-left transition-colors disabled:cursor-not-allowed data-[status=active]:bg-muted\">\n\t\t\t\t\t\t\t\t\t\t<Stepper.Indicator className=\"group grid size-6 shrink-0 place-items-center rounded-full border text-xs font-semibold transition-colors data-[status=active]:border-primary data-[status=active]:bg-primary data-[status=active]:text-primary-foreground data-[status=previous]:border-primary data-[status=previous]:bg-primary data-[status=previous]:text-primary-foreground data-[status=upcoming]:border-border data-[status=upcoming]:text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"group-data-[status=previous]:hidden\">\n\t\t\t\t\t\t\t\t\t\t\t\t{index + 1}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t<Check className=\"hidden size-3 group-data-[status=previous]:block\" />\n\t\t\t\t\t\t\t\t\t\t</Stepper.Indicator>\n\t\t\t\t\t\t\t\t\t\t<Stepper.Title className=\"text-sm font-medium\" />\n\t\t\t\t\t\t\t\t\t</Stepper.Trigger>\n\t\t\t\t\t\t\t\t</Stepper.Item>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Stepper.Items>\n\t\t\t\t\t</Stepper.List>\n\n\t\t\t\t\t<div className=\"flex min-h-44 flex-col\">\n\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t<Stepper.Content step=\"service\">\n\t\t\t\t\t\t\t\t<RadioGroup defaultValue=\"haircut\">\n\t\t\t\t\t\t\t\t\t<Option value=\"haircut\" label=\"Haircut\" hint=\"45 min\" />\n\t\t\t\t\t\t\t\t\t<Option value=\"color\" label=\"Color\" hint=\"90 min\" />\n\t\t\t\t\t\t\t\t\t<Option value=\"styling\" label=\"Styling\" hint=\"30 min\" />\n\t\t\t\t\t\t\t\t</RadioGroup>\n\t\t\t\t\t\t\t</Stepper.Content>\n\n\t\t\t\t\t\t\t<Stepper.Content step=\"staff\">\n\t\t\t\t\t\t\t\t<RadioGroup defaultValue=\"alex\">\n\t\t\t\t\t\t\t\t\t<Option value=\"alex\" label=\"Alex Rivera\" hint=\"Senior\" />\n\t\t\t\t\t\t\t\t\t<Option value=\"sam\" label=\"Sam Lee\" hint=\"Stylist\" />\n\t\t\t\t\t\t\t\t</RadioGroup>\n\t\t\t\t\t\t\t</Stepper.Content>\n\n\t\t\t\t\t\t\t<Stepper.Content step=\"slot\">\n\t\t\t\t\t\t\t\t<RadioGroup defaultValue=\"12:00\" className=\"grid-cols-3\">\n\t\t\t\t\t\t\t\t\t{SLOTS.map((time) => (\n\t\t\t\t\t\t\t\t\t\t<Label\n\t\t\t\t\t\t\t\t\t\t\tkey={time}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"group relative cursor-pointer justify-center rounded-lg border py-1.5 text-center text-sm transition-colors has-[[data-checked]]:border-primary has-[[data-checked]]:bg-primary/5 has-[[data-checked]]:font-medium has-[[data-checked]]:text-primary has-focus-visible:border-ring has-focus-visible:ring-3 has-focus-visible:ring-ring/50\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<RadioGroupItem\n\t\t\t\t\t\t\t\t\t\t\t\tvalue={time}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"!absolute !inset-0 !h-full !w-full cursor-pointer opacity-0\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t{time}\n\t\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</RadioGroup>\n\t\t\t\t\t\t\t</Stepper.Content>\n\n\t\t\t\t\t\t\t<Stepper.Content step=\"confirm\">\n\t\t\t\t\t\t\t\t<div className=\"rounded-lg border bg-muted/40 p-4 text-sm\">\n\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">\n\t\t\t\t\t\t\t\t\t\t{booked ? \"Appointment booked\" : \"Haircut · Alex Rivera\"}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t<p className=\"mt-1 text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t{booked\n\t\t\t\t\t\t\t\t\t\t\t? \"A confirmation email has been sent.\"\n\t\t\t\t\t\t\t\t\t\t\t: \"Tomorrow at 12:00 — see you then!\"}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</Stepper.Content>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<Stepper.Actions className=\"mt-4 flex justify-end gap-2\">\n\t\t\t\t\t\t\t<Stepper.Prev className={buttonVariants({ variant: \"outline\" })}>\n\t\t\t\t\t\t\t\tBack\n\t\t\t\t\t\t\t</Stepper.Prev>\n\t\t\t\t\t\t\t{booked ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetBooked(false);\n\t\t\t\t\t\t\t\t\t\tstepper.reset();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tclassName={buttonVariants()}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tBook another\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : stepper.isLast ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => setBooked(true)}\n\t\t\t\t\t\t\t\t\tclassName={buttonVariants()}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tBook appointment\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Stepper.Next className={buttonVariants()}>\n\t\t\t\t\t\t\t\t\tContinue\n\t\t\t\t\t\t\t\t</Stepper.Next>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Stepper.Actions>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</Stepper.Root>\n\t);\n}\n\nfunction Option({\n\tvalue,\n\tlabel,\n\thint,\n}: {\n\tvalue: string;\n\tlabel: string;\n\thint: string;\n}) {\n\treturn (\n\t\t<Label className=\"flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-2 text-sm font-normal transition-colors has-[[data-checked]]:border-primary has-[[data-checked]]:bg-primary/5\">\n\t\t\t<RadioGroupItem value={value} />\n\t\t\t<span className=\"font-medium\">{label}</span>\n\t\t\t<span className=\"ml-auto text-xs text-muted-foreground\">{hint}</span>\n\t\t</Label>\n\t);\n}\n\nexport default AppointmentBlock;\n"
    }
  ]
}
