"use client"; import React, { type ReactNode, useState } from "react"; import { cx } from "../lib/utils"; import { OnboardingButton } from "../primitives/onboarding-button"; import { OnboardingLabel } from "../primitives/onboarding-label"; import { OnboardingRadioCardGroup, OnboardingRadioCardItem, } from "../primitives/onboarding-radio-card"; import { OnboardingSelect, OnboardingSelectContent, OnboardingSelectItem, OnboardingSelectTrigger, OnboardingSelectValue, } from "../primitives/onboarding-select"; export interface CardOption { /** Unique identifier */ id: string; /** Display name */ name: string; /** Icon or logo - can be a ReactNode (e.g., Iconify icon) */ icon?: ReactNode; /** Optional description text */ description?: string; /** Optional badge text (e.g., "Popular", "New") */ badge?: string; /** Optional dropdown options for this card */ dropdown?: { label: string; placeholder?: string; options: { value: string; label: string }[]; defaultValue?: string; }; } export interface CardGridConfig { selectedId: string; dropdownValues: Record; } export interface CardGridStepProps { /** Title displayed at the top of the step */ title?: string; /** Description text below the title */ description?: string; /** Array of card options to display */ options: CardOption[]; /** Number of columns in the grid (default: 2) */ columns?: 1 | 2 | 3 | 4; /** Card layout orientation (default: 'vertical') */ layout?: "vertical" | "horizontal"; /** Initially selected option id */ defaultSelected?: string; /** Initial dropdown values */ defaultDropdownValues?: Record; /** Called when selection or dropdown changes */ onChange?: (config: CardGridConfig) => void; /** Called when the user submits the form */ onSubmit: (config: CardGridConfig) => void | Promise; /** Text for the submit button */ submitText?: string; /** Text shown while submitting */ loadingText?: string; /** Optional back button config */ backButton?: { text: string; onClick: () => void; }; } const columnClasses = { 1: "", 2: "md:grid-cols-2", 3: "md:grid-cols-3", 4: "md:grid-cols-4", }; export function CardGridStep({ title = "Select an option", description = "Choose the option that best fits your needs.", options, columns = 2, layout = "vertical", defaultSelected = "", defaultDropdownValues = {}, onChange, onSubmit, submitText = "Continue", loadingText = "Submitting...", backButton, }: CardGridStepProps) { const [selectedId, setSelectedId] = useState(defaultSelected); const [dropdownValues, setDropdownValues] = useState>( defaultDropdownValues, ); const [loading, setLoading] = useState(false); const handleSelectionChange = (id: string) => { setSelectedId(id); onChange?.({ selectedId: id, dropdownValues }); }; const handleDropdownChange = (optionId: string, value: string) => { const newValues = { ...dropdownValues, [optionId]: value }; setDropdownValues(newValues); onChange?.({ selectedId, dropdownValues: newValues }); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); try { await onSubmit({ selectedId, dropdownValues }); } finally { setLoading(false); } }; // Check if selected option has a required dropdown const selectedOption = options.find((opt) => opt.id === selectedId); const hasRequiredDropdown = selectedOption?.dropdown && !dropdownValues[selectedId]; const isValid = selectedId && !hasRequiredDropdown; return (

{title}

{description}

{title} {options.map((option, index) => (
{option.badge && ( {option.badge} )} {option.icon && (
{option.icon}
)}
{option.name} {option.description && (

{option.description}

)}
{/* Dropdown appears below the card when selected */} {option.dropdown && selectedId === option.id && (
{option.dropdown.label} handleDropdownChange(option.id, value) } > {option.dropdown.options.map((opt) => ( {opt.label} ))}
)}
))}
{backButton && ( {backButton.text} )} {loading ? loadingText : submitText}
); }