@servicetitan/dte-pdf-editor 1.86.0 → 1.88.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 (96) hide show
  1. package/README.md +37 -30
  2. package/dist/components/display-conditions/condition-row.d.ts.map +1 -1
  3. package/dist/components/display-conditions/condition-row.js +51 -22
  4. package/dist/components/display-conditions/condition-row.js.map +1 -1
  5. package/dist/components/display-conditions/condition-value-control.d.ts.map +1 -1
  6. package/dist/components/display-conditions/condition-value-control.js +1 -2
  7. package/dist/components/display-conditions/condition-value-control.js.map +1 -1
  8. package/dist/components/display-conditions/display-condition-modal.d.ts.map +1 -1
  9. package/dist/components/display-conditions/display-condition-modal.js +6 -49
  10. package/dist/components/display-conditions/display-condition-modal.js.map +1 -1
  11. package/dist/components/field-config-panel/e-sign-date-rules-summary.d.ts +11 -0
  12. package/dist/components/field-config-panel/e-sign-date-rules-summary.d.ts.map +1 -0
  13. package/dist/components/field-config-panel/e-sign-date-rules-summary.js +36 -0
  14. package/dist/components/field-config-panel/e-sign-date-rules-summary.js.map +1 -0
  15. package/dist/components/field-config-panel/e-sign-date-signed-offset-modal.d.ts +16 -0
  16. package/dist/components/field-config-panel/e-sign-date-signed-offset-modal.d.ts.map +1 -0
  17. package/dist/components/field-config-panel/e-sign-date-signed-offset-modal.js +62 -0
  18. package/dist/components/field-config-panel/e-sign-date-signed-offset-modal.js.map +1 -0
  19. package/dist/components/field-config-panel/e-sign-offset-rule-row.d.ts +16 -0
  20. package/dist/components/field-config-panel/e-sign-offset-rule-row.d.ts.map +1 -0
  21. package/dist/components/field-config-panel/e-sign-offset-rule-row.js +25 -0
  22. package/dist/components/field-config-panel/e-sign-offset-rule-row.js.map +1 -0
  23. package/dist/components/field-config-panel/e-sign-options-modal.d.ts +15 -0
  24. package/dist/components/field-config-panel/e-sign-options-modal.d.ts.map +1 -0
  25. package/dist/components/field-config-panel/e-sign-options-modal.js +26 -0
  26. package/dist/components/field-config-panel/e-sign-options-modal.js.map +1 -0
  27. package/dist/components/field-config-panel/field-config-panel.d.ts.map +1 -1
  28. package/dist/components/field-config-panel/field-config-panel.js +30 -28
  29. package/dist/components/field-config-panel/field-config-panel.js.map +1 -1
  30. package/dist/components/field-sidebar/e-sign-field-type-list.d.ts.map +1 -1
  31. package/dist/components/field-sidebar/e-sign-field-type-list.js +1 -6
  32. package/dist/components/field-sidebar/e-sign-field-type-list.js.map +1 -1
  33. package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.d.ts.map +1 -1
  34. package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.js +22 -8
  35. package/dist/components/pdf-fields-overlay/pdf-overlay-field-fillable.js.map +1 -1
  36. package/dist/components/pdf-view/pdf-view-fillable.d.ts.map +1 -1
  37. package/dist/components/pdf-view/pdf-view-fillable.js +3 -3
  38. package/dist/components/pdf-view/pdf-view-fillable.js.map +1 -1
  39. package/dist/constants/field.constants.d.ts +1 -5
  40. package/dist/constants/field.constants.d.ts.map +1 -1
  41. package/dist/constants/field.constants.js +21 -7
  42. package/dist/constants/field.constants.js.map +1 -1
  43. package/dist/hooks/usePdfFieldDnD.d.ts.map +1 -1
  44. package/dist/hooks/usePdfFieldDnD.js +13 -5
  45. package/dist/hooks/usePdfFieldDnD.js.map +1 -1
  46. package/dist/interface/types.d.ts +26 -1
  47. package/dist/interface/types.d.ts.map +1 -1
  48. package/dist/interface/types.js +1 -0
  49. package/dist/interface/types.js.map +1 -1
  50. package/dist/styles/field-config-panel.css +11 -0
  51. package/dist/styles/field-type.css +12 -0
  52. package/dist/styles/index.css +1 -0
  53. package/dist/styles/variables.css +2 -0
  54. package/dist/utils/conditions/condition-match-type.utils.d.ts +14 -0
  55. package/dist/utils/conditions/condition-match-type.utils.d.ts.map +1 -0
  56. package/dist/utils/conditions/condition-match-type.utils.js +50 -0
  57. package/dist/utils/conditions/condition-match-type.utils.js.map +1 -0
  58. package/dist/utils/conditions/e-sign-offset-rules.utils.d.ts +12 -0
  59. package/dist/utils/conditions/e-sign-offset-rules.utils.d.ts.map +1 -0
  60. package/dist/utils/conditions/e-sign-offset-rules.utils.js +19 -0
  61. package/dist/utils/conditions/e-sign-offset-rules.utils.js.map +1 -0
  62. package/dist/utils/conditions/index.d.ts +2 -0
  63. package/dist/utils/conditions/index.d.ts.map +1 -1
  64. package/dist/utils/conditions/index.js +2 -0
  65. package/dist/utils/conditions/index.js.map +1 -1
  66. package/dist/utils/field/field-placeholder-text.utils.d.ts +1 -0
  67. package/dist/utils/field/field-placeholder-text.utils.d.ts.map +1 -1
  68. package/dist/utils/field/field-placeholder-text.utils.js +9 -4
  69. package/dist/utils/field/field-placeholder-text.utils.js.map +1 -1
  70. package/package.json +1 -1
  71. package/src/__tests__/field-types/custom-fields.test.ts +187 -0
  72. package/src/components/display-conditions/condition-row.tsx +114 -131
  73. package/src/components/display-conditions/condition-value-control.tsx +1 -2
  74. package/src/components/display-conditions/display-condition-modal.tsx +13 -63
  75. package/src/components/field-config-panel/e-sign-date-rules-summary.tsx +114 -0
  76. package/src/components/field-config-panel/e-sign-date-signed-offset-modal.tsx +197 -0
  77. package/src/components/field-config-panel/e-sign-offset-rule-row.tsx +91 -0
  78. package/src/components/field-config-panel/e-sign-options-modal.tsx +112 -0
  79. package/src/components/field-config-panel/field-config-panel.tsx +70 -91
  80. package/src/components/field-sidebar/e-sign-field-type-list.tsx +11 -14
  81. package/src/components/pdf-fields-overlay/pdf-overlay-field-fillable.tsx +46 -14
  82. package/src/components/pdf-view/pdf-view-fillable.tsx +3 -2
  83. package/src/constants/field.constants.ts +21 -8
  84. package/src/hooks/usePdfFieldDnD.ts +17 -2
  85. package/src/interface/types.ts +34 -1
  86. package/src/styles/field-config-panel.css +11 -0
  87. package/src/styles/field-type.css +12 -0
  88. package/src/styles/index.css +1 -0
  89. package/src/styles/variables.css +2 -0
  90. package/src/utils/conditions/__tests__/condition-match-type.utils.test.ts +104 -0
  91. package/src/utils/conditions/__tests__/e-sign-offset-rules.utils.test.ts +51 -0
  92. package/src/utils/conditions/condition-match-type.utils.ts +80 -0
  93. package/src/utils/conditions/e-sign-offset-rules.utils.ts +32 -0
  94. package/src/utils/conditions/index.ts +2 -0
  95. package/src/utils/field/__tests__/field-placeholder-text.utils.test.ts +17 -1
  96. package/src/utils/field/field-placeholder-text.utils.ts +10 -4
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Why not `DisplayConditionModal`:
3
+ *
4
+ * `DisplayConditionModal` manages exactly one `DisplayConditionState` (a show/hide behavior
5
+ * toggle plus a list of condition groups) for a single field. `ESignDateSignedOffsetSettings` is
6
+ * a different shape entirely: a *list* of independent `{ days, condition }` rules, with no
7
+ * show/hide concept at all (`ESignDateSignedOffsetSettingsRule.condition` is "only show" by
8
+ * contract). The trailing, condition-less "else" row required by that settings type is owned by
9
+ * `ESignOptionsModal` (its own always-visible "Day offset" field) — this modal only ever manages
10
+ * the conditioned rules that precede it.
11
+ *
12
+ * Instead, this modal follows the same Dialog/Header/Content/Footer structure and reuses the same
13
+ * building blocks `DisplayConditionModal` is built from: `ConditionGroup`/`ConditionRow` for the
14
+ * condition-editing UI, `getSchemaDataPointOptions`/`getDocumentFieldsDataPointOptions` for
15
+ * data-point resolution, and `isValidConditionGroups` extracted from `DisplayConditionModal`.
16
+ * Nothing ESign-specific was added to any of those shared pieces.
17
+ */
18
+ import { Button, Chip, Dialog, Flex, NumberField, Text } from '@servicetitan/anvil2';
19
+ import PlusCircleIcon from '@servicetitan/anvil2/assets/icons/material/round/add_circle.svg';
20
+ import { FC, useCallback, useMemo, useState } from 'react';
21
+ import { MODAL_CONTENT_MAX_HEIGHT } from '../../constants';
22
+ import type {
23
+ ESignDateSignedOffsetSettingsFinalRule,
24
+ ESignDateSignedOffsetSettingsRule,
25
+ FormFieldsByFormIdI,
26
+ FormInfo,
27
+ PdfField,
28
+ SchemaObject,
29
+ } from '../../interface/types';
30
+ import {
31
+ addESignOffsetRule,
32
+ canAddESignOffsetRule,
33
+ createESignOffsetRule,
34
+ getDocumentFieldsDataPointOptions,
35
+ getSchemaDataPointOptions,
36
+ isValidConditionGroups,
37
+ updateESignOffsetRule,
38
+ } from '../../utils';
39
+ import { ESignOffsetRuleRow } from './e-sign-offset-rule-row';
40
+
41
+ export interface ESignDateSignedOffsetModalProps {
42
+ onClose: () => void;
43
+ onSave: (
44
+ state: ESignDateSignedOffsetSettingsRule[],
45
+ finalRule: ESignDateSignedOffsetSettingsFinalRule,
46
+ ) => void;
47
+ initialState: ESignDateSignedOffsetSettingsRule[];
48
+ finalRule: ESignDateSignedOffsetSettingsFinalRule;
49
+ schema?: SchemaObject;
50
+ documentFields?: PdfField[];
51
+ forms?: FormInfo[];
52
+ formFieldsByFormId?: FormFieldsByFormIdI;
53
+ onRequestFormFields?: (formId: number) => void;
54
+ formFieldsLoadingFormId?: number | null;
55
+ }
56
+
57
+ export const ESignDateSignedOffsetModal: FC<ESignDateSignedOffsetModalProps> = ({
58
+ documentFields,
59
+ finalRule,
60
+ formFieldsByFormId,
61
+ formFieldsLoadingFormId,
62
+ forms,
63
+ initialState,
64
+ onClose,
65
+ onRequestFormFields,
66
+ onSave,
67
+ schema,
68
+ }) => {
69
+ const [rules, setRules] = useState<ESignDateSignedOffsetSettingsRule[]>(
70
+ initialState.length === 0 ? [createESignOffsetRule()] : initialState,
71
+ );
72
+ const [finalRuleDays, setFinalRuleDays] = useState<number>(finalRule.days);
73
+
74
+ const mergeTagOptions = useMemo(() => getSchemaDataPointOptions(schema), [schema]);
75
+
76
+ const fillableOptions = useMemo(
77
+ () => getDocumentFieldsDataPointOptions(documentFields),
78
+ [documentFields],
79
+ );
80
+
81
+ const handleClose = useCallback(() => onClose(), [onClose]);
82
+
83
+ const handleSave = useCallback(() => {
84
+ onSave(rules, { days: finalRuleDays });
85
+ onClose();
86
+ }, [finalRuleDays, onClose, onSave, rules]);
87
+
88
+ const handleFinalRuleDaysChange = useCallback((days: number | null) => {
89
+ setFinalRuleDays(Math.max(Math.min(365, days ?? 0), 0));
90
+ }, []);
91
+
92
+ const handleAddRule = useCallback(() => {
93
+ setRules(prev => addESignOffsetRule(prev));
94
+ }, []);
95
+
96
+ const handleRemoveRule = useCallback((index: number) => {
97
+ setRules(prev => prev.filter((_, i) => i !== index));
98
+ }, []);
99
+
100
+ const handleUpdateRule = useCallback(
101
+ (index: number, rule: ESignDateSignedOffsetSettingsRule) => {
102
+ setRules(prev => updateESignOffsetRule(prev, index, rule));
103
+ },
104
+ [],
105
+ );
106
+
107
+ const canSave = useMemo(
108
+ () =>
109
+ rules.every(rule =>
110
+ isValidConditionGroups(rule.condition.groups, {
111
+ fillableOptions,
112
+ formFieldsByFormId,
113
+ mergeTagOptions,
114
+ }),
115
+ ),
116
+ [fillableOptions, formFieldsByFormId, mergeTagOptions, rules],
117
+ );
118
+
119
+ return (
120
+ <Dialog open onClose={handleClose} size="xlarge">
121
+ <Dialog.Header>Date Signed Offset Rules</Dialog.Header>
122
+ <Dialog.Content>
123
+ <Flex
124
+ direction="column"
125
+ gap="4"
126
+ flex={1}
127
+ style={{
128
+ maxHeight: MODAL_CONTENT_MAX_HEIGHT,
129
+ overflowY: 'auto',
130
+ }}
131
+ >
132
+ {rules.length === 0 && (
133
+ <Flex justifyContent="center" alignItems="center">
134
+ <Text variant="body" size="xlarge" subdued>
135
+ Rule not configured yet.
136
+ </Text>
137
+ </Flex>
138
+ )}
139
+
140
+ {rules.map((rule, index) => (
141
+ <ESignOffsetRuleRow
142
+ key={rule.condition.groups[0]?.id ?? index}
143
+ onDelete={() => handleRemoveRule(index)}
144
+ fillableOptions={fillableOptions}
145
+ formFieldsByFormId={formFieldsByFormId}
146
+ formFieldsLoadingFormId={formFieldsLoadingFormId}
147
+ forms={forms}
148
+ mergeTagOptions={mergeTagOptions}
149
+ onChange={next => handleUpdateRule(index, next)}
150
+ onRequestFormFields={onRequestFormFields}
151
+ rule={rule}
152
+ canRemove={rules.length > 1}
153
+ />
154
+ ))}
155
+ <Button
156
+ size="small"
157
+ appearance="ghost"
158
+ icon={{ before: PlusCircleIcon }}
159
+ onClick={handleAddRule}
160
+ disabled={!canAddESignOffsetRule(rules)}
161
+ >
162
+ Add Else Condition
163
+ </Button>
164
+
165
+ <Flex className="dte-rule-card">
166
+ <Flex
167
+ alignItems="center"
168
+ style={{
169
+ padding: '8px 12px',
170
+ }}
171
+ >
172
+ <Chip label="THEN" size="medium" />
173
+ </Flex>
174
+ <Flex direction="column" gap="1">
175
+ <NumberField
176
+ size="small"
177
+ label="Number of days"
178
+ value={finalRuleDays}
179
+ onChange={handleFinalRuleDaysChange}
180
+ suffix="days"
181
+ />
182
+ <Text variant="body" size="small" subdued>
183
+ days range up to 365
184
+ </Text>
185
+ </Flex>
186
+ </Flex>
187
+ </Flex>
188
+ </Dialog.Content>
189
+ <Dialog.Footer sticky>
190
+ <Dialog.CancelButton onClick={handleClose}>Cancel</Dialog.CancelButton>
191
+ <Button appearance="primary" disabled={!canSave} onClick={handleSave}>
192
+ Save Changes
193
+ </Button>
194
+ </Dialog.Footer>
195
+ </Dialog>
196
+ );
197
+ };
@@ -0,0 +1,91 @@
1
+ import { Chip, Flex, NumberField, Text } from '@servicetitan/anvil2';
2
+ import { FC } from 'react';
3
+ import { defaultCondition, defaultGroup } from '../../constants';
4
+ import type {
5
+ DataPointOption,
6
+ ESignDateSignedOffsetSettingsRule,
7
+ FormFieldsByFormIdI,
8
+ FormInfo,
9
+ } from '../../interface/types';
10
+ import { ConditionRow } from '../display-conditions/condition-row';
11
+
12
+ export interface ESignOffsetRuleRowProps {
13
+ canRemove: boolean;
14
+ rule: ESignDateSignedOffsetSettingsRule;
15
+ onChange: (rule: ESignDateSignedOffsetSettingsRule) => void;
16
+ onDelete: () => void;
17
+ fillableOptions: DataPointOption[];
18
+ mergeTagOptions: DataPointOption[];
19
+ forms?: FormInfo[];
20
+ formFieldsByFormId?: FormFieldsByFormIdI;
21
+ onRequestFormFields?: (formId: number) => void;
22
+ formFieldsLoadingFormId?: number | null;
23
+ }
24
+
25
+ export const ESignOffsetRuleRow: FC<ESignOffsetRuleRowProps> = ({
26
+ canRemove,
27
+ fillableOptions,
28
+ formFieldsByFormId,
29
+ formFieldsLoadingFormId,
30
+ forms,
31
+ mergeTagOptions,
32
+ onChange,
33
+ onDelete,
34
+ onRequestFormFields,
35
+ rule,
36
+ }) => {
37
+ const handleDaysChange = (days: number | null) => {
38
+ onChange({ ...rule, days: days ?? 0 });
39
+ };
40
+
41
+ const group = rule.condition.groups[0];
42
+ const condition = group?.conditions[0] ?? defaultCondition();
43
+
44
+ const handleConditionChange = (nextCondition: typeof condition) => {
45
+ onChange({
46
+ ...rule,
47
+ condition: {
48
+ ...rule.condition,
49
+ groups: [{ ...(group ?? defaultGroup()), conditions: [nextCondition] }],
50
+ },
51
+ });
52
+ };
53
+
54
+ return (
55
+ <Flex direction="column" gap="3" className="dte-rule-card">
56
+ <ConditionRow
57
+ canRemove={canRemove}
58
+ condition={condition}
59
+ fillableOptions={fillableOptions}
60
+ formFieldsByFormId={formFieldsByFormId}
61
+ formFieldsLoadingFormId={formFieldsLoadingFormId}
62
+ forms={forms}
63
+ mergeTagOptions={mergeTagOptions}
64
+ onChange={handleConditionChange}
65
+ onRemove={onDelete}
66
+ onRequestFormFields={onRequestFormFields}
67
+ />
68
+ <Flex>
69
+ <Flex
70
+ alignItems="center"
71
+ style={{
72
+ padding: '8px 12px',
73
+ }}
74
+ >
75
+ <Chip label="THEN" size="medium" />
76
+ </Flex>
77
+ <Flex direction="column" gap="1">
78
+ <NumberField
79
+ label="Number of days"
80
+ value={rule.days}
81
+ onChange={handleDaysChange}
82
+ suffix="days"
83
+ />
84
+ <Text variant="body" size="small" subdued>
85
+ days range up to 365
86
+ </Text>
87
+ </Flex>
88
+ </Flex>
89
+ </Flex>
90
+ );
91
+ };
@@ -0,0 +1,112 @@
1
+ import { Button, Flex, NumberField } from '@servicetitan/anvil2';
2
+ import AddIcon from '@servicetitan/anvil2/assets/icons/material/round/add_circle.svg';
3
+ import { FC, Fragment, useState } from 'react';
4
+ import {
5
+ ESignDateSignedOffsetSettings,
6
+ ESignDateSignedOffsetSettingsFinalRule,
7
+ ESignDateSignedOffsetSettingsRule,
8
+ FormFieldsByFormIdI,
9
+ FormInfo,
10
+ PdfField,
11
+ SchemaObject,
12
+ } from '../../interface/types';
13
+ import { ESignDateRulesSummary } from './e-sign-date-rules-summary';
14
+ import { ESignDateSignedOffsetModal } from './e-sign-date-signed-offset-modal';
15
+
16
+ interface ESignOptionsModalProps {
17
+ initialState: ESignDateSignedOffsetSettings;
18
+ onSave: (state: ESignDateSignedOffsetSettings) => void;
19
+ schema?: SchemaObject;
20
+ documentFields?: PdfField[];
21
+ forms?: FormInfo[];
22
+ formFieldsByFormId?: FormFieldsByFormIdI;
23
+ onRequestFormFields?: (formId: number) => void;
24
+ formFieldsLoadingFormId?: number | null;
25
+ }
26
+
27
+ export const ESignOptionsModal: FC<ESignOptionsModalProps> = ({
28
+ documentFields,
29
+ formFieldsByFormId,
30
+ formFieldsLoadingFormId,
31
+ forms,
32
+ initialState,
33
+ onRequestFormFields,
34
+ onSave,
35
+ schema,
36
+ }) => {
37
+ const [open, setOpen] = useState(false);
38
+ /*
39
+ * `rules` is a tuple: zero or more conditioned rules followed by exactly one condition-less
40
+ * final rule (enforced by `ESignDateSignedOffsetSettings['rules']`). Slicing/indexing a
41
+ * variadic tuple widens to the member union, so the split needs one explicit, deliberate cast.
42
+ */
43
+ const ruleWithOutCondition = initialState.rules[
44
+ initialState.rules.length - 1
45
+ ] as ESignDateSignedOffsetSettingsFinalRule;
46
+ const rulesWithConditions = initialState.rules.slice(
47
+ 0,
48
+ -1,
49
+ ) as ESignDateSignedOffsetSettingsRule[];
50
+
51
+ const handleDaysChange = (days: number | null) => {
52
+ onSave({
53
+ rules: [...rulesWithConditions, { days: Math.max(Math.min(365, days ?? 0), 0) }],
54
+ });
55
+ };
56
+
57
+ const handleSaveRules = (
58
+ rules: ESignDateSignedOffsetSettingsRule[],
59
+ finalRule: ESignDateSignedOffsetSettingsFinalRule,
60
+ ) => {
61
+ onSave({ rules: [...rules, finalRule] });
62
+ };
63
+
64
+ return (
65
+ <Fragment>
66
+ {rulesWithConditions.length === 0 ? (
67
+ <Flex gap={1} direction="column" justifyContent="space-between">
68
+ <NumberField
69
+ size="small"
70
+ label="Number of days"
71
+ value={ruleWithOutCondition.days}
72
+ onChange={handleDaysChange}
73
+ suffix="days"
74
+ width="100%"
75
+ />
76
+
77
+ <Button
78
+ appearance="ghost"
79
+ size="small"
80
+ icon={AddIcon}
81
+ onClick={() => setOpen(true)}
82
+ style={{ color: '#0265DC' }}
83
+ >
84
+ Set Date Rule
85
+ </Button>
86
+ </Flex>
87
+ ) : (
88
+ <ESignDateRulesSummary
89
+ documentFields={documentFields}
90
+ onEdit={() => setOpen(true)}
91
+ rulesWithConditions={rulesWithConditions}
92
+ schema={schema}
93
+ elseDays={ruleWithOutCondition.days}
94
+ />
95
+ )}
96
+ {open && (
97
+ <ESignDateSignedOffsetModal
98
+ onClose={() => setOpen(false)}
99
+ onSave={handleSaveRules}
100
+ documentFields={documentFields}
101
+ finalRule={ruleWithOutCondition}
102
+ formFieldsByFormId={formFieldsByFormId}
103
+ formFieldsLoadingFormId={formFieldsLoadingFormId}
104
+ forms={forms}
105
+ initialState={rulesWithConditions}
106
+ onRequestFormFields={onRequestFormFields}
107
+ schema={schema}
108
+ />
109
+ )}
110
+ </Fragment>
111
+ );
112
+ };
@@ -1,7 +1,8 @@
1
- import { Button, Checkbox, Combobox, Flex, Textarea, TextField } from '@servicetitan/anvil2';
1
+ import { Button, Checkbox, Flex, SelectFieldSync, Textarea, TextField } from '@servicetitan/anvil2';
2
2
  import { FC, useMemo } from 'react';
