@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
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { CONSENT_PURPOSE_STATUSES } from './values.ts'
|
|
4
|
+
import type { ConsentPurposeStatus, ConsentStatus } from './values.ts'
|
|
5
|
+
import { definedFields, nullableTimestampSchema } from './wire.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The effective consent status for a (person, purpose) pair.
|
|
9
|
+
*
|
|
10
|
+
* `inherited: true` means the person has no explicit `person_consents` row for
|
|
11
|
+
* the purpose and the reader is looking at the purpose's `default_status`. In
|
|
12
|
+
* that case `source` and `notedAt` are null. `inherited: false` means the row
|
|
13
|
+
* exists and carries its own `granted | withdrawn`, `source`, and `notedAt`.
|
|
14
|
+
*/
|
|
15
|
+
export interface PersonConsent {
|
|
16
|
+
/** The purpose's slug — the identity that survives a rename of its label. */
|
|
17
|
+
readonly purposeSlug: string
|
|
18
|
+
readonly purposeLabel: string
|
|
19
|
+
readonly status: ConsentPurposeStatus
|
|
20
|
+
readonly source: string | null
|
|
21
|
+
readonly notedAt: Date | null
|
|
22
|
+
readonly inherited: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const personConsentSchema: z.ZodType<PersonConsent, unknown> = z
|
|
26
|
+
.object({
|
|
27
|
+
purpose_slug: z.string(),
|
|
28
|
+
purpose_label: z.string(),
|
|
29
|
+
status: z.enum(CONSENT_PURPOSE_STATUSES),
|
|
30
|
+
source: z.string().nullable(),
|
|
31
|
+
noted_at: nullableTimestampSchema,
|
|
32
|
+
inherited: z.boolean(),
|
|
33
|
+
})
|
|
34
|
+
.transform(
|
|
35
|
+
(wire): PersonConsent => ({
|
|
36
|
+
purposeSlug: wire.purpose_slug,
|
|
37
|
+
purposeLabel: wire.purpose_label,
|
|
38
|
+
status: wire.status,
|
|
39
|
+
source: wire.source,
|
|
40
|
+
notedAt: wire.noted_at,
|
|
41
|
+
inherited: wire.inherited,
|
|
42
|
+
}),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* What a manual override writes for one (person, purpose) pair. A `status` of
|
|
47
|
+
* `granted` or `withdrawn` upserts the row (`source: manual`); a `null` status
|
|
48
|
+
* clears the row and falls the pair back to the purpose's default.
|
|
49
|
+
*/
|
|
50
|
+
export interface PersonConsentInput {
|
|
51
|
+
readonly purposeSlug: string
|
|
52
|
+
readonly status: ConsentStatus | null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function personConsentWriteBody(input: PersonConsentInput): Record<string, unknown> {
|
|
56
|
+
return definedFields({
|
|
57
|
+
purpose_slug: input.purposeSlug,
|
|
58
|
+
status: input.status,
|
|
59
|
+
})
|
|
60
|
+
}
|
package/src/raise.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
+
import { customFieldValuesBody, customFieldValuesSchema } from './customField.ts'
|
|
4
|
+
import type { CustomFieldValue, CustomFieldValues } from './customField.ts'
|
|
3
5
|
import { definedFields, idSchema, recordTimestamps } from './wire.ts'
|
|
4
6
|
import type { RecordTimestamps } from './wire.ts'
|
|
5
7
|
|
|
@@ -22,6 +24,8 @@ export interface Raise extends RecordTimestamps {
|
|
|
22
24
|
readonly personIds: readonly string[]
|
|
23
25
|
readonly summary: string
|
|
24
26
|
readonly tags: readonly string[]
|
|
27
|
+
/** Workspace-defined fields, keyed by definition key. Always present (default `{}`). */
|
|
28
|
+
readonly customFields: CustomFieldValues
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
export const raiseSchema: z.ZodType<Raise, unknown> = z
|
|
@@ -39,6 +43,7 @@ export const raiseSchema: z.ZodType<Raise, unknown> = z
|
|
|
39
43
|
person_ids: z.array(idSchema),
|
|
40
44
|
summary: z.string(),
|
|
41
45
|
tags: z.array(z.string()),
|
|
46
|
+
custom_fields: customFieldValuesSchema,
|
|
42
47
|
...recordTimestamps,
|
|
43
48
|
})
|
|
44
49
|
.transform(
|
|
@@ -56,6 +61,7 @@ export const raiseSchema: z.ZodType<Raise, unknown> = z
|
|
|
56
61
|
personIds: wire.person_ids,
|
|
57
62
|
summary: wire.summary,
|
|
58
63
|
tags: wire.tags,
|
|
64
|
+
customFields: wire.custom_fields,
|
|
59
65
|
createdAt: wire.created_at,
|
|
60
66
|
updatedAt: wire.updated_at,
|
|
61
67
|
}),
|
|
@@ -74,6 +80,11 @@ export interface RaiseInput {
|
|
|
74
80
|
readonly personIds?: readonly string[]
|
|
75
81
|
readonly summary?: string
|
|
76
82
|
readonly tags?: readonly string[]
|
|
83
|
+
/**
|
|
84
|
+
* Partial merge patch: sent keys change, `null` clears a key, absent keys are
|
|
85
|
+
* left alone. Unknown keys are rejected at `422`.
|
|
86
|
+
*/
|
|
87
|
+
readonly customFields?: Readonly<Record<string, CustomFieldValue | null>>
|
|
77
88
|
}
|
|
78
89
|
|
|
79
90
|
export function raiseBody(input: RaiseInput): Record<string, unknown> {
|
|
@@ -90,5 +101,7 @@ export function raiseBody(input: RaiseInput): Record<string, unknown> {
|
|
|
90
101
|
person_ids: input.personIds,
|
|
91
102
|
summary: input.summary,
|
|
92
103
|
tags: input.tags,
|
|
104
|
+
custom_fields:
|
|
105
|
+
input.customFields === undefined ? undefined : customFieldValuesBody(input.customFields),
|
|
93
106
|
})
|
|
94
107
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wire shape for `POST /v1/workspaces/:id/sample-data`.
|
|
5
|
+
*
|
|
6
|
+
* The endpoint takes no body. The response is a count per object type, so a
|
|
7
|
+
* caller can tell the reader what the button just did without a second query.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface SampleDataCounts {
|
|
11
|
+
readonly companies: number
|
|
12
|
+
readonly people: number
|
|
13
|
+
readonly positions: number
|
|
14
|
+
readonly deals: number
|
|
15
|
+
readonly planItems: number
|
|
16
|
+
readonly notes: number
|
|
17
|
+
readonly opportunities: number
|
|
18
|
+
readonly raises: number
|
|
19
|
+
readonly partnerships: number
|
|
20
|
+
readonly enquiries: number
|
|
21
|
+
readonly roles: number
|
|
22
|
+
readonly candidates: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const sampleDataCountsSchema: z.ZodType<SampleDataCounts, unknown> = z
|
|
26
|
+
.object({
|
|
27
|
+
companies: z.number().int().nonnegative(),
|
|
28
|
+
people: z.number().int().nonnegative(),
|
|
29
|
+
positions: z.number().int().nonnegative(),
|
|
30
|
+
deals: z.number().int().nonnegative(),
|
|
31
|
+
plan_items: z.number().int().nonnegative(),
|
|
32
|
+
notes: z.number().int().nonnegative(),
|
|
33
|
+
opportunities: z.number().int().nonnegative(),
|
|
34
|
+
raises: z.number().int().nonnegative(),
|
|
35
|
+
partnerships: z.number().int().nonnegative(),
|
|
36
|
+
enquiries: z.number().int().nonnegative(),
|
|
37
|
+
roles: z.number().int().nonnegative(),
|
|
38
|
+
candidates: z.number().int().nonnegative(),
|
|
39
|
+
})
|
|
40
|
+
.transform(
|
|
41
|
+
(wire): SampleDataCounts => ({
|
|
42
|
+
companies: wire.companies,
|
|
43
|
+
people: wire.people,
|
|
44
|
+
positions: wire.positions,
|
|
45
|
+
deals: wire.deals,
|
|
46
|
+
planItems: wire.plan_items,
|
|
47
|
+
notes: wire.notes,
|
|
48
|
+
opportunities: wire.opportunities,
|
|
49
|
+
raises: wire.raises,
|
|
50
|
+
partnerships: wire.partnerships,
|
|
51
|
+
enquiries: wire.enquiries,
|
|
52
|
+
roles: wire.roles,
|
|
53
|
+
candidates: wire.candidates,
|
|
54
|
+
}),
|
|
55
|
+
)
|
package/src/values.ts
CHANGED
|
@@ -90,6 +90,7 @@ export const EXTENSIBLE_RECORD_TYPES = [
|
|
|
90
90
|
'opportunity',
|
|
91
91
|
'partnership',
|
|
92
92
|
'raise',
|
|
93
|
+
'enquiry',
|
|
93
94
|
'role',
|
|
94
95
|
'candidate',
|
|
95
96
|
] as const
|
|
@@ -110,11 +111,24 @@ export const RECORD_TARGET_TYPES = [
|
|
|
110
111
|
'opportunity',
|
|
111
112
|
'partnership',
|
|
112
113
|
'raise',
|
|
114
|
+
'enquiry',
|
|
113
115
|
'candidate',
|
|
114
116
|
] as const
|
|
115
117
|
|
|
116
118
|
export type RecordTargetType = (typeof RECORD_TARGET_TYPES)[number]
|
|
117
119
|
|
|
120
|
+
/** Human labels for `RECORD_TARGET_TYPES`. Used by any picker that names a type. */
|
|
121
|
+
export const RECORD_TARGET_TYPE_LABELS: Readonly<Record<RecordTargetType, string>> = {
|
|
122
|
+
person: 'Person',
|
|
123
|
+
company: 'Company',
|
|
124
|
+
deal: 'Deal',
|
|
125
|
+
opportunity: 'Opportunity',
|
|
126
|
+
partnership: 'Partnership',
|
|
127
|
+
raise: 'Raise',
|
|
128
|
+
enquiry: 'Enquiry',
|
|
129
|
+
candidate: 'Candidate',
|
|
130
|
+
}
|
|
131
|
+
|
|
118
132
|
/**
|
|
119
133
|
* What `GET /v1/search` looks through, and the order its groups come back in.
|
|
120
134
|
*
|
|
@@ -131,6 +145,7 @@ export const SEARCH_COLLECTIONS = [
|
|
|
131
145
|
'person',
|
|
132
146
|
'role',
|
|
133
147
|
'company',
|
|
148
|
+
'enquiry',
|
|
134
149
|
'deal',
|
|
135
150
|
'opportunity',
|
|
136
151
|
'raise',
|
|
@@ -196,16 +211,23 @@ export const INTERVIEW_STAGE_LABELS: Readonly<Record<InterviewStage, string>> =
|
|
|
196
211
|
export const FIRST_INTERVIEW_STAGE: InterviewStage = INTERVIEW_STAGES[0]
|
|
197
212
|
|
|
198
213
|
/**
|
|
199
|
-
* The
|
|
214
|
+
* The five pipelines whose board columns live in `pipeline_stages`. A Deal moves
|
|
200
215
|
* through `deal` stages and so on; the kinds are fixed even though the stages
|
|
201
216
|
* within each are workspace-configurable.
|
|
202
217
|
*/
|
|
203
|
-
export const PIPELINE_KINDS = [
|
|
218
|
+
export const PIPELINE_KINDS = [
|
|
219
|
+
'enquiry',
|
|
220
|
+
'deal',
|
|
221
|
+
'opportunity',
|
|
222
|
+
'raise',
|
|
223
|
+
'partnership',
|
|
224
|
+
] as const
|
|
204
225
|
|
|
205
226
|
export type PipelineKind = (typeof PIPELINE_KINDS)[number]
|
|
206
227
|
|
|
207
228
|
/** Display names for `PIPELINE_KINDS`. "Fundraising" is what the nav calls a Raise. */
|
|
208
229
|
export const PIPELINE_KIND_LABELS: Readonly<Record<PipelineKind, string>> = {
|
|
230
|
+
enquiry: 'Enquiry',
|
|
209
231
|
deal: 'Deal',
|
|
210
232
|
opportunity: 'Opportunity',
|
|
211
233
|
raise: 'Fundraising',
|
|
@@ -302,24 +324,46 @@ export type FormStatus = (typeof FORM_STATUSES)[number]
|
|
|
302
324
|
* uploads, multi-page forms and branching out of scope, and every type here is
|
|
303
325
|
* one `<input>`, `<textarea>` or `<select>`.
|
|
304
326
|
*/
|
|
305
|
-
export const FORM_FIELD_TYPES = ['text', 'email', 'textarea', 'select'] as const
|
|
327
|
+
export const FORM_FIELD_TYPES = ['text', 'email', 'textarea', 'select', 'consent', 'notice'] as const
|
|
306
328
|
|
|
307
329
|
export type FormFieldType = (typeof FORM_FIELD_TYPES)[number]
|
|
308
330
|
|
|
331
|
+
/**
|
|
332
|
+
* The field types whose answer is stored as a boolean, not a string. A
|
|
333
|
+
* `consent` field renders as a checkbox whose label carries the consent
|
|
334
|
+
* statement; the answer records whether the visitor ticked it.
|
|
335
|
+
*/
|
|
336
|
+
export const FORM_BOOLEAN_FIELD_TYPES = ['consent'] as const
|
|
337
|
+
|
|
338
|
+
export type FormBooleanFieldType = (typeof FORM_BOOLEAN_FIELD_TYPES)[number]
|
|
339
|
+
|
|
309
340
|
/**
|
|
310
341
|
* Where a field's answer lands on submit.
|
|
311
342
|
*
|
|
312
343
|
* `position.title` rather than a person field, because a job title belongs to the
|
|
313
344
|
* Person↔Company link and nowhere else. `submission` stores the answer without
|
|
314
345
|
* writing any CRM record, which is what a free-text "How can we help?" wants.
|
|
346
|
+
*
|
|
347
|
+
* A form asks for a name the way its author wants it asked: one `person.name`
|
|
348
|
+
* box, or the `person.first_name` / `person.last_name` pair most sign-up forms
|
|
349
|
+
* use. Mapping the pair and no `person.name` composes the display name from the
|
|
350
|
+
* two, so neither arrangement makes a nameless person. `salutation` and `suffix`
|
|
351
|
+
* are storable on a Person but are not offered here — a public form asking for
|
|
352
|
+
* them is rare enough that the choice is not worth the room in this list.
|
|
315
353
|
*/
|
|
316
354
|
export const FORM_FIELD_MAP_TARGETS = [
|
|
317
355
|
'person.name',
|
|
356
|
+
'person.first_name',
|
|
357
|
+
'person.last_name',
|
|
318
358
|
'person.email',
|
|
359
|
+
'person.consent',
|
|
319
360
|
'company.name',
|
|
320
361
|
'company.domain',
|
|
321
362
|
'position.title',
|
|
363
|
+
'enquiry.name',
|
|
322
364
|
'deal.name',
|
|
365
|
+
'opportunity.name',
|
|
366
|
+
'partnership.name',
|
|
323
367
|
'submission',
|
|
324
368
|
] as const
|
|
325
369
|
|
|
@@ -327,14 +371,23 @@ export type FormFieldMapTarget = (typeof FORM_FIELD_MAP_TARGETS)[number]
|
|
|
327
371
|
|
|
328
372
|
export const FORM_FIELD_MAP_TARGET_LABELS: Readonly<Record<FormFieldMapTarget, string>> = {
|
|
329
373
|
'person.name': 'Person · name',
|
|
374
|
+
'person.first_name': 'Person · first name',
|
|
375
|
+
'person.last_name': 'Person · last name',
|
|
330
376
|
'person.email': 'Person · email',
|
|
377
|
+
'person.consent': 'Person · consent',
|
|
331
378
|
'company.name': 'Company · name',
|
|
332
379
|
'company.domain': 'Company · domain',
|
|
333
380
|
'position.title': 'Position · title',
|
|
381
|
+
'enquiry.name': 'Enquiry · name',
|
|
334
382
|
'deal.name': 'Deal · name',
|
|
383
|
+
'opportunity.name': 'Opportunity · name',
|
|
384
|
+
'partnership.name': 'Partnership · name',
|
|
335
385
|
submission: 'Submission only',
|
|
336
386
|
}
|
|
337
387
|
|
|
388
|
+
/** The map target for a consent field. Repeats per purpose, unlike `person.email`. */
|
|
389
|
+
export const PERSON_CONSENT_TARGET: FormFieldMapTarget = 'person.consent'
|
|
390
|
+
|
|
338
391
|
/** The one mapping a form cannot process without, and may carry at most once. */
|
|
339
392
|
export const PERSON_EMAIL_TARGET: FormFieldMapTarget = 'person.email'
|
|
340
393
|
|
|
@@ -348,6 +401,16 @@ export const FORM_OPTION_VALUE_TYPES = ['string', 'number', 'boolean'] as const
|
|
|
348
401
|
|
|
349
402
|
export type FormOptionValueType = (typeof FORM_OPTION_VALUE_TYPES)[number]
|
|
350
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Each post-submit action lands one of these on the submission's `action_log`.
|
|
406
|
+
* `ok` = the action ran; `skipped` = its precondition was absent (a company
|
|
407
|
+
* list on a submit that never resolved one); `error` = the savepoint rolled
|
|
408
|
+
* back so the rest of the submit could continue.
|
|
409
|
+
*/
|
|
410
|
+
export const FORM_ACTION_STATUSES = ['ok', 'skipped', 'error'] as const
|
|
411
|
+
|
|
412
|
+
export type FormActionStatus = (typeof FORM_ACTION_STATUSES)[number]
|
|
413
|
+
|
|
351
414
|
/**
|
|
352
415
|
* The domain events a webhook can subscribe to.
|
|
353
416
|
*
|
|
@@ -404,6 +467,7 @@ export const AGENT_TASK_TARGET_TYPES = [
|
|
|
404
467
|
'opportunity',
|
|
405
468
|
'partnership',
|
|
406
469
|
'raise',
|
|
470
|
+
'enquiry',
|
|
407
471
|
'candidate',
|
|
408
472
|
'role',
|
|
409
473
|
'handbook',
|
|
@@ -433,3 +497,116 @@ export const AGENT_RUN_STATUS_LABELS: Readonly<Record<AgentRunStatus, string>> =
|
|
|
433
497
|
succeeded: 'Succeeded',
|
|
434
498
|
failed: 'Failed',
|
|
435
499
|
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* The seven record types a workspace may attach custom field definitions to.
|
|
503
|
+
*
|
|
504
|
+
* The ones that already carry `tags` and agent-oriented fields on the record
|
|
505
|
+
* itself. Role and Candidate are absent on purpose: hiring state hangs off the
|
|
506
|
+
* Candidate link and a Role is a header rather than a rich record, so the demand
|
|
507
|
+
* signal for custom fields on either has not landed. Kept separate from
|
|
508
|
+
* `RECORD_TARGET_TYPES` and `EXTENSIBLE_RECORD_TYPES` so a later addition here
|
|
509
|
+
* cannot silently widen either of those.
|
|
510
|
+
*/
|
|
511
|
+
export const CUSTOM_FIELD_OBJECT_TYPES = [
|
|
512
|
+
'person',
|
|
513
|
+
'company',
|
|
514
|
+
'deal',
|
|
515
|
+
'opportunity',
|
|
516
|
+
'partnership',
|
|
517
|
+
'raise',
|
|
518
|
+
'enquiry',
|
|
519
|
+
] as const
|
|
520
|
+
|
|
521
|
+
export type CustomFieldObjectType = (typeof CUSTOM_FIELD_OBJECT_TYPES)[number]
|
|
522
|
+
|
|
523
|
+
export const CUSTOM_FIELD_OBJECT_TYPE_LABELS: Readonly<Record<CustomFieldObjectType, string>> = {
|
|
524
|
+
person: 'Person',
|
|
525
|
+
company: 'Company',
|
|
526
|
+
deal: 'Deal',
|
|
527
|
+
opportunity: 'Opportunity',
|
|
528
|
+
partnership: 'Partnership',
|
|
529
|
+
raise: 'Raise',
|
|
530
|
+
enquiry: 'Enquiry',
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* The nine editor types a custom field can carry.
|
|
535
|
+
*
|
|
536
|
+
* Deliberately distinct from `FORM_FIELD_TYPES`: a form asks a stranger for four
|
|
537
|
+
* kinds of text over one submit; a custom field is a first-class attribute on a
|
|
538
|
+
* record with numeric, date, boolean and currency answers. Sharing one list
|
|
539
|
+
* would drag either surface toward the other's shape.
|
|
540
|
+
*
|
|
541
|
+
* `record_reference` is intentionally absent from v1 — it needs the polymorphic
|
|
542
|
+
* existence check and delete-transaction cleanup that Phase 2 owns.
|
|
543
|
+
*/
|
|
544
|
+
export const CUSTOM_FIELD_TYPES = [
|
|
545
|
+
'text',
|
|
546
|
+
'long_text',
|
|
547
|
+
'number',
|
|
548
|
+
'currency',
|
|
549
|
+
'date',
|
|
550
|
+
'checkbox',
|
|
551
|
+
'select',
|
|
552
|
+
'multi_select',
|
|
553
|
+
'url',
|
|
554
|
+
] as const
|
|
555
|
+
|
|
556
|
+
export type CustomFieldType = (typeof CUSTOM_FIELD_TYPES)[number]
|
|
557
|
+
|
|
558
|
+
export const CUSTOM_FIELD_TYPE_LABELS: Readonly<Record<CustomFieldType, string>> = {
|
|
559
|
+
text: 'Text',
|
|
560
|
+
long_text: 'Long text',
|
|
561
|
+
number: 'Number',
|
|
562
|
+
currency: 'Currency',
|
|
563
|
+
date: 'Date',
|
|
564
|
+
checkbox: 'Checkbox',
|
|
565
|
+
select: 'Select',
|
|
566
|
+
multi_select: 'Multi-select',
|
|
567
|
+
url: 'URL',
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* The custom-field types whose definition carries an `options` list. Every
|
|
572
|
+
* other type stores its `options` as `[]` and any non-empty write is `422`.
|
|
573
|
+
*/
|
|
574
|
+
export const CUSTOM_FIELD_TYPES_WITH_OPTIONS = ['select', 'multi_select'] as const
|
|
575
|
+
|
|
576
|
+
export type CustomFieldTypeWithOptions = (typeof CUSTOM_FIELD_TYPES_WITH_OPTIONS)[number]
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* The default a consent purpose starts with, and what a person without an
|
|
580
|
+
* explicit `person_consents` row inherits for that purpose. `unknown` is the
|
|
581
|
+
* safest default — silence about a person's wishes is not a grant.
|
|
582
|
+
*/
|
|
583
|
+
export const CONSENT_PURPOSE_STATUSES = ['unknown', 'granted', 'withdrawn'] as const
|
|
584
|
+
|
|
585
|
+
export type ConsentPurposeStatus = (typeof CONSENT_PURPOSE_STATUSES)[number]
|
|
586
|
+
|
|
587
|
+
export const CONSENT_PURPOSE_STATUS_LABELS: Readonly<Record<ConsentPurposeStatus, string>> = {
|
|
588
|
+
unknown: 'Unknown',
|
|
589
|
+
granted: 'Granted',
|
|
590
|
+
withdrawn: 'Withdrawn',
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* The explicit status on a `person_consents` row. `unknown` is deliberately
|
|
595
|
+
* absent: no row is the unknown, so a stored row always carries a decision.
|
|
596
|
+
*/
|
|
597
|
+
export const CONSENT_STATUSES = ['granted', 'withdrawn'] as const
|
|
598
|
+
|
|
599
|
+
export type ConsentStatus = (typeof CONSENT_STATUSES)[number]
|
|
600
|
+
|
|
601
|
+
export const CONSENT_STATUS_LABELS: Readonly<Record<ConsentStatus, string>> = {
|
|
602
|
+
granted: 'Granted',
|
|
603
|
+
withdrawn: 'Withdrawn',
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Where a `person_consents` row came from. `form:<form_id>` and `list:<list_id>`
|
|
608
|
+
* are prefixed so the origin is inspectable; `import` and `manual` need no id.
|
|
609
|
+
*/
|
|
610
|
+
export const CONSENT_SOURCE_KINDS = ['form', 'list', 'import', 'manual'] as const
|
|
611
|
+
|
|
612
|
+
export type ConsentSourceKind = (typeof CONSENT_SOURCE_KINDS)[number]
|