@sanity/form-toolkit 2.2.3 → 3.0.0

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.
Files changed (63) hide show
  1. package/LICENSE +2 -1
  2. package/dist/_chunks-es/{create-handler.mjs → create-handler.js} +45 -17
  3. package/dist/_chunks-es/create-handler.js.map +1 -0
  4. package/dist/form-renderer/index.d.ts +53 -64
  5. package/dist/form-renderer/index.d.ts.map +1 -0
  6. package/dist/form-renderer/index.js +123 -108
  7. package/dist/form-renderer/index.js.map +1 -1
  8. package/dist/form-schema/index.d.ts +7 -11
  9. package/dist/form-schema/index.d.ts.map +1 -0
  10. package/dist/form-schema/index.js +181 -181
  11. package/dist/form-schema/index.js.map +1 -1
  12. package/dist/formium/index.d.ts +4 -7
  13. package/dist/formium/index.d.ts.map +1 -0
  14. package/dist/formium/index.js +35 -25
  15. package/dist/formium/index.js.map +1 -1
  16. package/dist/hubspot/index.d.ts +52 -48
  17. package/dist/hubspot/index.d.ts.map +1 -0
  18. package/dist/hubspot/index.js +52 -26
  19. package/dist/hubspot/index.js.map +1 -1
  20. package/dist/mailchimp/index.d.ts +56 -42
  21. package/dist/mailchimp/index.d.ts.map +1 -0
  22. package/dist/mailchimp/index.js +55 -37
  23. package/dist/mailchimp/index.js.map +1 -1
  24. package/package.json +36 -107
  25. package/dist/_chunks-cjs/create-handler.js +0 -68
  26. package/dist/_chunks-cjs/create-handler.js.map +0 -1
  27. package/dist/_chunks-es/create-handler.mjs.map +0 -1
  28. package/dist/form-renderer/index.d.mts +0 -66
  29. package/dist/form-renderer/index.mjs +0 -128
  30. package/dist/form-renderer/index.mjs.map +0 -1
  31. package/dist/form-schema/index.d.mts +0 -28
  32. package/dist/form-schema/index.mjs +0 -230
  33. package/dist/form-schema/index.mjs.map +0 -1
  34. package/dist/formium/index.d.mts +0 -20
  35. package/dist/formium/index.mjs +0 -30
  36. package/dist/formium/index.mjs.map +0 -1
  37. package/dist/hubspot/index.d.mts +0 -49
  38. package/dist/hubspot/index.mjs +0 -48
  39. package/dist/hubspot/index.mjs.map +0 -1
  40. package/dist/mailchimp/index.d.mts +0 -45
  41. package/dist/mailchimp/index.mjs +0 -49
  42. package/dist/mailchimp/index.mjs.map +0 -1
  43. package/sanity.json +0 -8
  44. package/src/form-renderer/components/default-field.tsx +0 -123
  45. package/src/form-renderer/components/form-renderer.tsx +0 -62
  46. package/src/form-renderer/components/types.ts +0 -51
  47. package/src/form-renderer/index.ts +0 -4
  48. package/src/form-schema/components/validation-type.tsx +0 -14
  49. package/src/form-schema/index.ts +0 -35
  50. package/src/form-schema/schema-types/form-field.ts +0 -224
  51. package/src/form-schema/schema-types/form.ts +0 -52
  52. package/src/form-schema/schema-types/index.ts +0 -9
  53. package/src/formium/index.ts +0 -52
  54. package/src/hubspot/components/option.tsx +0 -17
  55. package/src/hubspot/create-handler.ts +0 -6
  56. package/src/hubspot/fetch-hubspot-data.ts +0 -33
  57. package/src/hubspot/index.ts +0 -52
  58. package/src/index.ts +0 -19
  59. package/src/mailchimp/components/option.tsx +0 -30
  60. package/src/mailchimp/create-handler.ts +0 -39
  61. package/src/mailchimp/index.ts +0 -43
  62. package/src/shared/create-handler.ts +0 -109
  63. package/v2-incompatible.js +0 -11
