{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "fillout-flow-schema",
  "title": "Fillout Flow Schema",
  "description": "Helpers for turning Fillout metadata into a spec-driven custom flow definition.",
  "files": [
    {
      "path": "registry/berlin/blocks/fillout-flow/schema.ts",
      "content": "// Generated from packages/ui/src/components/fillout-flow/schema.ts\nimport type {\n  FilloutAnswerValue,\n  FilloutFlowDefinition,\n  FilloutFlowField,\n  FilloutFlowFieldInput,\n  FilloutFlowFieldKind,\n  FilloutFlowFieldSpec,\n  FilloutFlowSpec,\n  FilloutFlowStep,\n  FilloutFlowValues,\n  FilloutFormMetadata,\n  FilloutQuestion,\n  FilloutQuestionType,\n} from \"@/registry/berlin/blocks/fillout-flow-types\";\n\nconst fieldKindByQuestionType: Partial<\n  Record<FilloutQuestionType, FilloutFlowFieldKind>\n> = {\n  Checkboxes: \"choices\",\n  DatePicker: \"date\",\n  Dropdown: \"choice\",\n  EmailInput: \"email\",\n  LongAnswer: \"textarea\",\n  MultiSelect: \"choices\",\n  MultipleChoice: \"choice\",\n  NumberInput: \"number\",\n  PhoneNumber: \"phone\",\n  ShortAnswer: \"text\",\n  URLInput: \"text\",\n};\n\nfunction normalizeFieldInput(\n  field: FilloutFlowFieldInput,\n): FilloutFlowFieldSpec {\n  if (typeof field === \"string\") {\n    return {\n      questionId: field,\n    };\n  }\n\n  return field;\n}\n\nfunction inferFieldKind(question: FilloutQuestion): FilloutFlowFieldKind {\n  const kind = fieldKindByQuestionType[question.type];\n\n  if (!kind) {\n    throw new Error(\n      `Question \"${question.name}\" (${question.id}) uses unsupported Fillout type \"${question.type}\". Add a field.kind override or a custom renderer.`,\n    );\n  }\n\n  return kind;\n}\n\nfunction createField(\n  question: FilloutQuestion,\n  spec: FilloutFlowFieldSpec,\n): FilloutFlowField {\n  const kind = spec.kind ?? inferFieldKind(question);\n\n  return {\n    id: question.id,\n    question,\n    kind,\n    title: spec.title ?? question.name,\n    description: spec.description,\n    placeholder: spec.placeholder,\n    required: spec.required ?? false,\n    autoComplete: spec.autoComplete,\n    options: spec.options ?? question.options ?? [],\n    multiple: kind === \"choices\",\n  };\n}\n\nexport function createFilloutQuestionMap(form: FilloutFormMetadata) {\n  return new Map(form.questions.map((question) => [question.id, question]));\n}\n\nexport function createFilloutFlowDefinition(\n  form: FilloutFormMetadata,\n  spec: FilloutFlowSpec,\n): FilloutFlowDefinition {\n  if (spec.formId && spec.formId !== form.id) {\n    throw new Error(\n      `Flow spec formId \"${spec.formId}\" does not match Fillout form \"${form.id}\".`,\n    );\n  }\n\n  if (spec.steps.length === 0) {\n    throw new Error(\"Fillout flows need at least one step.\");\n  }\n\n  const questions = createFilloutQuestionMap(form);\n  const steps = spec.steps.map<FilloutFlowStep>((step) => {\n    if (step.fields.length === 0) {\n      throw new Error(`Fillout flow step \"${step.id}\" has no fields.`);\n    }\n\n    const fields = step.fields.map((fieldInput) => {\n      const fieldSpec = normalizeFieldInput(fieldInput);\n      const question = questions.get(fieldSpec.questionId);\n\n      if (!question) {\n        throw new Error(\n          `Question \"${fieldSpec.questionId}\" is missing from Fillout form \"${form.id}\".`,\n        );\n      }\n\n      return createField(question, fieldSpec);\n    });\n\n    return {\n      id: step.id,\n      title: step.title,\n      description: step.description,\n      fields,\n      ctaLabel: step.ctaLabel,\n      autoAdvance:\n        step.autoAdvance ?? (fields.length === 1 && fields[0].kind === \"choice\"),\n    };\n  });\n\n  return {\n    formId: form.id,\n    formName: form.name,\n    title: spec.title ?? form.name,\n    description: spec.description,\n    submitLabel: spec.submitLabel ?? \"Submit\",\n    questions: form.questions,\n    questionsById: Object.fromEntries(form.questions.map((q) => [q.id, q])),\n    steps,\n    urlParameters: form.urlParameters ?? [],\n  };\n}\n\nexport function hasFilloutValue(value: FilloutAnswerValue | undefined) {\n  if (typeof value === \"string\") {\n    return value.trim().length > 0;\n  }\n\n  if (Array.isArray(value)) {\n    return value.some((entry) => entry.trim().length > 0);\n  }\n\n  return false;\n}\n\nexport function isFilloutStepComplete(\n  step: FilloutFlowStep,\n  values: FilloutFlowValues,\n) {\n  return step.fields.every((field) => {\n    if (!field.required) {\n      return true;\n    }\n\n    return hasFilloutValue(values[field.id]);\n  });\n}\n\nexport function getFilloutStepValues(\n  step: FilloutFlowStep,\n  values: FilloutFlowValues,\n) {\n  return Object.fromEntries(step.fields.map((field) => [field.id, values[field.id]]));\n}\n",
      "type": "registry:lib",
      "target": "src/lib/fillout/schema.ts"
    }
  ],
  "type": "registry:lib"
}