@page-speed/forms 0.7.7 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -440,6 +440,7 @@ function getColumnSpanClass(span) {
440
440
  const clamped = Math.max(1, Math.min(span, 12));
441
441
  return columnSpanClasses[clamped] || "col-span-12";
442
442
  }
443
+ var FORM_GRID_FALLBACK_CSS = `[data-psf-grid]{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:1.5rem}@media(min-width:768px){[data-psf-grid]{gap:2.5rem}}[data-psf-col]{grid-column:span 12/span 12;min-width:0}@media(min-width:768px){[data-psf-col="1"]{grid-column:span 1/span 1}[data-psf-col="2"]{grid-column:span 2/span 2}[data-psf-col="3"]{grid-column:span 3/span 3}[data-psf-col="4"]{grid-column:span 4/span 4}[data-psf-col="5"]{grid-column:span 5/span 5}[data-psf-col="6"]{grid-column:span 6/span 6}[data-psf-col="7"]{grid-column:span 7/span 7}[data-psf-col="8"]{grid-column:span 8/span 8}[data-psf-col="9"]{grid-column:span 9/span 9}[data-psf-col="10"]{grid-column:span 10/span 10}[data-psf-col="11"]{grid-column:span 11/span 11}[data-psf-col="12"]{grid-column:span 12/span 12}}`;
443
444
  var DEFAULT_UPLOAD_ENDPOINT = "https://api.dashtrack.com/contacts/_/contact_form_uploads";
