@kelpie/schemas 0.7.0 → 0.9.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.
- package/dist/account.d.ts +35 -0
- package/dist/account.d.ts.map +1 -1
- package/dist/account.js +18 -0
- package/dist/account.js.map +1 -1
- package/dist/company.d.ts +11 -0
- package/dist/company.d.ts.map +1 -1
- package/dist/company.js +7 -0
- package/dist/company.js.map +1 -1
- package/dist/consentPurpose.d.ts +49 -0
- package/dist/consentPurpose.d.ts.map +1 -0
- package/dist/consentPurpose.js +40 -0
- package/dist/consentPurpose.js.map +1 -0
- package/dist/customField.d.ts +99 -0
- package/dist/customField.d.ts.map +1 -0
- package/dist/customField.js +133 -0
- package/dist/customField.js.map +1 -0
- package/dist/deal.d.ts +8 -0
- package/dist/deal.d.ts.map +1 -1
- package/dist/deal.js +4 -0
- package/dist/deal.js.map +1 -1
- package/dist/emailDomainLinker.d.ts +16 -0
- package/dist/emailDomainLinker.d.ts.map +1 -0
- package/dist/emailDomainLinker.js +11 -0
- package/dist/emailDomainLinker.js.map +1 -0
- package/dist/enquiry.d.ts +43 -0
- package/dist/enquiry.d.ts.map +1 -0
- package/dist/enquiry.js +47 -0
- package/dist/enquiry.js.map +1 -0
- package/dist/form.d.ts +117 -2
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +102 -1
- package/dist/form.js.map +1 -1
- package/dist/formSubmission.d.ts +29 -0
- package/dist/formSubmission.d.ts.map +1 -1
- package/dist/formSubmission.js +34 -0
- package/dist/formSubmission.js.map +1 -1
- package/dist/importExport.d.ts +28 -4
- package/dist/importExport.d.ts.map +1 -1
- package/dist/importExport.js +25 -2
- package/dist/importExport.js.map +1 -1
- package/dist/index.d.ts +27 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/list.d.ts +44 -0
- package/dist/list.d.ts.map +1 -0
- package/dist/list.js +41 -0
- package/dist/list.js.map +1 -0
- package/dist/listMember.d.ts +24 -0
- package/dist/listMember.d.ts.map +1 -0
- package/dist/listMember.js +27 -0
- package/dist/listMember.js.map +1 -0
- package/dist/listMembership.d.ts +20 -0
- package/dist/listMembership.d.ts.map +1 -0
- package/dist/listMembership.js +23 -0
- package/dist/listMembership.js.map +1 -0
- package/dist/names.d.ts +43 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +35 -0
- package/dist/names.js.map +1 -0
- package/dist/opportunity.d.ts +10 -0
- package/dist/opportunity.d.ts.map +1 -1
- package/dist/opportunity.js +7 -0
- package/dist/opportunity.js.map +1 -1
- package/dist/partnership.d.ts +8 -0
- package/dist/partnership.d.ts.map +1 -1
- package/dist/partnership.js +4 -0
- package/dist/partnership.js.map +1 -1
- package/dist/person.d.ts +42 -1
- package/dist/person.d.ts.map +1 -1
- package/dist/person.js +23 -0
- package/dist/person.js.map +1 -1
- package/dist/personConsent.d.ts +31 -0
- package/dist/personConsent.d.ts.map +1 -0
- package/dist/personConsent.js +27 -0
- package/dist/personConsent.js.map +1 -0
- package/dist/raise.d.ts +8 -0
- package/dist/raise.d.ts.map +1 -1
- package/dist/raise.js +4 -0
- package/dist/raise.js.map +1 -1
- package/dist/sampleData.d.ts +23 -0
- package/dist/sampleData.d.ts.map +1 -0
- package/dist/sampleData.js +31 -0
- package/dist/sampleData.js.map +1 -0
- package/dist/values.d.ts +88 -8
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +150 -3
- package/dist/values.js.map +1 -1
- package/package.json +1 -1
- package/src/account.ts +56 -0
- package/src/company.ts +19 -0
- package/src/consentPurpose.ts +95 -0
- package/src/customField.ts +211 -0
- package/src/deal.ts +13 -0
- package/src/emailDomainLinker.ts +28 -0
- package/src/enquiry.ts +94 -0
- package/src/form.ts +230 -2
- package/src/formSubmission.ts +63 -0
- package/src/importExport.ts +45 -6
- package/src/index.ts +86 -2
- package/src/list.ts +89 -0
- package/src/listMember.ts +54 -0
- package/src/listMembership.ts +45 -0
- package/src/names.ts +49 -0
- package/src/opportunity.ts +18 -0
- package/src/partnership.ts +13 -0
- package/src/person.ts +69 -1
- package/src/personConsent.ts +60 -0
- package/src/raise.ts +13 -0
- package/src/sampleData.ts +55 -0
- package/src/values.ts +180 -3
package/src/form.ts
CHANGED
|
@@ -5,8 +5,15 @@ import {
|
|
|
5
5
|
FORM_FIELD_TYPES,
|
|
6
6
|
FORM_OPTION_VALUE_TYPES,
|
|
7
7
|
FORM_STATUSES,
|
|
8
|
+
PIPELINE_KINDS,
|
|
9
|
+
} from './values.ts'
|
|
10
|
+
import type {
|
|
11
|
+
FormFieldMapTarget,
|
|
12
|
+
FormFieldType,
|
|
13
|
+
FormOptionValueType,
|
|
14
|
+
FormStatus,
|
|
15
|
+
PipelineKind,
|
|
8
16
|
} from './values.ts'
|
|
9
|
-
import type { FormFieldMapTarget, FormFieldType, FormOptionValueType, FormStatus } from './values.ts'
|
|
10
17
|
import { definedFields, idSchema, recordTimestamps } from './wire.ts'
|
|
11
18
|
import type { RecordTimestamps } from './wire.ts'
|
|
12
19
|
|
|
@@ -40,13 +47,46 @@ export interface FormField {
|
|
|
40
47
|
/** Empty for every type but `select`. */
|
|
41
48
|
readonly options: readonly FormFieldOption[]
|
|
42
49
|
readonly placeholder: string | null
|
|
50
|
+
/**
|
|
51
|
+
* The intro sentence for a `consent` field — sits above the list of purpose
|
|
52
|
+
* checkboxes. `label` is the field heading; `statement` is what the visitor
|
|
53
|
+
* reads before ticking. Null for every other type.
|
|
54
|
+
*/
|
|
55
|
+
readonly statement: string | null
|
|
56
|
+
/**
|
|
57
|
+
* Workspace consent purposes this `consent` field offers, in display order.
|
|
58
|
+
* Non-empty when `type === 'consent'`; empty otherwise. Each ticked box
|
|
59
|
+
* grants the purpose it belongs to; an unticked one is absence.
|
|
60
|
+
*/
|
|
61
|
+
readonly consentPurposeIds: readonly string[]
|
|
62
|
+
/**
|
|
63
|
+
* Per-purpose override for the checkbox text, keyed by purpose id. Absent
|
|
64
|
+
* keys fall back to the workspace purpose's own label. Only the wording
|
|
65
|
+
* changes — the checkbox stays bound to its purpose.
|
|
66
|
+
*/
|
|
67
|
+
readonly consentPurposeLabels: Readonly<Record<string, string>>
|
|
43
68
|
/** Position in the form, contiguous from 0. The server renumbers on every write. */
|
|
44
69
|
readonly sortOrder: number
|
|
45
70
|
}
|
|
46
71
|
|
|
72
|
+
/**
|
|
73
|
+
* One pre-existing pipeline record every submitter is linked into through
|
|
74
|
+
* `person_links`. `target_type` is fixed at form-write time; the row itself
|
|
75
|
+
* lives in `form_attach_targets` on the server.
|
|
76
|
+
*/
|
|
77
|
+
export interface FormAttachTarget {
|
|
78
|
+
readonly targetType: PipelineKind
|
|
79
|
+
readonly targetId: string
|
|
80
|
+
}
|
|
81
|
+
|
|
47
82
|
export interface Form extends RecordTimestamps {
|
|
48
83
|
readonly id: string
|
|
49
84
|
readonly name: string
|
|
85
|
+
/**
|
|
86
|
+
* Heading on the hosted/embed page. Defaults to `name` when the form is
|
|
87
|
+
* created; edit it from Settings without renaming the form in the CRM.
|
|
88
|
+
*/
|
|
89
|
+
readonly title: string
|
|
50
90
|
readonly description: string | null
|
|
51
91
|
readonly status: FormStatus
|
|
52
92
|
readonly fields: readonly FormField[]
|
|
@@ -56,6 +96,39 @@ export interface Form extends RecordTimestamps {
|
|
|
56
96
|
readonly dealStageId: string | null
|
|
57
97
|
/** Expands `{{company.name}}` and `{{person.name}}`. Null when `createDeal` is off. */
|
|
58
98
|
readonly dealNameTemplate: string | null
|
|
99
|
+
readonly createOpportunity: boolean
|
|
100
|
+
/** Required when `createOpportunity` is on: opportunities carry a `kind`. */
|
|
101
|
+
readonly opportunityKind: string | null
|
|
102
|
+
readonly opportunityStageId: string | null
|
|
103
|
+
readonly opportunityNameTemplate: string | null
|
|
104
|
+
/** Null falls back to the workspace default member at submit time. */
|
|
105
|
+
readonly opportunityOwnerId: string | null
|
|
106
|
+
readonly createPartnership: boolean
|
|
107
|
+
/** Required when `createPartnership` is on. */
|
|
108
|
+
readonly partnershipKind: string | null
|
|
109
|
+
readonly partnershipStageId: string | null
|
|
110
|
+
readonly partnershipNameTemplate: string | null
|
|
111
|
+
/** Null falls back to the workspace default member at submit time. */
|
|
112
|
+
readonly partnershipOwnerId: string | null
|
|
113
|
+
readonly createEnquiry: boolean
|
|
114
|
+
/**
|
|
115
|
+
* Free-text label for the enquiry's `source` column. Optional: an unset value
|
|
116
|
+
* stores an empty string. Enquiry needs no `kind` because it has no `kind`
|
|
117
|
+
* column — a form's source is the source.
|
|
118
|
+
*/
|
|
119
|
+
readonly enquirySource: string | null
|
|
120
|
+
readonly enquiryStageId: string | null
|
|
121
|
+
readonly enquiryNameTemplate: string | null
|
|
122
|
+
/** Null falls back to the workspace default member at submit time. */
|
|
123
|
+
readonly enquiryOwnerId: string | null
|
|
124
|
+
/** Tags to merge (union) into the resolved Person's `tags` on every submit. */
|
|
125
|
+
readonly personTags: readonly string[]
|
|
126
|
+
/** Tags to merge (union) into the resolved Company's `tags`; skipped when no company resolved. */
|
|
127
|
+
readonly companyTags: readonly string[]
|
|
128
|
+
/** Lists (person or company) every matching record is added to on submit. */
|
|
129
|
+
readonly listIds: readonly string[]
|
|
130
|
+
/** Pre-existing pipeline records the submitter is linked to via `person_links`. */
|
|
131
|
+
readonly attachTargets: readonly FormAttachTarget[]
|
|
59
132
|
/**
|
|
60
133
|
* The handle the public submit and embed endpoints resolve a workspace from.
|
|
61
134
|
* Globally unique, and the only credential those endpoints take.
|
|
@@ -86,6 +159,9 @@ const formFieldSchema = z
|
|
|
86
159
|
map_to: z.enum(FORM_FIELD_MAP_TARGETS),
|
|
87
160
|
options: z.array(formFieldOptionSchema),
|
|
88
161
|
placeholder: z.string().nullable(),
|
|
162
|
+
statement: z.string().nullable(),
|
|
163
|
+
consent_purpose_ids: z.array(idSchema),
|
|
164
|
+
consent_purpose_labels: z.record(z.string(), z.string()),
|
|
89
165
|
sort_order: z.number().int(),
|
|
90
166
|
})
|
|
91
167
|
.transform(
|
|
@@ -97,14 +173,30 @@ const formFieldSchema = z
|
|
|
97
173
|
mapTo: wire.map_to,
|
|
98
174
|
options: wire.options,
|
|
99
175
|
placeholder: wire.placeholder,
|
|
176
|
+
statement: wire.statement,
|
|
177
|
+
consentPurposeIds: wire.consent_purpose_ids,
|
|
178
|
+
consentPurposeLabels: wire.consent_purpose_labels,
|
|
100
179
|
sortOrder: wire.sort_order,
|
|
101
180
|
}),
|
|
102
181
|
)
|
|
103
182
|
|
|
183
|
+
const attachTargetSchema = z
|
|
184
|
+
.object({
|
|
185
|
+
target_type: z.enum(PIPELINE_KINDS),
|
|
186
|
+
target_id: idSchema,
|
|
187
|
+
})
|
|
188
|
+
.transform(
|
|
189
|
+
(wire): FormAttachTarget => ({
|
|
190
|
+
targetType: wire.target_type,
|
|
191
|
+
targetId: wire.target_id,
|
|
192
|
+
}),
|
|
193
|
+
)
|
|
194
|
+
|
|
104
195
|
export const formSchema: z.ZodType<Form, unknown> = z
|
|
105
196
|
.object({
|
|
106
197
|
id: idSchema,
|
|
107
198
|
name: z.string(),
|
|
199
|
+
title: z.string(),
|
|
108
200
|
description: z.string().nullable(),
|
|
109
201
|
status: z.enum(FORM_STATUSES),
|
|
110
202
|
fields: z.array(formFieldSchema),
|
|
@@ -112,6 +204,25 @@ export const formSchema: z.ZodType<Form, unknown> = z
|
|
|
112
204
|
create_deal: z.boolean(),
|
|
113
205
|
deal_stage_id: idSchema.nullable(),
|
|
114
206
|
deal_name_template: z.string().nullable(),
|
|
207
|
+
create_opportunity: z.boolean(),
|
|
208
|
+
opportunity_kind: z.string().nullable(),
|
|
209
|
+
opportunity_stage_id: idSchema.nullable(),
|
|
210
|
+
opportunity_name_template: z.string().nullable(),
|
|
211
|
+
opportunity_owner_id: idSchema.nullable(),
|
|
212
|
+
create_partnership: z.boolean(),
|
|
213
|
+
partnership_kind: z.string().nullable(),
|
|
214
|
+
partnership_stage_id: idSchema.nullable(),
|
|
215
|
+
partnership_name_template: z.string().nullable(),
|
|
216
|
+
partnership_owner_id: idSchema.nullable(),
|
|
217
|
+
create_enquiry: z.boolean(),
|
|
218
|
+
enquiry_source: z.string().nullable(),
|
|
219
|
+
enquiry_stage_id: idSchema.nullable(),
|
|
220
|
+
enquiry_name_template: z.string().nullable(),
|
|
221
|
+
enquiry_owner_id: idSchema.nullable(),
|
|
222
|
+
person_tags: z.array(z.string()),
|
|
223
|
+
company_tags: z.array(z.string()),
|
|
224
|
+
list_ids: z.array(idSchema),
|
|
225
|
+
attach_targets: z.array(attachTargetSchema),
|
|
115
226
|
public_key: z.string(),
|
|
116
227
|
...recordTimestamps,
|
|
117
228
|
})
|
|
@@ -119,6 +230,7 @@ export const formSchema: z.ZodType<Form, unknown> = z
|
|
|
119
230
|
(wire): Form => ({
|
|
120
231
|
id: wire.id,
|
|
121
232
|
name: wire.name,
|
|
233
|
+
title: wire.title,
|
|
122
234
|
description: wire.description,
|
|
123
235
|
status: wire.status,
|
|
124
236
|
fields: wire.fields,
|
|
@@ -126,6 +238,25 @@ export const formSchema: z.ZodType<Form, unknown> = z
|
|
|
126
238
|
createDeal: wire.create_deal,
|
|
127
239
|
dealStageId: wire.deal_stage_id,
|
|
128
240
|
dealNameTemplate: wire.deal_name_template,
|
|
241
|
+
createOpportunity: wire.create_opportunity,
|
|
242
|
+
opportunityKind: wire.opportunity_kind,
|
|
243
|
+
opportunityStageId: wire.opportunity_stage_id,
|
|
244
|
+
opportunityNameTemplate: wire.opportunity_name_template,
|
|
245
|
+
opportunityOwnerId: wire.opportunity_owner_id,
|
|
246
|
+
createPartnership: wire.create_partnership,
|
|
247
|
+
partnershipKind: wire.partnership_kind,
|
|
248
|
+
partnershipStageId: wire.partnership_stage_id,
|
|
249
|
+
partnershipNameTemplate: wire.partnership_name_template,
|
|
250
|
+
partnershipOwnerId: wire.partnership_owner_id,
|
|
251
|
+
createEnquiry: wire.create_enquiry,
|
|
252
|
+
enquirySource: wire.enquiry_source,
|
|
253
|
+
enquiryStageId: wire.enquiry_stage_id,
|
|
254
|
+
enquiryNameTemplate: wire.enquiry_name_template,
|
|
255
|
+
enquiryOwnerId: wire.enquiry_owner_id,
|
|
256
|
+
personTags: wire.person_tags,
|
|
257
|
+
companyTags: wire.company_tags,
|
|
258
|
+
listIds: wire.list_ids,
|
|
259
|
+
attachTargets: wire.attach_targets,
|
|
129
260
|
publicKey: wire.public_key,
|
|
130
261
|
createdAt: wire.created_at,
|
|
131
262
|
updatedAt: wire.updated_at,
|
|
@@ -152,10 +283,18 @@ export interface FormFieldInput {
|
|
|
152
283
|
readonly mapTo: FormFieldMapTarget
|
|
153
284
|
readonly options?: readonly FormFieldOptionInput[]
|
|
154
285
|
readonly placeholder?: string | null
|
|
286
|
+
/** The intro sentence above the list of consent checkboxes. */
|
|
287
|
+
readonly statement?: string | null
|
|
288
|
+
/** Non-empty for `consent` fields; each id becomes a checkbox in display order. */
|
|
289
|
+
readonly consentPurposeIds?: readonly string[]
|
|
290
|
+
/** Per-purpose override for the checkbox text. Falls back to the workspace label. */
|
|
291
|
+
readonly consentPurposeLabels?: Readonly<Record<string, string>>
|
|
155
292
|
}
|
|
156
293
|
|
|
157
294
|
export interface CreateFormInput {
|
|
158
295
|
readonly name: string
|
|
296
|
+
/** Defaults to `name` when omitted. */
|
|
297
|
+
readonly title?: string
|
|
159
298
|
readonly description?: string | null
|
|
160
299
|
readonly status?: FormStatus
|
|
161
300
|
readonly fields: readonly FormFieldInput[]
|
|
@@ -163,11 +302,34 @@ export interface CreateFormInput {
|
|
|
163
302
|
readonly createDeal?: boolean
|
|
164
303
|
readonly dealStageId?: string | null
|
|
165
304
|
readonly dealNameTemplate?: string | null
|
|
305
|
+
readonly createOpportunity?: boolean
|
|
306
|
+
readonly opportunityKind?: string | null
|
|
307
|
+
readonly opportunityStageId?: string | null
|
|
308
|
+
readonly opportunityNameTemplate?: string | null
|
|
309
|
+
readonly opportunityOwnerId?: string | null
|
|
310
|
+
readonly createPartnership?: boolean
|
|
311
|
+
readonly partnershipKind?: string | null
|
|
312
|
+
readonly partnershipStageId?: string | null
|
|
313
|
+
readonly partnershipNameTemplate?: string | null
|
|
314
|
+
readonly partnershipOwnerId?: string | null
|
|
315
|
+
readonly createEnquiry?: boolean
|
|
316
|
+
readonly enquirySource?: string | null
|
|
317
|
+
readonly enquiryStageId?: string | null
|
|
318
|
+
readonly enquiryNameTemplate?: string | null
|
|
319
|
+
readonly enquiryOwnerId?: string | null
|
|
320
|
+
readonly personTags?: readonly string[]
|
|
321
|
+
readonly companyTags?: readonly string[]
|
|
322
|
+
readonly listIds?: readonly string[]
|
|
323
|
+
readonly attachTargets?: readonly FormAttachTarget[]
|
|
166
324
|
}
|
|
167
325
|
|
|
168
|
-
/**
|
|
326
|
+
/**
|
|
327
|
+
* `fields`, `list_ids`, and `attach_targets` are each absent or the whole list;
|
|
328
|
+
* there is no per-entry patch.
|
|
329
|
+
*/
|
|
169
330
|
export interface FormInput {
|
|
170
331
|
readonly name?: string
|
|
332
|
+
readonly title?: string
|
|
171
333
|
readonly description?: string | null
|
|
172
334
|
readonly status?: FormStatus
|
|
173
335
|
readonly fields?: readonly FormFieldInput[]
|
|
@@ -175,6 +337,25 @@ export interface FormInput {
|
|
|
175
337
|
readonly createDeal?: boolean
|
|
176
338
|
readonly dealStageId?: string | null
|
|
177
339
|
readonly dealNameTemplate?: string | null
|
|
340
|
+
readonly createOpportunity?: boolean
|
|
341
|
+
readonly opportunityKind?: string | null
|
|
342
|
+
readonly opportunityStageId?: string | null
|
|
343
|
+
readonly opportunityNameTemplate?: string | null
|
|
344
|
+
readonly opportunityOwnerId?: string | null
|
|
345
|
+
readonly createPartnership?: boolean
|
|
346
|
+
readonly partnershipKind?: string | null
|
|
347
|
+
readonly partnershipStageId?: string | null
|
|
348
|
+
readonly partnershipNameTemplate?: string | null
|
|
349
|
+
readonly partnershipOwnerId?: string | null
|
|
350
|
+
readonly createEnquiry?: boolean
|
|
351
|
+
readonly enquirySource?: string | null
|
|
352
|
+
readonly enquiryStageId?: string | null
|
|
353
|
+
readonly enquiryNameTemplate?: string | null
|
|
354
|
+
readonly enquiryOwnerId?: string | null
|
|
355
|
+
readonly personTags?: readonly string[]
|
|
356
|
+
readonly companyTags?: readonly string[]
|
|
357
|
+
readonly listIds?: readonly string[]
|
|
358
|
+
readonly attachTargets?: readonly FormAttachTarget[]
|
|
178
359
|
}
|
|
179
360
|
|
|
180
361
|
function fieldBody(field: FormFieldInput): Record<string, unknown> {
|
|
@@ -187,12 +368,20 @@ function fieldBody(field: FormFieldInput): Record<string, unknown> {
|
|
|
187
368
|
definedFields({ key: option.key, value: option.value, value_type: option.valueType }),
|
|
188
369
|
),
|
|
189
370
|
placeholder: field.placeholder,
|
|
371
|
+
statement: field.statement,
|
|
372
|
+
consent_purpose_ids: field.consentPurposeIds,
|
|
373
|
+
consent_purpose_labels: field.consentPurposeLabels,
|
|
190
374
|
})
|
|
191
375
|
}
|
|
192
376
|
|
|
377
|
+
function attachTargetBody(target: FormAttachTarget): Record<string, unknown> {
|
|
378
|
+
return { target_type: target.targetType, target_id: target.targetId }
|
|
379
|
+
}
|
|
380
|
+
|
|
193
381
|
export function createFormBody(input: CreateFormInput): Record<string, unknown> {
|
|
194
382
|
return definedFields({
|
|
195
383
|
name: input.name,
|
|
384
|
+
title: input.title,
|
|
196
385
|
description: input.description,
|
|
197
386
|
status: input.status,
|
|
198
387
|
fields: input.fields.map(fieldBody),
|
|
@@ -200,12 +389,32 @@ export function createFormBody(input: CreateFormInput): Record<string, unknown>
|
|
|
200
389
|
create_deal: input.createDeal,
|
|
201
390
|
deal_stage_id: input.dealStageId,
|
|
202
391
|
deal_name_template: input.dealNameTemplate,
|
|
392
|
+
create_opportunity: input.createOpportunity,
|
|
393
|
+
opportunity_kind: input.opportunityKind,
|
|
394
|
+
opportunity_stage_id: input.opportunityStageId,
|
|
395
|
+
opportunity_name_template: input.opportunityNameTemplate,
|
|
396
|
+
opportunity_owner_id: input.opportunityOwnerId,
|
|
397
|
+
create_partnership: input.createPartnership,
|
|
398
|
+
partnership_kind: input.partnershipKind,
|
|
399
|
+
partnership_stage_id: input.partnershipStageId,
|
|
400
|
+
partnership_name_template: input.partnershipNameTemplate,
|
|
401
|
+
partnership_owner_id: input.partnershipOwnerId,
|
|
402
|
+
create_enquiry: input.createEnquiry,
|
|
403
|
+
enquiry_source: input.enquirySource,
|
|
404
|
+
enquiry_stage_id: input.enquiryStageId,
|
|
405
|
+
enquiry_name_template: input.enquiryNameTemplate,
|
|
406
|
+
enquiry_owner_id: input.enquiryOwnerId,
|
|
407
|
+
person_tags: input.personTags,
|
|
408
|
+
company_tags: input.companyTags,
|
|
409
|
+
list_ids: input.listIds,
|
|
410
|
+
attach_targets: input.attachTargets?.map(attachTargetBody),
|
|
203
411
|
})
|
|
204
412
|
}
|
|
205
413
|
|
|
206
414
|
export function formBody(input: FormInput): Record<string, unknown> {
|
|
207
415
|
return definedFields({
|
|
208
416
|
name: input.name,
|
|
417
|
+
title: input.title,
|
|
209
418
|
description: input.description,
|
|
210
419
|
status: input.status,
|
|
211
420
|
fields: input.fields?.map(fieldBody),
|
|
@@ -213,5 +422,24 @@ export function formBody(input: FormInput): Record<string, unknown> {
|
|
|
213
422
|
create_deal: input.createDeal,
|
|
214
423
|
deal_stage_id: input.dealStageId,
|
|
215
424
|
deal_name_template: input.dealNameTemplate,
|
|
425
|
+
create_opportunity: input.createOpportunity,
|
|
426
|
+
opportunity_kind: input.opportunityKind,
|
|
427
|
+
opportunity_stage_id: input.opportunityStageId,
|
|
428
|
+
opportunity_name_template: input.opportunityNameTemplate,
|
|
429
|
+
opportunity_owner_id: input.opportunityOwnerId,
|
|
430
|
+
create_partnership: input.createPartnership,
|
|
431
|
+
partnership_kind: input.partnershipKind,
|
|
432
|
+
partnership_stage_id: input.partnershipStageId,
|
|
433
|
+
partnership_name_template: input.partnershipNameTemplate,
|
|
434
|
+
partnership_owner_id: input.partnershipOwnerId,
|
|
435
|
+
create_enquiry: input.createEnquiry,
|
|
436
|
+
enquiry_source: input.enquirySource,
|
|
437
|
+
enquiry_stage_id: input.enquiryStageId,
|
|
438
|
+
enquiry_name_template: input.enquiryNameTemplate,
|
|
439
|
+
enquiry_owner_id: input.enquiryOwnerId,
|
|
440
|
+
person_tags: input.personTags,
|
|
441
|
+
company_tags: input.companyTags,
|
|
442
|
+
list_ids: input.listIds,
|
|
443
|
+
attach_targets: input.attachTargets?.map(attachTargetBody),
|
|
216
444
|
})
|
|
217
445
|
}
|
package/src/formSubmission.ts
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
+
import { FORM_ACTION_STATUSES } from './values.ts'
|
|
4
|
+
import type { FormActionStatus } from './values.ts'
|
|
3
5
|
import { idSchema, timestampSchema } from './wire.ts'
|
|
4
6
|
|
|
7
|
+
/**
|
|
8
|
+
* The record types a submission can name through its FK columns. Used by
|
|
9
|
+
* `GET /v1/form-submissions?target_type=…` so the query, the server, and the
|
|
10
|
+
* UI hook agree on the seven values.
|
|
11
|
+
*/
|
|
12
|
+
export const FORM_SUBMISSION_LINK_TARGETS = [
|
|
13
|
+
'person',
|
|
14
|
+
'company',
|
|
15
|
+
'position',
|
|
16
|
+
'deal',
|
|
17
|
+
'opportunity',
|
|
18
|
+
'partnership',
|
|
19
|
+
'enquiry',
|
|
20
|
+
] as const
|
|
21
|
+
|
|
22
|
+
export type FormSubmissionLinkTarget = (typeof FORM_SUBMISSION_LINK_TARGETS)[number]
|
|
23
|
+
|
|
5
24
|
/**
|
|
6
25
|
* Wire shape for `/v1/forms/:id/submissions`, and for what the public submit
|
|
7
26
|
* endpoint answers with.
|
|
@@ -11,6 +30,19 @@ import { idSchema, timestampSchema } from './wire.ts'
|
|
|
11
30
|
* submission) and no update at all.
|
|
12
31
|
*/
|
|
13
32
|
|
|
33
|
+
/**
|
|
34
|
+
* One post-submit action's outcome. The submission's `actionLog` carries these
|
|
35
|
+
* in the order the runner attempted them, so a reader can see what ran, what
|
|
36
|
+
* was skipped, and what rolled back.
|
|
37
|
+
*/
|
|
38
|
+
export interface FormSubmissionActionEntry {
|
|
39
|
+
/** A machine name for the action ('create_deal', 'tag_person', 'add_list'…). */
|
|
40
|
+
readonly action: string
|
|
41
|
+
readonly status: FormActionStatus
|
|
42
|
+
/** Human-readable message. Never a stack trace; safe for the Submissions UI. */
|
|
43
|
+
readonly detail: string
|
|
44
|
+
}
|
|
45
|
+
|
|
14
46
|
export interface FormSubmission {
|
|
15
47
|
readonly id: string
|
|
16
48
|
readonly formId: string
|
|
@@ -26,9 +58,32 @@ export interface FormSubmission {
|
|
|
26
58
|
readonly companyId: string | null
|
|
27
59
|
readonly positionId: string | null
|
|
28
60
|
readonly dealId: string | null
|
|
61
|
+
readonly opportunityId: string | null
|
|
62
|
+
readonly partnershipId: string | null
|
|
63
|
+
readonly enquiryId: string | null
|
|
64
|
+
/**
|
|
65
|
+
* Per-action outcome from the post-submit runner, in the order attempted.
|
|
66
|
+
* Only actions the form was configured to run appear; a form with no
|
|
67
|
+
* post-actions has an empty log.
|
|
68
|
+
*/
|
|
69
|
+
readonly actionLog: readonly FormSubmissionActionEntry[]
|
|
29
70
|
readonly createdAt: Date
|
|
30
71
|
}
|
|
31
72
|
|
|
73
|
+
const actionEntrySchema = z
|
|
74
|
+
.object({
|
|
75
|
+
action: z.string(),
|
|
76
|
+
status: z.enum(FORM_ACTION_STATUSES),
|
|
77
|
+
detail: z.string(),
|
|
78
|
+
})
|
|
79
|
+
.transform(
|
|
80
|
+
(wire): FormSubmissionActionEntry => ({
|
|
81
|
+
action: wire.action,
|
|
82
|
+
status: wire.status,
|
|
83
|
+
detail: wire.detail,
|
|
84
|
+
}),
|
|
85
|
+
)
|
|
86
|
+
|
|
32
87
|
export const formSubmissionSchema: z.ZodType<FormSubmission, unknown> = z
|
|
33
88
|
.object({
|
|
34
89
|
id: idSchema,
|
|
@@ -39,6 +94,10 @@ export const formSubmissionSchema: z.ZodType<FormSubmission, unknown> = z
|
|
|
39
94
|
company_id: idSchema.nullable(),
|
|
40
95
|
position_id: idSchema.nullable(),
|
|
41
96
|
deal_id: idSchema.nullable(),
|
|
97
|
+
opportunity_id: idSchema.nullable(),
|
|
98
|
+
partnership_id: idSchema.nullable(),
|
|
99
|
+
enquiry_id: idSchema.nullable(),
|
|
100
|
+
action_log: z.array(actionEntrySchema),
|
|
42
101
|
created_at: timestampSchema,
|
|
43
102
|
})
|
|
44
103
|
.transform(
|
|
@@ -51,6 +110,10 @@ export const formSubmissionSchema: z.ZodType<FormSubmission, unknown> = z
|
|
|
51
110
|
companyId: wire.company_id,
|
|
52
111
|
positionId: wire.position_id,
|
|
53
112
|
dealId: wire.deal_id,
|
|
113
|
+
opportunityId: wire.opportunity_id,
|
|
114
|
+
partnershipId: wire.partnership_id,
|
|
115
|
+
enquiryId: wire.enquiry_id,
|
|
116
|
+
actionLog: wire.action_log,
|
|
54
117
|
createdAt: wire.created_at,
|
|
55
118
|
}),
|
|
56
119
|
)
|
package/src/importExport.ts
CHANGED
|
@@ -115,12 +115,22 @@ export interface CsvColumn {
|
|
|
115
115
|
/**
|
|
116
116
|
* A column an import may map but an export never writes.
|
|
117
117
|
*
|
|
118
|
-
* The People affiliation columns are the
|
|
119
|
-
* positions, so a single company and title on the person row would be
|
|
120
|
-
* the way out. They map on the way in to drive a Position, and the
|
|
121
|
-
* template leave them out.
|
|
118
|
+
* The People affiliation columns are the original example: a person can hold
|
|
119
|
+
* many positions, so a single company and title on the person row would be
|
|
120
|
+
* lossy on the way out. They map on the way in to drive a Position, and the
|
|
121
|
+
* export and template leave them out. The consent grant columns follow the
|
|
122
|
+
* same rule — a person's consent state is exported serialised in `consents`,
|
|
123
|
+
* not row-by-row per purpose. See `headersFor`.
|
|
122
124
|
*/
|
|
123
125
|
readonly importOnly?: boolean
|
|
126
|
+
/**
|
|
127
|
+
* A column an export writes but an import never maps. `do_not_contact` and
|
|
128
|
+
* the serialised `consents` list are exported for readability; consent
|
|
129
|
+
* round-trips through `consent_status` / `consent_at` plus the job's
|
|
130
|
+
* `consent_purpose_id`, and `do_not_contact` is a Person patch, not part of
|
|
131
|
+
* an import job.
|
|
132
|
+
*/
|
|
133
|
+
readonly exportOnly?: boolean
|
|
124
134
|
}
|
|
125
135
|
|
|
126
136
|
/**
|
|
@@ -133,11 +143,19 @@ export interface CsvColumn {
|
|
|
133
143
|
* still carry `company_domain`, `company_name` and `title`, which drive a
|
|
134
144
|
* Position for the person rather than a field on them. Those three are
|
|
135
145
|
* `importOnly`, so an export and a template leave them out.
|
|
146
|
+
*
|
|
147
|
+
* A People row still has to name its person, but `name` is not marked `required`
|
|
148
|
+
* here: the row satisfies it with `name`, or with `first_name` / `last_name` for
|
|
149
|
+
* the name to be composed from. `required` is a per-column flag and that is a
|
|
150
|
+
* statement about a pair of them, so the rule lives in the People row check
|
|
151
|
+
* instead. Every CRM worth migrating from exports first and last name in their
|
|
152
|
+
* own columns and most export no full name at all, which is the case this
|
|
153
|
+
* exists for.
|
|
136
154
|
*/
|
|
137
155
|
export const OBJECT_COLUMNS: Readonly<Record<ImportObject, readonly CsvColumn[]>> = {
|
|
138
156
|
companies: [
|
|
139
157
|
{ key: 'name', label: 'Name', required: true },
|
|
140
|
-
{ key: 'domain', label: 'Domain', required:
|
|
158
|
+
{ key: 'domain', label: 'Domain', required: false },
|
|
141
159
|
{ key: 'industry', label: 'Industry', required: false },
|
|
142
160
|
{ key: 'stage', label: 'Stage', required: false },
|
|
143
161
|
{ key: 'size_band', label: 'Size band', required: false },
|
|
@@ -150,7 +168,11 @@ export const OBJECT_COLUMNS: Readonly<Record<ImportObject, readonly CsvColumn[]>
|
|
|
150
168
|
{ key: 'hq', label: 'HQ', required: false },
|
|
151
169
|
],
|
|
152
170
|
people: [
|
|
153
|
-
{ key: 'name', label: 'Name', required:
|
|
171
|
+
{ key: 'name', label: 'Name', required: false },
|
|
172
|
+
{ key: 'salutation', label: 'Salutation', required: false },
|
|
173
|
+
{ key: 'first_name', label: 'First name', required: false },
|
|
174
|
+
{ key: 'last_name', label: 'Last name', required: false },
|
|
175
|
+
{ key: 'suffix', label: 'Suffix', required: false },
|
|
154
176
|
{ key: 'email', label: 'Email', required: true },
|
|
155
177
|
{ key: 'timezone', label: 'Timezone', required: false },
|
|
156
178
|
{ key: 'location', label: 'Location', required: false },
|
|
@@ -160,9 +182,18 @@ export const OBJECT_COLUMNS: Readonly<Record<ImportObject, readonly CsvColumn[]>
|
|
|
160
182
|
{ key: 'summary', label: 'Summary', required: false },
|
|
161
183
|
{ key: 'tags', label: 'Tags', required: false },
|
|
162
184
|
{ key: 'phones', label: 'Phones', required: false },
|
|
185
|
+
{ key: 'do_not_contact', label: 'Do not contact', required: false, exportOnly: true },
|
|
186
|
+
{ key: 'consents', label: 'Consents', required: false, exportOnly: true },
|
|
163
187
|
{ key: 'company_domain', label: 'Company domain', required: false, importOnly: true },
|
|
164
188
|
{ key: 'company_name', label: 'Company name', required: false, importOnly: true },
|
|
165
189
|
{ key: 'title', label: 'Title', required: false, importOnly: true },
|
|
190
|
+
/**
|
|
191
|
+
* Consent grant to write for the job's purpose. `granted` or `withdrawn`.
|
|
192
|
+
* Mapping either column with no `consent_purpose_id` set on the job is a
|
|
193
|
+
* `422` — the writer would not know which purpose the row is for.
|
|
194
|
+
*/
|
|
195
|
+
{ key: 'consent_status', label: 'Consent status', required: false, importOnly: true },
|
|
196
|
+
{ key: 'consent_at', label: 'Consent at', required: false, importOnly: true },
|
|
166
197
|
],
|
|
167
198
|
positions: [
|
|
168
199
|
{ key: 'person_email', label: 'Person email', required: true },
|
|
@@ -298,6 +329,12 @@ export interface ImportJob extends RecordTimestamps {
|
|
|
298
329
|
readonly onMissingCompany: OnMissingCompany
|
|
299
330
|
readonly matchKey: string
|
|
300
331
|
readonly columnMap: ImportColumnMap
|
|
332
|
+
/**
|
|
333
|
+
* The consent purpose a People import grants (or withdraws) for each row
|
|
334
|
+
* that carries a `consent_status`. Required whenever the map names
|
|
335
|
+
* `consent_status` or `consent_at`. Null for every other object.
|
|
336
|
+
*/
|
|
337
|
+
readonly consentPurposeId: string | null
|
|
301
338
|
/** The headers as they appeared in the uploaded file, in file order. */
|
|
302
339
|
readonly sourceHeaders: readonly string[]
|
|
303
340
|
readonly fileName: string | null
|
|
@@ -348,6 +385,7 @@ export const importJobSchema: z.ZodType<ImportJob, unknown> = z
|
|
|
348
385
|
on_missing_company: z.enum(ON_MISSING_COMPANY),
|
|
349
386
|
match_key: z.string(),
|
|
350
387
|
column_map: z.record(z.string(), z.string().nullable()),
|
|
388
|
+
consent_purpose_id: idSchema.nullable(),
|
|
351
389
|
source_headers: z.array(z.string()),
|
|
352
390
|
file_name: z.string().nullable(),
|
|
353
391
|
counts: countsSchema,
|
|
@@ -366,6 +404,7 @@ export const importJobSchema: z.ZodType<ImportJob, unknown> = z
|
|
|
366
404
|
onMissingCompany: wire.on_missing_company,
|
|
367
405
|
matchKey: wire.match_key,
|
|
368
406
|
columnMap: wire.column_map,
|
|
407
|
+
consentPurposeId: wire.consent_purpose_id,
|
|
369
408
|
sourceHeaders: wire.source_headers,
|
|
370
409
|
fileName: wire.file_name,
|
|
371
410
|
counts: wire.counts,
|