3
- import { E_SIGN_FIELD_TYPE_OPTIONS, FIELD_FONT_SIZE_OPTIONS } from '../../constants';
3
+ import { FIELD_FONT_SIZE_OPTIONS } from '../../constants';
4
4
  import {
5
+ ESignDateSignedOffsetSettings,
5
6
  ESignFieldType,
6
7
  FieldTypeEnum,
7
8
  type FormFieldsByFormIdI,
@@ -19,6 +20,7 @@ import {
19
20
  supportsFieldFontSize,
20
21
  } from '../../utils';
21
22
  import { DisplayCondition } from '../display-conditions/display-condition';
23
+ import { ESignOptionsModal } from './e-sign-options-modal';
22
24
  import { FormulaGenerator } from './formula-generator';
23
25
  import { TableConfigs } from './table-configs';
24
26
 
@@ -56,39 +58,53 @@ export const FieldConfigPanel: FC<FieldConfigPanelProps> = ({
56
58
  }) => {
57
59
  const recipientOptions = useMemo(
58
60
  () =>
59
- field.type === FieldTypeEnum.fillable
61
+ (field.type === FieldTypeEnum.fillable
60
62
  ? getFillableFieldsRecipients(recipients)
61
- : recipients,
63
+ : recipients
64
+ ).map(recipient => ({ id: recipient.name, label: recipient.displayName })),
62
65
  [recipients, field.type],
63
66
  );
64
67
 
65
68
  const selectedRecipient = useMemo(
66
- () => recipientOptions.find(recipient => recipient.name === field.recipient) ?? null,
69
+ () => recipientOptions.find(option => option.id === field.recipient) ?? null,
67
70
  [recipientOptions, field.recipient],
68
71
  );
69
72
 
70
73
  const isRadioGroupSibling = (documentField: PdfField) =>
71
74
  documentField.subType === 'radio' && documentField.path === field.path;
72
75
 
73
- const handleRecipientChange = (item: RecipientInfo | null) => {
76
+ const isDateSignedOffsetSibling = (documentField: PdfField) =>
77
+ documentField.type === FieldTypeEnum.eSign &&
78
+ documentField.subType === ESignFieldType.dateSignedOffset;
79
+
80
+ const handleDateSignedOffsetSave = (data: ESignDateSignedOffsetSettings) => {
81
+ if (onMatchingFieldsConfigChange) {
82
+ onMatchingFieldsConfigChange(isDateSignedOffsetSibling, { data });
83
+ return;
84
+ }
85
+
86
+ onFieldConfigChange({ data });
87
+ };
88
+
89
+ const handleRecipientChange = (item: { id: string; label: string } | null) => {
74
90
  if (!item) {
75
91
  return;
76
92
  }
77
93
 
78
94
  let path = field.path;
79
95
  if (field.type === FieldTypeEnum.eSign) {
80
- path = generateESignPath(item.name, field.subType as ESignFieldType);
96
+ path = generateESignPath(item.id, field.subType as ESignFieldType);
81
97
  }
82
98
  if (field.type === FieldTypeEnum.fillable) {
83
99
  const pathName =
84
100
  field.subType === 'radio' && field.path
85
101
  ? parseFillablePathName(field.path)
86
102
  : field.id;
87
- path = generateFillablePath(item.name, pathName);
103
+ path = generateFillablePath(item.id, pathName);
88
104
  }
89
105
 
90
106
  const updates = {
91
- recipient: item.name,
107
+ recipient: item.id,
92
108
  path,
93
109
  };
94
110
 
@@ -105,31 +121,25 @@ export const FieldConfigPanel: FC<FieldConfigPanelProps> = ({
105
121
  onFieldConfigChange(updates);
106
122
  };
107
123
 
108
- const selectedESignFieldType = useMemo(
109
- () => E_SIGN_FIELD_TYPE_OPTIONS.find(option => option.id === field.subType) ?? null,
110
- [field],
111
- );
124
+ const showFontSizeControl = supportsFieldFontSize(field);
112
125
 
113
- const handleESignFieldTypeChange = (item: { id: ESignFieldType; name: string } | null) => {
114
- if (item) {
115
- onFieldConfigChange({
116
- subType: item.id,
117
- path: generateESignPath(field.recipient!, item.id),
118
- });
119
- }
120
- };
126
+ const fontSizeOptions = useMemo(
127
+ () => FIELD_FONT_SIZE_OPTIONS.map(size => ({ id: size, label: size.toString() })),
128
+ [],
129
+ );
121
130
 
122
- const showFontSizeControl = supportsFieldFontSize(field);
131
+ const selectedFontSize = useMemo(
132
+ () => fontSizeOptions.find(option => option.id === getFieldFontSize(field)) ?? null,
133
+ [fontSizeOptions, field],
134
+ );
123
135
 
124
- const handleFontSizeChange = (item: number | null) => {
125
- if (item) {
126
- onFieldConfigChange({
127
- styles: {
128
- ...field.styles,
129
- fontSize: item,
130
- },
131
- });
132
- }
136
+ const handleFontSizeChange = (item: { id: number; label: string }) => {
137
+ onFieldConfigChange({
138
+ styles: {
139
+ ...field.styles,
140
+ fontSize: item.id,
141
+ },
142
+ });
133
143
  };
134
144
 
135
145
  const handleRadioGroupNameChange = (groupName: string) => {
@@ -184,47 +194,14 @@ export const FieldConfigPanel: FC<FieldConfigPanelProps> = ({
184
194
  />
185
195
  )}
186
196
  {(field.type === FieldTypeEnum.fillable || field.type === FieldTypeEnum.eSign) && (
187
- <Combobox
188
- items={recipientOptions}
189
- itemToString={item => item?.displayName ?? ''}
190
- onChange={handleRecipientChange}
191
- selectedItem={selectedRecipient}
192
- filterOptions={{ keys: ['displayName'] }}
193
- >
194
- <Combobox.SearchField placeholder="" label="Recipient" />
195
- <Combobox.Content>
196
- {({ items }) => (
197
- <Combobox.List>
198
- {items.map((item, i) => (
199
- <Combobox.Item key={item.name} item={item} index={i}>
200
- {item.displayName}
201
- </Combobox.Item>
202
- ))}
203
- </Combobox.List>
204
- )}
205
- </Combobox.Content>
206
- </Combobox>
207
- )}
208
- {field.type === FieldTypeEnum.eSign && (
209
- <Combobox
210
- items={E_SIGN_FIELD_TYPE_OPTIONS}
211
- itemToString={item => item?.name ?? ''}
212
- onChange={handleESignFieldTypeChange}
213
- selectedItem={selectedESignFieldType}
214
- >
215
- <Combobox.SearchField label="Field Type" />
216
- <Combobox.Content>
217
- {({ items }) => (
218
- <Combobox.List>
219
- {items.map((item, i) => (
220
- <Combobox.Item key={item.id} item={item} index={i}>
221
- {item.name}
222
- </Combobox.Item>
223
- ))}
224
- </Combobox.List>
225
- )}
226
- </Combobox.Content>
227
- </Combobox>
197
+ <SelectFieldSync
198
+ label="Recipient"
199
+ disableClearButton
200
+ options={recipientOptions}
201
+ value={selectedRecipient}
202
+ onSelectedOptionChange={handleRecipientChange}
203
+ filter={{ keys: ['label'] }}
204
+ />
228
205
  )}
229
206
  {field.type !== FieldTypeEnum.generic && (
230
207
  <TextField label="Data Path" value={field.path} disabled />
@@ -271,31 +248,33 @@ export const FieldConfigPanel: FC<FieldConfigPanelProps> = ({
271
248
  )}
272
249
 
273
250
  {showFontSizeControl && (
274
- <Combobox
275
- items={FIELD_FONT_SIZE_OPTIONS}
276
- itemToString={item => item?.toString() ?? ''}
277
- onChange={handleFontSizeChange}
278
- selectedItem={getFieldFontSize(field)}
279
- >
280
- <Combobox.SearchField label="Font Size" />
281
- <Combobox.Content>
282
- {({ items }) => (
283
- <Combobox.List>
284
- {items.map((item, i) => (
285
- <Combobox.Item key={item} item={item} index={i}>
286
- {item}
287
- </Combobox.Item>
288
- ))}
289
- </Combobox.List>
290
- )}
291
- </Combobox.Content>
292
- </Combobox>
251
+ <SelectFieldSync
252
+ label="Font Size"
253
+ disableClearButton
254
+ options={fontSizeOptions}
255
+ value={selectedFontSize}
256
+ onSelectedOptionChange={handleFontSizeChange}
257
+ />
293
258
  )}
294
259
 
295
260
  {field.type === FieldTypeEnum.generic && field.subType === 'table' && (
296
261
  <TableConfigs field={field} onFieldConfigChange={onFieldConfigChange} />
297
262
  )}
298
263
 
264
+ {field.type === FieldTypeEnum.eSign &&
265
+ field.subType === ESignFieldType.dateSignedOffset && (
266
+ <ESignOptionsModal
267
+ initialState={field.data as ESignDateSignedOffsetSettings}
268
+ onSave={handleDateSignedOffsetSave}
269
+ documentFields={documentFields}
270
+ formFieldsByFormId={formFieldsByFormId}
271
+ formFieldsLoadingFormId={formulaLoadingFormId}
272
+ forms={forms}
273
+ onRequestFormFields={onRequestFormFields}
274
+ schema={dataModel}
275
+ />
276
+ )}
277
+
299
278
  {!hideConditionalLogic && (
300
279
  <DisplayCondition
301
280
  onSave={onFieldConfigChange}
@@ -1,4 +1,4 @@
1
- import { FC, Fragment } from 'react';
1
+ import { FC } from 'react';
2
2
  import { E_SIGN_FIELD_TYPES } from '../../constants';
3
3
  import { type FieldTypeOption } from '../../interface/types';
4
4
  import { FieldType } from './field-type';
@@ -10,18 +10,15 @@ interface ESignFieldTypeListProps {
10
10
 
11
11
  export const ESignFieldTypeList: FC<ESignFieldTypeListProps> = ({ onDragEnd, onDragStart }) => {
12
12
  return (
13
- <Fragment>
14
- {E_SIGN_FIELD_TYPES.map(fieldOption => {
15
- const key = `${fieldOption.type}-${fieldOption.path ?? fieldOption.label}`;
16
- return (
17
- <FieldType
18
- key={key}
19
- label={fieldOption.label}
20
- onDragEnd={onDragEnd}
21
- onDragStart={() => onDragStart(fieldOption)}
22
- />
23
- );
24
- })}
25
- </Fragment>
13
+ <div className="dte-field-type-items-wrapper">
14
+ {E_SIGN_FIELD_TYPES.map(fieldOption => (
15
+ <FieldType
16
+ key={fieldOption.subType}
17
+ label={fieldOption.label}
18
+ onDragEnd={onDragEnd}
19
+ onDragStart={() => onDragStart(fieldOption)}
20
+ />
21
+ ))}
22
+ </div>
26
23
  );
27
24
  };