444
445
  function useFileUpload(options) {
445
446
  const [uploadTokens, setUploadTokens] = React2.useState([]);
@@ -668,7 +669,7 @@ function DynamicFormField({
668
669
  }, [field.type, field.options]);
669
670
  const fieldClassName = React2__namespace.useMemo(() => {
670
671
  if (usesChoiceCard) {
671
- return "p-4 border rounded rounded-lg";
672
+ return "p-4 border rounded-lg";
672
673
  } else {
673
674
  return "";
674
675
  }
@@ -972,34 +973,44 @@ function FormEngine(props) {
972
973
  onNewSubmission: resetSubmissionState
973
974
  },
974
975
  !isButtonGroup && /* @__PURE__ */ React2__namespace.createElement(React2__namespace.Fragment, null, /* @__PURE__ */ React2__namespace.createElement(
976
+ "style",
977
+ {
978
+ dangerouslySetInnerHTML: { __html: FORM_GRID_FALLBACK_CSS }
979
+ }
980
+ ), /* @__PURE__ */ React2__namespace.createElement(
975
981
  "div",
976
982
  {
983
+ "data-psf-grid": "",
977
984
  className: chunkIGI4JJKE_cjs.cn(
978
985
  "grid grid-cols-12 gap-6 md:gap-10",
979
986
  styleRules?.fieldsContainer
980
987
  )
981
988
  },
982
- formFields.map((field) => /* @__PURE__ */ React2__namespace.createElement(
983
- "div",
984
- {
985
- key: field.name,
986
- className: chunkIGI4JJKE_cjs.cn(
987
- getColumnSpanClass(field.columnSpan ?? 12),
988
- "min-w-0"
989
- )
990
- },
991
- /* @__PURE__ */ React2__namespace.createElement(
992
- DynamicFormField,
989
+ formFields.map((field) => {
990
+ const span = field.columnSpan ?? 12;
991
+ return /* @__PURE__ */ React2__namespace.createElement(
992
+ "div",
993
993
  {
994
- field,
995
- className: field.className ?? styleRules?.fieldClassName,
996
- uploadProgress,
997
- onFileUpload,
998
- onFileRemove,
999
- isUploading
1000
- }
1001
- )
1002
- ))
994
+ key: field.name,
995
+ "data-psf-col": String(span),
996
+ className: chunkIGI4JJKE_cjs.cn(
997
+ getColumnSpanClass(span),
998
+ "min-w-0"
999
+ )
1000
+ },
1001
+ /* @__PURE__ */ React2__namespace.createElement(
1002
+ DynamicFormField,
1003
+ {
1004
+ field,
1005
+ className: field.className ?? styleRules?.fieldClassName,
1006
+ uploadProgress,
1007
+ onFileUpload,
1008
+ onFileRemove,
1009
+ isUploading
1010
+ }
1011
+ )
1012
+ );
1013
+ })
1003
1014
  ), /* @__PURE__ */ React2__namespace.createElement(
1004
1015
  chunkIGI4JJKE_cjs.Button,
1005
1016
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/integration/ContactFormSerializer.ts","../src/integration/BlockAdapter.tsx","../src/integration/form-submit.ts","../src/integration/form-field-types.ts","../src/integration/use-file-upload.ts","../src/integration/use-contact-form.ts","../src/integration/DynamicFormField.tsx","../src/integration/FormEngine.tsx"],"names":["camelField","errorMessage","React","Component","response","data","humanizeFieldName","useState","useCallback","useForm","useMemo","Icon","DEFAULT_ICON_API_BASE_URL","React2","fieldIsChoiceCard","Field","cn","TextInput","TextArea","Select","MultiSelect","Radio","Checkbox","CheckboxGroup","DatePicker","DateRangePicker","TimePicker","FileInput","React3","Form","Button"],"mappings":"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","file":"integration.cjs","sourcesContent":["/**\n * @page-speed/forms - Rails API Serializer\n *\n * Serializes form data for the DashTrack ContactsController API.\n * Handles field name conversion (camelCase → snake_case), custom fields separation,\n * and upload token arrays.\n *\n * @see https://github.com/opensite-ai/page-speed-forms\n */\n\n/**\n * Standard fields recognized by the Rails ContactsController API.\n * These are serialized to snake_case and sent in the contact object.\n */\nconst STANDARD_FIELDS = [\n \"content\",\n \"email\",\n \"firstName\",\n \"lastName\",\n \"locationId\",\n \"phone\",\n \"subject\",\n \"redemptionStatus\",\n \"birthday\",\n \"city\",\n \"state\",\n \"websiteFormAssignmentId\",\n \"websiteId\",\n \"acceptsSmsMarketing\",\n \"acceptsEmailMarketing\",\n \"visitorIpAddress\",\n] as const;\n\n/**\n * Configuration parameters for Rails API submission.\n */\nexport interface RailsApiConfig {\n /**\n * API key for authentication.\n * Sent as top-level parameter: api_key\n */\n apiKey: string;\n\n /**\n * Contact category token for categorization.\n * Sent as top-level parameter: contact_category_token\n */\n contactCategoryToken?: string;\n\n /**\n * Location ID for multi-location organizations.\n * Sent as top-level parameter: location_id\n */\n locationId?: string;\n\n /**\n * Website ID for tracking form submissions.\n * Sent within contact object: website_id\n */\n websiteId?: string;\n\n /**\n * Website form assignment ID for form tracking.\n * Sent within contact object: website_form_assignment_id\n */\n websiteFormAssignmentId?: string;\n\n /**\n * Visitor IP address. If not provided, will be auto-detected on server.\n * Sent within contact object: visitor_ip_address\n */\n visitorIpAddress?: string;\n}\n\n/**\n * Serialized form data ready for Rails API submission.\n */\nexport interface SerializedFormData {\n /**\n * Top-level API key parameter.\n */\n api_key: string;\n\n /**\n * Top-level contact category token (optional).\n */\n contact_category_token?: string;\n\n /**\n * Top-level location ID (optional).\n */\n location_id?: string;\n\n /**\n * Contact object with standard fields and metadata.\n */\n contact: {\n /**\n * Standard contact fields in snake_case.\n */\n [key: string]: unknown;\n\n /**\n * Custom fields that don't match standard schema.\n * Stored as separate hash in Rails.\n */\n custom_fields?: Record<string, unknown>;\n\n /**\n * Array of upload tokens from file uploads.\n * These reference ContactFormUpload records in Rails.\n */\n contact_form_upload_tokens?: string[];\n };\n}\n\n/**\n * Form values from the form library (camelCase keys).\n */\nexport type FormValues = Record<string, unknown>;\n\n/**\n * Convert camelCase to snake_case.\n *\n * @example\n * ```ts\n * camelToSnake(\"firstName\") // \"first_name\"\n * camelToSnake(\"acceptsSmsMarketing\") // \"accepts_sms_marketing\"\n * ```\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Check if a field name is a standard Rails contact field.\n */\nfunction isStandardField(fieldName: string): boolean {\n return STANDARD_FIELDS.includes(\n fieldName as (typeof STANDARD_FIELDS)[number],\n );\n}\n\n/**\n * Extract upload tokens from form values.\n * Handles both string tokens and arrays of tokens.\n */\nfunction extractUploadTokens(values: FormValues): string[] {\n const tokens: string[] = [];\n\n for (const value of Object.values(values)) {\n if (typeof value === \"string\" && value.startsWith(\"upload_\")) {\n tokens.push(value);\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (typeof item === \"string\" && item.startsWith(\"upload_\")) {\n tokens.push(item);\n }\n }\n }\n }\n\n return tokens;\n}\n\n/**\n * Format date/time values for Rails API.\n * Rails expects ISO 8601 format.\n */\nfunction formatDateForRails(value: unknown): string | undefined {\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === \"string\") {\n // Attempt to parse and reformat to ensure valid ISO 8601\n const date = new Date(value);\n if (!isNaN(date.getTime())) {\n return date.toISOString();\n }\n }\n return undefined;\n}\n\n/**\n * Serialize form values for Rails ContactsController API.\n *\n * This function:\n * 1. Converts camelCase field names to snake_case\n * 2. Separates standard fields from custom fields\n * 3. Extracts upload tokens into contact_form_upload_tokens array\n * 4. Formats dates to ISO 8601\n * 5. Includes API configuration parameters\n *\n * @param values - Form values from useForm hook (camelCase keys)\n * @param config - Rails API configuration (apiKey, locationId, etc.)\n * @returns Serialized data ready for POST to /contacts\n *\n * @example\n * ```ts\n * const serialized = serializeForRails(\n * {\n * firstName: \"John\",\n * lastName: \"Doe\",\n * email: \"john@example.com\",\n * phone: \"555-1234\",\n * companySize: \"50-100\", // Custom field\n * resumeToken: \"upload_abc123\",\n * },\n * {\n * apiKey: \"key_123\",\n * contactCategoryToken: \"cat_xyz\",\n * locationId: \"loc_456\",\n * }\n * );\n *\n * // Result:\n * // {\n * // api_key: \"key_123\",\n * // contact_category_token: \"cat_xyz\",\n * // location_id: \"loc_456\",\n * // contact: {\n * // first_name: \"John\",\n * // last_name: \"Doe\",\n * // email: \"john@example.com\",\n * // phone: \"555-1234\",\n * // custom_fields: {\n * // company_size: \"50-100\"\n * // },\n * // contact_form_upload_tokens: [\"upload_abc123\"]\n * // }\n * // }\n * ```\n */\nexport function serializeForRails(\n values: FormValues,\n config: RailsApiConfig,\n): SerializedFormData {\n const standardFields: Record<string, unknown> = {};\n const customFields: Record<string, unknown> = {};\n\n // Extract upload tokens\n const uploadTokens = extractUploadTokens(values);\n\n // Separate standard and custom fields\n for (const [key, value] of Object.entries(values)) {\n // Skip upload token fields - they're handled separately\n if (typeof value === \"string\" && value.startsWith(\"upload_\")) {\n continue;\n }\n if (\n Array.isArray(value) &&\n value.every(\n (item) => typeof item === \"string\" && item.startsWith(\"upload_\"),\n )\n ) {\n continue;\n }\n\n const snakeKey = camelToSnake(key);\n\n if (isStandardField(key)) {\n // Format dates for birthday field\n if (key === \"birthday\") {\n const formatted = formatDateForRails(value);\n if (formatted) {\n standardFields[snakeKey] = formatted;\n }\n } else {\n // Handle array values for standard fields\n // Standard fields expect scalar values (strings), but some field types\n // like checkbox-group produce arrays. Convert arrays to comma-separated strings.\n if (Array.isArray(value)) {\n standardFields[snakeKey] = value.join(\", \");\n } else {\n standardFields[snakeKey] = value;\n }\n }\n } else {\n // Custom fields\n customFields[snakeKey] = value;\n }\n }\n\n // Add config fields to standard fields if provided\n if (config.websiteId !== undefined) {\n standardFields.website_id = config.websiteId;\n }\n if (config.websiteFormAssignmentId !== undefined) {\n standardFields.website_form_assignment_id = config.websiteFormAssignmentId;\n }\n if (config.visitorIpAddress !== undefined) {\n standardFields.visitor_ip_address = config.visitorIpAddress;\n }\n\n // Build contact object\n const contact: SerializedFormData[\"contact\"] = {\n ...standardFields,\n };\n\n // Add custom fields if any\n if (Object.keys(customFields).length > 0) {\n contact.custom_fields = customFields;\n }\n\n // Add upload tokens if any\n if (uploadTokens.length > 0) {\n contact.contact_form_upload_tokens = uploadTokens;\n }\n\n // Build final serialized data\n const serialized: SerializedFormData = {\n api_key: config.apiKey,\n contact,\n };\n\n // Add optional top-level parameters\n if (config.contactCategoryToken !== undefined) {\n serialized.contact_category_token = config.contactCategoryToken;\n }\n if (config.locationId !== undefined) {\n serialized.location_id = config.locationId;\n }\n\n return serialized;\n}\n\n/**\n * Rails API error response format.\n */\nexport interface RailsErrorResponse {\n errors: {\n [field: string]: string[];\n };\n status: number;\n}\n\n/**\n * Form error format used by @page-speed/forms.\n */\nexport type FormErrors = Record<string, string | undefined>;\n\n/**\n * Convert snake_case to camelCase.\n *\n * @example\n * ```ts\n * snakeToCamel(\"first_name\") // \"firstName\"\n * snakeToCamel(\"accepts_sms_marketing\") // \"acceptsSmsMarketing\"\n * ```\n */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/**\n * Deserialize Rails API errors to form error format.\n *\n * Converts Rails error format to the format expected by @page-speed/forms:\n * - Maps snake_case field names to camelCase\n * - Flattens error arrays to single error string (first error)\n * - Handles custom_fields errors by mapping back to original field names\n * - Extracts base errors to form-level errors\n *\n * @param railsErrors - Error response from Rails API\n * @returns Form errors object (camelCase keys)\n *\n * @example\n * ```ts\n * const formErrors = deserializeErrors({\n * errors: {\n * first_name: [\"can't be blank\", \"is too short\"],\n * email: [\"is invalid\"],\n * custom_fields: {\n * company_size: [\"is required\"]\n * },\n * base: [\"Something went wrong\"]\n * },\n * status: 422\n * });\n *\n * // Result:\n * // {\n * // firstName: \"can't be blank\",\n * // email: \"is invalid\",\n * // companySize: \"is required\",\n * // _form: \"Something went wrong\"\n * // }\n * ```\n */\nexport function deserializeErrors(railsErrors: RailsErrorResponse): FormErrors {\n const formErrors: FormErrors = {};\n\n for (const [field, messages] of Object.entries(railsErrors.errors)) {\n // Handle base errors (form-level errors)\n if (field === \"base\") {\n formErrors._form = Array.isArray(messages) ? messages[0] : messages;\n continue;\n }\n\n // Handle custom_fields errors\n if (field === \"custom_fields\" && typeof messages === \"object\") {\n for (const [customField, customMessages] of Object.entries(messages)) {\n const camelField = snakeToCamel(customField);\n const errorMessage = Array.isArray(customMessages)\n ? customMessages[0]\n : customMessages;\n formErrors[camelField] = errorMessage;\n }\n continue;\n }\n\n // Handle standard field errors\n const camelField = snakeToCamel(field);\n const errorMessage = Array.isArray(messages) ? messages[0] : messages;\n formErrors[camelField] = errorMessage;\n }\n\n return formErrors;\n}\n","/**\n * @page-speed/forms - Block Adapter\n *\n * Adapts form components for use in block-based rendering systems.\n * Wraps form components to accept block prop structure and transforms them\n * to component-native props.\n *\n * @see https://github.com/opensite-ai/page-speed-forms\n */\n\n\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * Block structure for design payloads.\n * Minimal type definition for adapter compatibility.\n */\nexport interface Block {\n _id: string;\n _type: string;\n _name?: string;\n _parent?: string | null;\n tag?: string;\n styles?: string;\n content?: string;\n blockProps?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Options for Block adapter.\n */\nexport interface BlockAdapterOptions {\n /**\n * Default props to merge with block props.\n */\n defaultProps?: Record<string, unknown>;\n\n /**\n * Transform function to convert Block props to component props.\n * Useful for custom prop mapping logic.\n */\n transformProps?: (\n blockProps: Record<string, unknown>,\n block: Block,\n ) => Record<string, unknown>;\n\n /**\n * Extract display name from block for debugging.\n * Defaults to using _name or _type from block.\n */\n getDisplayName?: (block: Block) => string;\n\n /**\n * Enable React error boundary wrapping.\n * Defaults to true.\n */\n withErrorBoundary?: boolean;\n\n /**\n * Custom error fallback component.\n * If not provided, renders basic error message.\n */\n errorFallback?: (error: Error, block: Block) => React.ReactNode;\n}\n\n/**\n * Props passed to adapted component.\n */\nexport interface AdaptedComponentProps {\n /**\n * Block data from design payload.\n */\n block: Block;\n\n /**\n * Child blocks for rendering nested content.\n * Used by container components like Form.\n */\n children?: React.ReactNode;\n\n /**\n * Callback to render child blocks.\n * Provided by block rendering systems.\n */\n renderChildren?: (blockId: string) => React.ReactNode;\n}\n\n/**\n * Error boundary component for catching render errors.\n */\nclass BlockErrorBoundary extends React.Component<\n {\n block: Block;\n fallback?: (error: Error, block: Block) => React.ReactNode;\n children: React.ReactNode;\n },\n { error: Error | null }\n> {\n constructor(props: BlockErrorBoundary[\"props\"]) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(\n `Block render error (${this.props.block._id}):`,\n error,\n errorInfo,\n );\n }\n\n render() {\n if (this.state.error) {\n if (this.props.fallback) {\n return this.props.fallback(this.state.error, this.props.block);\n }\n\n return (\n <div\n className=\"block-error border border-destructive bg-destructive p-4 rounded text-destructive-foreground\"\n data-block-id={this.props.block._id}\n data-block-type={this.props.block._type}\n >\n <p className=\"font-semibold\">Block Render Error</p>\n <p className=\"text-sm\">\n Block: {this.props.block._name || this.props.block._id} (\n {this.props.block._type})\n </p>\n <p className=\"text-sm mt-1\">{this.state.error.message}</p>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Create a Block-compatible wrapper for a form component.\n *\n * This adapter transforms Block props (from design payload) into\n * component-native props. It handles:\n * - Extracting props from `blockProps` field\n * - Merging with default props\n * - Custom prop transformation\n * - Error boundary wrapping\n * - Children rendering support\n *\n * @param Component - React component to adapt\n * @param options - Adapter configuration options\n * @returns Block-compatible component\n *\n * @example\n * ```tsx\n * import { TextInput } from \"@page-speed/forms/inputs\";\n * import { createBlockAdapter } from \"@page-speed/forms/integration\";\n *\n * // Create Block-compatible TextInput\n * const BlockTextInput = createBlockAdapter(TextInput, {\n * defaultProps: {\n * placeholder: \"Enter text...\",\n * },\n * transformProps: (blockProps, block) => ({\n * ...blockProps,\n * // Map Block content to component props\n * label: block.content || blockProps.label,\n * // Apply Block styles as className\n * className: block.styles,\n * }),\n * });\n *\n * // Register with rendering system\n * registerBlockType(\"TextInput\", BlockTextInput);\n * ```\n *\n * @example\n * ```tsx\n * // Block from design payload:\n * {\n * _id: \"field-email\",\n * _type: \"TextInput\",\n * _name: \"Email Field\",\n * content: \"Email Address\",\n * styles: \"w-full mb-4\",\n * blockProps: {\n * name: \"email\",\n * type: \"email\",\n * placeholder: \"you@example.com\",\n * required: true,\n * }\n * }\n *\n * // Transformed to TextInput props:\n * <TextInput\n * name=\"email\"\n * type=\"email\"\n * placeholder=\"you@example.com\"\n * required={true}\n * label=\"Email Address\"\n * className=\"w-full mb-4\"\n * />\n * ```\n */\nexport function createBlockAdapter<TProps extends Record<string, unknown>>(\n Component: React.ComponentType<TProps>,\n options: BlockAdapterOptions = {},\n): React.ComponentType<AdaptedComponentProps> {\n const {\n defaultProps = {},\n transformProps,\n withErrorBoundary = true,\n errorFallback,\n } = options;\n\n const AdaptedComponent: React.FC<AdaptedComponentProps> = ({\n block,\n children,\n renderChildren,\n }) => {\n // Extract component props from blockProps\n const blockProps = (block.blockProps || {}) as Record<string, unknown>;\n\n // Merge with default props\n const mergedProps = { ...defaultProps, ...blockProps };\n\n // Apply custom transformation if provided\n const finalProps = transformProps\n ? transformProps(mergedProps, block)\n : mergedProps;\n\n // Add data attributes for debugging\n const dataAttrs = {\n \"data-block-id\": block._id,\n \"data-block-type\": block._type,\n ...(block._name && { \"data-block-name\": block._name }),\n };\n\n // Merge data attributes with final props\n const componentProps = {\n ...finalProps,\n ...dataAttrs,\n } as unknown as TProps;\n\n // Render children if renderChildren callback provided\n const renderedChildren = renderChildren\n ? renderChildren(block._id)\n : children;\n\n const element = (\n <Component {...componentProps}>{renderedChildren}</Component>\n );\n\n // Wrap with error boundary if enabled\n if (withErrorBoundary) {\n return (\n <BlockErrorBoundary block={block} fallback={errorFallback}>\n {element}\n </BlockErrorBoundary>\n );\n }\n\n return element;\n };\n\n // Set display name for debugging\n const componentName = Component.displayName || Component.name || \"Component\";\n AdaptedComponent.displayName = `BlockAdapter(${componentName})`;\n\n return AdaptedComponent;\n}\n\n/**\n * Standard prop transformer for form input components.\n *\n * Applies common transformations:\n * - Maps Block `content` to `label`\n * - Maps Block `styles` to `className`\n * - Preserves all blockProps\n *\n * @param blockProps - Props from Block.blockProps\n * @param block - Full Block object\n * @returns Transformed props for component\n *\n * @example\n * ```tsx\n * const BlockTextInput = createBlockAdapter(TextInput, {\n * transformProps: standardInputTransformer,\n * });\n * ```\n */\nexport function standardInputTransformer(\n blockProps: Record<string, unknown>,\n block: Block,\n): Record<string, unknown> {\n return {\n ...blockProps,\n // Use content as label if not already provided\n ...(block.content && !blockProps.label && { label: block.content }),\n // Apply Block styles as className\n ...(block.styles && { className: block.styles }),\n };\n}\n\n/**\n * Create multiple Block adapters with shared options.\n *\n * Convenience function for adapting multiple components at once\n * with the same configuration.\n *\n * @param components - Record of component name to component\n * @param options - Shared adapter options\n * @returns Record of adapted components\n *\n * @example\n * ```tsx\n * import * as Inputs from \"@page-speed/forms/inputs\";\n * import { createBlockAdapters, standardInputTransformer } from \"@page-speed/forms/integration\";\n *\n * const BlockInputs = createBlockAdapters(\n * {\n * TextInput: Inputs.TextInput,\n * TextArea: Inputs.TextArea,\n * Select: Inputs.Select,\n * },\n * {\n * transformProps: standardInputTransformer,\n * withErrorBoundary: true,\n * }\n * );\n *\n * // BlockInputs.TextInput, BlockInputs.TextArea, BlockInputs.Select\n * // are now Block-compatible\n * ```\n */\nexport function createBlockAdapters<\n TComponents extends Record<string, React.ComponentType<any>>,\n>(\n components: TComponents,\n options: BlockAdapterOptions = {},\n): Record<keyof TComponents, React.ComponentType<AdaptedComponentProps>> {\n const adapted: Record<\n string,\n React.ComponentType<AdaptedComponentProps>\n > = {};\n\n for (const [name, component] of Object.entries(components)) {\n adapted[name] = createBlockAdapter(component, options);\n }\n\n return adapted as Record<\n keyof TComponents,\n React.ComponentType<AdaptedComponentProps>\n >;\n}\n","import type {\n FormLayoutSettings,\n FormSubmissionConfig,\n} from \"../core/types\";\nimport {\n deserializeErrors,\n serializeForRails,\n type FormErrors,\n type RailsApiConfig,\n type RailsErrorResponse,\n} from \"./ContactFormSerializer\";\n\nexport type PageSpeedFormMethod = \"post\" | \"get\" | \"put\" | \"patch\";\nexport type PageSpeedFormSubmissionFormat = \"json\" | \"rails\";\n\nexport interface PageSpeedFormSubmissionResult {\n formData: Record<string, any>;\n responseData: unknown;\n}\n\n/**\n * PageSpeed-specific extension of the core FormSubmissionConfig.\n * Inherits behavior, customComponent, and newFormSubmissionAction from the\n * base type and adds integration-layer callbacks and redirect support.\n */\nexport interface PageSpeedFormSubmissionConfig extends FormSubmissionConfig {\n /**\n * Optional callback triggered on successful submission.\n */\n handleFormSubmission?: (\n result: PageSpeedFormSubmissionResult,\n ) => void | Promise<void>;\n\n /**\n * Redirect destination used when behavior is \"redirect\".\n */\n redirectUrl?: string;\n}\n\nexport interface PageSpeedFormConfig {\n /**\n * API endpoint used for submission (also applied to form action).\n */\n endpoint?: string;\n /**\n * HTTP method for submission.\n * @default \"post\"\n */\n method?: PageSpeedFormMethod;\n /**\n * Submission format.\n * Defaults to \"rails\" when apiKey is present, otherwise \"json\".\n */\n format?: PageSpeedFormSubmissionFormat;\n /**\n * Additional headers for the submission request.\n */\n headers?: Record<string, string>;\n /**\n * Static values merged into the payload (e.g. subject, content).\n */\n values?: Record<string, unknown>;\n /**\n * Rails API key (required for rails format).\n */\n apiKey?: string;\n /**\n * Rails contact category token.\n */\n contactCategoryToken?: string;\n /**\n * Rails location ID.\n */\n locationId?: string;\n /**\n * Rails website ID.\n */\n websiteId?: string;\n /**\n * Rails website form assignment ID.\n */\n websiteFormAssignmentId?: string;\n /**\n * Rails visitor IP address override.\n */\n visitorIpAddress?: string;\n /**\n * Reset form values after a successful submission.\n * @default true\n */\n resetOnSuccess?: boolean;\n /**\n * Optional post-submission behavior configuration.\n */\n submissionConfig?: PageSpeedFormSubmissionConfig;\n\n /**\n * Optional layout and presentation settings.\n * Provides a typed home for layout props (formLayout, buttonGroupSize, etc.)\n * so consumers don't need an `as any` cast when passing them alongside API config.\n */\n formLayoutSettings?: FormLayoutSettings;\n}\n\nexport class PageSpeedFormSubmissionError extends Error {\n formErrors?: FormErrors;\n status?: number;\n\n constructor(\n message: string,\n options: { formErrors?: FormErrors; status?: number } = {},\n ) {\n super(message);\n this.name = \"PageSpeedFormSubmissionError\";\n this.formErrors = options.formErrors;\n this.status = options.status;\n }\n}\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function isValidEmail(value: string): boolean {\n return EMAIL_REGEX.test(value);\n}\n\nfunction buildUrlWithParams(\n endpoint: string,\n values: Record<string, any>,\n): string {\n const base =\n typeof window === \"undefined\" ? \"http://localhost\" : window.location.origin;\n const url = new URL(endpoint, base);\n\n Object.entries(values).forEach(([key, value]) => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n url.searchParams.append(key, String(item));\n }\n });\n return;\n }\n\n url.searchParams.set(key, String(value));\n });\n\n return url.toString();\n}\n\nfunction normalizeMethod(method?: PageSpeedFormMethod): string {\n return (method || \"post\").toUpperCase();\n}\n\nfunction resolveFormat(\n config?: PageSpeedFormConfig,\n): PageSpeedFormSubmissionFormat {\n if (config?.format) return config.format;\n return config?.apiKey ? \"rails\" : \"json\";\n}\n\nfunction mergeValues(\n values: Record<string, any>,\n config?: PageSpeedFormConfig,\n): Record<string, any> {\n return {\n ...(config?.values ?? {}),\n ...values,\n };\n}\n\nexport async function submitPageSpeedForm(\n values: Record<string, any>,\n config?: PageSpeedFormConfig,\n): Promise<unknown> {\n if (!config?.endpoint) {\n return null;\n }\n\n const payload = mergeValues(values, config);\n const method = normalizeMethod(config.method);\n const format = resolveFormat(config);\n const headers: Record<string, string> = { ...(config.headers ?? {}) };\n\n if (format === \"rails\") {\n if (!config.apiKey) {\n throw new PageSpeedFormSubmissionError(\n \"Missing apiKey for Rails form submission.\",\n );\n }\n\n const railsConfig: RailsApiConfig = {\n apiKey: config.apiKey,\n contactCategoryToken: config.contactCategoryToken,\n locationId: config.locationId,\n websiteId: config.websiteId,\n websiteFormAssignmentId: config.websiteFormAssignmentId,\n visitorIpAddress: config.visitorIpAddress,\n };\n\n const serialized = serializeForRails(payload, railsConfig);\n\n if (serialized.contact.contact_form_upload_tokens) {\n serialized.contact.contact_form_upload_tokens = (\n serialized.contact.contact_form_upload_tokens as string[]\n ).map((token) => token.replace(/^upload_/, \"\"));\n }\n\n headers[\"Content-Type\"] ??= \"application/json\";\n\n const response = await fetch(config.endpoint, {\n method,\n headers,\n body: JSON.stringify(serialized),\n });\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok || (data && data.errors)) {\n const errorResponse: RailsErrorResponse = {\n errors: data?.errors ?? { base: [\"Form submission failed\"] },\n status: data?.status ?? response.status,\n };\n const formErrors = deserializeErrors(errorResponse);\n throw new PageSpeedFormSubmissionError(\"Form submission failed.\", {\n formErrors,\n status: errorResponse.status,\n });\n }\n\n return data;\n }\n\n if (method === \"GET\") {\n const url = buildUrlWithParams(config.endpoint, payload);\n const response = await fetch(url, { method, headers });\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n throw new PageSpeedFormSubmissionError(\n data?.message || \"Form submission failed.\",\n { status: response.status },\n );\n }\n\n return data;\n }\n\n headers[\"Content-Type\"] ??= \"application/json\";\n\n const response = await fetch(config.endpoint, {\n method,\n headers,\n body: JSON.stringify(payload),\n });\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n throw new PageSpeedFormSubmissionError(\n data?.message || \"Form submission failed.\",\n { status: response.status },\n );\n }\n\n return data;\n}\n","/**\n * Dynamic form field schema types and helpers.\n *\n * These utilities are intentionally exposed from the integration layer so\n * block/rendering libraries can share one field schema contract.\n */\n\nimport { humanizeFieldName } from \"../lib/utils\";\n\nexport type FormFieldType =\n | \"text\"\n | \"email\"\n | \"search\"\n | \"password\"\n | \"tel\"\n | \"textarea\"\n | \"select\"\n | \"radio\"\n | \"checkbox\"\n | \"checkbox-group\"\n | \"number\"\n | \"url\"\n | \"date\"\n | \"date-picker\"\n | \"date-range\"\n | \"time\"\n | \"file\"\n | \"multi-select\";\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n description?: string;\n}\n\nexport interface FormFieldConfig {\n /**\n * Optionally displays label for the field\n */\n label?: string;\n /**\n * Unique field name (used as the key in form values)\n */\n name: string;\n /**\n * Field type\n */\n type: FormFieldType;\n\n /**\n * Placeholder text\n */\n placeholder?: string;\n /**\n * Whether the field is required\n * @default false\n */\n required?: boolean;\n /**\n * Column span in grid layout (1-12)\n * @default 12 (full width)\n */\n columnSpan?: number;\n /**\n * Options for select/radio/checkbox-group fields\n */\n options?: SelectOption[];\n /**\n * Number of rows for textarea\n * @default 4\n */\n rows?: number;\n /**\n * Custom validation function\n * Return undefined for valid, or an error message string for invalid\n */\n validator?: (\n value: any,\n allValues: Record<string, any>,\n ) => string | undefined;\n /**\n * Additional CSS classes for the field wrapper\n */\n className?: string;\n /**\n * Whether the field is disabled\n * @default false\n */\n disabled?: boolean;\n /**\n * Accepted file types for file inputs (MIME types or extensions)\n * @example \".pdf,.doc,.docx\"\n * @example \"image/*,application/pdf\"\n */\n accept?: string;\n /**\n * Maximum file size in bytes for file inputs\n * @default 5MB (5 * 1024 * 1024)\n */\n maxSize?: number;\n /**\n * Maximum number of files for file inputs\n * @default 1\n */\n maxFiles?: number;\n /**\n * Allow multiple file selection\n * @default false\n */\n multiple?: boolean;\n /**\n * Description/help text displayed with the field\n */\n description?: string;\n /**\n * Layout for radio/checkbox groups\n * @default \"stacked\"\n */\n layout?: \"grid\" | \"stacked\";\n}\n\n/**\n * Generate initial values object from form field configs.\n */\nexport function generateInitialValues(\n fields: FormFieldConfig[],\n): Record<string, any> {\n return fields.reduce(\n (acc, field) => {\n if (field.type === \"checkbox\") {\n acc[field.name] = false;\n } else if (\n field.type === \"checkbox-group\" ||\n field.type === \"multi-select\"\n ) {\n acc[field.name] = [];\n } else if (field.type === \"file\") {\n acc[field.name] = [];\n } else if (field.type === \"date-range\") {\n acc[field.name] = { start: null, end: null };\n } else {\n acc[field.name] = \"\";\n }\n return acc;\n },\n {} as Record<string, any>,\n );\n}\n\n/**\n * Generate validation schema from form field configs.\n */\nexport function generateValidationSchema(\n fields: FormFieldConfig[],\n): Record<\n string,\n (value: any, allValues: Record<string, any>) => string | undefined\n> {\n return fields.reduce(\n (acc, field) => {\n acc[field.name] = (value: any, allValues: Record<string, any>) => {\n if (field.required) {\n if (!value || (typeof value === \"string\" && !value.trim())) {\n const displayName = field.label || humanizeFieldName(field.name);\n return `${displayName} is required`;\n }\n }\n\n if (field.type === \"email\" && value) {\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value)) {\n return \"Please enter a valid email address\";\n }\n }\n\n if (field.type === \"url\" && value) {\n try {\n new URL(value);\n } catch {\n return \"Please enter a valid URL\";\n }\n }\n\n if (field.validator) {\n return field.validator(value, allValues);\n }\n\n return undefined;\n };\n return acc;\n },\n {} as Record<\n string,\n (value: any, allValues: Record<string, any>) => string | undefined\n >,\n );\n}\n\n/**\n * Static mapping of column span values to Tailwind classes.\n *\n * IMPORTANT: These must remain complete, literal class strings so Tailwind's\n * scanner can detect and generate them.\n */\nconst columnSpanClasses: Record<number, string> = {\n 1: \"col-span-12 md:col-span-1\",\n 2: \"col-span-12 md:col-span-2\",\n 3: \"col-span-12 md:col-span-3\",\n 4: \"col-span-12 md:col-span-4\",\n 5: \"col-span-12 md:col-span-5\",\n 6: \"col-span-12 md:col-span-6\",\n 7: \"col-span-12 md:col-span-7\",\n 8: \"col-span-12 md:col-span-8\",\n 9: \"col-span-12 md:col-span-9\",\n 10: \"col-span-12 md:col-span-10\",\n 11: \"col-span-12 md:col-span-11\",\n 12: \"col-span-12\",\n};\n\n/**\n * Get grid column span class for Tailwind.\n *\n * On small screens the field is always full-width, then from `md` and up the\n * configured span is applied.\n */\nexport function getColumnSpanClass(span?: number): string {\n if (!span || span === 12) return \"col-span-12\";\n const clamped = Math.max(1, Math.min(span, 12));\n return columnSpanClasses[clamped] || \"col-span-12\";\n}\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\n\nexport interface FileUploadProgress {\n [fileName: string]: number;\n}\n\nexport interface UseFileUploadReturn {\n uploadTokens: string[];\n uploadProgress: FileUploadProgress;\n isUploading: boolean;\n uploadFiles: (files: File[]) => Promise<void>;\n removeFile: (file: File, index: number) => void;\n resetUpload: () => void;\n}\n\ninterface ContactFormUploadResponse {\n contact_form_upload?: {\n token?: string;\n };\n}\n\nexport interface UseFileUploadOptions {\n onError?: (error: Error) => void;\n /**\n * Upload endpoint.\n *\n * Defaults to DashTrack contact form uploads endpoint.\n */\n endpoint?: string;\n}\n\nconst DEFAULT_UPLOAD_ENDPOINT =\n \"https://api.dashtrack.com/contacts/_/contact_form_uploads\";\n\n/**\n * Upload helper for two-phase contact form uploads.\n */\nexport function useFileUpload(\n options?: UseFileUploadOptions,\n): UseFileUploadReturn {\n const [uploadTokens, setUploadTokens] = useState<string[]>([]);\n const [uploadProgress, setUploadProgress] = useState<FileUploadProgress>({});\n const [isUploading, setIsUploading] = useState(false);\n\n const endpoint = options?.endpoint || DEFAULT_UPLOAD_ENDPOINT;\n\n const uploadFiles = useCallback(\n async (files: File[]) => {\n if (files.length === 0) return;\n\n setIsUploading(true);\n\n try {\n const tokens: string[] = [];\n\n for (const file of files) {\n const formData = new FormData();\n formData.append(\"contact_form_upload[file_upload]\", file);\n formData.append(\"contact_form_upload[title]\", file.name);\n formData.append(\"contact_form_upload[file_name]\", file.name);\n formData.append(\"contact_form_upload[file_size]\", String(file.size));\n\n const response = await fetch(endpoint, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.statusText}`);\n }\n\n const data = (await response.json()) as ContactFormUploadResponse;\n if (data.contact_form_upload?.token) {\n tokens.push(`upload_${data.contact_form_upload.token}`);\n }\n\n setUploadProgress((prev) => ({\n ...prev,\n [file.name]: 100,\n }));\n }\n\n setUploadTokens(tokens);\n } catch (error) {\n options?.onError?.(error as Error);\n } finally {\n setIsUploading(false);\n }\n },\n [endpoint, options],\n );\n\n const removeFile = useCallback((file: File, index: number) => {\n setUploadTokens((prev) => prev.filter((_, i) => i !== index));\n setUploadProgress((prev) => {\n const next = { ...prev };\n delete next[file.name];\n return next;\n });\n }, []);\n\n const resetUpload = useCallback(() => {\n setUploadTokens([]);\n setUploadProgress({});\n }, []);\n\n return {\n uploadTokens,\n uploadProgress,\n isUploading,\n uploadFiles,\n removeFile,\n resetUpload,\n };\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useForm as usePageSpeedForm } from \"../core/useForm\";\nimport type { UseFormReturn } from \"../core/types\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport {\n generateInitialValues,\n generateValidationSchema,\n} from \"./form-field-types\";\nimport {\n PageSpeedFormSubmissionError,\n submitPageSpeedForm,\n type PageSpeedFormConfig,\n} from \"./form-submit\";\n\nexport interface UseContactFormOptions {\n /**\n * Form field configurations.\n */\n formFields: FormFieldConfig[];\n /**\n * Form submission configuration.\n */\n formConfig?: PageSpeedFormConfig;\n /**\n * Optional custom submit handler.\n */\n onSubmit?: (values: Record<string, any>) => void | Promise<void>;\n /**\n * Optional success callback.\n */\n onSuccess?: (data: unknown) => void;\n /**\n * Optional error callback.\n */\n onError?: (error: Error) => void;\n /**\n * Reset form values after successful submission.\n * @default true\n */\n resetOnSuccess?: boolean;\n /**\n * File upload tokens merged into payload.\n */\n uploadTokens?: string[];\n /**\n * Optional app-level navigation handler for internal redirects.\n * Return `false` to force fallback browser navigation.\n */\n navigate?: (href: string) => boolean | void;\n}\n\nexport interface UseContactFormReturn {\n form: UseFormReturn<Record<string, any>>;\n isSubmitted: boolean;\n submissionError: string | null;\n formMethod: \"get\" | \"post\";\n resetSubmissionState: () => void;\n}\n\ninterface RedirectResolution {\n destination: string;\n internalHref?: string;\n}\n\nfunction resolveRedirect(redirectUrl: string): RedirectResolution {\n const trimmed = redirectUrl.trim();\n if (trimmed.startsWith(\"/\") && !trimmed.startsWith(\"//\")) {\n return { destination: trimmed, internalHref: trimmed };\n }\n\n if (typeof window === \"undefined\") {\n return { destination: trimmed };\n }\n\n try {\n const url = new URL(trimmed, window.location.href);\n if (url.origin === window.location.origin) {\n return {\n destination: url.toString(),\n internalHref: `${url.pathname}${url.search}${url.hash}`,\n };\n }\n\n return { destination: url.toString() };\n } catch {\n return { destination: trimmed };\n }\n}\n\n/**\n * Form orchestration helper for dynamic contact forms.\n */\nexport function useContactForm(\n options: UseContactFormOptions,\n): UseContactFormReturn {\n const {\n formFields,\n formConfig,\n onSubmit,\n onSuccess,\n onError,\n resetOnSuccess = true,\n uploadTokens = [],\n navigate,\n } = options;\n\n const [submissionError, setSubmissionError] = useState<string | null>(null);\n const submissionConfig = formConfig?.submissionConfig;\n const redirectUrl = submissionConfig?.redirectUrl;\n\n const resetSubmissionState = useCallback(() => {\n setSubmissionError(null);\n }, []);\n\n const performRedirect = useCallback(() => {\n if (!redirectUrl || typeof window === \"undefined\") {\n return;\n }\n\n const { destination, internalHref } = resolveRedirect(redirectUrl);\n\n const attemptInternalNavigation = () => {\n if (!internalHref) return false;\n\n if (navigate) {\n return navigate(internalHref) !== false;\n }\n\n const handler = (window as any).__opensiteNavigationHandler;\n if (typeof handler === \"function\") {\n try {\n return handler(internalHref, undefined) !== false;\n } catch {\n return false;\n }\n }\n\n return false;\n };\n\n window.setTimeout(() => {\n if (attemptInternalNavigation()) return;\n window.location.assign(destination);\n }, 150);\n }, [navigate, redirectUrl]);\n\n const form = usePageSpeedForm<Record<string, any>>({\n initialValues: useMemo(\n () => generateInitialValues(formFields),\n [formFields],\n ),\n validationSchema: useMemo(\n () => generateValidationSchema(formFields),\n [formFields],\n ),\n onSubmit: async (values, helpers) => {\n resetSubmissionState();\n const shouldAutoSubmit = Boolean(formConfig?.endpoint);\n\n if (!shouldAutoSubmit && !onSubmit) {\n return;\n }\n\n try {\n let result: unknown;\n\n const submissionValues = {\n ...values,\n ...(uploadTokens.length > 0 && {\n contact_form_upload_tokens: uploadTokens,\n }),\n };\n\n if (shouldAutoSubmit) {\n result = await submitPageSpeedForm(submissionValues, formConfig);\n }\n\n if (onSubmit) {\n await onSubmit(submissionValues);\n }\n\n if (shouldAutoSubmit || onSubmit) {\n try {\n await submissionConfig?.handleFormSubmission?.({\n formData: submissionValues,\n responseData: result,\n });\n } catch {\n // Callback errors should not fail the submission lifecycle.\n }\n\n if (resetOnSuccess) {\n helpers.resetForm();\n }\n\n onSuccess?.(result);\n\n if (\n submissionConfig?.behavior === \"redirect\" &&\n submissionConfig.redirectUrl\n ) {\n performRedirect();\n }\n }\n } catch (error) {\n if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {\n helpers.setErrors(error.formErrors);\n }\n\n const errorMessage =\n error instanceof Error ? error.message : \"Form submission failed\";\n setSubmissionError(errorMessage);\n onError?.(error as Error);\n }\n },\n });\n\n const formMethod =\n formConfig?.method?.toLowerCase() === \"get\" ? \"get\" : \"post\";\n\n return {\n form,\n isSubmitted: form.status === \"success\",\n submissionError,\n formMethod,\n resetSubmissionState,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Field } from \"../core/Field\";\nimport {\n Checkbox,\n CheckboxGroup,\n DatePicker,\n DateRangePicker,\n FileInput,\n MultiSelect,\n Radio,\n Select,\n TextArea,\n TextInput,\n TimePicker,\n} from \"../inputs\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport { cn, fieldIsChoiceCard } from \"../lib/utils\";\nimport { DEFAULT_ICON_API_BASE_URL, Icon } from \"@page-speed/icon\";\n\nexport interface DynamicFormFieldProps {\n field: FormFieldConfig;\n className?: string;\n uploadProgress?: { [fileName: string]: number };\n onFileUpload?: (files: File[]) => Promise<void>;\n onFileRemove?: (file: File, index: number) => void;\n isUploading?: boolean;\n /**\n * Whether to render labels via the Field component.\n * When false, only the input component is rendered.\n * Default: true\n */\n renderLabel?: boolean;\n}\n\n/** Icon name mapping for field types that get an automatic start icon. */\nconst FIELD_TYPE_ICON_MAP: Record<string, string> = {\n email: \"material-symbols/mark-email-unread-outline-rounded\",\n tel: \"material-symbols/phone-iphone-outline\",\n url: \"flowbite/link-solid\",\n};\n\n/** Returns a default iconStart element for supported field types, or undefined. */\nfunction getDefaultIconStart(\n field: FormFieldConfig,\n): React.ReactNode | undefined {\n // Check explicit type mapping first\n const iconName = FIELD_TYPE_ICON_MAP[field.type];\n if (iconName) {\n return (\n <Icon name={iconName} apiKey={DEFAULT_ICON_API_BASE_URL} size={18} />\n );\n }\n\n // Special case: text field named \"table_server_name\"\n if (field.type === \"text\" && field.name === \"table_server_name\") {\n return (\n <Icon\n name=\"majesticons/user-box-line\"\n apiKey={DEFAULT_ICON_API_BASE_URL}\n size={18}\n />\n );\n }\n\n return undefined;\n}\n\n/**\n * Dynamic renderer for form field schema configuration.\n */\nexport function DynamicFormField({\n field,\n className,\n uploadProgress = {},\n onFileUpload,\n onFileRemove,\n isUploading = false,\n renderLabel = true,\n}: DynamicFormFieldProps): React.JSX.Element {\n const fieldId = field.name;\n const usesChoiceCard = React.useMemo(() => {\n return fieldIsChoiceCard(field);\n }, [field.type, field.options]);\n\n const fieldClassName = React.useMemo(() => {\n if (usesChoiceCard) {\n return \"p-4 border rounded rounded-lg\";\n } else {\n return \"\";\n }\n }, [usesChoiceCard]);\n\n const usesGroupLegend =\n field.type === \"radio\" || field.type === \"checkbox-group\";\n const usesInlineCheckboxLabel = field.type === \"checkbox\";\n const shouldRenderFieldLabel =\n renderLabel && !usesGroupLegend && !usesInlineCheckboxLabel;\n\n return (\n <Field\n name={field.name}\n label={shouldRenderFieldLabel ? field.label : undefined}\n description={shouldRenderFieldLabel ? field.description : undefined}\n required={field.required}\n className={cn(fieldClassName, className)}\n >\n {({ field: formField, meta }) => (\n <div>\n {(field.type === \"text\" ||\n field.type === \"email\" ||\n field.type === \"tel\" ||\n field.type === \"search\" ||\n field.type === \"password\" ||\n field.type === \"url\") && (\n <TextInput\n {...formField}\n id={fieldId}\n type={field.type}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n iconStart={getDefaultIconStart(field)}\n />\n )}\n\n {field.type === \"number\" && (\n <TextInput\n {...formField}\n id={fieldId}\n type=\"text\"\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"textarea\" && (\n <TextArea\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n rows={field.rows || 4}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"select\" && field.options && (\n <Select\n {...formField}\n id={fieldId}\n options={field.options}\n placeholder={\n field.placeholder ||\n `Select ${field.label ? field.label.toLocaleLowerCase() : field.name ? field.name.toLocaleLowerCase() : \"Item\"}`\n }\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"multi-select\" && field.options && (\n <MultiSelect\n {...formField}\n id={fieldId}\n options={field.options}\n placeholder={\n field.placeholder ||\n `Select ${field.label ? field.label.toLocaleLowerCase() : field.name ? field.name.toLocaleLowerCase() : \"Item\"}`\n }\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"radio\" && field.options && (\n <Radio\n {...formField}\n id={fieldId}\n options={field.options}\n label={field.label}\n description={field.description}\n required={field.required}\n disabled={field.disabled}\n layout={field.layout || \"stacked\"}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"checkbox\" && (\n <Checkbox\n {...formField}\n id={fieldId}\n value={formField.value === true || formField.value === \"true\"}\n onChange={(checked) => formField.onChange(checked)}\n label={field.label}\n description={field.description}\n disabled={field.disabled}\n required={field.required}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"checkbox-group\" && field.options && (\n <CheckboxGroup\n {...formField}\n id={fieldId}\n options={field.options}\n label={field.label}\n description={field.description}\n required={field.required}\n disabled={field.disabled}\n layout={field.layout || \"stacked\"}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {(field.type === \"date-picker\" || field.type === \"date\") && (\n <DatePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"date-range\" && (\n <DateRangePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"time\" && (\n <TimePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"file\" && (\n <FileInput\n {...formField}\n id={fieldId}\n accept={field.accept}\n maxSize={field.maxSize || 5 * 1024 * 1024}\n maxFiles={field.maxFiles || 1}\n multiple={field.multiple || false}\n placeholder={field.placeholder || \"Choose file(s)...\"}\n error={meta.touched && !!meta.error}\n disabled={field.disabled || isUploading}\n showProgress\n uploadProgress={uploadProgress}\n onChange={(files) => {\n formField.onChange(files);\n if (files.length > 0 && onFileUpload) {\n onFileUpload(files);\n }\n }}\n onFileRemove={onFileRemove}\n aria-label={field.label}\n />\n )}\n </div>\n )}\n </Field>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../components/ui/button\";\nimport { Form } from \"../core/Form\";\nimport { DynamicFormField } from \"./DynamicFormField\";\nimport { getColumnSpanClass } from \"./form-field-types\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport type { FormRenderConfig } from \"../core/types\";\nimport type { PageSpeedFormConfig } from \"./form-submit\";\nimport { useContactForm } from \"./use-contact-form\";\nimport { useFileUpload } from \"./use-file-upload\";\n\n// ─── Default Values ───────────────────────────────────────────────────────────\n\nconst DEFAULT_STYLE_RULES: FormEngineStyleRules = {\n formContainer: \"\",\n fieldsContainer: \"\",\n fieldClassName: \"\",\n formClassName: \"\",\n successMessageClassName:\n \"text-primary-foreground mt-4 p-3 rounded-md shadow-md bg-primary\",\n errorMessageClassName:\n \"text-destructive-foreground mt-4 p-3 rounded-md shadow-md bg-destructive\",\n};\n\nconst DEFAULT_SUBMIT_LABEL = \"Submit\";\nconst DEFAULT_BUTTON_GROUP_LABEL = \"Subscribe\";\nconst DEFAULT_BUTTON_VARIANT = \"default\";\nconst DEFAULT_BUTTON_GROUP_SIZE = \"default\";\n\n// ─── Setup / Style Types ──────────────────────────────────────────────────────\n\nexport interface ButtonGroupFormSetup {\n size?: \"xs\" | \"sm\" | \"default\" | \"lg\";\n submitLabel?: React.ReactNode;\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\";\n submitIconName?: string;\n submitIconComponent?: React.ReactNode;\n}\n\nexport interface FormEngineSubmitButtonSetup {\n submitLabel?: React.ReactNode;\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\";\n submitIconName?: string;\n submitIconComponent?: React.ReactNode;\n}\n\nexport interface FormEngineStyleRules {\n /** ClassName applied to the div wrapping the `<form>` element */\n formContainer?: string;\n /** ClassName applied to the grid div wrapping all field columns (standard layout) */\n fieldsContainer?: string;\n /** Fallback className applied to each field wrapper when the field has no own className */\n fieldClassName?: string;\n /** className forwarded to the `<form>` element itself via FormStyleConfig */\n formClassName?: string;\n /** className forwarded to the success message container via FormStyleConfig */\n successMessageClassName?: string;\n /** className forwarded to the error message container via FormStyleConfig */\n errorMessageClassName?: string;\n}\n\nexport interface FormEngineLayoutSettings {\n styleRules?: FormEngineStyleRules;\n formLayout?: \"standard\" | \"button-group\";\n /** Settings for button-group layout (only used when formLayout is \"button-group\") */\n buttonGroupSetup?: ButtonGroupFormSetup;\n /** Settings for the submit button in standard layout */\n submitButtonSetup?: FormEngineSubmitButtonSetup;\n}\n\nexport interface FormEngineSetup {\n /** API / submission configuration */\n api?: PageSpeedFormConfig;\n /** Form field definitions */\n fields?: FormFieldConfig[];\n /** Layout, style, and submit-button settings */\n formLayoutSettings?: FormEngineLayoutSettings;\n /** Success message shown after a successful submission */\n successMessage?: React.ReactNode;\n /** Custom submit handler (called in addition to any api endpoint) */\n onSubmit?: (values: Record<string, any>) => void | Promise<void>;\n /** Called after a successful submission with the server response */\n onSuccess?: (data: unknown) => void;\n /** Called when submission fails */\n onError?: (error: Error) => void;\n /** Navigation handler for internal redirects (return false to fall back to browser navigation) */\n navigate?: (href: string) => boolean | void;\n /** Reset form values after success @default true */\n resetOnSuccess?: boolean;\n /** File upload tokens to merge into the payload */\n uploadTokens?: string[];\n /** Called when files are selected for upload */\n onFileUpload?: (files: File[]) => Promise<void>;\n /** Called when a file is removed */\n onFileRemove?: (file: File, index: number) => void;\n /** Whether a file upload is in progress */\n isUploading?: boolean;\n /** Per-file upload progress map (fileName → 0-100) */\n uploadProgress?: { [fileName: string]: number };\n}\n\nexport interface FormEngineProps extends FormEngineSetup {\n /**\n * Optional wrapper object used by block libraries to pass the full setup as a\n * single prop. Direct props on FormEngine take precedence when both are provided.\n */\n formEngineSetup?: FormEngineSetup;\n /** Default form field definitions used when setup fields are missing/empty */\n defaultFields?: FormFieldConfig[];\n /** Default style rules merged before built-in FormEngine defaults */\n defaultStyleRules?: FormEngineStyleRules;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\n/**\n * FormEngine — declarative form component with built-in API integration.\n *\n * Handles `useContactForm` orchestration internally so callers can supply\n * either direct props or a `formEngineSetup` wrapper plus optional defaults.\n *\n * @example Standard layout\n * ```tsx\n * <FormEngine api={api} fields={fields} formLayoutSettings={{ submitButtonSetup: { submitLabel: \"Send\" } }} />\n * ```\n *\n * @example Wrapped setup with defaults\n * ```tsx\n * <FormEngine\n * formEngineSetup={{ api, fields: [emailField] }}\n * defaultFields={fallbackFields}\n * defaultStyleRules={fallbackStyleRules}\n * />\n * ```\n */\nexport function FormEngine(props: FormEngineProps) {\n const {\n formEngineSetup,\n defaultFields,\n defaultStyleRules,\n api: directApi,\n fields: directFields,\n formLayoutSettings: directFormLayoutSettings,\n successMessage: directSuccessMessage,\n onSubmit: directOnSubmit,\n onSuccess: directOnSuccess,\n onError: directOnError,\n navigate: directNavigate,\n resetOnSuccess: directResetOnSuccess,\n uploadTokens: directUploadTokens,\n onFileUpload: directOnFileUpload,\n onFileRemove: directOnFileRemove,\n isUploading: directIsUploading,\n uploadProgress: directUploadProgress,\n } = props;\n\n const api = directApi ?? formEngineSetup?.api;\n const fields = directFields ?? formEngineSetup?.fields;\n const formLayoutSettings =\n directFormLayoutSettings ?? formEngineSetup?.formLayoutSettings;\n const successMessage =\n directSuccessMessage ?? formEngineSetup?.successMessage;\n const onSubmit = directOnSubmit ?? formEngineSetup?.onSubmit;\n const onSuccess = directOnSuccess ?? formEngineSetup?.onSuccess;\n const onError = directOnError ?? formEngineSetup?.onError;\n const navigate = directNavigate ?? formEngineSetup?.navigate;\n const resetOnSuccess =\n directResetOnSuccess ?? formEngineSetup?.resetOnSuccess;\n const externalUploadTokens =\n directUploadTokens ?? formEngineSetup?.uploadTokens;\n const externalOnFileUpload =\n directOnFileUpload ?? formEngineSetup?.onFileUpload;\n const externalOnFileRemove =\n directOnFileRemove ?? formEngineSetup?.onFileRemove;\n const externalIsUploading = directIsUploading ?? formEngineSetup?.isUploading;\n const externalUploadProgress =\n directUploadProgress ?? formEngineSetup?.uploadProgress;\n\n const {\n styleRules: userStyleRules,\n formLayout = \"standard\",\n buttonGroupSetup,\n submitButtonSetup,\n } = formLayoutSettings ?? {};\n const isButtonGroup = formLayout === \"button-group\";\n\n const formFields = React.useMemo<FormFieldConfig[]>(() => {\n if (fields && fields.length > 0) return fields;\n if (defaultFields && defaultFields.length > 0) return defaultFields;\n return [];\n }, [fields, defaultFields]);\n\n // Merge style rules in order: user setup -> block defaults -> built-in defaults\n const styleRules = React.useMemo<FormEngineStyleRules>(\n () => ({\n formContainer:\n userStyleRules?.formContainer ??\n defaultStyleRules?.formContainer ??\n DEFAULT_STYLE_RULES.formContainer,\n fieldsContainer:\n userStyleRules?.fieldsContainer ??\n defaultStyleRules?.fieldsContainer ??\n DEFAULT_STYLE_RULES.fieldsContainer,\n fieldClassName:\n userStyleRules?.fieldClassName ??\n defaultStyleRules?.fieldClassName ??\n DEFAULT_STYLE_RULES.fieldClassName,\n formClassName:\n userStyleRules?.formClassName ??\n defaultStyleRules?.formClassName ??\n DEFAULT_STYLE_RULES.formClassName,\n successMessageClassName:\n userStyleRules?.successMessageClassName ??\n defaultStyleRules?.successMessageClassName ??\n DEFAULT_STYLE_RULES.successMessageClassName,\n errorMessageClassName:\n userStyleRules?.errorMessageClassName ??\n defaultStyleRules?.errorMessageClassName ??\n DEFAULT_STYLE_RULES.errorMessageClassName,\n }),\n [userStyleRules, defaultStyleRules],\n );\n\n // Integrate file upload functionality\n const {\n uploadTokens: internalUploadTokens,\n uploadProgress: internalUploadProgress,\n isUploading: internalIsUploading,\n uploadFiles: internalUploadFiles,\n removeFile: internalRemoveFile,\n resetUpload,\n } = useFileUpload({ onError });\n\n // Use external upload state if provided, otherwise use internal\n const uploadTokens = externalUploadTokens ?? internalUploadTokens;\n const uploadProgress = externalUploadProgress ?? internalUploadProgress;\n const isUploading = externalIsUploading ?? internalIsUploading;\n const onFileUpload = externalOnFileUpload ?? internalUploadFiles;\n const onFileRemove = externalOnFileRemove ?? internalRemoveFile;\n\n const { form, submissionError, formMethod, resetSubmissionState } =\n useContactForm({\n formFields,\n formConfig: api,\n onSubmit,\n onSuccess: (data) => {\n resetUpload();\n onSuccess?.(data);\n },\n onError,\n navigate,\n resetOnSuccess,\n uploadTokens,\n });\n\n // Map FormEngineLayoutSettings → FormRenderConfig for the legacy Form component\n const legacyFormConfig = React.useMemo<FormRenderConfig>(() => {\n if (isButtonGroup) {\n return {\n formLayout: \"button-group\",\n buttonGroupSize: buttonGroupSetup?.size ?? DEFAULT_BUTTON_GROUP_SIZE,\n submitLabel:\n buttonGroupSetup?.submitLabel ?? DEFAULT_BUTTON_GROUP_LABEL,\n submitVariant:\n buttonGroupSetup?.submitVariant ?? DEFAULT_BUTTON_VARIANT,\n submitIconName: buttonGroupSetup?.submitIconName,\n submitIconComponent: buttonGroupSetup?.submitIconComponent,\n endpoint: api?.endpoint,\n submissionConfig: api?.submissionConfig,\n };\n }\n return {\n formLayout: \"standard\",\n endpoint: api?.endpoint,\n submissionConfig: api?.submissionConfig,\n };\n }, [isButtonGroup, buttonGroupSetup, api]);\n\n return (\n <div className={styleRules?.formContainer}>\n <Form\n form={form}\n fields={isButtonGroup ? formFields : undefined}\n formConfig={legacyFormConfig}\n method={formMethod}\n notificationConfig={{\n submissionError: submissionError ?? undefined,\n successMessage,\n }}\n styleConfig={{\n formClassName: styleRules?.formClassName,\n successMessageClassName: styleRules?.successMessageClassName,\n errorMessageClassName: styleRules?.errorMessageClassName,\n }}\n onNewSubmission={resetSubmissionState}\n >\n {!isButtonGroup && (\n <>\n <div\n className={cn(\n \"grid grid-cols-12 gap-6 md:gap-10\",\n styleRules?.fieldsContainer,\n )}\n >\n {formFields.map((field) => (\n <div\n key={field.name}\n className={cn(\n getColumnSpanClass(field.columnSpan ?? 12),\n \"min-w-0\",\n )}\n >\n <DynamicFormField\n field={field}\n className={field.className ?? styleRules?.fieldClassName}\n uploadProgress={uploadProgress}\n onFileUpload={onFileUpload}\n onFileRemove={onFileRemove}\n isUploading={isUploading}\n />\n </div>\n ))}\n </div>\n <Button\n type=\"submit\"\n variant={\n submitButtonSetup?.submitVariant ?? DEFAULT_BUTTON_VARIANT\n }\n disabled={form.isSubmitting}\n className=\"mt-6 w-full\"\n >\n {submitButtonSetup?.submitLabel ?? DEFAULT_SUBMIT_LABEL}\n </Button>\n </>\n )}\n </Form>\n </div>\n );\n}\n\nFormEngine.displayName = \"FormEngine\";\n"]}
1
+ {"version":3,"sources":["../src/integration/ContactFormSerializer.ts","../src/integration/BlockAdapter.tsx","../src/integration/form-submit.ts","../src/integration/form-field-types.ts","../src/integration/use-file-upload.ts","../src/integration/use-contact-form.ts","../src/integration/DynamicFormField.tsx","../src/integration/FormEngine.tsx"],"names":["camelField","errorMessage","React","Component","response","data","humanizeFieldName","useState","useCallback","useForm","useMemo","Icon","DEFAULT_ICON_API_BASE_URL","React2","fieldIsChoiceCard","Field","cn","TextInput","TextArea","Select","MultiSelect","Radio","Checkbox","CheckboxGroup","DatePicker","DateRangePicker","TimePicker","FileInput","React3","Form","Button"],"mappings":"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","file":"integration.cjs","sourcesContent":["/**\n * @page-speed/forms - Rails API Serializer\n *\n * Serializes form data for the DashTrack ContactsController API.\n * Handles field name conversion (camelCase → snake_case), custom fields separation,\n * and upload token arrays.\n *\n * @see https://github.com/opensite-ai/page-speed-forms\n */\n\n/**\n * Standard fields recognized by the Rails ContactsController API.\n * These are serialized to snake_case and sent in the contact object.\n */\nconst STANDARD_FIELDS = [\n \"content\",\n \"email\",\n \"firstName\",\n \"lastName\",\n \"locationId\",\n \"phone\",\n \"subject\",\n \"redemptionStatus\",\n \"birthday\",\n \"city\",\n \"state\",\n \"websiteFormAssignmentId\",\n \"websiteId\",\n \"acceptsSmsMarketing\",\n \"acceptsEmailMarketing\",\n \"visitorIpAddress\",\n] as const;\n\n/**\n * Configuration parameters for Rails API submission.\n */\nexport interface RailsApiConfig {\n /**\n * API key for authentication.\n * Sent as top-level parameter: api_key\n */\n apiKey: string;\n\n /**\n * Contact category token for categorization.\n * Sent as top-level parameter: contact_category_token\n */\n contactCategoryToken?: string;\n\n /**\n * Location ID for multi-location organizations.\n * Sent as top-level parameter: location_id\n */\n locationId?: string;\n\n /**\n * Website ID for tracking form submissions.\n * Sent within contact object: website_id\n */\n websiteId?: string;\n\n /**\n * Website form assignment ID for form tracking.\n * Sent within contact object: website_form_assignment_id\n */\n websiteFormAssignmentId?: string;\n\n /**\n * Visitor IP address. If not provided, will be auto-detected on server.\n * Sent within contact object: visitor_ip_address\n */\n visitorIpAddress?: string;\n}\n\n/**\n * Serialized form data ready for Rails API submission.\n */\nexport interface SerializedFormData {\n /**\n * Top-level API key parameter.\n */\n api_key: string;\n\n /**\n * Top-level contact category token (optional).\n */\n contact_category_token?: string;\n\n /**\n * Top-level location ID (optional).\n */\n location_id?: string;\n\n /**\n * Contact object with standard fields and metadata.\n */\n contact: {\n /**\n * Standard contact fields in snake_case.\n */\n [key: string]: unknown;\n\n /**\n * Custom fields that don't match standard schema.\n * Stored as separate hash in Rails.\n */\n custom_fields?: Record<string, unknown>;\n\n /**\n * Array of upload tokens from file uploads.\n * These reference ContactFormUpload records in Rails.\n */\n contact_form_upload_tokens?: string[];\n };\n}\n\n/**\n * Form values from the form library (camelCase keys).\n */\nexport type FormValues = Record<string, unknown>;\n\n/**\n * Convert camelCase to snake_case.\n *\n * @example\n * ```ts\n * camelToSnake(\"firstName\") // \"first_name\"\n * camelToSnake(\"acceptsSmsMarketing\") // \"accepts_sms_marketing\"\n * ```\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Check if a field name is a standard Rails contact field.\n */\nfunction isStandardField(fieldName: string): boolean {\n return STANDARD_FIELDS.includes(\n fieldName as (typeof STANDARD_FIELDS)[number],\n );\n}\n\n/**\n * Extract upload tokens from form values.\n * Handles both string tokens and arrays of tokens.\n */\nfunction extractUploadTokens(values: FormValues): string[] {\n const tokens: string[] = [];\n\n for (const value of Object.values(values)) {\n if (typeof value === \"string\" && value.startsWith(\"upload_\")) {\n tokens.push(value);\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (typeof item === \"string\" && item.startsWith(\"upload_\")) {\n tokens.push(item);\n }\n }\n }\n }\n\n return tokens;\n}\n\n/**\n * Format date/time values for Rails API.\n * Rails expects ISO 8601 format.\n */\nfunction formatDateForRails(value: unknown): string | undefined {\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === \"string\") {\n // Attempt to parse and reformat to ensure valid ISO 8601\n const date = new Date(value);\n if (!isNaN(date.getTime())) {\n return date.toISOString();\n }\n }\n return undefined;\n}\n\n/**\n * Serialize form values for Rails ContactsController API.\n *\n * This function:\n * 1. Converts camelCase field names to snake_case\n * 2. Separates standard fields from custom fields\n * 3. Extracts upload tokens into contact_form_upload_tokens array\n * 4. Formats dates to ISO 8601\n * 5. Includes API configuration parameters\n *\n * @param values - Form values from useForm hook (camelCase keys)\n * @param config - Rails API configuration (apiKey, locationId, etc.)\n * @returns Serialized data ready for POST to /contacts\n *\n * @example\n * ```ts\n * const serialized = serializeForRails(\n * {\n * firstName: \"John\",\n * lastName: \"Doe\",\n * email: \"john@example.com\",\n * phone: \"555-1234\",\n * companySize: \"50-100\", // Custom field\n * resumeToken: \"upload_abc123\",\n * },\n * {\n * apiKey: \"key_123\",\n * contactCategoryToken: \"cat_xyz\",\n * locationId: \"loc_456\",\n * }\n * );\n *\n * // Result:\n * // {\n * // api_key: \"key_123\",\n * // contact_category_token: \"cat_xyz\",\n * // location_id: \"loc_456\",\n * // contact: {\n * // first_name: \"John\",\n * // last_name: \"Doe\",\n * // email: \"john@example.com\",\n * // phone: \"555-1234\",\n * // custom_fields: {\n * // company_size: \"50-100\"\n * // },\n * // contact_form_upload_tokens: [\"upload_abc123\"]\n * // }\n * // }\n * ```\n */\nexport function serializeForRails(\n values: FormValues,\n config: RailsApiConfig,\n): SerializedFormData {\n const standardFields: Record<string, unknown> = {};\n const customFields: Record<string, unknown> = {};\n\n // Extract upload tokens\n const uploadTokens = extractUploadTokens(values);\n\n // Separate standard and custom fields\n for (const [key, value] of Object.entries(values)) {\n // Skip upload token fields - they're handled separately\n if (typeof value === \"string\" && value.startsWith(\"upload_\")) {\n continue;\n }\n if (\n Array.isArray(value) &&\n value.every(\n (item) => typeof item === \"string\" && item.startsWith(\"upload_\"),\n )\n ) {\n continue;\n }\n\n const snakeKey = camelToSnake(key);\n\n if (isStandardField(key)) {\n // Format dates for birthday field\n if (key === \"birthday\") {\n const formatted = formatDateForRails(value);\n if (formatted) {\n standardFields[snakeKey] = formatted;\n }\n } else {\n // Handle array values for standard fields\n // Standard fields expect scalar values (strings), but some field types\n // like checkbox-group produce arrays. Convert arrays to comma-separated strings.\n if (Array.isArray(value)) {\n standardFields[snakeKey] = value.join(\", \");\n } else {\n standardFields[snakeKey] = value;\n }\n }\n } else {\n // Custom fields\n customFields[snakeKey] = value;\n }\n }\n\n // Add config fields to standard fields if provided\n if (config.websiteId !== undefined) {\n standardFields.website_id = config.websiteId;\n }\n if (config.websiteFormAssignmentId !== undefined) {\n standardFields.website_form_assignment_id = config.websiteFormAssignmentId;\n }\n if (config.visitorIpAddress !== undefined) {\n standardFields.visitor_ip_address = config.visitorIpAddress;\n }\n\n // Build contact object\n const contact: SerializedFormData[\"contact\"] = {\n ...standardFields,\n };\n\n // Add custom fields if any\n if (Object.keys(customFields).length > 0) {\n contact.custom_fields = customFields;\n }\n\n // Add upload tokens if any\n if (uploadTokens.length > 0) {\n contact.contact_form_upload_tokens = uploadTokens;\n }\n\n // Build final serialized data\n const serialized: SerializedFormData = {\n api_key: config.apiKey,\n contact,\n };\n\n // Add optional top-level parameters\n if (config.contactCategoryToken !== undefined) {\n serialized.contact_category_token = config.contactCategoryToken;\n }\n if (config.locationId !== undefined) {\n serialized.location_id = config.locationId;\n }\n\n return serialized;\n}\n\n/**\n * Rails API error response format.\n */\nexport interface RailsErrorResponse {\n errors: {\n [field: string]: string[];\n };\n status: number;\n}\n\n/**\n * Form error format used by @page-speed/forms.\n */\nexport type FormErrors = Record<string, string | undefined>;\n\n/**\n * Convert snake_case to camelCase.\n *\n * @example\n * ```ts\n * snakeToCamel(\"first_name\") // \"firstName\"\n * snakeToCamel(\"accepts_sms_marketing\") // \"acceptsSmsMarketing\"\n * ```\n */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/**\n * Deserialize Rails API errors to form error format.\n *\n * Converts Rails error format to the format expected by @page-speed/forms:\n * - Maps snake_case field names to camelCase\n * - Flattens error arrays to single error string (first error)\n * - Handles custom_fields errors by mapping back to original field names\n * - Extracts base errors to form-level errors\n *\n * @param railsErrors - Error response from Rails API\n * @returns Form errors object (camelCase keys)\n *\n * @example\n * ```ts\n * const formErrors = deserializeErrors({\n * errors: {\n * first_name: [\"can't be blank\", \"is too short\"],\n * email: [\"is invalid\"],\n * custom_fields: {\n * company_size: [\"is required\"]\n * },\n * base: [\"Something went wrong\"]\n * },\n * status: 422\n * });\n *\n * // Result:\n * // {\n * // firstName: \"can't be blank\",\n * // email: \"is invalid\",\n * // companySize: \"is required\",\n * // _form: \"Something went wrong\"\n * // }\n * ```\n */\nexport function deserializeErrors(railsErrors: RailsErrorResponse): FormErrors {\n const formErrors: FormErrors = {};\n\n for (const [field, messages] of Object.entries(railsErrors.errors)) {\n // Handle base errors (form-level errors)\n if (field === \"base\") {\n formErrors._form = Array.isArray(messages) ? messages[0] : messages;\n continue;\n }\n\n // Handle custom_fields errors\n if (field === \"custom_fields\" && typeof messages === \"object\") {\n for (const [customField, customMessages] of Object.entries(messages)) {\n const camelField = snakeToCamel(customField);\n const errorMessage = Array.isArray(customMessages)\n ? customMessages[0]\n : customMessages;\n formErrors[camelField] = errorMessage;\n }\n continue;\n }\n\n // Handle standard field errors\n const camelField = snakeToCamel(field);\n const errorMessage = Array.isArray(messages) ? messages[0] : messages;\n formErrors[camelField] = errorMessage;\n }\n\n return formErrors;\n}\n","/**\n * @page-speed/forms - Block Adapter\n *\n * Adapts form components for use in block-based rendering systems.\n * Wraps form components to accept block prop structure and transforms them\n * to component-native props.\n *\n * @see https://github.com/opensite-ai/page-speed-forms\n */\n\n\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * Block structure for design payloads.\n * Minimal type definition for adapter compatibility.\n */\nexport interface Block {\n _id: string;\n _type: string;\n _name?: string;\n _parent?: string | null;\n tag?: string;\n styles?: string;\n content?: string;\n blockProps?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Options for Block adapter.\n */\nexport interface BlockAdapterOptions {\n /**\n * Default props to merge with block props.\n */\n defaultProps?: Record<string, unknown>;\n\n /**\n * Transform function to convert Block props to component props.\n * Useful for custom prop mapping logic.\n */\n transformProps?: (\n blockProps: Record<string, unknown>,\n block: Block,\n ) => Record<string, unknown>;\n\n /**\n * Extract display name from block for debugging.\n * Defaults to using _name or _type from block.\n */\n getDisplayName?: (block: Block) => string;\n\n /**\n * Enable React error boundary wrapping.\n * Defaults to true.\n */\n withErrorBoundary?: boolean;\n\n /**\n * Custom error fallback component.\n * If not provided, renders basic error message.\n */\n errorFallback?: (error: Error, block: Block) => React.ReactNode;\n}\n\n/**\n * Props passed to adapted component.\n */\nexport interface AdaptedComponentProps {\n /**\n * Block data from design payload.\n */\n block: Block;\n\n /**\n * Child blocks for rendering nested content.\n * Used by container components like Form.\n */\n children?: React.ReactNode;\n\n /**\n * Callback to render child blocks.\n * Provided by block rendering systems.\n */\n renderChildren?: (blockId: string) => React.ReactNode;\n}\n\n/**\n * Error boundary component for catching render errors.\n */\nclass BlockErrorBoundary extends React.Component<\n {\n block: Block;\n fallback?: (error: Error, block: Block) => React.ReactNode;\n children: React.ReactNode;\n },\n { error: Error | null }\n> {\n constructor(props: BlockErrorBoundary[\"props\"]) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(\n `Block render error (${this.props.block._id}):`,\n error,\n errorInfo,\n );\n }\n\n render() {\n if (this.state.error) {\n if (this.props.fallback) {\n return this.props.fallback(this.state.error, this.props.block);\n }\n\n return (\n <div\n className=\"block-error border border-destructive bg-destructive p-4 rounded text-destructive-foreground\"\n data-block-id={this.props.block._id}\n data-block-type={this.props.block._type}\n >\n <p className=\"font-semibold\">Block Render Error</p>\n <p className=\"text-sm\">\n Block: {this.props.block._name || this.props.block._id} (\n {this.props.block._type})\n </p>\n <p className=\"text-sm mt-1\">{this.state.error.message}</p>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Create a Block-compatible wrapper for a form component.\n *\n * This adapter transforms Block props (from design payload) into\n * component-native props. It handles:\n * - Extracting props from `blockProps` field\n * - Merging with default props\n * - Custom prop transformation\n * - Error boundary wrapping\n * - Children rendering support\n *\n * @param Component - React component to adapt\n * @param options - Adapter configuration options\n * @returns Block-compatible component\n *\n * @example\n * ```tsx\n * import { TextInput } from \"@page-speed/forms/inputs\";\n * import { createBlockAdapter } from \"@page-speed/forms/integration\";\n *\n * // Create Block-compatible TextInput\n * const BlockTextInput = createBlockAdapter(TextInput, {\n * defaultProps: {\n * placeholder: \"Enter text...\",\n * },\n * transformProps: (blockProps, block) => ({\n * ...blockProps,\n * // Map Block content to component props\n * label: block.content || blockProps.label,\n * // Apply Block styles as className\n * className: block.styles,\n * }),\n * });\n *\n * // Register with rendering system\n * registerBlockType(\"TextInput\", BlockTextInput);\n * ```\n *\n * @example\n * ```tsx\n * // Block from design payload:\n * {\n * _id: \"field-email\",\n * _type: \"TextInput\",\n * _name: \"Email Field\",\n * content: \"Email Address\",\n * styles: \"w-full mb-4\",\n * blockProps: {\n * name: \"email\",\n * type: \"email\",\n * placeholder: \"you@example.com\",\n * required: true,\n * }\n * }\n *\n * // Transformed to TextInput props:\n * <TextInput\n * name=\"email\"\n * type=\"email\"\n * placeholder=\"you@example.com\"\n * required={true}\n * label=\"Email Address\"\n * className=\"w-full mb-4\"\n * />\n * ```\n */\nexport function createBlockAdapter<TProps extends Record<string, unknown>>(\n Component: React.ComponentType<TProps>,\n options: BlockAdapterOptions = {},\n): React.ComponentType<AdaptedComponentProps> {\n const {\n defaultProps = {},\n transformProps,\n withErrorBoundary = true,\n errorFallback,\n } = options;\n\n const AdaptedComponent: React.FC<AdaptedComponentProps> = ({\n block,\n children,\n renderChildren,\n }) => {\n // Extract component props from blockProps\n const blockProps = (block.blockProps || {}) as Record<string, unknown>;\n\n // Merge with default props\n const mergedProps = { ...defaultProps, ...blockProps };\n\n // Apply custom transformation if provided\n const finalProps = transformProps\n ? transformProps(mergedProps, block)\n : mergedProps;\n\n // Add data attributes for debugging\n const dataAttrs = {\n \"data-block-id\": block._id,\n \"data-block-type\": block._type,\n ...(block._name && { \"data-block-name\": block._name }),\n };\n\n // Merge data attributes with final props\n const componentProps = {\n ...finalProps,\n ...dataAttrs,\n } as unknown as TProps;\n\n // Render children if renderChildren callback provided\n const renderedChildren = renderChildren\n ? renderChildren(block._id)\n : children;\n\n const element = (\n <Component {...componentProps}>{renderedChildren}</Component>\n );\n\n // Wrap with error boundary if enabled\n if (withErrorBoundary) {\n return (\n <BlockErrorBoundary block={block} fallback={errorFallback}>\n {element}\n </BlockErrorBoundary>\n );\n }\n\n return element;\n };\n\n // Set display name for debugging\n const componentName = Component.displayName || Component.name || \"Component\";\n AdaptedComponent.displayName = `BlockAdapter(${componentName})`;\n\n return AdaptedComponent;\n}\n\n/**\n * Standard prop transformer for form input components.\n *\n * Applies common transformations:\n * - Maps Block `content` to `label`\n * - Maps Block `styles` to `className`\n * - Preserves all blockProps\n *\n * @param blockProps - Props from Block.blockProps\n * @param block - Full Block object\n * @returns Transformed props for component\n *\n * @example\n * ```tsx\n * const BlockTextInput = createBlockAdapter(TextInput, {\n * transformProps: standardInputTransformer,\n * });\n * ```\n */\nexport function standardInputTransformer(\n blockProps: Record<string, unknown>,\n block: Block,\n): Record<string, unknown> {\n return {\n ...blockProps,\n // Use content as label if not already provided\n ...(block.content && !blockProps.label && { label: block.content }),\n // Apply Block styles as className\n ...(block.styles && { className: block.styles }),\n };\n}\n\n/**\n * Create multiple Block adapters with shared options.\n *\n * Convenience function for adapting multiple components at once\n * with the same configuration.\n *\n * @param components - Record of component name to component\n * @param options - Shared adapter options\n * @returns Record of adapted components\n *\n * @example\n * ```tsx\n * import * as Inputs from \"@page-speed/forms/inputs\";\n * import { createBlockAdapters, standardInputTransformer } from \"@page-speed/forms/integration\";\n *\n * const BlockInputs = createBlockAdapters(\n * {\n * TextInput: Inputs.TextInput,\n * TextArea: Inputs.TextArea,\n * Select: Inputs.Select,\n * },\n * {\n * transformProps: standardInputTransformer,\n * withErrorBoundary: true,\n * }\n * );\n *\n * // BlockInputs.TextInput, BlockInputs.TextArea, BlockInputs.Select\n * // are now Block-compatible\n * ```\n */\nexport function createBlockAdapters<\n TComponents extends Record<string, React.ComponentType<any>>,\n>(\n components: TComponents,\n options: BlockAdapterOptions = {},\n): Record<keyof TComponents, React.ComponentType<AdaptedComponentProps>> {\n const adapted: Record<\n string,\n React.ComponentType<AdaptedComponentProps>\n > = {};\n\n for (const [name, component] of Object.entries(components)) {\n adapted[name] = createBlockAdapter(component, options);\n }\n\n return adapted as Record<\n keyof TComponents,\n React.ComponentType<AdaptedComponentProps>\n >;\n}\n","import type {\n FormLayoutSettings,\n FormSubmissionConfig,\n} from \"../core/types\";\nimport {\n deserializeErrors,\n serializeForRails,\n type FormErrors,\n type RailsApiConfig,\n type RailsErrorResponse,\n} from \"./ContactFormSerializer\";\n\nexport type PageSpeedFormMethod = \"post\" | \"get\" | \"put\" | \"patch\";\nexport type PageSpeedFormSubmissionFormat = \"json\" | \"rails\";\n\nexport interface PageSpeedFormSubmissionResult {\n formData: Record<string, any>;\n responseData: unknown;\n}\n\n/**\n * PageSpeed-specific extension of the core FormSubmissionConfig.\n * Inherits behavior, customComponent, and newFormSubmissionAction from the\n * base type and adds integration-layer callbacks and redirect support.\n */\nexport interface PageSpeedFormSubmissionConfig extends FormSubmissionConfig {\n /**\n * Optional callback triggered on successful submission.\n */\n handleFormSubmission?: (\n result: PageSpeedFormSubmissionResult,\n ) => void | Promise<void>;\n\n /**\n * Redirect destination used when behavior is \"redirect\".\n */\n redirectUrl?: string;\n}\n\nexport interface PageSpeedFormConfig {\n /**\n * API endpoint used for submission (also applied to form action).\n */\n endpoint?: string;\n /**\n * HTTP method for submission.\n * @default \"post\"\n */\n method?: PageSpeedFormMethod;\n /**\n * Submission format.\n * Defaults to \"rails\" when apiKey is present, otherwise \"json\".\n */\n format?: PageSpeedFormSubmissionFormat;\n /**\n * Additional headers for the submission request.\n */\n headers?: Record<string, string>;\n /**\n * Static values merged into the payload (e.g. subject, content).\n */\n values?: Record<string, unknown>;\n /**\n * Rails API key (required for rails format).\n */\n apiKey?: string;\n /**\n * Rails contact category token.\n */\n contactCategoryToken?: string;\n /**\n * Rails location ID.\n */\n locationId?: string;\n /**\n * Rails website ID.\n */\n websiteId?: string;\n /**\n * Rails website form assignment ID.\n */\n websiteFormAssignmentId?: string;\n /**\n * Rails visitor IP address override.\n */\n visitorIpAddress?: string;\n /**\n * Reset form values after a successful submission.\n * @default true\n */\n resetOnSuccess?: boolean;\n /**\n * Optional post-submission behavior configuration.\n */\n submissionConfig?: PageSpeedFormSubmissionConfig;\n\n /**\n * Optional layout and presentation settings.\n * Provides a typed home for layout props (formLayout, buttonGroupSize, etc.)\n * so consumers don't need an `as any` cast when passing them alongside API config.\n */\n formLayoutSettings?: FormLayoutSettings;\n}\n\nexport class PageSpeedFormSubmissionError extends Error {\n formErrors?: FormErrors;\n status?: number;\n\n constructor(\n message: string,\n options: { formErrors?: FormErrors; status?: number } = {},\n ) {\n super(message);\n this.name = \"PageSpeedFormSubmissionError\";\n this.formErrors = options.formErrors;\n this.status = options.status;\n }\n}\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function isValidEmail(value: string): boolean {\n return EMAIL_REGEX.test(value);\n}\n\nfunction buildUrlWithParams(\n endpoint: string,\n values: Record<string, any>,\n): string {\n const base =\n typeof window === \"undefined\" ? \"http://localhost\" : window.location.origin;\n const url = new URL(endpoint, base);\n\n Object.entries(values).forEach(([key, value]) => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n url.searchParams.append(key, String(item));\n }\n });\n return;\n }\n\n url.searchParams.set(key, String(value));\n });\n\n return url.toString();\n}\n\nfunction normalizeMethod(method?: PageSpeedFormMethod): string {\n return (method || \"post\").toUpperCase();\n}\n\nfunction resolveFormat(\n config?: PageSpeedFormConfig,\n): PageSpeedFormSubmissionFormat {\n if (config?.format) return config.format;\n return config?.apiKey ? \"rails\" : \"json\";\n}\n\nfunction mergeValues(\n values: Record<string, any>,\n config?: PageSpeedFormConfig,\n): Record<string, any> {\n return {\n ...(config?.values ?? {}),\n ...values,\n };\n}\n\nexport async function submitPageSpeedForm(\n values: Record<string, any>,\n config?: PageSpeedFormConfig,\n): Promise<unknown> {\n if (!config?.endpoint) {\n return null;\n }\n\n const payload = mergeValues(values, config);\n const method = normalizeMethod(config.method);\n const format = resolveFormat(config);\n const headers: Record<string, string> = { ...(config.headers ?? {}) };\n\n if (format === \"rails\") {\n if (!config.apiKey) {\n throw new PageSpeedFormSubmissionError(\n \"Missing apiKey for Rails form submission.\",\n );\n }\n\n const railsConfig: RailsApiConfig = {\n apiKey: config.apiKey,\n contactCategoryToken: config.contactCategoryToken,\n locationId: config.locationId,\n websiteId: config.websiteId,\n websiteFormAssignmentId: config.websiteFormAssignmentId,\n visitorIpAddress: config.visitorIpAddress,\n };\n\n const serialized = serializeForRails(payload, railsConfig);\n\n if (serialized.contact.contact_form_upload_tokens) {\n serialized.contact.contact_form_upload_tokens = (\n serialized.contact.contact_form_upload_tokens as string[]\n ).map((token) => token.replace(/^upload_/, \"\"));\n }\n\n headers[\"Content-Type\"] ??= \"application/json\";\n\n const response = await fetch(config.endpoint, {\n method,\n headers,\n body: JSON.stringify(serialized),\n });\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok || (data && data.errors)) {\n const errorResponse: RailsErrorResponse = {\n errors: data?.errors ?? { base: [\"Form submission failed\"] },\n status: data?.status ?? response.status,\n };\n const formErrors = deserializeErrors(errorResponse);\n throw new PageSpeedFormSubmissionError(\"Form submission failed.\", {\n formErrors,\n status: errorResponse.status,\n });\n }\n\n return data;\n }\n\n if (method === \"GET\") {\n const url = buildUrlWithParams(config.endpoint, payload);\n const response = await fetch(url, { method, headers });\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n throw new PageSpeedFormSubmissionError(\n data?.message || \"Form submission failed.\",\n { status: response.status },\n );\n }\n\n return data;\n }\n\n headers[\"Content-Type\"] ??= \"application/json\";\n\n const response = await fetch(config.endpoint, {\n method,\n headers,\n body: JSON.stringify(payload),\n });\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n throw new PageSpeedFormSubmissionError(\n data?.message || \"Form submission failed.\",\n { status: response.status },\n );\n }\n\n return data;\n}\n","/**\n * Dynamic form field schema types and helpers.\n *\n * These utilities are intentionally exposed from the integration layer so\n * block/rendering libraries can share one field schema contract.\n */\n\nimport { humanizeFieldName } from \"../lib/utils\";\n\nexport type FormFieldType =\n | \"text\"\n | \"email\"\n | \"search\"\n | \"password\"\n | \"tel\"\n | \"textarea\"\n | \"select\"\n | \"radio\"\n | \"checkbox\"\n | \"checkbox-group\"\n | \"number\"\n | \"url\"\n | \"date\"\n | \"date-picker\"\n | \"date-range\"\n | \"time\"\n | \"file\"\n | \"multi-select\";\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n description?: string;\n}\n\nexport interface FormFieldConfig {\n /**\n * Optionally displays label for the field\n */\n label?: string;\n /**\n * Unique field name (used as the key in form values)\n */\n name: string;\n /**\n * Field type\n */\n type: FormFieldType;\n\n /**\n * Placeholder text\n */\n placeholder?: string;\n /**\n * Whether the field is required\n * @default false\n */\n required?: boolean;\n /**\n * Column span in grid layout (1-12)\n * @default 12 (full width)\n */\n columnSpan?: number;\n /**\n * Options for select/radio/checkbox-group fields\n */\n options?: SelectOption[];\n /**\n * Number of rows for textarea\n * @default 4\n */\n rows?: number;\n /**\n * Custom validation function\n * Return undefined for valid, or an error message string for invalid\n */\n validator?: (\n value: any,\n allValues: Record<string, any>,\n ) => string | undefined;\n /**\n * Additional CSS classes for the field wrapper\n */\n className?: string;\n /**\n * Whether the field is disabled\n * @default false\n */\n disabled?: boolean;\n /**\n * Accepted file types for file inputs (MIME types or extensions)\n * @example \".pdf,.doc,.docx\"\n * @example \"image/*,application/pdf\"\n */\n accept?: string;\n /**\n * Maximum file size in bytes for file inputs\n * @default 5MB (5 * 1024 * 1024)\n */\n maxSize?: number;\n /**\n * Maximum number of files for file inputs\n * @default 1\n */\n maxFiles?: number;\n /**\n * Allow multiple file selection\n * @default false\n */\n multiple?: boolean;\n /**\n * Description/help text displayed with the field\n */\n description?: string;\n /**\n * Layout for radio/checkbox groups\n * @default \"stacked\"\n */\n layout?: \"grid\" | \"stacked\";\n}\n\n/**\n * Generate initial values object from form field configs.\n */\nexport function generateInitialValues(\n fields: FormFieldConfig[],\n): Record<string, any> {\n return fields.reduce(\n (acc, field) => {\n if (field.type === \"checkbox\") {\n acc[field.name] = false;\n } else if (\n field.type === \"checkbox-group\" ||\n field.type === \"multi-select\"\n ) {\n acc[field.name] = [];\n } else if (field.type === \"file\") {\n acc[field.name] = [];\n } else if (field.type === \"date-range\") {\n acc[field.name] = { start: null, end: null };\n } else {\n acc[field.name] = \"\";\n }\n return acc;\n },\n {} as Record<string, any>,\n );\n}\n\n/**\n * Generate validation schema from form field configs.\n */\nexport function generateValidationSchema(\n fields: FormFieldConfig[],\n): Record<\n string,\n (value: any, allValues: Record<string, any>) => string | undefined\n> {\n return fields.reduce(\n (acc, field) => {\n acc[field.name] = (value: any, allValues: Record<string, any>) => {\n if (field.required) {\n if (!value || (typeof value === \"string\" && !value.trim())) {\n const displayName = field.label || humanizeFieldName(field.name);\n return `${displayName} is required`;\n }\n }\n\n if (field.type === \"email\" && value) {\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value)) {\n return \"Please enter a valid email address\";\n }\n }\n\n if (field.type === \"url\" && value) {\n try {\n new URL(value);\n } catch {\n return \"Please enter a valid URL\";\n }\n }\n\n if (field.validator) {\n return field.validator(value, allValues);\n }\n\n return undefined;\n };\n return acc;\n },\n {} as Record<\n string,\n (value: any, allValues: Record<string, any>) => string | undefined\n >,\n );\n}\n\n/**\n * Static mapping of column span values to Tailwind classes.\n *\n * IMPORTANT: These must remain complete, literal class strings so Tailwind's\n * scanner can detect and generate them.\n */\nconst columnSpanClasses: Record<number, string> = {\n 1: \"col-span-12 md:col-span-1\",\n 2: \"col-span-12 md:col-span-2\",\n 3: \"col-span-12 md:col-span-3\",\n 4: \"col-span-12 md:col-span-4\",\n 5: \"col-span-12 md:col-span-5\",\n 6: \"col-span-12 md:col-span-6\",\n 7: \"col-span-12 md:col-span-7\",\n 8: \"col-span-12 md:col-span-8\",\n 9: \"col-span-12 md:col-span-9\",\n 10: \"col-span-12 md:col-span-10\",\n 11: \"col-span-12 md:col-span-11\",\n 12: \"col-span-12\",\n};\n\n/**\n * Get grid column span class for Tailwind.\n *\n * On small screens the field is always full-width, then from `md` and up the\n * configured span is applied.\n */\nexport function getColumnSpanClass(span?: number): string {\n if (!span || span === 12) return \"col-span-12\";\n const clamped = Math.max(1, Math.min(span, 12));\n return columnSpanClasses[clamped] || \"col-span-12\";\n}\n\n/**\n * Self-contained CSS that makes the 12-column form grid work even when\n * the consuming app's Tailwind build does not generate the col-span-*\n * and grid-cols-12 utility classes from this library's dist output.\n *\n * Uses data-attribute selectors so there is no collision with Tailwind.\n * When Tailwind IS present the rules are harmless duplicates.\n */\nexport const FORM_GRID_FALLBACK_CSS = `[data-psf-grid]{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:1.5rem}@media(min-width:768px){[data-psf-grid]{gap:2.5rem}}[data-psf-col]{grid-column:span 12/span 12;min-width:0}@media(min-width:768px){[data-psf-col=\"1\"]{grid-column:span 1/span 1}[data-psf-col=\"2\"]{grid-column:span 2/span 2}[data-psf-col=\"3\"]{grid-column:span 3/span 3}[data-psf-col=\"4\"]{grid-column:span 4/span 4}[data-psf-col=\"5\"]{grid-column:span 5/span 5}[data-psf-col=\"6\"]{grid-column:span 6/span 6}[data-psf-col=\"7\"]{grid-column:span 7/span 7}[data-psf-col=\"8\"]{grid-column:span 8/span 8}[data-psf-col=\"9\"]{grid-column:span 9/span 9}[data-psf-col=\"10\"]{grid-column:span 10/span 10}[data-psf-col=\"11\"]{grid-column:span 11/span 11}[data-psf-col=\"12\"]{grid-column:span 12/span 12}}`;\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\n\nexport interface FileUploadProgress {\n [fileName: string]: number;\n}\n\nexport interface UseFileUploadReturn {\n uploadTokens: string[];\n uploadProgress: FileUploadProgress;\n isUploading: boolean;\n uploadFiles: (files: File[]) => Promise<void>;\n removeFile: (file: File, index: number) => void;\n resetUpload: () => void;\n}\n\ninterface ContactFormUploadResponse {\n contact_form_upload?: {\n token?: string;\n };\n}\n\nexport interface UseFileUploadOptions {\n onError?: (error: Error) => void;\n /**\n * Upload endpoint.\n *\n * Defaults to DashTrack contact form uploads endpoint.\n */\n endpoint?: string;\n}\n\nconst DEFAULT_UPLOAD_ENDPOINT =\n \"https://api.dashtrack.com/contacts/_/contact_form_uploads\";\n\n/**\n * Upload helper for two-phase contact form uploads.\n */\nexport function useFileUpload(\n options?: UseFileUploadOptions,\n): UseFileUploadReturn {\n const [uploadTokens, setUploadTokens] = useState<string[]>([]);\n const [uploadProgress, setUploadProgress] = useState<FileUploadProgress>({});\n const [isUploading, setIsUploading] = useState(false);\n\n const endpoint = options?.endpoint || DEFAULT_UPLOAD_ENDPOINT;\n\n const uploadFiles = useCallback(\n async (files: File[]) => {\n if (files.length === 0) return;\n\n setIsUploading(true);\n\n try {\n const tokens: string[] = [];\n\n for (const file of files) {\n const formData = new FormData();\n formData.append(\"contact_form_upload[file_upload]\", file);\n formData.append(\"contact_form_upload[title]\", file.name);\n formData.append(\"contact_form_upload[file_name]\", file.name);\n formData.append(\"contact_form_upload[file_size]\", String(file.size));\n\n const response = await fetch(endpoint, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.statusText}`);\n }\n\n const data = (await response.json()) as ContactFormUploadResponse;\n if (data.contact_form_upload?.token) {\n tokens.push(`upload_${data.contact_form_upload.token}`);\n }\n\n setUploadProgress((prev) => ({\n ...prev,\n [file.name]: 100,\n }));\n }\n\n setUploadTokens(tokens);\n } catch (error) {\n options?.onError?.(error as Error);\n } finally {\n setIsUploading(false);\n }\n },\n [endpoint, options],\n );\n\n const removeFile = useCallback((file: File, index: number) => {\n setUploadTokens((prev) => prev.filter((_, i) => i !== index));\n setUploadProgress((prev) => {\n const next = { ...prev };\n delete next[file.name];\n return next;\n });\n }, []);\n\n const resetUpload = useCallback(() => {\n setUploadTokens([]);\n setUploadProgress({});\n }, []);\n\n return {\n uploadTokens,\n uploadProgress,\n isUploading,\n uploadFiles,\n removeFile,\n resetUpload,\n };\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useForm as usePageSpeedForm } from \"../core/useForm\";\nimport type { UseFormReturn } from \"../core/types\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport {\n generateInitialValues,\n generateValidationSchema,\n} from \"./form-field-types\";\nimport {\n PageSpeedFormSubmissionError,\n submitPageSpeedForm,\n type PageSpeedFormConfig,\n} from \"./form-submit\";\n\nexport interface UseContactFormOptions {\n /**\n * Form field configurations.\n */\n formFields: FormFieldConfig[];\n /**\n * Form submission configuration.\n */\n formConfig?: PageSpeedFormConfig;\n /**\n * Optional custom submit handler.\n */\n onSubmit?: (values: Record<string, any>) => void | Promise<void>;\n /**\n * Optional success callback.\n */\n onSuccess?: (data: unknown) => void;\n /**\n * Optional error callback.\n */\n onError?: (error: Error) => void;\n /**\n * Reset form values after successful submission.\n * @default true\n */\n resetOnSuccess?: boolean;\n /**\n * File upload tokens merged into payload.\n */\n uploadTokens?: string[];\n /**\n * Optional app-level navigation handler for internal redirects.\n * Return `false` to force fallback browser navigation.\n */\n navigate?: (href: string) => boolean | void;\n}\n\nexport interface UseContactFormReturn {\n form: UseFormReturn<Record<string, any>>;\n isSubmitted: boolean;\n submissionError: string | null;\n formMethod: \"get\" | \"post\";\n resetSubmissionState: () => void;\n}\n\ninterface RedirectResolution {\n destination: string;\n internalHref?: string;\n}\n\nfunction resolveRedirect(redirectUrl: string): RedirectResolution {\n const trimmed = redirectUrl.trim();\n if (trimmed.startsWith(\"/\") && !trimmed.startsWith(\"//\")) {\n return { destination: trimmed, internalHref: trimmed };\n }\n\n if (typeof window === \"undefined\") {\n return { destination: trimmed };\n }\n\n try {\n const url = new URL(trimmed, window.location.href);\n if (url.origin === window.location.origin) {\n return {\n destination: url.toString(),\n internalHref: `${url.pathname}${url.search}${url.hash}`,\n };\n }\n\n return { destination: url.toString() };\n } catch {\n return { destination: trimmed };\n }\n}\n\n/**\n * Form orchestration helper for dynamic contact forms.\n */\nexport function useContactForm(\n options: UseContactFormOptions,\n): UseContactFormReturn {\n const {\n formFields,\n formConfig,\n onSubmit,\n onSuccess,\n onError,\n resetOnSuccess = true,\n uploadTokens = [],\n navigate,\n } = options;\n\n const [submissionError, setSubmissionError] = useState<string | null>(null);\n const submissionConfig = formConfig?.submissionConfig;\n const redirectUrl = submissionConfig?.redirectUrl;\n\n const resetSubmissionState = useCallback(() => {\n setSubmissionError(null);\n }, []);\n\n const performRedirect = useCallback(() => {\n if (!redirectUrl || typeof window === \"undefined\") {\n return;\n }\n\n const { destination, internalHref } = resolveRedirect(redirectUrl);\n\n const attemptInternalNavigation = () => {\n if (!internalHref) return false;\n\n if (navigate) {\n return navigate(internalHref) !== false;\n }\n\n const handler = (window as any).__opensiteNavigationHandler;\n if (typeof handler === \"function\") {\n try {\n return handler(internalHref, undefined) !== false;\n } catch {\n return false;\n }\n }\n\n return false;\n };\n\n window.setTimeout(() => {\n if (attemptInternalNavigation()) return;\n window.location.assign(destination);\n }, 150);\n }, [navigate, redirectUrl]);\n\n const form = usePageSpeedForm<Record<string, any>>({\n initialValues: useMemo(\n () => generateInitialValues(formFields),\n [formFields],\n ),\n validationSchema: useMemo(\n () => generateValidationSchema(formFields),\n [formFields],\n ),\n onSubmit: async (values, helpers) => {\n resetSubmissionState();\n const shouldAutoSubmit = Boolean(formConfig?.endpoint);\n\n if (!shouldAutoSubmit && !onSubmit) {\n return;\n }\n\n try {\n let result: unknown;\n\n const submissionValues = {\n ...values,\n ...(uploadTokens.length > 0 && {\n contact_form_upload_tokens: uploadTokens,\n }),\n };\n\n if (shouldAutoSubmit) {\n result = await submitPageSpeedForm(submissionValues, formConfig);\n }\n\n if (onSubmit) {\n await onSubmit(submissionValues);\n }\n\n if (shouldAutoSubmit || onSubmit) {\n try {\n await submissionConfig?.handleFormSubmission?.({\n formData: submissionValues,\n responseData: result,\n });\n } catch {\n // Callback errors should not fail the submission lifecycle.\n }\n\n if (resetOnSuccess) {\n helpers.resetForm();\n }\n\n onSuccess?.(result);\n\n if (\n submissionConfig?.behavior === \"redirect\" &&\n submissionConfig.redirectUrl\n ) {\n performRedirect();\n }\n }\n } catch (error) {\n if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {\n helpers.setErrors(error.formErrors);\n }\n\n const errorMessage =\n error instanceof Error ? error.message : \"Form submission failed\";\n setSubmissionError(errorMessage);\n onError?.(error as Error);\n }\n },\n });\n\n const formMethod =\n formConfig?.method?.toLowerCase() === \"get\" ? \"get\" : \"post\";\n\n return {\n form,\n isSubmitted: form.status === \"success\",\n submissionError,\n formMethod,\n resetSubmissionState,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Field } from \"../core/Field\";\nimport {\n Checkbox,\n CheckboxGroup,\n DatePicker,\n DateRangePicker,\n FileInput,\n MultiSelect,\n Radio,\n Select,\n TextArea,\n TextInput,\n TimePicker,\n} from \"../inputs\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport { cn, fieldIsChoiceCard } from \"../lib/utils\";\nimport { DEFAULT_ICON_API_BASE_URL, Icon } from \"@page-speed/icon\";\n\nexport interface DynamicFormFieldProps {\n field: FormFieldConfig;\n className?: string;\n uploadProgress?: { [fileName: string]: number };\n onFileUpload?: (files: File[]) => Promise<void>;\n onFileRemove?: (file: File, index: number) => void;\n isUploading?: boolean;\n /**\n * Whether to render labels via the Field component.\n * When false, only the input component is rendered.\n * Default: true\n */\n renderLabel?: boolean;\n}\n\n/** Icon name mapping for field types that get an automatic start icon. */\nconst FIELD_TYPE_ICON_MAP: Record<string, string> = {\n email: \"material-symbols/mark-email-unread-outline-rounded\",\n tel: \"material-symbols/phone-iphone-outline\",\n url: \"flowbite/link-solid\",\n};\n\n/** Returns a default iconStart element for supported field types, or undefined. */\nfunction getDefaultIconStart(\n field: FormFieldConfig,\n): React.ReactNode | undefined {\n // Check explicit type mapping first\n const iconName = FIELD_TYPE_ICON_MAP[field.type];\n if (iconName) {\n return (\n <Icon name={iconName} apiKey={DEFAULT_ICON_API_BASE_URL} size={18} />\n );\n }\n\n // Special case: text field named \"table_server_name\"\n if (field.type === \"text\" && field.name === \"table_server_name\") {\n return (\n <Icon\n name=\"majesticons/user-box-line\"\n apiKey={DEFAULT_ICON_API_BASE_URL}\n size={18}\n />\n );\n }\n\n return undefined;\n}\n\n/**\n * Dynamic renderer for form field schema configuration.\n */\nexport function DynamicFormField({\n field,\n className,\n uploadProgress = {},\n onFileUpload,\n onFileRemove,\n isUploading = false,\n renderLabel = true,\n}: DynamicFormFieldProps): React.JSX.Element {\n const fieldId = field.name;\n const usesChoiceCard = React.useMemo(() => {\n return fieldIsChoiceCard(field);\n }, [field.type, field.options]);\n\n const fieldClassName = React.useMemo(() => {\n if (usesChoiceCard) {\n return \"p-4 border rounded-lg\";\n } else {\n return \"\";\n }\n }, [usesChoiceCard]);\n\n const usesGroupLegend =\n field.type === \"radio\" || field.type === \"checkbox-group\";\n const usesInlineCheckboxLabel = field.type === \"checkbox\";\n const shouldRenderFieldLabel =\n renderLabel && !usesGroupLegend && !usesInlineCheckboxLabel;\n\n return (\n <Field\n name={field.name}\n label={shouldRenderFieldLabel ? field.label : undefined}\n description={shouldRenderFieldLabel ? field.description : undefined}\n required={field.required}\n className={cn(fieldClassName, className)}\n >\n {({ field: formField, meta }) => (\n <div>\n {(field.type === \"text\" ||\n field.type === \"email\" ||\n field.type === \"tel\" ||\n field.type === \"search\" ||\n field.type === \"password\" ||\n field.type === \"url\") && (\n <TextInput\n {...formField}\n id={fieldId}\n type={field.type}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n iconStart={getDefaultIconStart(field)}\n />\n )}\n\n {field.type === \"number\" && (\n <TextInput\n {...formField}\n id={fieldId}\n type=\"text\"\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"textarea\" && (\n <TextArea\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n rows={field.rows || 4}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"select\" && field.options && (\n <Select\n {...formField}\n id={fieldId}\n options={field.options}\n placeholder={\n field.placeholder ||\n `Select ${field.label ? field.label.toLocaleLowerCase() : field.name ? field.name.toLocaleLowerCase() : \"Item\"}`\n }\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"multi-select\" && field.options && (\n <MultiSelect\n {...formField}\n id={fieldId}\n options={field.options}\n placeholder={\n field.placeholder ||\n `Select ${field.label ? field.label.toLocaleLowerCase() : field.name ? field.name.toLocaleLowerCase() : \"Item\"}`\n }\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"radio\" && field.options && (\n <Radio\n {...formField}\n id={fieldId}\n options={field.options}\n label={field.label}\n description={field.description}\n required={field.required}\n disabled={field.disabled}\n layout={field.layout || \"stacked\"}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"checkbox\" && (\n <Checkbox\n {...formField}\n id={fieldId}\n value={formField.value === true || formField.value === \"true\"}\n onChange={(checked) => formField.onChange(checked)}\n label={field.label}\n description={field.description}\n disabled={field.disabled}\n required={field.required}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"checkbox-group\" && field.options && (\n <CheckboxGroup\n {...formField}\n id={fieldId}\n options={field.options}\n label={field.label}\n description={field.description}\n required={field.required}\n disabled={field.disabled}\n layout={field.layout || \"stacked\"}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {(field.type === \"date-picker\" || field.type === \"date\") && (\n <DatePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"date-range\" && (\n <DateRangePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"time\" && (\n <TimePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"file\" && (\n <FileInput\n {...formField}\n id={fieldId}\n accept={field.accept}\n maxSize={field.maxSize || 5 * 1024 * 1024}\n maxFiles={field.maxFiles || 1}\n multiple={field.multiple || false}\n placeholder={field.placeholder || \"Choose file(s)...\"}\n error={meta.touched && !!meta.error}\n disabled={field.disabled || isUploading}\n showProgress\n uploadProgress={uploadProgress}\n onChange={(files) => {\n formField.onChange(files);\n if (files.length > 0 && onFileUpload) {\n onFileUpload(files);\n }\n }}\n onFileRemove={onFileRemove}\n aria-label={field.label}\n />\n )}\n </div>\n )}\n </Field>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../components/ui/button\";\nimport { Form } from \"../core/Form\";\nimport { DynamicFormField } from \"./DynamicFormField\";\nimport {\n getColumnSpanClass,\n FORM_GRID_FALLBACK_CSS,\n} from \"./form-field-types\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport type { FormRenderConfig } from \"../core/types\";\nimport type { PageSpeedFormConfig } from \"./form-submit\";\nimport { useContactForm } from \"./use-contact-form\";\nimport { useFileUpload } from \"./use-file-upload\";\n\n// ─── Default Values ───────────────────────────────────────────────────────────\n\nconst DEFAULT_STYLE_RULES: FormEngineStyleRules = {\n formContainer: \"\",\n fieldsContainer: \"\",\n fieldClassName: \"\",\n formClassName: \"\",\n successMessageClassName:\n \"text-primary-foreground mt-4 p-3 rounded-md shadow-md bg-primary\",\n errorMessageClassName:\n \"text-destructive-foreground mt-4 p-3 rounded-md shadow-md bg-destructive\",\n};\n\nconst DEFAULT_SUBMIT_LABEL = \"Submit\";\nconst DEFAULT_BUTTON_GROUP_LABEL = \"Subscribe\";\nconst DEFAULT_BUTTON_VARIANT = \"default\";\nconst DEFAULT_BUTTON_GROUP_SIZE = \"default\";\n\n// ─── Setup / Style Types ──────────────────────────────────────────────────────\n\nexport interface ButtonGroupFormSetup {\n size?: \"xs\" | \"sm\" | \"default\" | \"lg\";\n submitLabel?: React.ReactNode;\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\";\n submitIconName?: string;\n submitIconComponent?: React.ReactNode;\n}\n\nexport interface FormEngineSubmitButtonSetup {\n submitLabel?: React.ReactNode;\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\";\n submitIconName?: string;\n submitIconComponent?: React.ReactNode;\n}\n\nexport interface FormEngineStyleRules {\n /** ClassName applied to the div wrapping the `<form>` element */\n formContainer?: string;\n /** ClassName applied to the grid div wrapping all field columns (standard layout) */\n fieldsContainer?: string;\n /** Fallback className applied to each field wrapper when the field has no own className */\n fieldClassName?: string;\n /** className forwarded to the `<form>` element itself via FormStyleConfig */\n formClassName?: string;\n /** className forwarded to the success message container via FormStyleConfig */\n successMessageClassName?: string;\n /** className forwarded to the error message container via FormStyleConfig */\n errorMessageClassName?: string;\n}\n\nexport interface FormEngineLayoutSettings {\n styleRules?: FormEngineStyleRules;\n formLayout?: \"standard\" | \"button-group\";\n /** Settings for button-group layout (only used when formLayout is \"button-group\") */\n buttonGroupSetup?: ButtonGroupFormSetup;\n /** Settings for the submit button in standard layout */\n submitButtonSetup?: FormEngineSubmitButtonSetup;\n}\n\nexport interface FormEngineSetup {\n /** API / submission configuration */\n api?: PageSpeedFormConfig;\n /** Form field definitions */\n fields?: FormFieldConfig[];\n /** Layout, style, and submit-button settings */\n formLayoutSettings?: FormEngineLayoutSettings;\n /** Success message shown after a successful submission */\n successMessage?: React.ReactNode;\n /** Custom submit handler (called in addition to any api endpoint) */\n onSubmit?: (values: Record<string, any>) => void | Promise<void>;\n /** Called after a successful submission with the server response */\n onSuccess?: (data: unknown) => void;\n /** Called when submission fails */\n onError?: (error: Error) => void;\n /** Navigation handler for internal redirects (return false to fall back to browser navigation) */\n navigate?: (href: string) => boolean | void;\n /** Reset form values after success @default true */\n resetOnSuccess?: boolean;\n /** File upload tokens to merge into the payload */\n uploadTokens?: string[];\n /** Called when files are selected for upload */\n onFileUpload?: (files: File[]) => Promise<void>;\n /** Called when a file is removed */\n onFileRemove?: (file: File, index: number) => void;\n /** Whether a file upload is in progress */\n isUploading?: boolean;\n /** Per-file upload progress map (fileName → 0-100) */\n uploadProgress?: { [fileName: string]: number };\n}\n\nexport interface FormEngineProps extends FormEngineSetup {\n /**\n * Optional wrapper object used by block libraries to pass the full setup as a\n * single prop. Direct props on FormEngine take precedence when both are provided.\n */\n formEngineSetup?: FormEngineSetup;\n /** Default form field definitions used when setup fields are missing/empty */\n defaultFields?: FormFieldConfig[];\n /** Default style rules merged before built-in FormEngine defaults */\n defaultStyleRules?: FormEngineStyleRules;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\n/**\n * FormEngine — declarative form component with built-in API integration.\n *\n * Handles `useContactForm` orchestration internally so callers can supply\n * either direct props or a `formEngineSetup` wrapper plus optional defaults.\n *\n * @example Standard layout\n * ```tsx\n * <FormEngine api={api} fields={fields} formLayoutSettings={{ submitButtonSetup: { submitLabel: \"Send\" } }} />\n * ```\n *\n * @example Wrapped setup with defaults\n * ```tsx\n * <FormEngine\n * formEngineSetup={{ api, fields: [emailField] }}\n * defaultFields={fallbackFields}\n * defaultStyleRules={fallbackStyleRules}\n * />\n * ```\n */\nexport function FormEngine(props: FormEngineProps) {\n const {\n formEngineSetup,\n defaultFields,\n defaultStyleRules,\n api: directApi,\n fields: directFields,\n formLayoutSettings: directFormLayoutSettings,\n successMessage: directSuccessMessage,\n onSubmit: directOnSubmit,\n onSuccess: directOnSuccess,\n onError: directOnError,\n navigate: directNavigate,\n resetOnSuccess: directResetOnSuccess,\n uploadTokens: directUploadTokens,\n onFileUpload: directOnFileUpload,\n onFileRemove: directOnFileRemove,\n isUploading: directIsUploading,\n uploadProgress: directUploadProgress,\n } = props;\n\n const api = directApi ?? formEngineSetup?.api;\n const fields = directFields ?? formEngineSetup?.fields;\n const formLayoutSettings =\n directFormLayoutSettings ?? formEngineSetup?.formLayoutSettings;\n const successMessage =\n directSuccessMessage ?? formEngineSetup?.successMessage;\n const onSubmit = directOnSubmit ?? formEngineSetup?.onSubmit;\n const onSuccess = directOnSuccess ?? formEngineSetup?.onSuccess;\n const onError = directOnError ?? formEngineSetup?.onError;\n const navigate = directNavigate ?? formEngineSetup?.navigate;\n const resetOnSuccess =\n directResetOnSuccess ?? formEngineSetup?.resetOnSuccess;\n const externalUploadTokens =\n directUploadTokens ?? formEngineSetup?.uploadTokens;\n const externalOnFileUpload =\n directOnFileUpload ?? formEngineSetup?.onFileUpload;\n const externalOnFileRemove =\n directOnFileRemove ?? formEngineSetup?.onFileRemove;\n const externalIsUploading = directIsUploading ?? formEngineSetup?.isUploading;\n const externalUploadProgress =\n directUploadProgress ?? formEngineSetup?.uploadProgress;\n\n const {\n styleRules: userStyleRules,\n formLayout = \"standard\",\n buttonGroupSetup,\n submitButtonSetup,\n } = formLayoutSettings ?? {};\n const isButtonGroup = formLayout === \"button-group\";\n\n const formFields = React.useMemo<FormFieldConfig[]>(() => {\n if (fields && fields.length > 0) return fields;\n if (defaultFields && defaultFields.length > 0) return defaultFields;\n return [];\n }, [fields, defaultFields]);\n\n // Merge style rules in order: user setup -> block defaults -> built-in defaults\n const styleRules = React.useMemo<FormEngineStyleRules>(\n () => ({\n formContainer:\n userStyleRules?.formContainer ??\n defaultStyleRules?.formContainer ??\n DEFAULT_STYLE_RULES.formContainer,\n fieldsContainer:\n userStyleRules?.fieldsContainer ??\n defaultStyleRules?.fieldsContainer ??\n DEFAULT_STYLE_RULES.fieldsContainer,\n fieldClassName:\n userStyleRules?.fieldClassName ??\n defaultStyleRules?.fieldClassName ??\n DEFAULT_STYLE_RULES.fieldClassName,\n formClassName:\n userStyleRules?.formClassName ??\n defaultStyleRules?.formClassName ??\n DEFAULT_STYLE_RULES.formClassName,\n successMessageClassName:\n userStyleRules?.successMessageClassName ??\n defaultStyleRules?.successMessageClassName ??\n DEFAULT_STYLE_RULES.successMessageClassName,\n errorMessageClassName:\n userStyleRules?.errorMessageClassName ??\n defaultStyleRules?.errorMessageClassName ??\n DEFAULT_STYLE_RULES.errorMessageClassName,\n }),\n [userStyleRules, defaultStyleRules],\n );\n\n // Integrate file upload functionality\n const {\n uploadTokens: internalUploadTokens,\n uploadProgress: internalUploadProgress,\n isUploading: internalIsUploading,\n uploadFiles: internalUploadFiles,\n removeFile: internalRemoveFile,\n resetUpload,\n } = useFileUpload({ onError });\n\n // Use external upload state if provided, otherwise use internal\n const uploadTokens = externalUploadTokens ?? internalUploadTokens;\n const uploadProgress = externalUploadProgress ?? internalUploadProgress;\n const isUploading = externalIsUploading ?? internalIsUploading;\n const onFileUpload = externalOnFileUpload ?? internalUploadFiles;\n const onFileRemove = externalOnFileRemove ?? internalRemoveFile;\n\n const { form, submissionError, formMethod, resetSubmissionState } =\n useContactForm({\n formFields,\n formConfig: api,\n onSubmit,\n onSuccess: (data) => {\n resetUpload();\n onSuccess?.(data);\n },\n onError,\n navigate,\n resetOnSuccess,\n uploadTokens,\n });\n\n // Map FormEngineLayoutSettings → FormRenderConfig for the legacy Form component\n const legacyFormConfig = React.useMemo<FormRenderConfig>(() => {\n if (isButtonGroup) {\n return {\n formLayout: \"button-group\",\n buttonGroupSize: buttonGroupSetup?.size ?? DEFAULT_BUTTON_GROUP_SIZE,\n submitLabel:\n buttonGroupSetup?.submitLabel ?? DEFAULT_BUTTON_GROUP_LABEL,\n submitVariant:\n buttonGroupSetup?.submitVariant ?? DEFAULT_BUTTON_VARIANT,\n submitIconName: buttonGroupSetup?.submitIconName,\n submitIconComponent: buttonGroupSetup?.submitIconComponent,\n endpoint: api?.endpoint,\n submissionConfig: api?.submissionConfig,\n };\n }\n return {\n formLayout: \"standard\",\n endpoint: api?.endpoint,\n submissionConfig: api?.submissionConfig,\n };\n }, [isButtonGroup, buttonGroupSetup, api]);\n\n return (\n <div className={styleRules?.formContainer}>\n <Form\n form={form}\n fields={isButtonGroup ? formFields : undefined}\n formConfig={legacyFormConfig}\n method={formMethod}\n notificationConfig={{\n submissionError: submissionError ?? undefined,\n successMessage,\n }}\n styleConfig={{\n formClassName: styleRules?.formClassName,\n successMessageClassName: styleRules?.successMessageClassName,\n errorMessageClassName: styleRules?.errorMessageClassName,\n }}\n onNewSubmission={resetSubmissionState}\n >\n {!isButtonGroup && (\n <>\n {/* Fallback CSS ensures grid works even when consuming app's\n Tailwind build doesn't generate the col-span-* utilities */}\n <style\n dangerouslySetInnerHTML={{ __html: FORM_GRID_FALLBACK_CSS }}\n />\n <div\n data-psf-grid=\"\"\n className={cn(\n \"grid grid-cols-12 gap-6 md:gap-10\",\n styleRules?.fieldsContainer,\n )}\n >\n {formFields.map((field) => {\n const span = field.columnSpan ?? 12;\n return (\n <div\n key={field.name}\n data-psf-col={String(span)}\n className={cn(\n getColumnSpanClass(span),\n \"min-w-0\",\n )}\n >\n <DynamicFormField\n field={field}\n className={field.className ?? styleRules?.fieldClassName}\n uploadProgress={uploadProgress}\n onFileUpload={onFileUpload}\n onFileRemove={onFileRemove}\n isUploading={isUploading}\n />\n </div>\n );\n })}\n </div>\n <Button\n type=\"submit\"\n variant={\n submitButtonSetup?.submitVariant ?? DEFAULT_BUTTON_VARIANT\n }\n disabled={form.isSubmitting}\n className=\"mt-6 w-full\"\n >\n {submitButtonSetup?.submitLabel ?? DEFAULT_SUBMIT_LABEL}\n </Button>\n </>\n )}\n </Form>\n </div>\n );\n}\n\nFormEngine.displayName = \"FormEngine\";\n"]}
@@ -419,6 +419,7 @@ function getColumnSpanClass(span) {
419
419
  const clamped = Math.max(1, Math.min(span, 12));
420
420
  return columnSpanClasses[clamped] || "col-span-12";
421
421
  }
422
+ var FORM_GRID_FALLBACK_CSS = `[data-psf-grid]{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:1.5rem}@media(min-width:768px){[data-psf-grid]{gap:2.5rem}}[data-psf-col]{grid-column:span 12/span 12;min-width:0}@media(min-width:768px){[data-psf-col="1"]{grid-column:span 1/span 1}[data-psf-col="2"]{grid-column:span 2/span 2}[data-psf-col="3"]{grid-column:span 3/span 3}[data-psf-col="4"]{grid-column:span 4/span 4}[data-psf-col="5"]{grid-column:span 5/span 5}[data-psf-col="6"]{grid-column:span 6/span 6}[data-psf-col="7"]{grid-column:span 7/span 7}[data-psf-col="8"]{grid-column:span 8/span 8}[data-psf-col="9"]{grid-column:span 9/span 9}[data-psf-col="10"]{grid-column:span 10/span 10}[data-psf-col="11"]{grid-column:span 11/span 11}[data-psf-col="12"]{grid-column:span 12/span 12}}`;
422
423
  var DEFAULT_UPLOAD_ENDPOINT = "https://api.dashtrack.com/contacts/_/contact_form_uploads";
423
424
  function useFileUpload(options) {
424
425
  const [uploadTokens, setUploadTokens] = useState([]);
@@ -647,7 +648,7 @@ function DynamicFormField({
647
648
  }, [field.type, field.options]);
648
649
  const fieldClassName = React2.useMemo(() => {
649
650
  if (usesChoiceCard) {
650
- return "p-4 border rounded rounded-lg";
651
+ return "p-4 border rounded-lg";
651
652
  } else {
652
653
  return "";
653
654
  }
@@ -951,34 +952,44 @@ function FormEngine(props) {
951
952
  onNewSubmission: resetSubmissionState
952
953
  },
953
954
  !isButtonGroup && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
955
+ "style",
956
+ {
957
+ dangerouslySetInnerHTML: { __html: FORM_GRID_FALLBACK_CSS }
958
+ }
959
+ ), /* @__PURE__ */ React2.createElement(
954
960
  "div",
955
961
  {
962
+ "data-psf-grid": "",
956
963
  className: cn(
957
964
  "grid grid-cols-12 gap-6 md:gap-10",
958
965
  styleRules?.fieldsContainer
959
966
  )
960
967
  },
961
- formFields.map((field) => /* @__PURE__ */ React2.createElement(
962
- "div",
963
- {
964
- key: field.name,
965
- className: cn(
966
- getColumnSpanClass(field.columnSpan ?? 12),
967
- "min-w-0"
968
- )
969
- },
970
- /* @__PURE__ */ React2.createElement(
971
- DynamicFormField,
968
+ formFields.map((field) => {
969
+ const span = field.columnSpan ?? 12;
970
+ return /* @__PURE__ */ React2.createElement(
971
+ "div",
972
972
  {
973
- field,
974
- className: field.className ?? styleRules?.fieldClassName,
975
- uploadProgress,
976
- onFileUpload,
977
- onFileRemove,
978
- isUploading
979
- }
980
- )
981
- ))
973
+ key: field.name,
974
+ "data-psf-col": String(span),
975
+ className: cn(
976
+ getColumnSpanClass(span),
977
+ "min-w-0"
978
+ )
979
+ },
980
+ /* @__PURE__ */ React2.createElement(
981
+ DynamicFormField,
982
+ {
983
+ field,
984
+ className: field.className ?? styleRules?.fieldClassName,
985
+ uploadProgress,
986
+ onFileUpload,
987
+ onFileRemove,
988
+ isUploading
989
+ }
990
+ )
991
+ );
992
+ })
982
993
  ), /* @__PURE__ */ React2.createElement(
983
994
  Button,
984
995
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/integration/ContactFormSerializer.ts","../src/integration/BlockAdapter.tsx","../src/integration/form-submit.ts","../src/integration/form-field-types.ts","../src/integration/use-file-upload.ts","../src/integration/use-contact-form.ts","../src/integration/DynamicFormField.tsx","../src/integration/FormEngine.tsx"],"names":["camelField","errorMessage","React","Component","response","data","useState","useCallback","React3"],"mappings":"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","file":"integration.js","sourcesContent":["/**\n * @page-speed/forms - Rails API Serializer\n *\n * Serializes form data for the DashTrack ContactsController API.\n * Handles field name conversion (camelCase → snake_case), custom fields separation,\n * and upload token arrays.\n *\n * @see https://github.com/opensite-ai/page-speed-forms\n */\n\n/**\n * Standard fields recognized by the Rails ContactsController API.\n * These are serialized to snake_case and sent in the contact object.\n */\nconst STANDARD_FIELDS = [\n \"content\",\n \"email\",\n \"firstName\",\n \"lastName\",\n \"locationId\",\n \"phone\",\n \"subject\",\n \"redemptionStatus\",\n \"birthday\",\n \"city\",\n \"state\",\n \"websiteFormAssignmentId\",\n \"websiteId\",\n \"acceptsSmsMarketing\",\n \"acceptsEmailMarketing\",\n \"visitorIpAddress\",\n] as const;\n\n/**\n * Configuration parameters for Rails API submission.\n */\nexport interface RailsApiConfig {\n /**\n * API key for authentication.\n * Sent as top-level parameter: api_key\n */\n apiKey: string;\n\n /**\n * Contact category token for categorization.\n * Sent as top-level parameter: contact_category_token\n */\n contactCategoryToken?: string;\n\n /**\n * Location ID for multi-location organizations.\n * Sent as top-level parameter: location_id\n */\n locationId?: string;\n\n /**\n * Website ID for tracking form submissions.\n * Sent within contact object: website_id\n */\n websiteId?: string;\n\n /**\n * Website form assignment ID for form tracking.\n * Sent within contact object: website_form_assignment_id\n */\n websiteFormAssignmentId?: string;\n\n /**\n * Visitor IP address. If not provided, will be auto-detected on server.\n * Sent within contact object: visitor_ip_address\n */\n visitorIpAddress?: string;\n}\n\n/**\n * Serialized form data ready for Rails API submission.\n */\nexport interface SerializedFormData {\n /**\n * Top-level API key parameter.\n */\n api_key: string;\n\n /**\n * Top-level contact category token (optional).\n */\n contact_category_token?: string;\n\n /**\n * Top-level location ID (optional).\n */\n location_id?: string;\n\n /**\n * Contact object with standard fields and metadata.\n */\n contact: {\n /**\n * Standard contact fields in snake_case.\n */\n [key: string]: unknown;\n\n /**\n * Custom fields that don't match standard schema.\n * Stored as separate hash in Rails.\n */\n custom_fields?: Record<string, unknown>;\n\n /**\n * Array of upload tokens from file uploads.\n * These reference ContactFormUpload records in Rails.\n */\n contact_form_upload_tokens?: string[];\n };\n}\n\n/**\n * Form values from the form library (camelCase keys).\n */\nexport type FormValues = Record<string, unknown>;\n\n/**\n * Convert camelCase to snake_case.\n *\n * @example\n * ```ts\n * camelToSnake(\"firstName\") // \"first_name\"\n * camelToSnake(\"acceptsSmsMarketing\") // \"accepts_sms_marketing\"\n * ```\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Check if a field name is a standard Rails contact field.\n */\nfunction isStandardField(fieldName: string): boolean {\n return STANDARD_FIELDS.includes(\n fieldName as (typeof STANDARD_FIELDS)[number],\n );\n}\n\n/**\n * Extract upload tokens from form values.\n * Handles both string tokens and arrays of tokens.\n */\nfunction extractUploadTokens(values: FormValues): string[] {\n const tokens: string[] = [];\n\n for (const value of Object.values(values)) {\n if (typeof value === \"string\" && value.startsWith(\"upload_\")) {\n tokens.push(value);\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (typeof item === \"string\" && item.startsWith(\"upload_\")) {\n tokens.push(item);\n }\n }\n }\n }\n\n return tokens;\n}\n\n/**\n * Format date/time values for Rails API.\n * Rails expects ISO 8601 format.\n */\nfunction formatDateForRails(value: unknown): string | undefined {\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === \"string\") {\n // Attempt to parse and reformat to ensure valid ISO 8601\n const date = new Date(value);\n if (!isNaN(date.getTime())) {\n return date.toISOString();\n }\n }\n return undefined;\n}\n\n/**\n * Serialize form values for Rails ContactsController API.\n *\n * This function:\n * 1. Converts camelCase field names to snake_case\n * 2. Separates standard fields from custom fields\n * 3. Extracts upload tokens into contact_form_upload_tokens array\n * 4. Formats dates to ISO 8601\n * 5. Includes API configuration parameters\n *\n * @param values - Form values from useForm hook (camelCase keys)\n * @param config - Rails API configuration (apiKey, locationId, etc.)\n * @returns Serialized data ready for POST to /contacts\n *\n * @example\n * ```ts\n * const serialized = serializeForRails(\n * {\n * firstName: \"John\",\n * lastName: \"Doe\",\n * email: \"john@example.com\",\n * phone: \"555-1234\",\n * companySize: \"50-100\", // Custom field\n * resumeToken: \"upload_abc123\",\n * },\n * {\n * apiKey: \"key_123\",\n * contactCategoryToken: \"cat_xyz\",\n * locationId: \"loc_456\",\n * }\n * );\n *\n * // Result:\n * // {\n * // api_key: \"key_123\",\n * // contact_category_token: \"cat_xyz\",\n * // location_id: \"loc_456\",\n * // contact: {\n * // first_name: \"John\",\n * // last_name: \"Doe\",\n * // email: \"john@example.com\",\n * // phone: \"555-1234\",\n * // custom_fields: {\n * // company_size: \"50-100\"\n * // },\n * // contact_form_upload_tokens: [\"upload_abc123\"]\n * // }\n * // }\n * ```\n */\nexport function serializeForRails(\n values: FormValues,\n config: RailsApiConfig,\n): SerializedFormData {\n const standardFields: Record<string, unknown> = {};\n const customFields: Record<string, unknown> = {};\n\n // Extract upload tokens\n const uploadTokens = extractUploadTokens(values);\n\n // Separate standard and custom fields\n for (const [key, value] of Object.entries(values)) {\n // Skip upload token fields - they're handled separately\n if (typeof value === \"string\" && value.startsWith(\"upload_\")) {\n continue;\n }\n if (\n Array.isArray(value) &&\n value.every(\n (item) => typeof item === \"string\" && item.startsWith(\"upload_\"),\n )\n ) {\n continue;\n }\n\n const snakeKey = camelToSnake(key);\n\n if (isStandardField(key)) {\n // Format dates for birthday field\n if (key === \"birthday\") {\n const formatted = formatDateForRails(value);\n if (formatted) {\n standardFields[snakeKey] = formatted;\n }\n } else {\n // Handle array values for standard fields\n // Standard fields expect scalar values (strings), but some field types\n // like checkbox-group produce arrays. Convert arrays to comma-separated strings.\n if (Array.isArray(value)) {\n standardFields[snakeKey] = value.join(\", \");\n } else {\n standardFields[snakeKey] = value;\n }\n }\n } else {\n // Custom fields\n customFields[snakeKey] = value;\n }\n }\n\n // Add config fields to standard fields if provided\n if (config.websiteId !== undefined) {\n standardFields.website_id = config.websiteId;\n }\n if (config.websiteFormAssignmentId !== undefined) {\n standardFields.website_form_assignment_id = config.websiteFormAssignmentId;\n }\n if (config.visitorIpAddress !== undefined) {\n standardFields.visitor_ip_address = config.visitorIpAddress;\n }\n\n // Build contact object\n const contact: SerializedFormData[\"contact\"] = {\n ...standardFields,\n };\n\n // Add custom fields if any\n if (Object.keys(customFields).length > 0) {\n contact.custom_fields = customFields;\n }\n\n // Add upload tokens if any\n if (uploadTokens.length > 0) {\n contact.contact_form_upload_tokens = uploadTokens;\n }\n\n // Build final serialized data\n const serialized: SerializedFormData = {\n api_key: config.apiKey,\n contact,\n };\n\n // Add optional top-level parameters\n if (config.contactCategoryToken !== undefined) {\n serialized.contact_category_token = config.contactCategoryToken;\n }\n if (config.locationId !== undefined) {\n serialized.location_id = config.locationId;\n }\n\n return serialized;\n}\n\n/**\n * Rails API error response format.\n */\nexport interface RailsErrorResponse {\n errors: {\n [field: string]: string[];\n };\n status: number;\n}\n\n/**\n * Form error format used by @page-speed/forms.\n */\nexport type FormErrors = Record<string, string | undefined>;\n\n/**\n * Convert snake_case to camelCase.\n *\n * @example\n * ```ts\n * snakeToCamel(\"first_name\") // \"firstName\"\n * snakeToCamel(\"accepts_sms_marketing\") // \"acceptsSmsMarketing\"\n * ```\n */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/**\n * Deserialize Rails API errors to form error format.\n *\n * Converts Rails error format to the format expected by @page-speed/forms:\n * - Maps snake_case field names to camelCase\n * - Flattens error arrays to single error string (first error)\n * - Handles custom_fields errors by mapping back to original field names\n * - Extracts base errors to form-level errors\n *\n * @param railsErrors - Error response from Rails API\n * @returns Form errors object (camelCase keys)\n *\n * @example\n * ```ts\n * const formErrors = deserializeErrors({\n * errors: {\n * first_name: [\"can't be blank\", \"is too short\"],\n * email: [\"is invalid\"],\n * custom_fields: {\n * company_size: [\"is required\"]\n * },\n * base: [\"Something went wrong\"]\n * },\n * status: 422\n * });\n *\n * // Result:\n * // {\n * // firstName: \"can't be blank\",\n * // email: \"is invalid\",\n * // companySize: \"is required\",\n * // _form: \"Something went wrong\"\n * // }\n * ```\n */\nexport function deserializeErrors(railsErrors: RailsErrorResponse): FormErrors {\n const formErrors: FormErrors = {};\n\n for (const [field, messages] of Object.entries(railsErrors.errors)) {\n // Handle base errors (form-level errors)\n if (field === \"base\") {\n formErrors._form = Array.isArray(messages) ? messages[0] : messages;\n continue;\n }\n\n // Handle custom_fields errors\n if (field === \"custom_fields\" && typeof messages === \"object\") {\n for (const [customField, customMessages] of Object.entries(messages)) {\n const camelField = snakeToCamel(customField);\n const errorMessage = Array.isArray(customMessages)\n ? customMessages[0]\n : customMessages;\n formErrors[camelField] = errorMessage;\n }\n continue;\n }\n\n // Handle standard field errors\n const camelField = snakeToCamel(field);\n const errorMessage = Array.isArray(messages) ? messages[0] : messages;\n formErrors[camelField] = errorMessage;\n }\n\n return formErrors;\n}\n","/**\n * @page-speed/forms - Block Adapter\n *\n * Adapts form components for use in block-based rendering systems.\n * Wraps form components to accept block prop structure and transforms them\n * to component-native props.\n *\n * @see https://github.com/opensite-ai/page-speed-forms\n */\n\n\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * Block structure for design payloads.\n * Minimal type definition for adapter compatibility.\n */\nexport interface Block {\n _id: string;\n _type: string;\n _name?: string;\n _parent?: string | null;\n tag?: string;\n styles?: string;\n content?: string;\n blockProps?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Options for Block adapter.\n */\nexport interface BlockAdapterOptions {\n /**\n * Default props to merge with block props.\n */\n defaultProps?: Record<string, unknown>;\n\n /**\n * Transform function to convert Block props to component props.\n * Useful for custom prop mapping logic.\n */\n transformProps?: (\n blockProps: Record<string, unknown>,\n block: Block,\n ) => Record<string, unknown>;\n\n /**\n * Extract display name from block for debugging.\n * Defaults to using _name or _type from block.\n */\n getDisplayName?: (block: Block) => string;\n\n /**\n * Enable React error boundary wrapping.\n * Defaults to true.\n */\n withErrorBoundary?: boolean;\n\n /**\n * Custom error fallback component.\n * If not provided, renders basic error message.\n */\n errorFallback?: (error: Error, block: Block) => React.ReactNode;\n}\n\n/**\n * Props passed to adapted component.\n */\nexport interface AdaptedComponentProps {\n /**\n * Block data from design payload.\n */\n block: Block;\n\n /**\n * Child blocks for rendering nested content.\n * Used by container components like Form.\n */\n children?: React.ReactNode;\n\n /**\n * Callback to render child blocks.\n * Provided by block rendering systems.\n */\n renderChildren?: (blockId: string) => React.ReactNode;\n}\n\n/**\n * Error boundary component for catching render errors.\n */\nclass BlockErrorBoundary extends React.Component<\n {\n block: Block;\n fallback?: (error: Error, block: Block) => React.ReactNode;\n children: React.ReactNode;\n },\n { error: Error | null }\n> {\n constructor(props: BlockErrorBoundary[\"props\"]) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(\n `Block render error (${this.props.block._id}):`,\n error,\n errorInfo,\n );\n }\n\n render() {\n if (this.state.error) {\n if (this.props.fallback) {\n return this.props.fallback(this.state.error, this.props.block);\n }\n\n return (\n <div\n className=\"block-error border border-destructive bg-destructive p-4 rounded text-destructive-foreground\"\n data-block-id={this.props.block._id}\n data-block-type={this.props.block._type}\n >\n <p className=\"font-semibold\">Block Render Error</p>\n <p className=\"text-sm\">\n Block: {this.props.block._name || this.props.block._id} (\n {this.props.block._type})\n </p>\n <p className=\"text-sm mt-1\">{this.state.error.message}</p>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Create a Block-compatible wrapper for a form component.\n *\n * This adapter transforms Block props (from design payload) into\n * component-native props. It handles:\n * - Extracting props from `blockProps` field\n * - Merging with default props\n * - Custom prop transformation\n * - Error boundary wrapping\n * - Children rendering support\n *\n * @param Component - React component to adapt\n * @param options - Adapter configuration options\n * @returns Block-compatible component\n *\n * @example\n * ```tsx\n * import { TextInput } from \"@page-speed/forms/inputs\";\n * import { createBlockAdapter } from \"@page-speed/forms/integration\";\n *\n * // Create Block-compatible TextInput\n * const BlockTextInput = createBlockAdapter(TextInput, {\n * defaultProps: {\n * placeholder: \"Enter text...\",\n * },\n * transformProps: (blockProps, block) => ({\n * ...blockProps,\n * // Map Block content to component props\n * label: block.content || blockProps.label,\n * // Apply Block styles as className\n * className: block.styles,\n * }),\n * });\n *\n * // Register with rendering system\n * registerBlockType(\"TextInput\", BlockTextInput);\n * ```\n *\n * @example\n * ```tsx\n * // Block from design payload:\n * {\n * _id: \"field-email\",\n * _type: \"TextInput\",\n * _name: \"Email Field\",\n * content: \"Email Address\",\n * styles: \"w-full mb-4\",\n * blockProps: {\n * name: \"email\",\n * type: \"email\",\n * placeholder: \"you@example.com\",\n * required: true,\n * }\n * }\n *\n * // Transformed to TextInput props:\n * <TextInput\n * name=\"email\"\n * type=\"email\"\n * placeholder=\"you@example.com\"\n * required={true}\n * label=\"Email Address\"\n * className=\"w-full mb-4\"\n * />\n * ```\n */\nexport function createBlockAdapter<TProps extends Record<string, unknown>>(\n Component: React.ComponentType<TProps>,\n options: BlockAdapterOptions = {},\n): React.ComponentType<AdaptedComponentProps> {\n const {\n defaultProps = {},\n transformProps,\n withErrorBoundary = true,\n errorFallback,\n } = options;\n\n const AdaptedComponent: React.FC<AdaptedComponentProps> = ({\n block,\n children,\n renderChildren,\n }) => {\n // Extract component props from blockProps\n const blockProps = (block.blockProps || {}) as Record<string, unknown>;\n\n // Merge with default props\n const mergedProps = { ...defaultProps, ...blockProps };\n\n // Apply custom transformation if provided\n const finalProps = transformProps\n ? transformProps(mergedProps, block)\n : mergedProps;\n\n // Add data attributes for debugging\n const dataAttrs = {\n \"data-block-id\": block._id,\n \"data-block-type\": block._type,\n ...(block._name && { \"data-block-name\": block._name }),\n };\n\n // Merge data attributes with final props\n const componentProps = {\n ...finalProps,\n ...dataAttrs,\n } as unknown as TProps;\n\n // Render children if renderChildren callback provided\n const renderedChildren = renderChildren\n ? renderChildren(block._id)\n : children;\n\n const element = (\n <Component {...componentProps}>{renderedChildren}</Component>\n );\n\n // Wrap with error boundary if enabled\n if (withErrorBoundary) {\n return (\n <BlockErrorBoundary block={block} fallback={errorFallback}>\n {element}\n </BlockErrorBoundary>\n );\n }\n\n return element;\n };\n\n // Set display name for debugging\n const componentName = Component.displayName || Component.name || \"Component\";\n AdaptedComponent.displayName = `BlockAdapter(${componentName})`;\n\n return AdaptedComponent;\n}\n\n/**\n * Standard prop transformer for form input components.\n *\n * Applies common transformations:\n * - Maps Block `content` to `label`\n * - Maps Block `styles` to `className`\n * - Preserves all blockProps\n *\n * @param blockProps - Props from Block.blockProps\n * @param block - Full Block object\n * @returns Transformed props for component\n *\n * @example\n * ```tsx\n * const BlockTextInput = createBlockAdapter(TextInput, {\n * transformProps: standardInputTransformer,\n * });\n * ```\n */\nexport function standardInputTransformer(\n blockProps: Record<string, unknown>,\n block: Block,\n): Record<string, unknown> {\n return {\n ...blockProps,\n // Use content as label if not already provided\n ...(block.content && !blockProps.label && { label: block.content }),\n // Apply Block styles as className\n ...(block.styles && { className: block.styles }),\n };\n}\n\n/**\n * Create multiple Block adapters with shared options.\n *\n * Convenience function for adapting multiple components at once\n * with the same configuration.\n *\n * @param components - Record of component name to component\n * @param options - Shared adapter options\n * @returns Record of adapted components\n *\n * @example\n * ```tsx\n * import * as Inputs from \"@page-speed/forms/inputs\";\n * import { createBlockAdapters, standardInputTransformer } from \"@page-speed/forms/integration\";\n *\n * const BlockInputs = createBlockAdapters(\n * {\n * TextInput: Inputs.TextInput,\n * TextArea: Inputs.TextArea,\n * Select: Inputs.Select,\n * },\n * {\n * transformProps: standardInputTransformer,\n * withErrorBoundary: true,\n * }\n * );\n *\n * // BlockInputs.TextInput, BlockInputs.TextArea, BlockInputs.Select\n * // are now Block-compatible\n * ```\n */\nexport function createBlockAdapters<\n TComponents extends Record<string, React.ComponentType<any>>,\n>(\n components: TComponents,\n options: BlockAdapterOptions = {},\n): Record<keyof TComponents, React.ComponentType<AdaptedComponentProps>> {\n const adapted: Record<\n string,\n React.ComponentType<AdaptedComponentProps>\n > = {};\n\n for (const [name, component] of Object.entries(components)) {\n adapted[name] = createBlockAdapter(component, options);\n }\n\n return adapted as Record<\n keyof TComponents,\n React.ComponentType<AdaptedComponentProps>\n >;\n}\n","import type {\n FormLayoutSettings,\n FormSubmissionConfig,\n} from \"../core/types\";\nimport {\n deserializeErrors,\n serializeForRails,\n type FormErrors,\n type RailsApiConfig,\n type RailsErrorResponse,\n} from \"./ContactFormSerializer\";\n\nexport type PageSpeedFormMethod = \"post\" | \"get\" | \"put\" | \"patch\";\nexport type PageSpeedFormSubmissionFormat = \"json\" | \"rails\";\n\nexport interface PageSpeedFormSubmissionResult {\n formData: Record<string, any>;\n responseData: unknown;\n}\n\n/**\n * PageSpeed-specific extension of the core FormSubmissionConfig.\n * Inherits behavior, customComponent, and newFormSubmissionAction from the\n * base type and adds integration-layer callbacks and redirect support.\n */\nexport interface PageSpeedFormSubmissionConfig extends FormSubmissionConfig {\n /**\n * Optional callback triggered on successful submission.\n */\n handleFormSubmission?: (\n result: PageSpeedFormSubmissionResult,\n ) => void | Promise<void>;\n\n /**\n * Redirect destination used when behavior is \"redirect\".\n */\n redirectUrl?: string;\n}\n\nexport interface PageSpeedFormConfig {\n /**\n * API endpoint used for submission (also applied to form action).\n */\n endpoint?: string;\n /**\n * HTTP method for submission.\n * @default \"post\"\n */\n method?: PageSpeedFormMethod;\n /**\n * Submission format.\n * Defaults to \"rails\" when apiKey is present, otherwise \"json\".\n */\n format?: PageSpeedFormSubmissionFormat;\n /**\n * Additional headers for the submission request.\n */\n headers?: Record<string, string>;\n /**\n * Static values merged into the payload (e.g. subject, content).\n */\n values?: Record<string, unknown>;\n /**\n * Rails API key (required for rails format).\n */\n apiKey?: string;\n /**\n * Rails contact category token.\n */\n contactCategoryToken?: string;\n /**\n * Rails location ID.\n */\n locationId?: string;\n /**\n * Rails website ID.\n */\n websiteId?: string;\n /**\n * Rails website form assignment ID.\n */\n websiteFormAssignmentId?: string;\n /**\n * Rails visitor IP address override.\n */\n visitorIpAddress?: string;\n /**\n * Reset form values after a successful submission.\n * @default true\n */\n resetOnSuccess?: boolean;\n /**\n * Optional post-submission behavior configuration.\n */\n submissionConfig?: PageSpeedFormSubmissionConfig;\n\n /**\n * Optional layout and presentation settings.\n * Provides a typed home for layout props (formLayout, buttonGroupSize, etc.)\n * so consumers don't need an `as any` cast when passing them alongside API config.\n */\n formLayoutSettings?: FormLayoutSettings;\n}\n\nexport class PageSpeedFormSubmissionError extends Error {\n formErrors?: FormErrors;\n status?: number;\n\n constructor(\n message: string,\n options: { formErrors?: FormErrors; status?: number } = {},\n ) {\n super(message);\n this.name = \"PageSpeedFormSubmissionError\";\n this.formErrors = options.formErrors;\n this.status = options.status;\n }\n}\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function isValidEmail(value: string): boolean {\n return EMAIL_REGEX.test(value);\n}\n\nfunction buildUrlWithParams(\n endpoint: string,\n values: Record<string, any>,\n): string {\n const base =\n typeof window === \"undefined\" ? \"http://localhost\" : window.location.origin;\n const url = new URL(endpoint, base);\n\n Object.entries(values).forEach(([key, value]) => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n url.searchParams.append(key, String(item));\n }\n });\n return;\n }\n\n url.searchParams.set(key, String(value));\n });\n\n return url.toString();\n}\n\nfunction normalizeMethod(method?: PageSpeedFormMethod): string {\n return (method || \"post\").toUpperCase();\n}\n\nfunction resolveFormat(\n config?: PageSpeedFormConfig,\n): PageSpeedFormSubmissionFormat {\n if (config?.format) return config.format;\n return config?.apiKey ? \"rails\" : \"json\";\n}\n\nfunction mergeValues(\n values: Record<string, any>,\n config?: PageSpeedFormConfig,\n): Record<string, any> {\n return {\n ...(config?.values ?? {}),\n ...values,\n };\n}\n\nexport async function submitPageSpeedForm(\n values: Record<string, any>,\n config?: PageSpeedFormConfig,\n): Promise<unknown> {\n if (!config?.endpoint) {\n return null;\n }\n\n const payload = mergeValues(values, config);\n const method = normalizeMethod(config.method);\n const format = resolveFormat(config);\n const headers: Record<string, string> = { ...(config.headers ?? {}) };\n\n if (format === \"rails\") {\n if (!config.apiKey) {\n throw new PageSpeedFormSubmissionError(\n \"Missing apiKey for Rails form submission.\",\n );\n }\n\n const railsConfig: RailsApiConfig = {\n apiKey: config.apiKey,\n contactCategoryToken: config.contactCategoryToken,\n locationId: config.locationId,\n websiteId: config.websiteId,\n websiteFormAssignmentId: config.websiteFormAssignmentId,\n visitorIpAddress: config.visitorIpAddress,\n };\n\n const serialized = serializeForRails(payload, railsConfig);\n\n if (serialized.contact.contact_form_upload_tokens) {\n serialized.contact.contact_form_upload_tokens = (\n serialized.contact.contact_form_upload_tokens as string[]\n ).map((token) => token.replace(/^upload_/, \"\"));\n }\n\n headers[\"Content-Type\"] ??= \"application/json\";\n\n const response = await fetch(config.endpoint, {\n method,\n headers,\n body: JSON.stringify(serialized),\n });\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok || (data && data.errors)) {\n const errorResponse: RailsErrorResponse = {\n errors: data?.errors ?? { base: [\"Form submission failed\"] },\n status: data?.status ?? response.status,\n };\n const formErrors = deserializeErrors(errorResponse);\n throw new PageSpeedFormSubmissionError(\"Form submission failed.\", {\n formErrors,\n status: errorResponse.status,\n });\n }\n\n return data;\n }\n\n if (method === \"GET\") {\n const url = buildUrlWithParams(config.endpoint, payload);\n const response = await fetch(url, { method, headers });\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n throw new PageSpeedFormSubmissionError(\n data?.message || \"Form submission failed.\",\n { status: response.status },\n );\n }\n\n return data;\n }\n\n headers[\"Content-Type\"] ??= \"application/json\";\n\n const response = await fetch(config.endpoint, {\n method,\n headers,\n body: JSON.stringify(payload),\n });\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n throw new PageSpeedFormSubmissionError(\n data?.message || \"Form submission failed.\",\n { status: response.status },\n );\n }\n\n return data;\n}\n","/**\n * Dynamic form field schema types and helpers.\n *\n * These utilities are intentionally exposed from the integration layer so\n * block/rendering libraries can share one field schema contract.\n */\n\nimport { humanizeFieldName } from \"../lib/utils\";\n\nexport type FormFieldType =\n | \"text\"\n | \"email\"\n | \"search\"\n | \"password\"\n | \"tel\"\n | \"textarea\"\n | \"select\"\n | \"radio\"\n | \"checkbox\"\n | \"checkbox-group\"\n | \"number\"\n | \"url\"\n | \"date\"\n | \"date-picker\"\n | \"date-range\"\n | \"time\"\n | \"file\"\n | \"multi-select\";\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n description?: string;\n}\n\nexport interface FormFieldConfig {\n /**\n * Optionally displays label for the field\n */\n label?: string;\n /**\n * Unique field name (used as the key in form values)\n */\n name: string;\n /**\n * Field type\n */\n type: FormFieldType;\n\n /**\n * Placeholder text\n */\n placeholder?: string;\n /**\n * Whether the field is required\n * @default false\n */\n required?: boolean;\n /**\n * Column span in grid layout (1-12)\n * @default 12 (full width)\n */\n columnSpan?: number;\n /**\n * Options for select/radio/checkbox-group fields\n */\n options?: SelectOption[];\n /**\n * Number of rows for textarea\n * @default 4\n */\n rows?: number;\n /**\n * Custom validation function\n * Return undefined for valid, or an error message string for invalid\n */\n validator?: (\n value: any,\n allValues: Record<string, any>,\n ) => string | undefined;\n /**\n * Additional CSS classes for the field wrapper\n */\n className?: string;\n /**\n * Whether the field is disabled\n * @default false\n */\n disabled?: boolean;\n /**\n * Accepted file types for file inputs (MIME types or extensions)\n * @example \".pdf,.doc,.docx\"\n * @example \"image/*,application/pdf\"\n */\n accept?: string;\n /**\n * Maximum file size in bytes for file inputs\n * @default 5MB (5 * 1024 * 1024)\n */\n maxSize?: number;\n /**\n * Maximum number of files for file inputs\n * @default 1\n */\n maxFiles?: number;\n /**\n * Allow multiple file selection\n * @default false\n */\n multiple?: boolean;\n /**\n * Description/help text displayed with the field\n */\n description?: string;\n /**\n * Layout for radio/checkbox groups\n * @default \"stacked\"\n */\n layout?: \"grid\" | \"stacked\";\n}\n\n/**\n * Generate initial values object from form field configs.\n */\nexport function generateInitialValues(\n fields: FormFieldConfig[],\n): Record<string, any> {\n return fields.reduce(\n (acc, field) => {\n if (field.type === \"checkbox\") {\n acc[field.name] = false;\n } else if (\n field.type === \"checkbox-group\" ||\n field.type === \"multi-select\"\n ) {\n acc[field.name] = [];\n } else if (field.type === \"file\") {\n acc[field.name] = [];\n } else if (field.type === \"date-range\") {\n acc[field.name] = { start: null, end: null };\n } else {\n acc[field.name] = \"\";\n }\n return acc;\n },\n {} as Record<string, any>,\n );\n}\n\n/**\n * Generate validation schema from form field configs.\n */\nexport function generateValidationSchema(\n fields: FormFieldConfig[],\n): Record<\n string,\n (value: any, allValues: Record<string, any>) => string | undefined\n> {\n return fields.reduce(\n (acc, field) => {\n acc[field.name] = (value: any, allValues: Record<string, any>) => {\n if (field.required) {\n if (!value || (typeof value === \"string\" && !value.trim())) {\n const displayName = field.label || humanizeFieldName(field.name);\n return `${displayName} is required`;\n }\n }\n\n if (field.type === \"email\" && value) {\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value)) {\n return \"Please enter a valid email address\";\n }\n }\n\n if (field.type === \"url\" && value) {\n try {\n new URL(value);\n } catch {\n return \"Please enter a valid URL\";\n }\n }\n\n if (field.validator) {\n return field.validator(value, allValues);\n }\n\n return undefined;\n };\n return acc;\n },\n {} as Record<\n string,\n (value: any, allValues: Record<string, any>) => string | undefined\n >,\n );\n}\n\n/**\n * Static mapping of column span values to Tailwind classes.\n *\n * IMPORTANT: These must remain complete, literal class strings so Tailwind's\n * scanner can detect and generate them.\n */\nconst columnSpanClasses: Record<number, string> = {\n 1: \"col-span-12 md:col-span-1\",\n 2: \"col-span-12 md:col-span-2\",\n 3: \"col-span-12 md:col-span-3\",\n 4: \"col-span-12 md:col-span-4\",\n 5: \"col-span-12 md:col-span-5\",\n 6: \"col-span-12 md:col-span-6\",\n 7: \"col-span-12 md:col-span-7\",\n 8: \"col-span-12 md:col-span-8\",\n 9: \"col-span-12 md:col-span-9\",\n 10: \"col-span-12 md:col-span-10\",\n 11: \"col-span-12 md:col-span-11\",\n 12: \"col-span-12\",\n};\n\n/**\n * Get grid column span class for Tailwind.\n *\n * On small screens the field is always full-width, then from `md` and up the\n * configured span is applied.\n */\nexport function getColumnSpanClass(span?: number): string {\n if (!span || span === 12) return \"col-span-12\";\n const clamped = Math.max(1, Math.min(span, 12));\n return columnSpanClasses[clamped] || \"col-span-12\";\n}\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\n\nexport interface FileUploadProgress {\n [fileName: string]: number;\n}\n\nexport interface UseFileUploadReturn {\n uploadTokens: string[];\n uploadProgress: FileUploadProgress;\n isUploading: boolean;\n uploadFiles: (files: File[]) => Promise<void>;\n removeFile: (file: File, index: number) => void;\n resetUpload: () => void;\n}\n\ninterface ContactFormUploadResponse {\n contact_form_upload?: {\n token?: string;\n };\n}\n\nexport interface UseFileUploadOptions {\n onError?: (error: Error) => void;\n /**\n * Upload endpoint.\n *\n * Defaults to DashTrack contact form uploads endpoint.\n */\n endpoint?: string;\n}\n\nconst DEFAULT_UPLOAD_ENDPOINT =\n \"https://api.dashtrack.com/contacts/_/contact_form_uploads\";\n\n/**\n * Upload helper for two-phase contact form uploads.\n */\nexport function useFileUpload(\n options?: UseFileUploadOptions,\n): UseFileUploadReturn {\n const [uploadTokens, setUploadTokens] = useState<string[]>([]);\n const [uploadProgress, setUploadProgress] = useState<FileUploadProgress>({});\n const [isUploading, setIsUploading] = useState(false);\n\n const endpoint = options?.endpoint || DEFAULT_UPLOAD_ENDPOINT;\n\n const uploadFiles = useCallback(\n async (files: File[]) => {\n if (files.length === 0) return;\n\n setIsUploading(true);\n\n try {\n const tokens: string[] = [];\n\n for (const file of files) {\n const formData = new FormData();\n formData.append(\"contact_form_upload[file_upload]\", file);\n formData.append(\"contact_form_upload[title]\", file.name);\n formData.append(\"contact_form_upload[file_name]\", file.name);\n formData.append(\"contact_form_upload[file_size]\", String(file.size));\n\n const response = await fetch(endpoint, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.statusText}`);\n }\n\n const data = (await response.json()) as ContactFormUploadResponse;\n if (data.contact_form_upload?.token) {\n tokens.push(`upload_${data.contact_form_upload.token}`);\n }\n\n setUploadProgress((prev) => ({\n ...prev,\n [file.name]: 100,\n }));\n }\n\n setUploadTokens(tokens);\n } catch (error) {\n options?.onError?.(error as Error);\n } finally {\n setIsUploading(false);\n }\n },\n [endpoint, options],\n );\n\n const removeFile = useCallback((file: File, index: number) => {\n setUploadTokens((prev) => prev.filter((_, i) => i !== index));\n setUploadProgress((prev) => {\n const next = { ...prev };\n delete next[file.name];\n return next;\n });\n }, []);\n\n const resetUpload = useCallback(() => {\n setUploadTokens([]);\n setUploadProgress({});\n }, []);\n\n return {\n uploadTokens,\n uploadProgress,\n isUploading,\n uploadFiles,\n removeFile,\n resetUpload,\n };\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useForm as usePageSpeedForm } from \"../core/useForm\";\nimport type { UseFormReturn } from \"../core/types\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport {\n generateInitialValues,\n generateValidationSchema,\n} from \"./form-field-types\";\nimport {\n PageSpeedFormSubmissionError,\n submitPageSpeedForm,\n type PageSpeedFormConfig,\n} from \"./form-submit\";\n\nexport interface UseContactFormOptions {\n /**\n * Form field configurations.\n */\n formFields: FormFieldConfig[];\n /**\n * Form submission configuration.\n */\n formConfig?: PageSpeedFormConfig;\n /**\n * Optional custom submit handler.\n */\n onSubmit?: (values: Record<string, any>) => void | Promise<void>;\n /**\n * Optional success callback.\n */\n onSuccess?: (data: unknown) => void;\n /**\n * Optional error callback.\n */\n onError?: (error: Error) => void;\n /**\n * Reset form values after successful submission.\n * @default true\n */\n resetOnSuccess?: boolean;\n /**\n * File upload tokens merged into payload.\n */\n uploadTokens?: string[];\n /**\n * Optional app-level navigation handler for internal redirects.\n * Return `false` to force fallback browser navigation.\n */\n navigate?: (href: string) => boolean | void;\n}\n\nexport interface UseContactFormReturn {\n form: UseFormReturn<Record<string, any>>;\n isSubmitted: boolean;\n submissionError: string | null;\n formMethod: \"get\" | \"post\";\n resetSubmissionState: () => void;\n}\n\ninterface RedirectResolution {\n destination: string;\n internalHref?: string;\n}\n\nfunction resolveRedirect(redirectUrl: string): RedirectResolution {\n const trimmed = redirectUrl.trim();\n if (trimmed.startsWith(\"/\") && !trimmed.startsWith(\"//\")) {\n return { destination: trimmed, internalHref: trimmed };\n }\n\n if (typeof window === \"undefined\") {\n return { destination: trimmed };\n }\n\n try {\n const url = new URL(trimmed, window.location.href);\n if (url.origin === window.location.origin) {\n return {\n destination: url.toString(),\n internalHref: `${url.pathname}${url.search}${url.hash}`,\n };\n }\n\n return { destination: url.toString() };\n } catch {\n return { destination: trimmed };\n }\n}\n\n/**\n * Form orchestration helper for dynamic contact forms.\n */\nexport function useContactForm(\n options: UseContactFormOptions,\n): UseContactFormReturn {\n const {\n formFields,\n formConfig,\n onSubmit,\n onSuccess,\n onError,\n resetOnSuccess = true,\n uploadTokens = [],\n navigate,\n } = options;\n\n const [submissionError, setSubmissionError] = useState<string | null>(null);\n const submissionConfig = formConfig?.submissionConfig;\n const redirectUrl = submissionConfig?.redirectUrl;\n\n const resetSubmissionState = useCallback(() => {\n setSubmissionError(null);\n }, []);\n\n const performRedirect = useCallback(() => {\n if (!redirectUrl || typeof window === \"undefined\") {\n return;\n }\n\n const { destination, internalHref } = resolveRedirect(redirectUrl);\n\n const attemptInternalNavigation = () => {\n if (!internalHref) return false;\n\n if (navigate) {\n return navigate(internalHref) !== false;\n }\n\n const handler = (window as any).__opensiteNavigationHandler;\n if (typeof handler === \"function\") {\n try {\n return handler(internalHref, undefined) !== false;\n } catch {\n return false;\n }\n }\n\n return false;\n };\n\n window.setTimeout(() => {\n if (attemptInternalNavigation()) return;\n window.location.assign(destination);\n }, 150);\n }, [navigate, redirectUrl]);\n\n const form = usePageSpeedForm<Record<string, any>>({\n initialValues: useMemo(\n () => generateInitialValues(formFields),\n [formFields],\n ),\n validationSchema: useMemo(\n () => generateValidationSchema(formFields),\n [formFields],\n ),\n onSubmit: async (values, helpers) => {\n resetSubmissionState();\n const shouldAutoSubmit = Boolean(formConfig?.endpoint);\n\n if (!shouldAutoSubmit && !onSubmit) {\n return;\n }\n\n try {\n let result: unknown;\n\n const submissionValues = {\n ...values,\n ...(uploadTokens.length > 0 && {\n contact_form_upload_tokens: uploadTokens,\n }),\n };\n\n if (shouldAutoSubmit) {\n result = await submitPageSpeedForm(submissionValues, formConfig);\n }\n\n if (onSubmit) {\n await onSubmit(submissionValues);\n }\n\n if (shouldAutoSubmit || onSubmit) {\n try {\n await submissionConfig?.handleFormSubmission?.({\n formData: submissionValues,\n responseData: result,\n });\n } catch {\n // Callback errors should not fail the submission lifecycle.\n }\n\n if (resetOnSuccess) {\n helpers.resetForm();\n }\n\n onSuccess?.(result);\n\n if (\n submissionConfig?.behavior === \"redirect\" &&\n submissionConfig.redirectUrl\n ) {\n performRedirect();\n }\n }\n } catch (error) {\n if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {\n helpers.setErrors(error.formErrors);\n }\n\n const errorMessage =\n error instanceof Error ? error.message : \"Form submission failed\";\n setSubmissionError(errorMessage);\n onError?.(error as Error);\n }\n },\n });\n\n const formMethod =\n formConfig?.method?.toLowerCase() === \"get\" ? \"get\" : \"post\";\n\n return {\n form,\n isSubmitted: form.status === \"success\",\n submissionError,\n formMethod,\n resetSubmissionState,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Field } from \"../core/Field\";\nimport {\n Checkbox,\n CheckboxGroup,\n DatePicker,\n DateRangePicker,\n FileInput,\n MultiSelect,\n Radio,\n Select,\n TextArea,\n TextInput,\n TimePicker,\n} from \"../inputs\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport { cn, fieldIsChoiceCard } from \"../lib/utils\";\nimport { DEFAULT_ICON_API_BASE_URL, Icon } from \"@page-speed/icon\";\n\nexport interface DynamicFormFieldProps {\n field: FormFieldConfig;\n className?: string;\n uploadProgress?: { [fileName: string]: number };\n onFileUpload?: (files: File[]) => Promise<void>;\n onFileRemove?: (file: File, index: number) => void;\n isUploading?: boolean;\n /**\n * Whether to render labels via the Field component.\n * When false, only the input component is rendered.\n * Default: true\n */\n renderLabel?: boolean;\n}\n\n/** Icon name mapping for field types that get an automatic start icon. */\nconst FIELD_TYPE_ICON_MAP: Record<string, string> = {\n email: \"material-symbols/mark-email-unread-outline-rounded\",\n tel: \"material-symbols/phone-iphone-outline\",\n url: \"flowbite/link-solid\",\n};\n\n/** Returns a default iconStart element for supported field types, or undefined. */\nfunction getDefaultIconStart(\n field: FormFieldConfig,\n): React.ReactNode | undefined {\n // Check explicit type mapping first\n const iconName = FIELD_TYPE_ICON_MAP[field.type];\n if (iconName) {\n return (\n <Icon name={iconName} apiKey={DEFAULT_ICON_API_BASE_URL} size={18} />\n );\n }\n\n // Special case: text field named \"table_server_name\"\n if (field.type === \"text\" && field.name === \"table_server_name\") {\n return (\n <Icon\n name=\"majesticons/user-box-line\"\n apiKey={DEFAULT_ICON_API_BASE_URL}\n size={18}\n />\n );\n }\n\n return undefined;\n}\n\n/**\n * Dynamic renderer for form field schema configuration.\n */\nexport function DynamicFormField({\n field,\n className,\n uploadProgress = {},\n onFileUpload,\n onFileRemove,\n isUploading = false,\n renderLabel = true,\n}: DynamicFormFieldProps): React.JSX.Element {\n const fieldId = field.name;\n const usesChoiceCard = React.useMemo(() => {\n return fieldIsChoiceCard(field);\n }, [field.type, field.options]);\n\n const fieldClassName = React.useMemo(() => {\n if (usesChoiceCard) {\n return \"p-4 border rounded rounded-lg\";\n } else {\n return \"\";\n }\n }, [usesChoiceCard]);\n\n const usesGroupLegend =\n field.type === \"radio\" || field.type === \"checkbox-group\";\n const usesInlineCheckboxLabel = field.type === \"checkbox\";\n const shouldRenderFieldLabel =\n renderLabel && !usesGroupLegend && !usesInlineCheckboxLabel;\n\n return (\n <Field\n name={field.name}\n label={shouldRenderFieldLabel ? field.label : undefined}\n description={shouldRenderFieldLabel ? field.description : undefined}\n required={field.required}\n className={cn(fieldClassName, className)}\n >\n {({ field: formField, meta }) => (\n <div>\n {(field.type === \"text\" ||\n field.type === \"email\" ||\n field.type === \"tel\" ||\n field.type === \"search\" ||\n field.type === \"password\" ||\n field.type === \"url\") && (\n <TextInput\n {...formField}\n id={fieldId}\n type={field.type}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n iconStart={getDefaultIconStart(field)}\n />\n )}\n\n {field.type === \"number\" && (\n <TextInput\n {...formField}\n id={fieldId}\n type=\"text\"\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"textarea\" && (\n <TextArea\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n rows={field.rows || 4}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"select\" && field.options && (\n <Select\n {...formField}\n id={fieldId}\n options={field.options}\n placeholder={\n field.placeholder ||\n `Select ${field.label ? field.label.toLocaleLowerCase() : field.name ? field.name.toLocaleLowerCase() : \"Item\"}`\n }\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"multi-select\" && field.options && (\n <MultiSelect\n {...formField}\n id={fieldId}\n options={field.options}\n placeholder={\n field.placeholder ||\n `Select ${field.label ? field.label.toLocaleLowerCase() : field.name ? field.name.toLocaleLowerCase() : \"Item\"}`\n }\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"radio\" && field.options && (\n <Radio\n {...formField}\n id={fieldId}\n options={field.options}\n label={field.label}\n description={field.description}\n required={field.required}\n disabled={field.disabled}\n layout={field.layout || \"stacked\"}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"checkbox\" && (\n <Checkbox\n {...formField}\n id={fieldId}\n value={formField.value === true || formField.value === \"true\"}\n onChange={(checked) => formField.onChange(checked)}\n label={field.label}\n description={field.description}\n disabled={field.disabled}\n required={field.required}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"checkbox-group\" && field.options && (\n <CheckboxGroup\n {...formField}\n id={fieldId}\n options={field.options}\n label={field.label}\n description={field.description}\n required={field.required}\n disabled={field.disabled}\n layout={field.layout || \"stacked\"}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {(field.type === \"date-picker\" || field.type === \"date\") && (\n <DatePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"date-range\" && (\n <DateRangePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"time\" && (\n <TimePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"file\" && (\n <FileInput\n {...formField}\n id={fieldId}\n accept={field.accept}\n maxSize={field.maxSize || 5 * 1024 * 1024}\n maxFiles={field.maxFiles || 1}\n multiple={field.multiple || false}\n placeholder={field.placeholder || \"Choose file(s)...\"}\n error={meta.touched && !!meta.error}\n disabled={field.disabled || isUploading}\n showProgress\n uploadProgress={uploadProgress}\n onChange={(files) => {\n formField.onChange(files);\n if (files.length > 0 && onFileUpload) {\n onFileUpload(files);\n }\n }}\n onFileRemove={onFileRemove}\n aria-label={field.label}\n />\n )}\n </div>\n )}\n </Field>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../components/ui/button\";\nimport { Form } from \"../core/Form\";\nimport { DynamicFormField } from \"./DynamicFormField\";\nimport { getColumnSpanClass } from \"./form-field-types\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport type { FormRenderConfig } from \"../core/types\";\nimport type { PageSpeedFormConfig } from \"./form-submit\";\nimport { useContactForm } from \"./use-contact-form\";\nimport { useFileUpload } from \"./use-file-upload\";\n\n// ─── Default Values ───────────────────────────────────────────────────────────\n\nconst DEFAULT_STYLE_RULES: FormEngineStyleRules = {\n formContainer: \"\",\n fieldsContainer: \"\",\n fieldClassName: \"\",\n formClassName: \"\",\n successMessageClassName:\n \"text-primary-foreground mt-4 p-3 rounded-md shadow-md bg-primary\",\n errorMessageClassName:\n \"text-destructive-foreground mt-4 p-3 rounded-md shadow-md bg-destructive\",\n};\n\nconst DEFAULT_SUBMIT_LABEL = \"Submit\";\nconst DEFAULT_BUTTON_GROUP_LABEL = \"Subscribe\";\nconst DEFAULT_BUTTON_VARIANT = \"default\";\nconst DEFAULT_BUTTON_GROUP_SIZE = \"default\";\n\n// ─── Setup / Style Types ──────────────────────────────────────────────────────\n\nexport interface ButtonGroupFormSetup {\n size?: \"xs\" | \"sm\" | \"default\" | \"lg\";\n submitLabel?: React.ReactNode;\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\";\n submitIconName?: string;\n submitIconComponent?: React.ReactNode;\n}\n\nexport interface FormEngineSubmitButtonSetup {\n submitLabel?: React.ReactNode;\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\";\n submitIconName?: string;\n submitIconComponent?: React.ReactNode;\n}\n\nexport interface FormEngineStyleRules {\n /** ClassName applied to the div wrapping the `<form>` element */\n formContainer?: string;\n /** ClassName applied to the grid div wrapping all field columns (standard layout) */\n fieldsContainer?: string;\n /** Fallback className applied to each field wrapper when the field has no own className */\n fieldClassName?: string;\n /** className forwarded to the `<form>` element itself via FormStyleConfig */\n formClassName?: string;\n /** className forwarded to the success message container via FormStyleConfig */\n successMessageClassName?: string;\n /** className forwarded to the error message container via FormStyleConfig */\n errorMessageClassName?: string;\n}\n\nexport interface FormEngineLayoutSettings {\n styleRules?: FormEngineStyleRules;\n formLayout?: \"standard\" | \"button-group\";\n /** Settings for button-group layout (only used when formLayout is \"button-group\") */\n buttonGroupSetup?: ButtonGroupFormSetup;\n /** Settings for the submit button in standard layout */\n submitButtonSetup?: FormEngineSubmitButtonSetup;\n}\n\nexport interface FormEngineSetup {\n /** API / submission configuration */\n api?: PageSpeedFormConfig;\n /** Form field definitions */\n fields?: FormFieldConfig[];\n /** Layout, style, and submit-button settings */\n formLayoutSettings?: FormEngineLayoutSettings;\n /** Success message shown after a successful submission */\n successMessage?: React.ReactNode;\n /** Custom submit handler (called in addition to any api endpoint) */\n onSubmit?: (values: Record<string, any>) => void | Promise<void>;\n /** Called after a successful submission with the server response */\n onSuccess?: (data: unknown) => void;\n /** Called when submission fails */\n onError?: (error: Error) => void;\n /** Navigation handler for internal redirects (return false to fall back to browser navigation) */\n navigate?: (href: string) => boolean | void;\n /** Reset form values after success @default true */\n resetOnSuccess?: boolean;\n /** File upload tokens to merge into the payload */\n uploadTokens?: string[];\n /** Called when files are selected for upload */\n onFileUpload?: (files: File[]) => Promise<void>;\n /** Called when a file is removed */\n onFileRemove?: (file: File, index: number) => void;\n /** Whether a file upload is in progress */\n isUploading?: boolean;\n /** Per-file upload progress map (fileName → 0-100) */\n uploadProgress?: { [fileName: string]: number };\n}\n\nexport interface FormEngineProps extends FormEngineSetup {\n /**\n * Optional wrapper object used by block libraries to pass the full setup as a\n * single prop. Direct props on FormEngine take precedence when both are provided.\n */\n formEngineSetup?: FormEngineSetup;\n /** Default form field definitions used when setup fields are missing/empty */\n defaultFields?: FormFieldConfig[];\n /** Default style rules merged before built-in FormEngine defaults */\n defaultStyleRules?: FormEngineStyleRules;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\n/**\n * FormEngine — declarative form component with built-in API integration.\n *\n * Handles `useContactForm` orchestration internally so callers can supply\n * either direct props or a `formEngineSetup` wrapper plus optional defaults.\n *\n * @example Standard layout\n * ```tsx\n * <FormEngine api={api} fields={fields} formLayoutSettings={{ submitButtonSetup: { submitLabel: \"Send\" } }} />\n * ```\n *\n * @example Wrapped setup with defaults\n * ```tsx\n * <FormEngine\n * formEngineSetup={{ api, fields: [emailField] }}\n * defaultFields={fallbackFields}\n * defaultStyleRules={fallbackStyleRules}\n * />\n * ```\n */\nexport function FormEngine(props: FormEngineProps) {\n const {\n formEngineSetup,\n defaultFields,\n defaultStyleRules,\n api: directApi,\n fields: directFields,\n formLayoutSettings: directFormLayoutSettings,\n successMessage: directSuccessMessage,\n onSubmit: directOnSubmit,\n onSuccess: directOnSuccess,\n onError: directOnError,\n navigate: directNavigate,\n resetOnSuccess: directResetOnSuccess,\n uploadTokens: directUploadTokens,\n onFileUpload: directOnFileUpload,\n onFileRemove: directOnFileRemove,\n isUploading: directIsUploading,\n uploadProgress: directUploadProgress,\n } = props;\n\n const api = directApi ?? formEngineSetup?.api;\n const fields = directFields ?? formEngineSetup?.fields;\n const formLayoutSettings =\n directFormLayoutSettings ?? formEngineSetup?.formLayoutSettings;\n const successMessage =\n directSuccessMessage ?? formEngineSetup?.successMessage;\n const onSubmit = directOnSubmit ?? formEngineSetup?.onSubmit;\n const onSuccess = directOnSuccess ?? formEngineSetup?.onSuccess;\n const onError = directOnError ?? formEngineSetup?.onError;\n const navigate = directNavigate ?? formEngineSetup?.navigate;\n const resetOnSuccess =\n directResetOnSuccess ?? formEngineSetup?.resetOnSuccess;\n const externalUploadTokens =\n directUploadTokens ?? formEngineSetup?.uploadTokens;\n const externalOnFileUpload =\n directOnFileUpload ?? formEngineSetup?.onFileUpload;\n const externalOnFileRemove =\n directOnFileRemove ?? formEngineSetup?.onFileRemove;\n const externalIsUploading = directIsUploading ?? formEngineSetup?.isUploading;\n const externalUploadProgress =\n directUploadProgress ?? formEngineSetup?.uploadProgress;\n\n const {\n styleRules: userStyleRules,\n formLayout = \"standard\",\n buttonGroupSetup,\n submitButtonSetup,\n } = formLayoutSettings ?? {};\n const isButtonGroup = formLayout === \"button-group\";\n\n const formFields = React.useMemo<FormFieldConfig[]>(() => {\n if (fields && fields.length > 0) return fields;\n if (defaultFields && defaultFields.length > 0) return defaultFields;\n return [];\n }, [fields, defaultFields]);\n\n // Merge style rules in order: user setup -> block defaults -> built-in defaults\n const styleRules = React.useMemo<FormEngineStyleRules>(\n () => ({\n formContainer:\n userStyleRules?.formContainer ??\n defaultStyleRules?.formContainer ??\n DEFAULT_STYLE_RULES.formContainer,\n fieldsContainer:\n userStyleRules?.fieldsContainer ??\n defaultStyleRules?.fieldsContainer ??\n DEFAULT_STYLE_RULES.fieldsContainer,\n fieldClassName:\n userStyleRules?.fieldClassName ??\n defaultStyleRules?.fieldClassName ??\n DEFAULT_STYLE_RULES.fieldClassName,\n formClassName:\n userStyleRules?.formClassName ??\n defaultStyleRules?.formClassName ??\n DEFAULT_STYLE_RULES.formClassName,\n successMessageClassName:\n userStyleRules?.successMessageClassName ??\n defaultStyleRules?.successMessageClassName ??\n DEFAULT_STYLE_RULES.successMessageClassName,\n errorMessageClassName:\n userStyleRules?.errorMessageClassName ??\n defaultStyleRules?.errorMessageClassName ??\n DEFAULT_STYLE_RULES.errorMessageClassName,\n }),\n [userStyleRules, defaultStyleRules],\n );\n\n // Integrate file upload functionality\n const {\n uploadTokens: internalUploadTokens,\n uploadProgress: internalUploadProgress,\n isUploading: internalIsUploading,\n uploadFiles: internalUploadFiles,\n removeFile: internalRemoveFile,\n resetUpload,\n } = useFileUpload({ onError });\n\n // Use external upload state if provided, otherwise use internal\n const uploadTokens = externalUploadTokens ?? internalUploadTokens;\n const uploadProgress = externalUploadProgress ?? internalUploadProgress;\n const isUploading = externalIsUploading ?? internalIsUploading;\n const onFileUpload = externalOnFileUpload ?? internalUploadFiles;\n const onFileRemove = externalOnFileRemove ?? internalRemoveFile;\n\n const { form, submissionError, formMethod, resetSubmissionState } =\n useContactForm({\n formFields,\n formConfig: api,\n onSubmit,\n onSuccess: (data) => {\n resetUpload();\n onSuccess?.(data);\n },\n onError,\n navigate,\n resetOnSuccess,\n uploadTokens,\n });\n\n // Map FormEngineLayoutSettings → FormRenderConfig for the legacy Form component\n const legacyFormConfig = React.useMemo<FormRenderConfig>(() => {\n if (isButtonGroup) {\n return {\n formLayout: \"button-group\",\n buttonGroupSize: buttonGroupSetup?.size ?? DEFAULT_BUTTON_GROUP_SIZE,\n submitLabel:\n buttonGroupSetup?.submitLabel ?? DEFAULT_BUTTON_GROUP_LABEL,\n submitVariant:\n buttonGroupSetup?.submitVariant ?? DEFAULT_BUTTON_VARIANT,\n submitIconName: buttonGroupSetup?.submitIconName,\n submitIconComponent: buttonGroupSetup?.submitIconComponent,\n endpoint: api?.endpoint,\n submissionConfig: api?.submissionConfig,\n };\n }\n return {\n formLayout: \"standard\",\n endpoint: api?.endpoint,\n submissionConfig: api?.submissionConfig,\n };\n }, [isButtonGroup, buttonGroupSetup, api]);\n\n return (\n <div className={styleRules?.formContainer}>\n <Form\n form={form}\n fields={isButtonGroup ? formFields : undefined}\n formConfig={legacyFormConfig}\n method={formMethod}\n notificationConfig={{\n submissionError: submissionError ?? undefined,\n successMessage,\n }}\n styleConfig={{\n formClassName: styleRules?.formClassName,\n successMessageClassName: styleRules?.successMessageClassName,\n errorMessageClassName: styleRules?.errorMessageClassName,\n }}\n onNewSubmission={resetSubmissionState}\n >\n {!isButtonGroup && (\n <>\n <div\n className={cn(\n \"grid grid-cols-12 gap-6 md:gap-10\",\n styleRules?.fieldsContainer,\n )}\n >\n {formFields.map((field) => (\n <div\n key={field.name}\n className={cn(\n getColumnSpanClass(field.columnSpan ?? 12),\n \"min-w-0\",\n )}\n >\n <DynamicFormField\n field={field}\n className={field.className ?? styleRules?.fieldClassName}\n uploadProgress={uploadProgress}\n onFileUpload={onFileUpload}\n onFileRemove={onFileRemove}\n isUploading={isUploading}\n />\n </div>\n ))}\n </div>\n <Button\n type=\"submit\"\n variant={\n submitButtonSetup?.submitVariant ?? DEFAULT_BUTTON_VARIANT\n }\n disabled={form.isSubmitting}\n className=\"mt-6 w-full\"\n >\n {submitButtonSetup?.submitLabel ?? DEFAULT_SUBMIT_LABEL}\n </Button>\n </>\n )}\n </Form>\n </div>\n );\n}\n\nFormEngine.displayName = \"FormEngine\";\n"]}
1
+ {"version":3,"sources":["../src/integration/ContactFormSerializer.ts","../src/integration/BlockAdapter.tsx","../src/integration/form-submit.ts","../src/integration/form-field-types.ts","../src/integration/use-file-upload.ts","../src/integration/use-contact-form.ts","../src/integration/DynamicFormField.tsx","../src/integration/FormEngine.tsx"],"names":["camelField","errorMessage","React","Component","response","data","useState","useCallback","React3"],"mappings":"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","file":"integration.js","sourcesContent":["/**\n * @page-speed/forms - Rails API Serializer\n *\n * Serializes form data for the DashTrack ContactsController API.\n * Handles field name conversion (camelCase → snake_case), custom fields separation,\n * and upload token arrays.\n *\n * @see https://github.com/opensite-ai/page-speed-forms\n */\n\n/**\n * Standard fields recognized by the Rails ContactsController API.\n * These are serialized to snake_case and sent in the contact object.\n */\nconst STANDARD_FIELDS = [\n \"content\",\n \"email\",\n \"firstName\",\n \"lastName\",\n \"locationId\",\n \"phone\",\n \"subject\",\n \"redemptionStatus\",\n \"birthday\",\n \"city\",\n \"state\",\n \"websiteFormAssignmentId\",\n \"websiteId\",\n \"acceptsSmsMarketing\",\n \"acceptsEmailMarketing\",\n \"visitorIpAddress\",\n] as const;\n\n/**\n * Configuration parameters for Rails API submission.\n */\nexport interface RailsApiConfig {\n /**\n * API key for authentication.\n * Sent as top-level parameter: api_key\n */\n apiKey: string;\n\n /**\n * Contact category token for categorization.\n * Sent as top-level parameter: contact_category_token\n */\n contactCategoryToken?: string;\n\n /**\n * Location ID for multi-location organizations.\n * Sent as top-level parameter: location_id\n */\n locationId?: string;\n\n /**\n * Website ID for tracking form submissions.\n * Sent within contact object: website_id\n */\n websiteId?: string;\n\n /**\n * Website form assignment ID for form tracking.\n * Sent within contact object: website_form_assignment_id\n */\n websiteFormAssignmentId?: string;\n\n /**\n * Visitor IP address. If not provided, will be auto-detected on server.\n * Sent within contact object: visitor_ip_address\n */\n visitorIpAddress?: string;\n}\n\n/**\n * Serialized form data ready for Rails API submission.\n */\nexport interface SerializedFormData {\n /**\n * Top-level API key parameter.\n */\n api_key: string;\n\n /**\n * Top-level contact category token (optional).\n */\n contact_category_token?: string;\n\n /**\n * Top-level location ID (optional).\n */\n location_id?: string;\n\n /**\n * Contact object with standard fields and metadata.\n */\n contact: {\n /**\n * Standard contact fields in snake_case.\n */\n [key: string]: unknown;\n\n /**\n * Custom fields that don't match standard schema.\n * Stored as separate hash in Rails.\n */\n custom_fields?: Record<string, unknown>;\n\n /**\n * Array of upload tokens from file uploads.\n * These reference ContactFormUpload records in Rails.\n */\n contact_form_upload_tokens?: string[];\n };\n}\n\n/**\n * Form values from the form library (camelCase keys).\n */\nexport type FormValues = Record<string, unknown>;\n\n/**\n * Convert camelCase to snake_case.\n *\n * @example\n * ```ts\n * camelToSnake(\"firstName\") // \"first_name\"\n * camelToSnake(\"acceptsSmsMarketing\") // \"accepts_sms_marketing\"\n * ```\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Check if a field name is a standard Rails contact field.\n */\nfunction isStandardField(fieldName: string): boolean {\n return STANDARD_FIELDS.includes(\n fieldName as (typeof STANDARD_FIELDS)[number],\n );\n}\n\n/**\n * Extract upload tokens from form values.\n * Handles both string tokens and arrays of tokens.\n */\nfunction extractUploadTokens(values: FormValues): string[] {\n const tokens: string[] = [];\n\n for (const value of Object.values(values)) {\n if (typeof value === \"string\" && value.startsWith(\"upload_\")) {\n tokens.push(value);\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (typeof item === \"string\" && item.startsWith(\"upload_\")) {\n tokens.push(item);\n }\n }\n }\n }\n\n return tokens;\n}\n\n/**\n * Format date/time values for Rails API.\n * Rails expects ISO 8601 format.\n */\nfunction formatDateForRails(value: unknown): string | undefined {\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === \"string\") {\n // Attempt to parse and reformat to ensure valid ISO 8601\n const date = new Date(value);\n if (!isNaN(date.getTime())) {\n return date.toISOString();\n }\n }\n return undefined;\n}\n\n/**\n * Serialize form values for Rails ContactsController API.\n *\n * This function:\n * 1. Converts camelCase field names to snake_case\n * 2. Separates standard fields from custom fields\n * 3. Extracts upload tokens into contact_form_upload_tokens array\n * 4. Formats dates to ISO 8601\n * 5. Includes API configuration parameters\n *\n * @param values - Form values from useForm hook (camelCase keys)\n * @param config - Rails API configuration (apiKey, locationId, etc.)\n * @returns Serialized data ready for POST to /contacts\n *\n * @example\n * ```ts\n * const serialized = serializeForRails(\n * {\n * firstName: \"John\",\n * lastName: \"Doe\",\n * email: \"john@example.com\",\n * phone: \"555-1234\",\n * companySize: \"50-100\", // Custom field\n * resumeToken: \"upload_abc123\",\n * },\n * {\n * apiKey: \"key_123\",\n * contactCategoryToken: \"cat_xyz\",\n * locationId: \"loc_456\",\n * }\n * );\n *\n * // Result:\n * // {\n * // api_key: \"key_123\",\n * // contact_category_token: \"cat_xyz\",\n * // location_id: \"loc_456\",\n * // contact: {\n * // first_name: \"John\",\n * // last_name: \"Doe\",\n * // email: \"john@example.com\",\n * // phone: \"555-1234\",\n * // custom_fields: {\n * // company_size: \"50-100\"\n * // },\n * // contact_form_upload_tokens: [\"upload_abc123\"]\n * // }\n * // }\n * ```\n */\nexport function serializeForRails(\n values: FormValues,\n config: RailsApiConfig,\n): SerializedFormData {\n const standardFields: Record<string, unknown> = {};\n const customFields: Record<string, unknown> = {};\n\n // Extract upload tokens\n const uploadTokens = extractUploadTokens(values);\n\n // Separate standard and custom fields\n for (const [key, value] of Object.entries(values)) {\n // Skip upload token fields - they're handled separately\n if (typeof value === \"string\" && value.startsWith(\"upload_\")) {\n continue;\n }\n if (\n Array.isArray(value) &&\n value.every(\n (item) => typeof item === \"string\" && item.startsWith(\"upload_\"),\n )\n ) {\n continue;\n }\n\n const snakeKey = camelToSnake(key);\n\n if (isStandardField(key)) {\n // Format dates for birthday field\n if (key === \"birthday\") {\n const formatted = formatDateForRails(value);\n if (formatted) {\n standardFields[snakeKey] = formatted;\n }\n } else {\n // Handle array values for standard fields\n // Standard fields expect scalar values (strings), but some field types\n // like checkbox-group produce arrays. Convert arrays to comma-separated strings.\n if (Array.isArray(value)) {\n standardFields[snakeKey] = value.join(\", \");\n } else {\n standardFields[snakeKey] = value;\n }\n }\n } else {\n // Custom fields\n customFields[snakeKey] = value;\n }\n }\n\n // Add config fields to standard fields if provided\n if (config.websiteId !== undefined) {\n standardFields.website_id = config.websiteId;\n }\n if (config.websiteFormAssignmentId !== undefined) {\n standardFields.website_form_assignment_id = config.websiteFormAssignmentId;\n }\n if (config.visitorIpAddress !== undefined) {\n standardFields.visitor_ip_address = config.visitorIpAddress;\n }\n\n // Build contact object\n const contact: SerializedFormData[\"contact\"] = {\n ...standardFields,\n };\n\n // Add custom fields if any\n if (Object.keys(customFields).length > 0) {\n contact.custom_fields = customFields;\n }\n\n // Add upload tokens if any\n if (uploadTokens.length > 0) {\n contact.contact_form_upload_tokens = uploadTokens;\n }\n\n // Build final serialized data\n const serialized: SerializedFormData = {\n api_key: config.apiKey,\n contact,\n };\n\n // Add optional top-level parameters\n if (config.contactCategoryToken !== undefined) {\n serialized.contact_category_token = config.contactCategoryToken;\n }\n if (config.locationId !== undefined) {\n serialized.location_id = config.locationId;\n }\n\n return serialized;\n}\n\n/**\n * Rails API error response format.\n */\nexport interface RailsErrorResponse {\n errors: {\n [field: string]: string[];\n };\n status: number;\n}\n\n/**\n * Form error format used by @page-speed/forms.\n */\nexport type FormErrors = Record<string, string | undefined>;\n\n/**\n * Convert snake_case to camelCase.\n *\n * @example\n * ```ts\n * snakeToCamel(\"first_name\") // \"firstName\"\n * snakeToCamel(\"accepts_sms_marketing\") // \"acceptsSmsMarketing\"\n * ```\n */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/**\n * Deserialize Rails API errors to form error format.\n *\n * Converts Rails error format to the format expected by @page-speed/forms:\n * - Maps snake_case field names to camelCase\n * - Flattens error arrays to single error string (first error)\n * - Handles custom_fields errors by mapping back to original field names\n * - Extracts base errors to form-level errors\n *\n * @param railsErrors - Error response from Rails API\n * @returns Form errors object (camelCase keys)\n *\n * @example\n * ```ts\n * const formErrors = deserializeErrors({\n * errors: {\n * first_name: [\"can't be blank\", \"is too short\"],\n * email: [\"is invalid\"],\n * custom_fields: {\n * company_size: [\"is required\"]\n * },\n * base: [\"Something went wrong\"]\n * },\n * status: 422\n * });\n *\n * // Result:\n * // {\n * // firstName: \"can't be blank\",\n * // email: \"is invalid\",\n * // companySize: \"is required\",\n * // _form: \"Something went wrong\"\n * // }\n * ```\n */\nexport function deserializeErrors(railsErrors: RailsErrorResponse): FormErrors {\n const formErrors: FormErrors = {};\n\n for (const [field, messages] of Object.entries(railsErrors.errors)) {\n // Handle base errors (form-level errors)\n if (field === \"base\") {\n formErrors._form = Array.isArray(messages) ? messages[0] : messages;\n continue;\n }\n\n // Handle custom_fields errors\n if (field === \"custom_fields\" && typeof messages === \"object\") {\n for (const [customField, customMessages] of Object.entries(messages)) {\n const camelField = snakeToCamel(customField);\n const errorMessage = Array.isArray(customMessages)\n ? customMessages[0]\n : customMessages;\n formErrors[camelField] = errorMessage;\n }\n continue;\n }\n\n // Handle standard field errors\n const camelField = snakeToCamel(field);\n const errorMessage = Array.isArray(messages) ? messages[0] : messages;\n formErrors[camelField] = errorMessage;\n }\n\n return formErrors;\n}\n","/**\n * @page-speed/forms - Block Adapter\n *\n * Adapts form components for use in block-based rendering systems.\n * Wraps form components to accept block prop structure and transforms them\n * to component-native props.\n *\n * @see https://github.com/opensite-ai/page-speed-forms\n */\n\n\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * Block structure for design payloads.\n * Minimal type definition for adapter compatibility.\n */\nexport interface Block {\n _id: string;\n _type: string;\n _name?: string;\n _parent?: string | null;\n tag?: string;\n styles?: string;\n content?: string;\n blockProps?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Options for Block adapter.\n */\nexport interface BlockAdapterOptions {\n /**\n * Default props to merge with block props.\n */\n defaultProps?: Record<string, unknown>;\n\n /**\n * Transform function to convert Block props to component props.\n * Useful for custom prop mapping logic.\n */\n transformProps?: (\n blockProps: Record<string, unknown>,\n block: Block,\n ) => Record<string, unknown>;\n\n /**\n * Extract display name from block for debugging.\n * Defaults to using _name or _type from block.\n */\n getDisplayName?: (block: Block) => string;\n\n /**\n * Enable React error boundary wrapping.\n * Defaults to true.\n */\n withErrorBoundary?: boolean;\n\n /**\n * Custom error fallback component.\n * If not provided, renders basic error message.\n */\n errorFallback?: (error: Error, block: Block) => React.ReactNode;\n}\n\n/**\n * Props passed to adapted component.\n */\nexport interface AdaptedComponentProps {\n /**\n * Block data from design payload.\n */\n block: Block;\n\n /**\n * Child blocks for rendering nested content.\n * Used by container components like Form.\n */\n children?: React.ReactNode;\n\n /**\n * Callback to render child blocks.\n * Provided by block rendering systems.\n */\n renderChildren?: (blockId: string) => React.ReactNode;\n}\n\n/**\n * Error boundary component for catching render errors.\n */\nclass BlockErrorBoundary extends React.Component<\n {\n block: Block;\n fallback?: (error: Error, block: Block) => React.ReactNode;\n children: React.ReactNode;\n },\n { error: Error | null }\n> {\n constructor(props: BlockErrorBoundary[\"props\"]) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(\n `Block render error (${this.props.block._id}):`,\n error,\n errorInfo,\n );\n }\n\n render() {\n if (this.state.error) {\n if (this.props.fallback) {\n return this.props.fallback(this.state.error, this.props.block);\n }\n\n return (\n <div\n className=\"block-error border border-destructive bg-destructive p-4 rounded text-destructive-foreground\"\n data-block-id={this.props.block._id}\n data-block-type={this.props.block._type}\n >\n <p className=\"font-semibold\">Block Render Error</p>\n <p className=\"text-sm\">\n Block: {this.props.block._name || this.props.block._id} (\n {this.props.block._type})\n </p>\n <p className=\"text-sm mt-1\">{this.state.error.message}</p>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Create a Block-compatible wrapper for a form component.\n *\n * This adapter transforms Block props (from design payload) into\n * component-native props. It handles:\n * - Extracting props from `blockProps` field\n * - Merging with default props\n * - Custom prop transformation\n * - Error boundary wrapping\n * - Children rendering support\n *\n * @param Component - React component to adapt\n * @param options - Adapter configuration options\n * @returns Block-compatible component\n *\n * @example\n * ```tsx\n * import { TextInput } from \"@page-speed/forms/inputs\";\n * import { createBlockAdapter } from \"@page-speed/forms/integration\";\n *\n * // Create Block-compatible TextInput\n * const BlockTextInput = createBlockAdapter(TextInput, {\n * defaultProps: {\n * placeholder: \"Enter text...\",\n * },\n * transformProps: (blockProps, block) => ({\n * ...blockProps,\n * // Map Block content to component props\n * label: block.content || blockProps.label,\n * // Apply Block styles as className\n * className: block.styles,\n * }),\n * });\n *\n * // Register with rendering system\n * registerBlockType(\"TextInput\", BlockTextInput);\n * ```\n *\n * @example\n * ```tsx\n * // Block from design payload:\n * {\n * _id: \"field-email\",\n * _type: \"TextInput\",\n * _name: \"Email Field\",\n * content: \"Email Address\",\n * styles: \"w-full mb-4\",\n * blockProps: {\n * name: \"email\",\n * type: \"email\",\n * placeholder: \"you@example.com\",\n * required: true,\n * }\n * }\n *\n * // Transformed to TextInput props:\n * <TextInput\n * name=\"email\"\n * type=\"email\"\n * placeholder=\"you@example.com\"\n * required={true}\n * label=\"Email Address\"\n * className=\"w-full mb-4\"\n * />\n * ```\n */\nexport function createBlockAdapter<TProps extends Record<string, unknown>>(\n Component: React.ComponentType<TProps>,\n options: BlockAdapterOptions = {},\n): React.ComponentType<AdaptedComponentProps> {\n const {\n defaultProps = {},\n transformProps,\n withErrorBoundary = true,\n errorFallback,\n } = options;\n\n const AdaptedComponent: React.FC<AdaptedComponentProps> = ({\n block,\n children,\n renderChildren,\n }) => {\n // Extract component props from blockProps\n const blockProps = (block.blockProps || {}) as Record<string, unknown>;\n\n // Merge with default props\n const mergedProps = { ...defaultProps, ...blockProps };\n\n // Apply custom transformation if provided\n const finalProps = transformProps\n ? transformProps(mergedProps, block)\n : mergedProps;\n\n // Add data attributes for debugging\n const dataAttrs = {\n \"data-block-id\": block._id,\n \"data-block-type\": block._type,\n ...(block._name && { \"data-block-name\": block._name }),\n };\n\n // Merge data attributes with final props\n const componentProps = {\n ...finalProps,\n ...dataAttrs,\n } as unknown as TProps;\n\n // Render children if renderChildren callback provided\n const renderedChildren = renderChildren\n ? renderChildren(block._id)\n : children;\n\n const element = (\n <Component {...componentProps}>{renderedChildren}</Component>\n );\n\n // Wrap with error boundary if enabled\n if (withErrorBoundary) {\n return (\n <BlockErrorBoundary block={block} fallback={errorFallback}>\n {element}\n </BlockErrorBoundary>\n );\n }\n\n return element;\n };\n\n // Set display name for debugging\n const componentName = Component.displayName || Component.name || \"Component\";\n AdaptedComponent.displayName = `BlockAdapter(${componentName})`;\n\n return AdaptedComponent;\n}\n\n/**\n * Standard prop transformer for form input components.\n *\n * Applies common transformations:\n * - Maps Block `content` to `label`\n * - Maps Block `styles` to `className`\n * - Preserves all blockProps\n *\n * @param blockProps - Props from Block.blockProps\n * @param block - Full Block object\n * @returns Transformed props for component\n *\n * @example\n * ```tsx\n * const BlockTextInput = createBlockAdapter(TextInput, {\n * transformProps: standardInputTransformer,\n * });\n * ```\n */\nexport function standardInputTransformer(\n blockProps: Record<string, unknown>,\n block: Block,\n): Record<string, unknown> {\n return {\n ...blockProps,\n // Use content as label if not already provided\n ...(block.content && !blockProps.label && { label: block.content }),\n // Apply Block styles as className\n ...(block.styles && { className: block.styles }),\n };\n}\n\n/**\n * Create multiple Block adapters with shared options.\n *\n * Convenience function for adapting multiple components at once\n * with the same configuration.\n *\n * @param components - Record of component name to component\n * @param options - Shared adapter options\n * @returns Record of adapted components\n *\n * @example\n * ```tsx\n * import * as Inputs from \"@page-speed/forms/inputs\";\n * import { createBlockAdapters, standardInputTransformer } from \"@page-speed/forms/integration\";\n *\n * const BlockInputs = createBlockAdapters(\n * {\n * TextInput: Inputs.TextInput,\n * TextArea: Inputs.TextArea,\n * Select: Inputs.Select,\n * },\n * {\n * transformProps: standardInputTransformer,\n * withErrorBoundary: true,\n * }\n * );\n *\n * // BlockInputs.TextInput, BlockInputs.TextArea, BlockInputs.Select\n * // are now Block-compatible\n * ```\n */\nexport function createBlockAdapters<\n TComponents extends Record<string, React.ComponentType<any>>,\n>(\n components: TComponents,\n options: BlockAdapterOptions = {},\n): Record<keyof TComponents, React.ComponentType<AdaptedComponentProps>> {\n const adapted: Record<\n string,\n React.ComponentType<AdaptedComponentProps>\n > = {};\n\n for (const [name, component] of Object.entries(components)) {\n adapted[name] = createBlockAdapter(component, options);\n }\n\n return adapted as Record<\n keyof TComponents,\n React.ComponentType<AdaptedComponentProps>\n >;\n}\n","import type {\n FormLayoutSettings,\n FormSubmissionConfig,\n} from \"../core/types\";\nimport {\n deserializeErrors,\n serializeForRails,\n type FormErrors,\n type RailsApiConfig,\n type RailsErrorResponse,\n} from \"./ContactFormSerializer\";\n\nexport type PageSpeedFormMethod = \"post\" | \"get\" | \"put\" | \"patch\";\nexport type PageSpeedFormSubmissionFormat = \"json\" | \"rails\";\n\nexport interface PageSpeedFormSubmissionResult {\n formData: Record<string, any>;\n responseData: unknown;\n}\n\n/**\n * PageSpeed-specific extension of the core FormSubmissionConfig.\n * Inherits behavior, customComponent, and newFormSubmissionAction from the\n * base type and adds integration-layer callbacks and redirect support.\n */\nexport interface PageSpeedFormSubmissionConfig extends FormSubmissionConfig {\n /**\n * Optional callback triggered on successful submission.\n */\n handleFormSubmission?: (\n result: PageSpeedFormSubmissionResult,\n ) => void | Promise<void>;\n\n /**\n * Redirect destination used when behavior is \"redirect\".\n */\n redirectUrl?: string;\n}\n\nexport interface PageSpeedFormConfig {\n /**\n * API endpoint used for submission (also applied to form action).\n */\n endpoint?: string;\n /**\n * HTTP method for submission.\n * @default \"post\"\n */\n method?: PageSpeedFormMethod;\n /**\n * Submission format.\n * Defaults to \"rails\" when apiKey is present, otherwise \"json\".\n */\n format?: PageSpeedFormSubmissionFormat;\n /**\n * Additional headers for the submission request.\n */\n headers?: Record<string, string>;\n /**\n * Static values merged into the payload (e.g. subject, content).\n */\n values?: Record<string, unknown>;\n /**\n * Rails API key (required for rails format).\n */\n apiKey?: string;\n /**\n * Rails contact category token.\n */\n contactCategoryToken?: string;\n /**\n * Rails location ID.\n */\n locationId?: string;\n /**\n * Rails website ID.\n */\n websiteId?: string;\n /**\n * Rails website form assignment ID.\n */\n websiteFormAssignmentId?: string;\n /**\n * Rails visitor IP address override.\n */\n visitorIpAddress?: string;\n /**\n * Reset form values after a successful submission.\n * @default true\n */\n resetOnSuccess?: boolean;\n /**\n * Optional post-submission behavior configuration.\n */\n submissionConfig?: PageSpeedFormSubmissionConfig;\n\n /**\n * Optional layout and presentation settings.\n * Provides a typed home for layout props (formLayout, buttonGroupSize, etc.)\n * so consumers don't need an `as any` cast when passing them alongside API config.\n */\n formLayoutSettings?: FormLayoutSettings;\n}\n\nexport class PageSpeedFormSubmissionError extends Error {\n formErrors?: FormErrors;\n status?: number;\n\n constructor(\n message: string,\n options: { formErrors?: FormErrors; status?: number } = {},\n ) {\n super(message);\n this.name = \"PageSpeedFormSubmissionError\";\n this.formErrors = options.formErrors;\n this.status = options.status;\n }\n}\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport function isValidEmail(value: string): boolean {\n return EMAIL_REGEX.test(value);\n}\n\nfunction buildUrlWithParams(\n endpoint: string,\n values: Record<string, any>,\n): string {\n const base =\n typeof window === \"undefined\" ? \"http://localhost\" : window.location.origin;\n const url = new URL(endpoint, base);\n\n Object.entries(values).forEach(([key, value]) => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n url.searchParams.append(key, String(item));\n }\n });\n return;\n }\n\n url.searchParams.set(key, String(value));\n });\n\n return url.toString();\n}\n\nfunction normalizeMethod(method?: PageSpeedFormMethod): string {\n return (method || \"post\").toUpperCase();\n}\n\nfunction resolveFormat(\n config?: PageSpeedFormConfig,\n): PageSpeedFormSubmissionFormat {\n if (config?.format) return config.format;\n return config?.apiKey ? \"rails\" : \"json\";\n}\n\nfunction mergeValues(\n values: Record<string, any>,\n config?: PageSpeedFormConfig,\n): Record<string, any> {\n return {\n ...(config?.values ?? {}),\n ...values,\n };\n}\n\nexport async function submitPageSpeedForm(\n values: Record<string, any>,\n config?: PageSpeedFormConfig,\n): Promise<unknown> {\n if (!config?.endpoint) {\n return null;\n }\n\n const payload = mergeValues(values, config);\n const method = normalizeMethod(config.method);\n const format = resolveFormat(config);\n const headers: Record<string, string> = { ...(config.headers ?? {}) };\n\n if (format === \"rails\") {\n if (!config.apiKey) {\n throw new PageSpeedFormSubmissionError(\n \"Missing apiKey for Rails form submission.\",\n );\n }\n\n const railsConfig: RailsApiConfig = {\n apiKey: config.apiKey,\n contactCategoryToken: config.contactCategoryToken,\n locationId: config.locationId,\n websiteId: config.websiteId,\n websiteFormAssignmentId: config.websiteFormAssignmentId,\n visitorIpAddress: config.visitorIpAddress,\n };\n\n const serialized = serializeForRails(payload, railsConfig);\n\n if (serialized.contact.contact_form_upload_tokens) {\n serialized.contact.contact_form_upload_tokens = (\n serialized.contact.contact_form_upload_tokens as string[]\n ).map((token) => token.replace(/^upload_/, \"\"));\n }\n\n headers[\"Content-Type\"] ??= \"application/json\";\n\n const response = await fetch(config.endpoint, {\n method,\n headers,\n body: JSON.stringify(serialized),\n });\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok || (data && data.errors)) {\n const errorResponse: RailsErrorResponse = {\n errors: data?.errors ?? { base: [\"Form submission failed\"] },\n status: data?.status ?? response.status,\n };\n const formErrors = deserializeErrors(errorResponse);\n throw new PageSpeedFormSubmissionError(\"Form submission failed.\", {\n formErrors,\n status: errorResponse.status,\n });\n }\n\n return data;\n }\n\n if (method === \"GET\") {\n const url = buildUrlWithParams(config.endpoint, payload);\n const response = await fetch(url, { method, headers });\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n throw new PageSpeedFormSubmissionError(\n data?.message || \"Form submission failed.\",\n { status: response.status },\n );\n }\n\n return data;\n }\n\n headers[\"Content-Type\"] ??= \"application/json\";\n\n const response = await fetch(config.endpoint, {\n method,\n headers,\n body: JSON.stringify(payload),\n });\n\n const data = await response.json().catch(() => null);\n\n if (!response.ok) {\n throw new PageSpeedFormSubmissionError(\n data?.message || \"Form submission failed.\",\n { status: response.status },\n );\n }\n\n return data;\n}\n","/**\n * Dynamic form field schema types and helpers.\n *\n * These utilities are intentionally exposed from the integration layer so\n * block/rendering libraries can share one field schema contract.\n */\n\nimport { humanizeFieldName } from \"../lib/utils\";\n\nexport type FormFieldType =\n | \"text\"\n | \"email\"\n | \"search\"\n | \"password\"\n | \"tel\"\n | \"textarea\"\n | \"select\"\n | \"radio\"\n | \"checkbox\"\n | \"checkbox-group\"\n | \"number\"\n | \"url\"\n | \"date\"\n | \"date-picker\"\n | \"date-range\"\n | \"time\"\n | \"file\"\n | \"multi-select\";\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n description?: string;\n}\n\nexport interface FormFieldConfig {\n /**\n * Optionally displays label for the field\n */\n label?: string;\n /**\n * Unique field name (used as the key in form values)\n */\n name: string;\n /**\n * Field type\n */\n type: FormFieldType;\n\n /**\n * Placeholder text\n */\n placeholder?: string;\n /**\n * Whether the field is required\n * @default false\n */\n required?: boolean;\n /**\n * Column span in grid layout (1-12)\n * @default 12 (full width)\n */\n columnSpan?: number;\n /**\n * Options for select/radio/checkbox-group fields\n */\n options?: SelectOption[];\n /**\n * Number of rows for textarea\n * @default 4\n */\n rows?: number;\n /**\n * Custom validation function\n * Return undefined for valid, or an error message string for invalid\n */\n validator?: (\n value: any,\n allValues: Record<string, any>,\n ) => string | undefined;\n /**\n * Additional CSS classes for the field wrapper\n */\n className?: string;\n /**\n * Whether the field is disabled\n * @default false\n */\n disabled?: boolean;\n /**\n * Accepted file types for file inputs (MIME types or extensions)\n * @example \".pdf,.doc,.docx\"\n * @example \"image/*,application/pdf\"\n */\n accept?: string;\n /**\n * Maximum file size in bytes for file inputs\n * @default 5MB (5 * 1024 * 1024)\n */\n maxSize?: number;\n /**\n * Maximum number of files for file inputs\n * @default 1\n */\n maxFiles?: number;\n /**\n * Allow multiple file selection\n * @default false\n */\n multiple?: boolean;\n /**\n * Description/help text displayed with the field\n */\n description?: string;\n /**\n * Layout for radio/checkbox groups\n * @default \"stacked\"\n */\n layout?: \"grid\" | \"stacked\";\n}\n\n/**\n * Generate initial values object from form field configs.\n */\nexport function generateInitialValues(\n fields: FormFieldConfig[],\n): Record<string, any> {\n return fields.reduce(\n (acc, field) => {\n if (field.type === \"checkbox\") {\n acc[field.name] = false;\n } else if (\n field.type === \"checkbox-group\" ||\n field.type === \"multi-select\"\n ) {\n acc[field.name] = [];\n } else if (field.type === \"file\") {\n acc[field.name] = [];\n } else if (field.type === \"date-range\") {\n acc[field.name] = { start: null, end: null };\n } else {\n acc[field.name] = \"\";\n }\n return acc;\n },\n {} as Record<string, any>,\n );\n}\n\n/**\n * Generate validation schema from form field configs.\n */\nexport function generateValidationSchema(\n fields: FormFieldConfig[],\n): Record<\n string,\n (value: any, allValues: Record<string, any>) => string | undefined\n> {\n return fields.reduce(\n (acc, field) => {\n acc[field.name] = (value: any, allValues: Record<string, any>) => {\n if (field.required) {\n if (!value || (typeof value === \"string\" && !value.trim())) {\n const displayName = field.label || humanizeFieldName(field.name);\n return `${displayName} is required`;\n }\n }\n\n if (field.type === \"email\" && value) {\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value)) {\n return \"Please enter a valid email address\";\n }\n }\n\n if (field.type === \"url\" && value) {\n try {\n new URL(value);\n } catch {\n return \"Please enter a valid URL\";\n }\n }\n\n if (field.validator) {\n return field.validator(value, allValues);\n }\n\n return undefined;\n };\n return acc;\n },\n {} as Record<\n string,\n (value: any, allValues: Record<string, any>) => string | undefined\n >,\n );\n}\n\n/**\n * Static mapping of column span values to Tailwind classes.\n *\n * IMPORTANT: These must remain complete, literal class strings so Tailwind's\n * scanner can detect and generate them.\n */\nconst columnSpanClasses: Record<number, string> = {\n 1: \"col-span-12 md:col-span-1\",\n 2: \"col-span-12 md:col-span-2\",\n 3: \"col-span-12 md:col-span-3\",\n 4: \"col-span-12 md:col-span-4\",\n 5: \"col-span-12 md:col-span-5\",\n 6: \"col-span-12 md:col-span-6\",\n 7: \"col-span-12 md:col-span-7\",\n 8: \"col-span-12 md:col-span-8\",\n 9: \"col-span-12 md:col-span-9\",\n 10: \"col-span-12 md:col-span-10\",\n 11: \"col-span-12 md:col-span-11\",\n 12: \"col-span-12\",\n};\n\n/**\n * Get grid column span class for Tailwind.\n *\n * On small screens the field is always full-width, then from `md` and up the\n * configured span is applied.\n */\nexport function getColumnSpanClass(span?: number): string {\n if (!span || span === 12) return \"col-span-12\";\n const clamped = Math.max(1, Math.min(span, 12));\n return columnSpanClasses[clamped] || \"col-span-12\";\n}\n\n/**\n * Self-contained CSS that makes the 12-column form grid work even when\n * the consuming app's Tailwind build does not generate the col-span-*\n * and grid-cols-12 utility classes from this library's dist output.\n *\n * Uses data-attribute selectors so there is no collision with Tailwind.\n * When Tailwind IS present the rules are harmless duplicates.\n */\nexport const FORM_GRID_FALLBACK_CSS = `[data-psf-grid]{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:1.5rem}@media(min-width:768px){[data-psf-grid]{gap:2.5rem}}[data-psf-col]{grid-column:span 12/span 12;min-width:0}@media(min-width:768px){[data-psf-col=\"1\"]{grid-column:span 1/span 1}[data-psf-col=\"2\"]{grid-column:span 2/span 2}[data-psf-col=\"3\"]{grid-column:span 3/span 3}[data-psf-col=\"4\"]{grid-column:span 4/span 4}[data-psf-col=\"5\"]{grid-column:span 5/span 5}[data-psf-col=\"6\"]{grid-column:span 6/span 6}[data-psf-col=\"7\"]{grid-column:span 7/span 7}[data-psf-col=\"8\"]{grid-column:span 8/span 8}[data-psf-col=\"9\"]{grid-column:span 9/span 9}[data-psf-col=\"10\"]{grid-column:span 10/span 10}[data-psf-col=\"11\"]{grid-column:span 11/span 11}[data-psf-col=\"12\"]{grid-column:span 12/span 12}}`;\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\n\nexport interface FileUploadProgress {\n [fileName: string]: number;\n}\n\nexport interface UseFileUploadReturn {\n uploadTokens: string[];\n uploadProgress: FileUploadProgress;\n isUploading: boolean;\n uploadFiles: (files: File[]) => Promise<void>;\n removeFile: (file: File, index: number) => void;\n resetUpload: () => void;\n}\n\ninterface ContactFormUploadResponse {\n contact_form_upload?: {\n token?: string;\n };\n}\n\nexport interface UseFileUploadOptions {\n onError?: (error: Error) => void;\n /**\n * Upload endpoint.\n *\n * Defaults to DashTrack contact form uploads endpoint.\n */\n endpoint?: string;\n}\n\nconst DEFAULT_UPLOAD_ENDPOINT =\n \"https://api.dashtrack.com/contacts/_/contact_form_uploads\";\n\n/**\n * Upload helper for two-phase contact form uploads.\n */\nexport function useFileUpload(\n options?: UseFileUploadOptions,\n): UseFileUploadReturn {\n const [uploadTokens, setUploadTokens] = useState<string[]>([]);\n const [uploadProgress, setUploadProgress] = useState<FileUploadProgress>({});\n const [isUploading, setIsUploading] = useState(false);\n\n const endpoint = options?.endpoint || DEFAULT_UPLOAD_ENDPOINT;\n\n const uploadFiles = useCallback(\n async (files: File[]) => {\n if (files.length === 0) return;\n\n setIsUploading(true);\n\n try {\n const tokens: string[] = [];\n\n for (const file of files) {\n const formData = new FormData();\n formData.append(\"contact_form_upload[file_upload]\", file);\n formData.append(\"contact_form_upload[title]\", file.name);\n formData.append(\"contact_form_upload[file_name]\", file.name);\n formData.append(\"contact_form_upload[file_size]\", String(file.size));\n\n const response = await fetch(endpoint, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.statusText}`);\n }\n\n const data = (await response.json()) as ContactFormUploadResponse;\n if (data.contact_form_upload?.token) {\n tokens.push(`upload_${data.contact_form_upload.token}`);\n }\n\n setUploadProgress((prev) => ({\n ...prev,\n [file.name]: 100,\n }));\n }\n\n setUploadTokens(tokens);\n } catch (error) {\n options?.onError?.(error as Error);\n } finally {\n setIsUploading(false);\n }\n },\n [endpoint, options],\n );\n\n const removeFile = useCallback((file: File, index: number) => {\n setUploadTokens((prev) => prev.filter((_, i) => i !== index));\n setUploadProgress((prev) => {\n const next = { ...prev };\n delete next[file.name];\n return next;\n });\n }, []);\n\n const resetUpload = useCallback(() => {\n setUploadTokens([]);\n setUploadProgress({});\n }, []);\n\n return {\n uploadTokens,\n uploadProgress,\n isUploading,\n uploadFiles,\n removeFile,\n resetUpload,\n };\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useForm as usePageSpeedForm } from \"../core/useForm\";\nimport type { UseFormReturn } from \"../core/types\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport {\n generateInitialValues,\n generateValidationSchema,\n} from \"./form-field-types\";\nimport {\n PageSpeedFormSubmissionError,\n submitPageSpeedForm,\n type PageSpeedFormConfig,\n} from \"./form-submit\";\n\nexport interface UseContactFormOptions {\n /**\n * Form field configurations.\n */\n formFields: FormFieldConfig[];\n /**\n * Form submission configuration.\n */\n formConfig?: PageSpeedFormConfig;\n /**\n * Optional custom submit handler.\n */\n onSubmit?: (values: Record<string, any>) => void | Promise<void>;\n /**\n * Optional success callback.\n */\n onSuccess?: (data: unknown) => void;\n /**\n * Optional error callback.\n */\n onError?: (error: Error) => void;\n /**\n * Reset form values after successful submission.\n * @default true\n */\n resetOnSuccess?: boolean;\n /**\n * File upload tokens merged into payload.\n */\n uploadTokens?: string[];\n /**\n * Optional app-level navigation handler for internal redirects.\n * Return `false` to force fallback browser navigation.\n */\n navigate?: (href: string) => boolean | void;\n}\n\nexport interface UseContactFormReturn {\n form: UseFormReturn<Record<string, any>>;\n isSubmitted: boolean;\n submissionError: string | null;\n formMethod: \"get\" | \"post\";\n resetSubmissionState: () => void;\n}\n\ninterface RedirectResolution {\n destination: string;\n internalHref?: string;\n}\n\nfunction resolveRedirect(redirectUrl: string): RedirectResolution {\n const trimmed = redirectUrl.trim();\n if (trimmed.startsWith(\"/\") && !trimmed.startsWith(\"//\")) {\n return { destination: trimmed, internalHref: trimmed };\n }\n\n if (typeof window === \"undefined\") {\n return { destination: trimmed };\n }\n\n try {\n const url = new URL(trimmed, window.location.href);\n if (url.origin === window.location.origin) {\n return {\n destination: url.toString(),\n internalHref: `${url.pathname}${url.search}${url.hash}`,\n };\n }\n\n return { destination: url.toString() };\n } catch {\n return { destination: trimmed };\n }\n}\n\n/**\n * Form orchestration helper for dynamic contact forms.\n */\nexport function useContactForm(\n options: UseContactFormOptions,\n): UseContactFormReturn {\n const {\n formFields,\n formConfig,\n onSubmit,\n onSuccess,\n onError,\n resetOnSuccess = true,\n uploadTokens = [],\n navigate,\n } = options;\n\n const [submissionError, setSubmissionError] = useState<string | null>(null);\n const submissionConfig = formConfig?.submissionConfig;\n const redirectUrl = submissionConfig?.redirectUrl;\n\n const resetSubmissionState = useCallback(() => {\n setSubmissionError(null);\n }, []);\n\n const performRedirect = useCallback(() => {\n if (!redirectUrl || typeof window === \"undefined\") {\n return;\n }\n\n const { destination, internalHref } = resolveRedirect(redirectUrl);\n\n const attemptInternalNavigation = () => {\n if (!internalHref) return false;\n\n if (navigate) {\n return navigate(internalHref) !== false;\n }\n\n const handler = (window as any).__opensiteNavigationHandler;\n if (typeof handler === \"function\") {\n try {\n return handler(internalHref, undefined) !== false;\n } catch {\n return false;\n }\n }\n\n return false;\n };\n\n window.setTimeout(() => {\n if (attemptInternalNavigation()) return;\n window.location.assign(destination);\n }, 150);\n }, [navigate, redirectUrl]);\n\n const form = usePageSpeedForm<Record<string, any>>({\n initialValues: useMemo(\n () => generateInitialValues(formFields),\n [formFields],\n ),\n validationSchema: useMemo(\n () => generateValidationSchema(formFields),\n [formFields],\n ),\n onSubmit: async (values, helpers) => {\n resetSubmissionState();\n const shouldAutoSubmit = Boolean(formConfig?.endpoint);\n\n if (!shouldAutoSubmit && !onSubmit) {\n return;\n }\n\n try {\n let result: unknown;\n\n const submissionValues = {\n ...values,\n ...(uploadTokens.length > 0 && {\n contact_form_upload_tokens: uploadTokens,\n }),\n };\n\n if (shouldAutoSubmit) {\n result = await submitPageSpeedForm(submissionValues, formConfig);\n }\n\n if (onSubmit) {\n await onSubmit(submissionValues);\n }\n\n if (shouldAutoSubmit || onSubmit) {\n try {\n await submissionConfig?.handleFormSubmission?.({\n formData: submissionValues,\n responseData: result,\n });\n } catch {\n // Callback errors should not fail the submission lifecycle.\n }\n\n if (resetOnSuccess) {\n helpers.resetForm();\n }\n\n onSuccess?.(result);\n\n if (\n submissionConfig?.behavior === \"redirect\" &&\n submissionConfig.redirectUrl\n ) {\n performRedirect();\n }\n }\n } catch (error) {\n if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {\n helpers.setErrors(error.formErrors);\n }\n\n const errorMessage =\n error instanceof Error ? error.message : \"Form submission failed\";\n setSubmissionError(errorMessage);\n onError?.(error as Error);\n }\n },\n });\n\n const formMethod =\n formConfig?.method?.toLowerCase() === \"get\" ? \"get\" : \"post\";\n\n return {\n form,\n isSubmitted: form.status === \"success\",\n submissionError,\n formMethod,\n resetSubmissionState,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Field } from \"../core/Field\";\nimport {\n Checkbox,\n CheckboxGroup,\n DatePicker,\n DateRangePicker,\n FileInput,\n MultiSelect,\n Radio,\n Select,\n TextArea,\n TextInput,\n TimePicker,\n} from \"../inputs\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport { cn, fieldIsChoiceCard } from \"../lib/utils\";\nimport { DEFAULT_ICON_API_BASE_URL, Icon } from \"@page-speed/icon\";\n\nexport interface DynamicFormFieldProps {\n field: FormFieldConfig;\n className?: string;\n uploadProgress?: { [fileName: string]: number };\n onFileUpload?: (files: File[]) => Promise<void>;\n onFileRemove?: (file: File, index: number) => void;\n isUploading?: boolean;\n /**\n * Whether to render labels via the Field component.\n * When false, only the input component is rendered.\n * Default: true\n */\n renderLabel?: boolean;\n}\n\n/** Icon name mapping for field types that get an automatic start icon. */\nconst FIELD_TYPE_ICON_MAP: Record<string, string> = {\n email: \"material-symbols/mark-email-unread-outline-rounded\",\n tel: \"material-symbols/phone-iphone-outline\",\n url: \"flowbite/link-solid\",\n};\n\n/** Returns a default iconStart element for supported field types, or undefined. */\nfunction getDefaultIconStart(\n field: FormFieldConfig,\n): React.ReactNode | undefined {\n // Check explicit type mapping first\n const iconName = FIELD_TYPE_ICON_MAP[field.type];\n if (iconName) {\n return (\n <Icon name={iconName} apiKey={DEFAULT_ICON_API_BASE_URL} size={18} />\n );\n }\n\n // Special case: text field named \"table_server_name\"\n if (field.type === \"text\" && field.name === \"table_server_name\") {\n return (\n <Icon\n name=\"majesticons/user-box-line\"\n apiKey={DEFAULT_ICON_API_BASE_URL}\n size={18}\n />\n );\n }\n\n return undefined;\n}\n\n/**\n * Dynamic renderer for form field schema configuration.\n */\nexport function DynamicFormField({\n field,\n className,\n uploadProgress = {},\n onFileUpload,\n onFileRemove,\n isUploading = false,\n renderLabel = true,\n}: DynamicFormFieldProps): React.JSX.Element {\n const fieldId = field.name;\n const usesChoiceCard = React.useMemo(() => {\n return fieldIsChoiceCard(field);\n }, [field.type, field.options]);\n\n const fieldClassName = React.useMemo(() => {\n if (usesChoiceCard) {\n return \"p-4 border rounded-lg\";\n } else {\n return \"\";\n }\n }, [usesChoiceCard]);\n\n const usesGroupLegend =\n field.type === \"radio\" || field.type === \"checkbox-group\";\n const usesInlineCheckboxLabel = field.type === \"checkbox\";\n const shouldRenderFieldLabel =\n renderLabel && !usesGroupLegend && !usesInlineCheckboxLabel;\n\n return (\n <Field\n name={field.name}\n label={shouldRenderFieldLabel ? field.label : undefined}\n description={shouldRenderFieldLabel ? field.description : undefined}\n required={field.required}\n className={cn(fieldClassName, className)}\n >\n {({ field: formField, meta }) => (\n <div>\n {(field.type === \"text\" ||\n field.type === \"email\" ||\n field.type === \"tel\" ||\n field.type === \"search\" ||\n field.type === \"password\" ||\n field.type === \"url\") && (\n <TextInput\n {...formField}\n id={fieldId}\n type={field.type}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n iconStart={getDefaultIconStart(field)}\n />\n )}\n\n {field.type === \"number\" && (\n <TextInput\n {...formField}\n id={fieldId}\n type=\"text\"\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"textarea\" && (\n <TextArea\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n rows={field.rows || 4}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"select\" && field.options && (\n <Select\n {...formField}\n id={fieldId}\n options={field.options}\n placeholder={\n field.placeholder ||\n `Select ${field.label ? field.label.toLocaleLowerCase() : field.name ? field.name.toLocaleLowerCase() : \"Item\"}`\n }\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"multi-select\" && field.options && (\n <MultiSelect\n {...formField}\n id={fieldId}\n options={field.options}\n placeholder={\n field.placeholder ||\n `Select ${field.label ? field.label.toLocaleLowerCase() : field.name ? field.name.toLocaleLowerCase() : \"Item\"}`\n }\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"radio\" && field.options && (\n <Radio\n {...formField}\n id={fieldId}\n options={field.options}\n label={field.label}\n description={field.description}\n required={field.required}\n disabled={field.disabled}\n layout={field.layout || \"stacked\"}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"checkbox\" && (\n <Checkbox\n {...formField}\n id={fieldId}\n value={formField.value === true || formField.value === \"true\"}\n onChange={(checked) => formField.onChange(checked)}\n label={field.label}\n description={field.description}\n disabled={field.disabled}\n required={field.required}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"checkbox-group\" && field.options && (\n <CheckboxGroup\n {...formField}\n id={fieldId}\n options={field.options}\n label={field.label}\n description={field.description}\n required={field.required}\n disabled={field.disabled}\n layout={field.layout || \"stacked\"}\n error={meta.touched && !!meta.error}\n aria-label={field.label}\n />\n )}\n\n {(field.type === \"date-picker\" || field.type === \"date\") && (\n <DatePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"date-range\" && (\n <DateRangePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"time\" && (\n <TimePicker\n {...formField}\n id={fieldId}\n placeholder={field.placeholder}\n error={meta.touched && !!meta.error}\n disabled={field.disabled}\n aria-label={field.label}\n />\n )}\n\n {field.type === \"file\" && (\n <FileInput\n {...formField}\n id={fieldId}\n accept={field.accept}\n maxSize={field.maxSize || 5 * 1024 * 1024}\n maxFiles={field.maxFiles || 1}\n multiple={field.multiple || false}\n placeholder={field.placeholder || \"Choose file(s)...\"}\n error={meta.touched && !!meta.error}\n disabled={field.disabled || isUploading}\n showProgress\n uploadProgress={uploadProgress}\n onChange={(files) => {\n formField.onChange(files);\n if (files.length > 0 && onFileUpload) {\n onFileUpload(files);\n }\n }}\n onFileRemove={onFileRemove}\n aria-label={field.label}\n />\n )}\n </div>\n )}\n </Field>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../components/ui/button\";\nimport { Form } from \"../core/Form\";\nimport { DynamicFormField } from \"./DynamicFormField\";\nimport {\n getColumnSpanClass,\n FORM_GRID_FALLBACK_CSS,\n} from \"./form-field-types\";\nimport type { FormFieldConfig } from \"./form-field-types\";\nimport type { FormRenderConfig } from \"../core/types\";\nimport type { PageSpeedFormConfig } from \"./form-submit\";\nimport { useContactForm } from \"./use-contact-form\";\nimport { useFileUpload } from \"./use-file-upload\";\n\n// ─── Default Values ───────────────────────────────────────────────────────────\n\nconst DEFAULT_STYLE_RULES: FormEngineStyleRules = {\n formContainer: \"\",\n fieldsContainer: \"\",\n fieldClassName: \"\",\n formClassName: \"\",\n successMessageClassName:\n \"text-primary-foreground mt-4 p-3 rounded-md shadow-md bg-primary\",\n errorMessageClassName:\n \"text-destructive-foreground mt-4 p-3 rounded-md shadow-md bg-destructive\",\n};\n\nconst DEFAULT_SUBMIT_LABEL = \"Submit\";\nconst DEFAULT_BUTTON_GROUP_LABEL = \"Subscribe\";\nconst DEFAULT_BUTTON_VARIANT = \"default\";\nconst DEFAULT_BUTTON_GROUP_SIZE = \"default\";\n\n// ─── Setup / Style Types ──────────────────────────────────────────────────────\n\nexport interface ButtonGroupFormSetup {\n size?: \"xs\" | \"sm\" | \"default\" | \"lg\";\n submitLabel?: React.ReactNode;\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\";\n submitIconName?: string;\n submitIconComponent?: React.ReactNode;\n}\n\nexport interface FormEngineSubmitButtonSetup {\n submitLabel?: React.ReactNode;\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\";\n submitIconName?: string;\n submitIconComponent?: React.ReactNode;\n}\n\nexport interface FormEngineStyleRules {\n /** ClassName applied to the div wrapping the `<form>` element */\n formContainer?: string;\n /** ClassName applied to the grid div wrapping all field columns (standard layout) */\n fieldsContainer?: string;\n /** Fallback className applied to each field wrapper when the field has no own className */\n fieldClassName?: string;\n /** className forwarded to the `<form>` element itself via FormStyleConfig */\n formClassName?: string;\n /** className forwarded to the success message container via FormStyleConfig */\n successMessageClassName?: string;\n /** className forwarded to the error message container via FormStyleConfig */\n errorMessageClassName?: string;\n}\n\nexport interface FormEngineLayoutSettings {\n styleRules?: FormEngineStyleRules;\n formLayout?: \"standard\" | \"button-group\";\n /** Settings for button-group layout (only used when formLayout is \"button-group\") */\n buttonGroupSetup?: ButtonGroupFormSetup;\n /** Settings for the submit button in standard layout */\n submitButtonSetup?: FormEngineSubmitButtonSetup;\n}\n\nexport interface FormEngineSetup {\n /** API / submission configuration */\n api?: PageSpeedFormConfig;\n /** Form field definitions */\n fields?: FormFieldConfig[];\n /** Layout, style, and submit-button settings */\n formLayoutSettings?: FormEngineLayoutSettings;\n /** Success message shown after a successful submission */\n successMessage?: React.ReactNode;\n /** Custom submit handler (called in addition to any api endpoint) */\n onSubmit?: (values: Record<string, any>) => void | Promise<void>;\n /** Called after a successful submission with the server response */\n onSuccess?: (data: unknown) => void;\n /** Called when submission fails */\n onError?: (error: Error) => void;\n /** Navigation handler for internal redirects (return false to fall back to browser navigation) */\n navigate?: (href: string) => boolean | void;\n /** Reset form values after success @default true */\n resetOnSuccess?: boolean;\n /** File upload tokens to merge into the payload */\n uploadTokens?: string[];\n /** Called when files are selected for upload */\n onFileUpload?: (files: File[]) => Promise<void>;\n /** Called when a file is removed */\n onFileRemove?: (file: File, index: number) => void;\n /** Whether a file upload is in progress */\n isUploading?: boolean;\n /** Per-file upload progress map (fileName → 0-100) */\n uploadProgress?: { [fileName: string]: number };\n}\n\nexport interface FormEngineProps extends FormEngineSetup {\n /**\n * Optional wrapper object used by block libraries to pass the full setup as a\n * single prop. Direct props on FormEngine take precedence when both are provided.\n */\n formEngineSetup?: FormEngineSetup;\n /** Default form field definitions used when setup fields are missing/empty */\n defaultFields?: FormFieldConfig[];\n /** Default style rules merged before built-in FormEngine defaults */\n defaultStyleRules?: FormEngineStyleRules;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\n/**\n * FormEngine — declarative form component with built-in API integration.\n *\n * Handles `useContactForm` orchestration internally so callers can supply\n * either direct props or a `formEngineSetup` wrapper plus optional defaults.\n *\n * @example Standard layout\n * ```tsx\n * <FormEngine api={api} fields={fields} formLayoutSettings={{ submitButtonSetup: { submitLabel: \"Send\" } }} />\n * ```\n *\n * @example Wrapped setup with defaults\n * ```tsx\n * <FormEngine\n * formEngineSetup={{ api, fields: [emailField] }}\n * defaultFields={fallbackFields}\n * defaultStyleRules={fallbackStyleRules}\n * />\n * ```\n */\nexport function FormEngine(props: FormEngineProps) {\n const {\n formEngineSetup,\n defaultFields,\n defaultStyleRules,\n api: directApi,\n fields: directFields,\n formLayoutSettings: directFormLayoutSettings,\n successMessage: directSuccessMessage,\n onSubmit: directOnSubmit,\n onSuccess: directOnSuccess,\n onError: directOnError,\n navigate: directNavigate,\n resetOnSuccess: directResetOnSuccess,\n uploadTokens: directUploadTokens,\n onFileUpload: directOnFileUpload,\n onFileRemove: directOnFileRemove,\n isUploading: directIsUploading,\n uploadProgress: directUploadProgress,\n } = props;\n\n const api = directApi ?? formEngineSetup?.api;\n const fields = directFields ?? formEngineSetup?.fields;\n const formLayoutSettings =\n directFormLayoutSettings ?? formEngineSetup?.formLayoutSettings;\n const successMessage =\n directSuccessMessage ?? formEngineSetup?.successMessage;\n const onSubmit = directOnSubmit ?? formEngineSetup?.onSubmit;\n const onSuccess = directOnSuccess ?? formEngineSetup?.onSuccess;\n const onError = directOnError ?? formEngineSetup?.onError;\n const navigate = directNavigate ?? formEngineSetup?.navigate;\n const resetOnSuccess =\n directResetOnSuccess ?? formEngineSetup?.resetOnSuccess;\n const externalUploadTokens =\n directUploadTokens ?? formEngineSetup?.uploadTokens;\n const externalOnFileUpload =\n directOnFileUpload ?? formEngineSetup?.onFileUpload;\n const externalOnFileRemove =\n directOnFileRemove ?? formEngineSetup?.onFileRemove;\n const externalIsUploading = directIsUploading ?? formEngineSetup?.isUploading;\n const externalUploadProgress =\n directUploadProgress ?? formEngineSetup?.uploadProgress;\n\n const {\n styleRules: userStyleRules,\n formLayout = \"standard\",\n buttonGroupSetup,\n submitButtonSetup,\n } = formLayoutSettings ?? {};\n const isButtonGroup = formLayout === \"button-group\";\n\n const formFields = React.useMemo<FormFieldConfig[]>(() => {\n if (fields && fields.length > 0) return fields;\n if (defaultFields && defaultFields.length > 0) return defaultFields;\n return [];\n }, [fields, defaultFields]);\n\n // Merge style rules in order: user setup -> block defaults -> built-in defaults\n const styleRules = React.useMemo<FormEngineStyleRules>(\n () => ({\n formContainer:\n userStyleRules?.formContainer ??\n defaultStyleRules?.formContainer ??\n DEFAULT_STYLE_RULES.formContainer,\n fieldsContainer:\n userStyleRules?.fieldsContainer ??\n defaultStyleRules?.fieldsContainer ??\n DEFAULT_STYLE_RULES.fieldsContainer,\n fieldClassName:\n userStyleRules?.fieldClassName ??\n defaultStyleRules?.fieldClassName ??\n DEFAULT_STYLE_RULES.fieldClassName,\n formClassName:\n userStyleRules?.formClassName ??\n defaultStyleRules?.formClassName ??\n DEFAULT_STYLE_RULES.formClassName,\n successMessageClassName:\n userStyleRules?.successMessageClassName ??\n defaultStyleRules?.successMessageClassName ??\n DEFAULT_STYLE_RULES.successMessageClassName,\n errorMessageClassName:\n userStyleRules?.errorMessageClassName ??\n defaultStyleRules?.errorMessageClassName ??\n DEFAULT_STYLE_RULES.errorMessageClassName,\n }),\n [userStyleRules, defaultStyleRules],\n );\n\n // Integrate file upload functionality\n const {\n uploadTokens: internalUploadTokens,\n uploadProgress: internalUploadProgress,\n isUploading: internalIsUploading,\n uploadFiles: internalUploadFiles,\n removeFile: internalRemoveFile,\n resetUpload,\n } = useFileUpload({ onError });\n\n // Use external upload state if provided, otherwise use internal\n const uploadTokens = externalUploadTokens ?? internalUploadTokens;\n const uploadProgress = externalUploadProgress ?? internalUploadProgress;\n const isUploading = externalIsUploading ?? internalIsUploading;\n const onFileUpload = externalOnFileUpload ?? internalUploadFiles;\n const onFileRemove = externalOnFileRemove ?? internalRemoveFile;\n\n const { form, submissionError, formMethod, resetSubmissionState } =\n useContactForm({\n formFields,\n formConfig: api,\n onSubmit,\n onSuccess: (data) => {\n resetUpload();\n onSuccess?.(data);\n },\n onError,\n navigate,\n resetOnSuccess,\n uploadTokens,\n });\n\n // Map FormEngineLayoutSettings → FormRenderConfig for the legacy Form component\n const legacyFormConfig = React.useMemo<FormRenderConfig>(() => {\n if (isButtonGroup) {\n return {\n formLayout: \"button-group\",\n buttonGroupSize: buttonGroupSetup?.size ?? DEFAULT_BUTTON_GROUP_SIZE,\n submitLabel:\n buttonGroupSetup?.submitLabel ?? DEFAULT_BUTTON_GROUP_LABEL,\n submitVariant:\n buttonGroupSetup?.submitVariant ?? DEFAULT_BUTTON_VARIANT,\n submitIconName: buttonGroupSetup?.submitIconName,\n submitIconComponent: buttonGroupSetup?.submitIconComponent,\n endpoint: api?.endpoint,\n submissionConfig: api?.submissionConfig,\n };\n }\n return {\n formLayout: \"standard\",\n endpoint: api?.endpoint,\n submissionConfig: api?.submissionConfig,\n };\n }, [isButtonGroup, buttonGroupSetup, api]);\n\n return (\n <div className={styleRules?.formContainer}>\n <Form\n form={form}\n fields={isButtonGroup ? formFields : undefined}\n formConfig={legacyFormConfig}\n method={formMethod}\n notificationConfig={{\n submissionError: submissionError ?? undefined,\n successMessage,\n }}\n styleConfig={{\n formClassName: styleRules?.formClassName,\n successMessageClassName: styleRules?.successMessageClassName,\n errorMessageClassName: styleRules?.errorMessageClassName,\n }}\n onNewSubmission={resetSubmissionState}\n >\n {!isButtonGroup && (\n <>\n {/* Fallback CSS ensures grid works even when consuming app's\n Tailwind build doesn't generate the col-span-* utilities */}\n <style\n dangerouslySetInnerHTML={{ __html: FORM_GRID_FALLBACK_CSS }}\n />\n <div\n data-psf-grid=\"\"\n className={cn(\n \"grid grid-cols-12 gap-6 md:gap-10\",\n styleRules?.fieldsContainer,\n )}\n >\n {formFields.map((field) => {\n const span = field.columnSpan ?? 12;\n return (\n <div\n key={field.name}\n data-psf-col={String(span)}\n className={cn(\n getColumnSpanClass(span),\n \"min-w-0\",\n )}\n >\n <DynamicFormField\n field={field}\n className={field.className ?? styleRules?.fieldClassName}\n uploadProgress={uploadProgress}\n onFileUpload={onFileUpload}\n onFileRemove={onFileRemove}\n isUploading={isUploading}\n />\n </div>\n );\n })}\n </div>\n <Button\n type=\"submit\"\n variant={\n submitButtonSetup?.submitVariant ?? DEFAULT_BUTTON_VARIANT\n }\n disabled={form.isSubmitting}\n className=\"mt-6 w-full\"\n >\n {submitButtonSetup?.submitLabel ?? DEFAULT_SUBMIT_LABEL}\n </Button>\n </>\n )}\n </Form>\n </div>\n );\n}\n\nFormEngine.displayName = \"FormEngine\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@page-speed/forms",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "description": "Ultra-high-performance React form library with field-level reactivity and tree-shakable architecture",
5
5
  "keywords": [
6
6
  "react",