@@ -1,123 +0,0 @@
1
- import type {ChangeEvent, FC, LegacyRef} from 'react'
2
-
3
- import type {FieldComponentProps} from './types'
4
-
5
- export const DefaultField: FC<FieldComponentProps> = ({field, fieldState, error}) => {
6
- const {type, label, name, options = {}, choices = [], validation = []} = field
7
- if (!type || !name) return null
8
- const validationRules = validation.reduce((acc: Record<string, string>, v) => {
9
- acc[v.type] = v.value
10
- return acc
11
- }, {})
12
- const {value, onChange, onBlur, ref} = fieldState
13
-
14
- const handleChange = (
15
- e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
16
- ) => {
17
- onChange(e.target.value)
18
- }
19
-
20
- const handleCheckboxChange = (e: ChangeEvent<HTMLInputElement>, choiceValue: string) => {
21
- if (Array.isArray(value)) {
22
- const newValue = e.target.checked
23
- ? [...value, choiceValue]
24
- : value.filter((v: string) => v !== choiceValue)
25
- onChange(newValue)
26
- } else {
27
- onChange(e.target.checked ? choiceValue : '')
28
- }
29
- }
30
-
31
- const renderInput = () => {
32
- switch (type) {
33
- case 'submit':
34
- return <button type="submit">{label || 'Submit'}</button>
35
- case 'textarea':
36
- return (
37
- <textarea
38
- ref={ref as LegacyRef<HTMLTextAreaElement>}
39
- name={name}
40
- onChange={handleChange}
41
- onBlur={onBlur}
42
- placeholder={options.placeholder}
43
- {...validationRules}
44
- value={value ?? ''}
45
- />
46
- )
47
-
48
- case 'select':
49
- return (
50
- <select
51
- ref={ref as LegacyRef<HTMLSelectElement>}
52
- name={name}
53
- value={value ?? ''}
54
- onChange={handleChange}
55
- {...validationRules}
56
- onBlur={onBlur}
57
- >
58
- {choices?.map((choice, i) => (
59
- <option key={i} value={choice.value}>
60
- {choice.label}
61
- </option>
62
- ))}
63
- </select>
64
- )
65
-
66
- case 'radio':
67
- return choices?.map((choice, i) => (
68
- <label key={i}>
69
- <input
70
- type="radio"
71
- name={name}
72
- ref={ref as LegacyRef<HTMLInputElement>}
73
- value={choice.value}
74
- checked={value === choice.value}
75
- onChange={handleChange}
76
- onBlur={onBlur}
77
- {...validationRules}
78
- />
79
- {choice.label}
80
- </label>
81
- ))
82
-
83
- case 'checkbox':
84
- return choices?.map((choice, i) => (
85
- <label key={i}>
86
- <input
87
- type="checkbox"
88
- name={name}
89
- ref={ref as LegacyRef<HTMLInputElement>}
90
- value={choice.value}
91
- checked={Array.isArray(value) ? value.includes(choice.value) : value === choice.value}
92
- onChange={(e) => handleCheckboxChange(e, choice.value)}
93
- onBlur={onBlur}
94
- {...validationRules}
95
- />
96
- {choice.label}
97
- </label>
98
- ))
99
-
100
- default:
101
- return (
102
- <input
103
- type={type}
104
- ref={ref as LegacyRef<HTMLInputElement>}
105
- name={name}
106
- value={value ?? options.defaultValue ?? ''}
107
- onChange={handleChange}
108
- {...validationRules}
109
- onBlur={onBlur}
110
- placeholder={options.placeholder}
111
- />
112
- )
113
- }
114
- }
115
-
116
- return (
117
- <>
118
- {label && !['hidden', 'submit'].includes(type) && <label htmlFor={name}>{label}</label>}
119
- {renderInput()}
120
- {error && <span className="error">{error}</span>}
121
- </>
122
- )
123
- }
@@ -1,62 +0,0 @@
1
- import type {ComponentType, FC, HTMLProps} from 'react'
2
-
3
- import {DefaultField} from './default-field'
4
- import type {FieldComponentProps, FieldState, FormDataProps, FormField} from './types'
5
-
6
- interface FormRendererProps extends HTMLProps<HTMLFormElement> {
7
- formData?: FormDataProps
8
- // Function to get field state for a given field name
9
- getFieldState?: (fieldName: string) => FieldState
10
- // Function to get field error for a given field name
11
- getFieldError?: (fieldName: string) => string | undefined
12
- // Override default field components
13
- fieldComponents?: Record<string, ComponentType<FieldComponentProps>>
14
- }
15
-
16
- export const FormRenderer: FC<FormRendererProps> = (props) => {
17
- const {
18
- formData,
19
- getFieldState = (name) => ({
20
- value: undefined,
21
- onChange: () => {},
22
- name, // Pass name to field for native form handling
23
- }),
24
- getFieldError,
25
- fieldComponents = {},
26
- children,
27
- } = props
28
- const renderField = (field: FormField) => {
29
- const CustomComponent = fieldComponents[field.type]
30
- const fieldState = getFieldState(field.name)
31
- const error = getFieldError?.(field.name)
32
-
33
- if (CustomComponent) {
34
- return <CustomComponent field={field} fieldState={fieldState} error={error} />
35
- }
36
-
37
- return <DefaultField field={field} fieldState={fieldState} error={error} />
38
- }
39
- const elProps = Object.assign({}, props)
40
- delete elProps.formData
41
- delete elProps.getFieldState
42
- delete elProps.getFieldError
43
- delete elProps.fieldComponents
44
-
45
- return (
46
- <form {...elProps} id={elProps.id ?? formData?.id?.current}>
47
- {formData?.fields?.map((field) => (
48
- <div key={field._key} className="form-field">
49
- {renderField(field)}
50
- </div>
51
- ))}
52
-
53
- {children}
54
-
55
- {renderField({
56
- type: 'submit',
57
- name: 'submit',
58
- label: formData?.submitButton?.text || 'Submit',
59
- })}
60
- </form>
61
- )
62
- }
@@ -1,51 +0,0 @@
1
- export type ValidationRule = {
2
- type: string
3
- value: string
4
- message: string
5
- }
6
-
7
- export type FieldChoice = {
8
- label: string
9
- value: string
10
- }
11
-
12
- export type FieldOptions = {
13
- placeholder?: string
14
- defaultValue?: string
15
- }
16
-
17
- export type FormField = {
18
- type: string
19
- label?: string
20
- name: string
21
- required?: boolean
22
- validation?: ValidationRule[]
23
- options?: FieldOptions
24
- choices?: FieldChoice[]
25
- _key?: string
26
- }
27
-
28
- export type FormDataProps = {
29
- title: string
30
- id: {
31
- current: string
32
- }
33
- fields?: FormField[]
34
- submitButton?: {
35
- text: string
36
- position: 'left' | 'center' | 'right'
37
- }
38
- }
39
-
40
- export interface FieldState {
41
- value?: string | number | readonly string[]
42
- onChange: (value: unknown) => void
43
- onBlur?: () => void
44
- ref?: unknown
45
- }
46
-
47
- export interface FieldComponentProps {
48
- field: FormField
49
- fieldState: FieldState
50
- error?: string
51
- }
@@ -1,4 +0,0 @@
1
- import {FormRenderer} from './components/form-renderer'
2
- import type {FormDataProps} from './components/types'
3
- export type {FormDataProps}
4
- export {FormRenderer}
@@ -1,14 +0,0 @@
1
- import type {StringInputProps} from 'sanity'
2
- import {useFormValue} from 'sanity'
3
-
4
- import type {FormField} from '../../form-renderer/components/types'
5
- import {validationTypesByFieldType} from '../schema-types/form-field'
6
-
7
- export const ValidationType = (props: StringInputProps) => {
8
- const {type} = useFormValue([...props.path.slice(0, 2)]) as FormField
9
- if (!type) return props.renderDefault(props)
10
- if (props.schemaType?.options) {
11
- props.schemaType.options.list = validationTypesByFieldType[type]
12
- }
13
- return props.renderDefault(props)
14
- }
@@ -1,35 +0,0 @@
1
- import {definePlugin, type FieldDefinition} from 'sanity'
2
-
3
- // import {structureTool} from 'sanity/structure'
4
- // import {FormRenderer} from './components/form-renderer'
5
- import {schema} from './schema-types'
6
- // import {defaultDocumentNode} from './structure'
7
-
8
- /**
9
- * Usage in `sanity.config.ts` (or .js)
10
- *
11
- * ```ts
12
- * import {defineConfig} from 'sanity'
13
- * import {formSchema} from '@sanity/form-toolkit'
14
- *
15
- * export default defineConfig({
16
- * // ...
17
- * plugins: [formSchema()],
18
- * })
19
- * ```
20
- */
21
- export type FieldsOption = Array<FieldDefinition>
22
- interface FormSchemaPluginOptions {
23
- /**
24
- * Array of field definitions to be used in the form schema.
25
- */
26
- fields?: FieldsOption
27
- }
28
-
29
- export const formSchema = definePlugin((options: FormSchemaPluginOptions | undefined) => {
30
- return {
31
- name: 'form-toolkit_form-schema',
32
- schema: schema(options?.fields ?? []),
33
- // plugins: [structureTool({defaultDocumentNode})],
34
- }
35
- })
@@ -1,224 +0,0 @@
1
- import {LuTextCursorInput} from 'react-icons/lu'
2
- import {defineField, defineType} from 'sanity'
3
-
4
- import {ValidationType} from '../components/validation-type'
5
- interface ValidationContextDocument {
6
- fields?: Array<{
7
- name: string
8
- type?: string
9
- }>
10
- }
11
-
12
- // Validation options by field type
13
- export const validationTypesByFieldType: Record<string, string[]> = {
14
- checkbox: ['minSelectedCount', 'maxSelectedCount'],
15
- color: [],
16
- date: ['minDate', 'maxDate'],
17
- 'datetime-local': ['minDate', 'maxDate'],
18
- email: ['pattern'],
19
- file: ['maxSize', 'fileType'],
20
- hidden: [],
21
- number: ['min', 'max'],
22
- // password: ['minLength', 'pattern'],
23
- radio: [],
24
- range: ['min', 'max', 'step'],
25
- select: [],
26
- tel: ['pattern'],
27
- text: ['minLength', 'maxLength', 'pattern'],
28
- textarea: ['minLength', 'maxLength'],
29
- time: [],
30
- url: ['pattern'],
31
- }
32
- export const formFieldType = defineType({
33
- name: 'formField',
34
- title: 'Form Field',
35
- type: 'object',
36
- icon: LuTextCursorInput,
37
- fields: [
38
- defineField({
39
- name: 'type',
40
- title: 'Field Type',
41
- type: 'string',
42
- options: {
43
- list: Object.keys(validationTypesByFieldType).map((type) => {
44
- const title = (fieldType: string) => {
45
- switch (fieldType) {
46
- case 'datetime-local':
47
- return 'Date & Time'
48
- case 'textarea':
49
- return 'Text Area'
50
- case 'tel':
51
- return 'Phone Number'
52
- default:
53
- return fieldType.charAt(0).toUpperCase() + fieldType.slice(1)
54
- }
55
- }
56
- return {title: title(type), value: type}
57
- }),
58
- },
59
- }),
60
- defineField({
61
- name: 'label',
62
- title: 'Field Label',
63
- type: 'string',
64
- }),
65
- defineField({
66
- name: 'name',
67
- title: 'Field Name',
68
- type: 'string',
69
- description:
70
- 'Must start with a letter and contain only letters, numbers, underscores, or hyphens. Must be unique within the form.',
71
- validation: (Rule) =>
72
- Rule.required().custom((name, context) => {
73
- if (!name) {
74
- return 'Required'
75
- }
76
- // Check format (HTML ID/name rules)
77
- if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
78
- return 'Field name must start with a letter and contain only letters, numbers, underscores, or hyphens'
79
- }
80
-
81
- // Check uniqueness across all fields
82
- const doc = context.document as ValidationContextDocument
83
- const allFieldNames = doc?.fields?.map((field) => field.name) || []
84
-
85
- // Count occurrences of this name
86
- const nameCount = allFieldNames.filter((n) => n === name).length
87
-
88
- // If we find more than one occurrence (including current field), it's not unique
89
- if (nameCount > 1) {
90
- return 'Field name must be unique across all form fields'
91
- }
92
-
93
- // Check for reserved HTML form attributes
94
- const reservedNames = [
95
- 'action',
96
- 'method',
97
- 'target',
98
- 'enctype',
99
- 'accept-charset',
100
- 'autocomplete',
101
- 'novalidate',
102
- 'rel',
103
- 'submit',
104
- 'reset',
105
- ]
106
- if (reservedNames.includes(name.toLowerCase())) {
107
- return 'This name is reserved for HTML form attributes. Please choose a different name.'
108
- }
109
-
110
- return true
111
- }),
112
- }),
113
- defineField({
114
- name: 'required',
115
- title: 'Required',
116
- type: 'boolean',
117
- initialValue: false,
118
- }),
119
- defineField({
120
- name: 'validation',
121
- title: 'Validation Rules',
122
- type: 'array',
123
- hidden: ({parent}) => {
124
- if (!parent?.type) return true
125
- const validationTypes = validationTypesByFieldType[parent.type]
126
- return !validationTypes || validationTypes.length === 0
127
- },
128
- of: [
129
- {
130
- type: 'object',
131
- fields: [
132
- defineField({
133
- name: 'type',
134
- title: 'Validation Type',
135
- type: 'string',
136
- options: {
137
- // TODO: I think this needs to be a custom input component?
138
- // list: ({parent}) => (parent?.type ? validationTypesByFieldType[parent.type] : []),
139
- list: [],
140
- },
141
- components: {
142
- input: ValidationType,
143
- },
144
- }),
145
- defineField({
146
- name: 'value',
147
- title: 'Value',
148
- type: 'string',
149
- }),
150
- defineField({
151
- name: 'message',
152
- title: 'Error Message',
153
- type: 'string',
154
- }),
155
- ],
156
- preview: {
157
- select: {
158
- title: 'type',
159
- subtitle: 'value',
160
- },
161
- },
162
- },
163
- ],
164
- }),
165
- defineField({
166
- name: 'choices',
167
- title: 'Choices',
168
- type: 'array',
169
- hidden: ({parent}) => {
170
- return !['select', 'radio', 'checkbox'].includes(parent?.type)
171
- },
172
- of: [
173
- {
174
- type: 'object',
175
- fields: [
176
- defineField({
177
- name: 'label',
178
- title: 'Label',
179
- type: 'string',
180
- }),
181
- defineField({
182
- name: 'value',
183
- title: 'Value',
184
- type: 'string',
185
- }),
186
- ],
187
- },
188
- ],
189
- }),
190
- defineField({
191
- name: 'options',
192
- title: 'Field Options',
193
- type: 'object',
194
- hidden: ({parent}) => {
195
- return ['select', 'radio', 'checkbox', 'file'].includes(parent?.type)
196
- },
197
- fields: [
198
- defineField({
199
- name: 'placeholder',
200
- title: 'Placeholder',
201
- type: 'string',
202
- }),
203
- defineField({
204
- name: 'defaultValue',
205
- title: 'Default Value',
206
- type: 'string',
207
- }),
208
- ],
209
- }),
210
- ],
211
- preview: {
212
- select: {
213
- label: 'label',
214
- name: 'name',
215
- type: 'type',
216
- },
217
- prepare({label, name, type}) {
218
- return {
219
- title: label || name,
220
- subtitle: type,
221
- }
222
- },
223
- },
224
- })
@@ -1,52 +0,0 @@
1
- import {FaWpforms} from 'react-icons/fa'
2
- import {defineField, defineType, type SchemaTypeDefinition} from 'sanity'
3
-
4
- import type {FieldsOption} from '..'
5
-
6
- export const formType = (fields: FieldsOption): SchemaTypeDefinition => {
7
- // const fieldsOf =
8
- // fields && fields.length ? [{type: 'formField'}, ...fields] : [{type: 'formField'}]
9
- return defineType({
10
- name: 'form',
11
- title: 'Form',
12
- type: 'document',
13
- icon: FaWpforms,
14
- fields: [
15
- defineField({
16
- name: 'title',
17
- title: 'Form Title',
18
- type: 'string',
19
- description: 'Internal title for the form',
20
- validation: (Rule) => Rule.required(),
21
- }),
22
- defineField({
23
- name: 'id',
24
- title: 'Form ID',
25
- type: 'slug',
26
- options: {
27
- source: 'title',
28
- },
29
- // validation: (Rule) => Rule.required(),
30
- }),
31
- defineField({
32
- name: 'fields',
33
- title: 'Form Fields',
34
- type: 'array',
35
- of: [{type: 'formField'}, ...fields],
36
- }),
37
- defineField({
38
- name: 'submitButton',
39
- title: 'Submit Button',
40
- type: 'object',
41
- fields: [
42
- defineField({
43
- name: 'text',
44
- title: 'Button Text',
45
- type: 'string',
46
- initialValue: 'Submit',
47
- }),
48
- ],
49
- }),
50
- ],
51
- })
52
- }
@@ -1,9 +0,0 @@
1
- import type {SchemaTypeDefinition} from 'sanity'
2
-
3
- import type {FieldsOption} from '..'
4
- import {formType} from './form'
5
- import {formFieldType} from './form-field'
6
-
7
- export const schema = (fields: FieldsOption): {types: SchemaTypeDefinition[]} => {
8
- return {types: [formType(fields), formFieldType]}
9
- }
@@ -1,52 +0,0 @@
1
- import {createClient, type Form} from '@formium/client'
2
- import {asyncList} from '@sanity/sanity-plugin-async-list'
3
- import {definePlugin} from 'sanity'
4
-
5
- interface FormiumInputConfig {
6
- /* nothing here yet */
7
- }
8
-
9
- /**
10
- * Usage in `sanity.config.ts` (or .js)
11
- *
12
- * ```ts
13
- * import {defineConfig} from 'sanity'
14
- * import {formiumInput} from 'sanity-plugin-form-toolkit'
15
- *
16
- * export default defineConfig({
17
- * // ...
18
- * plugins: [formiumInput()],
19
- * })
20
- * ```
21
- */
22
- // Is Formium dead? All attempts to use the API come back with an expired cert https://github.com/formium/formium/issues/77
23
- export const formiumInput = definePlugin<FormiumInputConfig | void>(() => {
24
- return {
25
- name: 'sanity-plugin-form-toolkit_formium-input',
26
- plugins: [
27
- asyncList({
28
- schemaType: 'formiumInput',
29
- secrets: {
30
- keys: [
31
- {key: 'projectId', title: 'Project ID'},
32
- {key: 'token', title: 'Token'},
33
- ],
34
- },
35
- loader: async ({secrets}) => {
36
- const formium = createClient(secrets?.projectId || '', {
37
- apiToken: secrets?.token,
38
- })
39
- const {data}: {data: Form[]} = await formium.findForms()
40
- return data && data.length
41
- ? data.map(({name, id}) => {
42
- return {
43
- title: name,
44
- value: id,
45
- }
46
- })
47
- : []
48
- },
49
- }),
50
- ],
51
- }
52
- })
@@ -1,17 +0,0 @@
1
- import {Card, Text} from '@sanity/ui'
2
- import type {ReactElement} from 'react'
3
-
4
- export const Option = (option: {value: string; name: string}): ReactElement => {
5
- return (
6
- <Card data-as="button" padding={3} radius={2} tone="inherit">
7
- <Text size={2} textOverflow="ellipsis">
8
- {option.name}
9
- </Text>
10
- <Card paddingTop={2} tone="inherit" style={{background: 'inherit'}}>
11
- <Text size={1} textOverflow="ellipsis">
12
- {`ID: ${option.value}`}
13
- </Text>
14
- </Card>
15
- </Card>
16
- )
17
- }
@@ -1,6 +0,0 @@
1
- import createHandler from '../shared/create-handler'
2
- import {fetchHubSpotData} from './fetch-hubspot-data'
3
-
4
- export const hubSpotHandler = ({token}: {token: string}) => {
5
- return createHandler(() => fetchHubSpotData({token}))
6
- }
@@ -1,33 +0,0 @@
1
- type HubSpotForm = {
2
- id: string
3
- name: string
4
- [key: string]: unknown // Additional properties from the API response
5
- }
6
-
7
- type MappedResult = HubSpotForm & {
8
- value: string
9
- }
10
- export async function fetchHubSpotData({token}: {token: string}): Promise<MappedResult[] | null> {
11
- try {
12
- const apiResponse = await fetch('https://api.hubapi.com/marketing/v3/forms/?limit=9999', {
13
- headers: {
14
- Authorization: `Bearer ${token}`,
15
- },
16
- })
17
-
18
- if (!apiResponse.ok) {
19
- console.error(`Failed to fetch data: ${apiResponse.statusText}`)
20
- return null
21
- }
22
-
23
- const {results}: {results: HubSpotForm[]} = await apiResponse.json()
24
-
25
- return results.map((result) => ({
26
- ...result,
27
- value: result.id,
28
- }))
29
- } catch (e: unknown) {
30
- console.error(e)
31
- return null // Explicitly return null on error
32
- }
33
- }