{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"modal-02","type":"registry:component","title":"Project Creation Form Modal","description":"A comprehensive form modal with React Hook Form validation and Zod schema for creating projects. Features text input, textarea, select dropdown, switch controls with proper validation, error handling, and accessibility support. Perfect for data collection workflows requiring robust form validation.","dependencies":["lucide-react","react-hook-form","@hookform/resolvers","zod"],"registryDependencies":["button","card","dialog","form","input","select","switch","textarea"],"files":[{"path":"src/registry/blocks/application/feedback/modals/modal-02.tsx","content":"\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Edit, type LucideIcon, Plus, UserPlus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as z from \"zod\";\nimport { cn } from \"@/registry/lib/utils\";\nimport { Button } from \"@/registry/ui/button\";\nimport { Card, CardContent } from \"@/registry/ui/card\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/registry/ui/dialog\";\nimport {\n  Form,\n  FormControl,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from \"@/registry/ui/form\";\nimport { Input } from \"@/registry/ui/input\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/registry/ui/select\";\nimport { Switch } from \"@/registry/ui/switch\";\nimport { Textarea } from \"@/registry/ui/textarea\";\n\ninterface FormModal {\n  id: string;\n  title: string;\n  description: string;\n  icon: LucideIcon;\n  buttonLabel: string;\n  buttonVariant: \"default\" | \"outline\" | \"secondary\" | \"destructive\";\n  confirmLabel: string;\n  schema: z.ZodSchema;\n  defaultValues: Record<string, unknown>;\n}\n\nexport interface Modal02Props {\n  className?: string;\n}\n\n// Form schemas for each modal type\nconst createProjectSchema = z.object({\n  name: z\n    .string()\n    .min(1, \"Project name is required\")\n    .min(3, \"Project name must be at least 3 characters\"),\n  description: z.string().optional(),\n  status: z\n    .enum([\"draft\", \"active\", \"archived\"], {\n      required_error: \"Please select a status\",\n    })\n    .optional(),\n  isPublic: z.boolean().default(false),\n});\n\nconst editProfileSchema = z.object({\n  firstName: z.string().min(1, \"First name is required\"),\n  lastName: z.string().min(1, \"Last name is required\"),\n  email: z.string().email(\"Please enter a valid email address\"),\n  bio: z.string().optional(),\n});\n\nconst inviteUserSchema = z.object({\n  email: z.string().email(\"Please enter a valid email address\"),\n  role: z.enum([\"viewer\", \"editor\", \"admin\"], {\n    required_error: \"Please select a role\",\n  }),\n  message: z.string().optional(),\n});\n\ntype CreateProjectFormValues = z.input<typeof createProjectSchema>;\ntype EditProfileFormValues = z.infer<typeof editProfileSchema>;\ntype InviteUserFormValues = z.infer<typeof inviteUserSchema>;\n\nconst content: {\n  header: {\n    title: string;\n    subtitle: string;\n  };\n  modals: FormModal[];\n  emptyState: {\n    message: string;\n    icon: LucideIcon;\n  };\n} = {\n  header: {\n    title: \"Form Modals\",\n    subtitle:\n      \"Modal dialogs containing forms for creating and editing application data\",\n  },\n  modals: [\n    {\n      id: \"create-project\",\n      title: \"Create New Project\",\n      description:\n        \"Set up a new project with the details below. You can always change these later.\",\n      icon: Plus,\n      buttonLabel: \"Create Project\",\n      buttonVariant: \"default\",\n      confirmLabel: \"Create Project\",\n      schema: createProjectSchema,\n      defaultValues: {\n        name: \"\",\n        description: \"\",\n        status: undefined,\n        isPublic: undefined,\n      },\n    },\n    {\n      id: \"edit-profile\",\n      title: \"Edit Profile\",\n      description:\n        \"Update your profile information. Changes will be saved automatically.\",\n      icon: Edit,\n      buttonLabel: \"Edit Profile\",\n      buttonVariant: \"outline\",\n      confirmLabel: \"Save Changes\",\n      schema: editProfileSchema,\n      defaultValues: {\n        firstName: \"John\",\n        lastName: \"Doe\",\n        email: \"john@example.com\",\n        bio: \"Software developer passionate about creating great user experiences.\",\n      },\n    },\n    {\n      id: \"invite-user\",\n      title: \"Invite Team Member\",\n      description:\n        \"Send an invitation to join your team. They'll receive an email with instructions.\",\n      icon: UserPlus,\n      buttonLabel: \"Invite User\",\n      buttonVariant: \"secondary\",\n      confirmLabel: \"Send Invitation\",\n      schema: inviteUserSchema,\n      defaultValues: {\n        email: \"\",\n        role: undefined,\n        message: \"\",\n      },\n    },\n  ],\n  emptyState: {\n    message: \"No form modals configured\",\n    icon: Plus,\n  },\n};\n\nfunction CreateProjectForm({ onSuccess }: { onSuccess: () => void }) {\n  const form = useForm<CreateProjectFormValues>({\n    resolver: zodResolver(createProjectSchema),\n    defaultValues: {\n      name: \"\",\n      description: \"\",\n      status: undefined,\n      isPublic: undefined,\n    },\n  });\n\n  const onSubmit = (values: CreateProjectFormValues) => {\n    console.log(\"Create project form submitted:\", values);\n    onSuccess();\n    form.reset();\n  };\n\n  return (\n    <Form {...form}>\n      <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n        <FormField\n          control={form.control}\n          name=\"name\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>\n                Project Name <span className=\"text-red-500 ml-1\">*</span>\n              </FormLabel>\n              <FormControl>\n                <Input\n                  placeholder=\"Enter project name\"\n                  {...field}\n                  data-testid=\"form-input-create-project-name\"\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"description\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Description</FormLabel>\n              <FormControl>\n                <Textarea\n                  placeholder=\"Describe your project\"\n                  rows={3}\n                  {...field}\n                  data-testid=\"form-textarea-create-project-description\"\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"status\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Status</FormLabel>\n              <Select onValueChange={field.onChange} value={field.value}>\n                <FormControl>\n                  <SelectTrigger data-testid=\"form-select-create-project-status\">\n                    <SelectValue placeholder=\"Select status\" />\n                  </SelectTrigger>\n                </FormControl>\n                <SelectContent>\n                  <SelectItem value=\"draft\">Draft</SelectItem>\n                  <SelectItem value=\"active\">Active</SelectItem>\n                  <SelectItem value=\"archived\">Archived</SelectItem>\n                </SelectContent>\n              </Select>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"isPublic\"\n          render={({ field }) => (\n            <FormItem className=\"flex items-center space-x-2\">\n              <FormControl>\n                <Switch\n                  checked={field.value}\n                  onCheckedChange={field.onChange}\n                  data-testid=\"form-switch-create-project-isPublic\"\n                />\n              </FormControl>\n              <FormLabel>Make project public</FormLabel>\n            </FormItem>\n          )}\n        />\n\n        <DialogFooter>\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            onClick={onSuccess}\n            data-testid=\"form-modal-cancel-create-project\"\n          >\n            Cancel\n          </Button>\n          <Button\n            type=\"submit\"\n            disabled={form.formState.isSubmitting}\n            data-testid=\"form-modal-submit-create-project\"\n          >\n            {form.formState.isSubmitting ? \"Creating...\" : \"Create Project\"}\n          </Button>\n        </DialogFooter>\n      </form>\n    </Form>\n  );\n}\n\nfunction EditProfileForm({ onSuccess }: { onSuccess: () => void }) {\n  const form = useForm<EditProfileFormValues>({\n    resolver: zodResolver(editProfileSchema),\n    defaultValues: {\n      firstName: \"John\",\n      lastName: \"Doe\",\n      email: \"john@example.com\",\n      bio: \"Software developer passionate about creating great user experiences.\",\n    },\n  });\n\n  const onSubmit = (values: EditProfileFormValues) => {\n    console.log(\"Edit profile form submitted:\", values);\n    onSuccess();\n    form.reset();\n  };\n\n  return (\n    <Form {...form}>\n      <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n        <FormField\n          control={form.control}\n          name=\"firstName\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>\n                First Name <span className=\"text-red-500 ml-1\">*</span>\n              </FormLabel>\n              <FormControl>\n                <Input\n                  placeholder=\"Enter first name\"\n                  {...field}\n                  data-testid=\"form-input-edit-profile-firstName\"\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"lastName\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>\n                Last Name <span className=\"text-red-500 ml-1\">*</span>\n              </FormLabel>\n              <FormControl>\n                <Input\n                  placeholder=\"Enter last name\"\n                  {...field}\n                  data-testid=\"form-input-edit-profile-lastName\"\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"email\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>\n                Email <span className=\"text-red-500 ml-1\">*</span>\n              </FormLabel>\n              <FormControl>\n                <Input\n                  type=\"email\"\n                  placeholder=\"Enter email address\"\n                  {...field}\n                  data-testid=\"form-input-edit-profile-email\"\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"bio\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Bio</FormLabel>\n              <FormControl>\n                <Textarea\n                  placeholder=\"Tell us about yourself\"\n                  rows={3}\n                  {...field}\n                  data-testid=\"form-textarea-edit-profile-bio\"\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <DialogFooter>\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            onClick={onSuccess}\n            data-testid=\"form-modal-cancel-edit-profile\"\n          >\n            Cancel\n          </Button>\n          <Button\n            type=\"submit\"\n            disabled={form.formState.isSubmitting}\n            data-testid=\"form-modal-submit-edit-profile\"\n          >\n            {form.formState.isSubmitting ? \"Saving...\" : \"Save Changes\"}\n          </Button>\n        </DialogFooter>\n      </form>\n    </Form>\n  );\n}\n\nfunction InviteUserForm({ onSuccess }: { onSuccess: () => void }) {\n  const form = useForm<InviteUserFormValues>({\n    resolver: zodResolver(inviteUserSchema),\n    defaultValues: {\n      email: \"\",\n      role: undefined,\n      message: \"\",\n    },\n  });\n\n  const onSubmit = (values: InviteUserFormValues) => {\n    console.log(\"Invite user form submitted:\", values);\n    onSuccess();\n    form.reset();\n  };\n\n  return (\n    <Form {...form}>\n      <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n        <FormField\n          control={form.control}\n          name=\"email\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>\n                Email Address <span className=\"text-red-500 ml-1\">*</span>\n              </FormLabel>\n              <FormControl>\n                <Input\n                  type=\"email\"\n                  placeholder=\"colleague@company.com\"\n                  {...field}\n                  data-testid=\"form-input-invite-user-email\"\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"role\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>\n                Role <span className=\"text-red-500 ml-1\">*</span>\n              </FormLabel>\n              <Select onValueChange={field.onChange} value={field.value}>\n                <FormControl>\n                  <SelectTrigger data-testid=\"form-select-invite-user-role\">\n                    <SelectValue placeholder=\"Select role\" />\n                  </SelectTrigger>\n                </FormControl>\n                <SelectContent>\n                  <SelectItem value=\"viewer\">Viewer</SelectItem>\n                  <SelectItem value=\"editor\">Editor</SelectItem>\n                  <SelectItem value=\"admin\">Admin</SelectItem>\n                </SelectContent>\n              </Select>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"message\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Personal Message (Optional)</FormLabel>\n              <FormControl>\n                <Textarea\n                  placeholder=\"Add a personal message to the invitation\"\n                  rows={3}\n                  {...field}\n                  data-testid=\"form-textarea-invite-user-message\"\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <DialogFooter>\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            onClick={onSuccess}\n            data-testid=\"form-modal-cancel-invite-user\"\n          >\n            Cancel\n          </Button>\n          <Button\n            type=\"submit\"\n            disabled={form.formState.isSubmitting}\n            data-testid=\"form-modal-submit-invite-user\"\n          >\n            {form.formState.isSubmitting ? \"Sending...\" : \"Send Invitation\"}\n          </Button>\n        </DialogFooter>\n      </form>\n    </Form>\n  );\n}\n\nexport function Modal02({ className }: Modal02Props) {\n  const [openStates, setOpenStates] = useState<Record<string, boolean>>({});\n\n  const handleOpenChange = (modalId: string, isOpen: boolean) => {\n    setOpenStates((prev) => ({ ...prev, [modalId]: isOpen }));\n  };\n\n  const handleModalClose = (modalId: string) => {\n    setOpenStates((prev) => ({ ...prev, [modalId]: false }));\n  };\n\n  const renderFormComponent = (modal: FormModal) => {\n    switch (modal.id) {\n      case \"create-project\":\n        return (\n          <CreateProjectForm onSuccess={() => handleModalClose(modal.id)} />\n        );\n      case \"edit-profile\":\n        return <EditProfileForm onSuccess={() => handleModalClose(modal.id)} />;\n      case \"invite-user\":\n        return <InviteUserForm onSuccess={() => handleModalClose(modal.id)} />;\n      default:\n        return null;\n    }\n  };\n\n  return (\n    <Card\n      className={cn(\"w-full max-w-4xl mx-auto\", className)}\n      data-testid=\"form-modals-container\"\n    >\n      <CardContent className=\"p-6\">\n        <div className=\"space-y-4\">\n          <div>\n            <h3 className=\"text-lg font-semibold\">{content.header.title}</h3>\n            <p className=\"text-sm text-muted-foreground\">\n              {content.header.subtitle}\n            </p>\n          </div>\n\n          <div\n            className=\"flex flex-wrap gap-2\"\n            data-testid=\"form-modal-buttons-container\"\n          >\n            {content.modals.map((modal) => {\n              const IconComponent = modal.icon;\n              const isOpen = openStates[modal.id] || false;\n\n              return (\n                <Dialog\n                  key={modal.id}\n                  open={isOpen}\n                  onOpenChange={(open) => handleOpenChange(modal.id, open)}\n                >\n                  <DialogTrigger asChild>\n                    <Button\n                      variant={modal.buttonVariant}\n                      size=\"sm\"\n                      data-testid={`form-modal-trigger-${modal.id}`}\n                    >\n                      <IconComponent className=\"h-4 w-4 mr-2\" />\n                      {modal.buttonLabel}\n                    </Button>\n                  </DialogTrigger>\n                  <DialogContent\n                    className=\"sm:max-w-[525px]\"\n                    data-testid={`form-modal-dialog-${modal.id}`}\n                  >\n                    <DialogHeader>\n                      <DialogTitle data-testid={`form-modal-title-${modal.id}`}>\n                        {modal.title}\n                      </DialogTitle>\n                      <DialogDescription\n                        data-testid={`form-modal-description-${modal.id}`}\n                      >\n                        {modal.description}\n                      </DialogDescription>\n                    </DialogHeader>\n                    <div\n                      className=\"py-4\"\n                      data-testid={`form-modal-fields-${modal.id}`}\n                    >\n                      {renderFormComponent(modal)}\n                    </div>\n                  </DialogContent>\n                </Dialog>\n              );\n            })}\n          </div>\n\n          {/* Empty state */}\n          {content.modals.length === 0 && (\n            <div\n              className=\"p-8 text-center\"\n              data-testid=\"form-modals-empty-state\"\n            >\n              <content.emptyState.icon className=\"h-12 w-12 text-muted-foreground mx-auto mb-4\" />\n              <p className=\"text-muted-foreground\">\n                {content.emptyState.message}\n              </p>\n            </div>\n          )}\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n","type":"registry:component"},{"path":"src/registry/lib/utils.ts","content":"import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n","type":"registry:lib"}]}