@servicetitan/dte-pdf-editor 1.35.0 → 1.37.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 (85) hide show
  1. package/README.md +28 -12
  2. package/dist/components/field-config-panel/advanced-settings.d.ts.map +1 -1
  3. package/dist/components/field-config-panel/advanced-settings.js +20 -10
  4. package/dist/components/field-config-panel/advanced-settings.js.map +1 -1
  5. package/dist/components/field-config-panel/formula-generator.d.ts.map +1 -1
  6. package/dist/components/field-config-panel/formula-generator.js +2 -1
  7. package/dist/components/field-config-panel/formula-generator.js.map +1 -1
  8. package/dist/components/field-config-panel/formula-modal.d.ts.map +1 -1
  9. package/dist/components/field-config-panel/formula-modal.js +48 -7
  10. package/dist/components/field-config-panel/formula-modal.js.map +1 -1
  11. package/dist/components/field-config-panel/formula-workspace.d.ts +1 -0
  12. package/dist/components/field-config-panel/formula-workspace.d.ts.map +1 -1
  13. package/dist/components/field-config-panel/formula-workspace.js +10 -4
  14. package/dist/components/field-config-panel/formula-workspace.js.map +1 -1
  15. package/dist/components/field-config-panel/result-type-selector.d.ts.map +1 -1
  16. package/dist/components/field-config-panel/result-type-selector.js +1 -0
  17. package/dist/components/field-config-panel/result-type-selector.js.map +1 -1
  18. package/dist/components/pdf-view/pdf-view-calculated.d.ts +1 -0
  19. package/dist/components/pdf-view/pdf-view-calculated.d.ts.map +1 -1
  20. package/dist/components/pdf-view/pdf-view-calculated.js +20 -3
  21. package/dist/components/pdf-view/pdf-view-calculated.js.map +1 -1
  22. package/dist/components/pdf-view/pdf-view-fillable.d.ts.map +1 -1
  23. package/dist/components/pdf-view/pdf-view-fillable.js +5 -2
  24. package/dist/components/pdf-view/pdf-view-fillable.js.map +1 -1
  25. package/dist/components/pdf-view/pdf-view.d.ts +6 -0
  26. package/dist/components/pdf-view/pdf-view.d.ts.map +1 -1
  27. package/dist/components/pdf-view/pdf-view.js +2 -2
  28. package/dist/components/pdf-view/pdf-view.js.map +1 -1
  29. package/dist/constants/calculated.constants.d.ts +2 -0
  30. package/dist/constants/calculated.constants.d.ts.map +1 -1
  31. package/dist/constants/calculated.constants.js +2 -0
  32. package/dist/constants/calculated.constants.js.map +1 -1
  33. package/dist/hooks/useFormulaEditor.d.ts +3 -0
  34. package/dist/hooks/useFormulaEditor.d.ts.map +1 -1
  35. package/dist/hooks/useFormulaEditor.js +24 -1
  36. package/dist/hooks/useFormulaEditor.js.map +1 -1
  37. package/dist/interface/types.d.ts +6 -2
  38. package/dist/interface/types.d.ts.map +1 -1
  39. package/dist/interface/types.js.map +1 -1
  40. package/dist/utils/data-model/extract-fields.utils.d.ts.map +1 -1
  41. package/dist/utils/data-model/extract-fields.utils.js +11 -1
  42. package/dist/utils/data-model/extract-fields.utils.js.map +1 -1
  43. package/dist/utils/formula/evaluate-formula.utils.d.ts +10 -2
  44. package/dist/utils/formula/evaluate-formula.utils.d.ts.map +1 -1
  45. package/dist/utils/formula/evaluate-formula.utils.js +205 -4
  46. package/dist/utils/formula/evaluate-formula.utils.js.map +1 -1
  47. package/dist/utils/formula/expression.utils.d.ts +3 -3
  48. package/dist/utils/formula/expression.utils.d.ts.map +1 -1
  49. package/dist/utils/formula/expression.utils.js +3 -5
  50. package/dist/utils/formula/expression.utils.js.map +1 -1
  51. package/dist/utils/formula/format-calculated-result.utils.d.ts +1 -1
  52. package/dist/utils/formula/format-calculated-result.utils.d.ts.map +1 -1
  53. package/dist/utils/formula/format-calculated-result.utils.js +29 -8
  54. package/dist/utils/formula/format-calculated-result.utils.js.map +1 -1
  55. package/dist/utils/formula/validate-formula.utils.d.ts +1 -1
  56. package/dist/utils/formula/validate-formula.utils.d.ts.map +1 -1
  57. package/dist/utils/formula/validate-formula.utils.js +18 -3
  58. package/dist/utils/formula/validate-formula.utils.js.map +1 -1
  59. package/dist/utils/shared/date.utils.d.ts +2 -0
  60. package/dist/utils/shared/date.utils.d.ts.map +1 -0
  61. package/dist/utils/shared/date.utils.js +6 -0
  62. package/dist/utils/shared/date.utils.js.map +1 -0
  63. package/dist/utils/shared/index.d.ts +1 -0
  64. package/dist/utils/shared/index.d.ts.map +1 -1
  65. package/dist/utils/shared/index.js +1 -0
  66. package/dist/utils/shared/index.js.map +1 -1
  67. package/package.json +1 -1
  68. package/src/components/field-config-panel/advanced-settings.tsx +103 -77
  69. package/src/components/field-config-panel/formula-generator.tsx +2 -1
  70. package/src/components/field-config-panel/formula-modal.tsx +48 -8
  71. package/src/components/field-config-panel/formula-workspace.tsx +21 -7
  72. package/src/components/field-config-panel/result-type-selector.tsx +1 -0
  73. package/src/components/pdf-view/pdf-view-calculated.tsx +20 -3
  74. package/src/components/pdf-view/pdf-view-fillable.tsx +6 -3
  75. package/src/components/pdf-view/pdf-view.tsx +8 -0
  76. package/src/constants/calculated.constants.ts +4 -0
  77. package/src/hooks/useFormulaEditor.ts +32 -1
  78. package/src/interface/types.ts +8 -2
  79. package/src/utils/data-model/extract-fields.utils.ts +13 -1
  80. package/src/utils/formula/evaluate-formula.utils.ts +271 -5
  81. package/src/utils/formula/expression.utils.ts +3 -8
  82. package/src/utils/formula/format-calculated-result.utils.ts +32 -10
  83. package/src/utils/formula/validate-formula.utils.ts +27 -1
  84. package/src/utils/shared/date.utils.ts +6 -0
  85. package/src/utils/shared/index.ts +1 -0
@@ -1,3 +1,4 @@
1
+ import { DEFAULT_DATE_FORMAT } from '../../constants';
1
2
  import { CalculatedFieldFormat } from '../../interface/types';
2
3
 
3
4
  function roundValue(
@@ -36,9 +37,33 @@ function formatIntegerPart(
36
37
  return parts.join(sep);
37
38
  }
38
39
 
40
+ const DATE_FORMAT_TOKENS: Record<string, (d: Date) => string> = {
41
+ YYYY: d => String(d.getFullYear()),
42
+ YY: d => String(d.getFullYear()).slice(-2),
43
+ MMMM: d => d.toLocaleString('en-US', { month: 'long' }),
44
+ MMM: d => d.toLocaleString('en-US', { month: 'short' }),
45
+ MM: d => String(d.getMonth() + 1).padStart(2, '0'),
46
+ M: d => String(d.getMonth() + 1),
47
+ DD: d => String(d.getDate()).padStart(2, '0'),
48
+ D: d => String(d.getDate()),
49
+ };
50
+
51
+ const DATE_TOKEN_PATTERN = new RegExp(Object.keys(DATE_FORMAT_TOKENS).join('|'), 'g');
52
+
53
+ function formatDateValue(epochMs: number, dateFormat: string): string {
54
+ const date = new Date(epochMs);
55
+ if (isNaN(date.getTime())) {
56
+ return '';
57
+ }
58
+ return dateFormat.replace(DATE_TOKEN_PATTERN, match => {
59
+ const fn = DATE_FORMAT_TOKENS[match];
60
+ return fn ? fn(date) : match;
61
+ });
62
+ }
63
+
39
64
  /**
40
65
  * Format a calculated formula result for display using advanced settings.
41
- * Applies rounding, decimal places, thousands/decimal separators, result type (number/currency/percent), and prefix/postfix.
66
+ * Applies rounding, decimal places, thousands/decimal separators, result type (number/currency/percent/date), and prefix/postfix.
42
67
  */
43
68
  export function formatCalculatedResult(
44
69
  value: number,
@@ -48,23 +73,21 @@ export function formatCalculatedResult(
48
73
  return String(value);
49
74
  }
50
75
 
76
+ if (format.resultType === 'date') {
77
+ return formatDateValue(value, format.dateFormat ?? DEFAULT_DATE_FORMAT);
78
+ }
79
+
51
80
  const {
52
81
  decimalSeparator,
53
82
  decimalSeparatorEnabled,
54
83
  decimals,
55
84
  postfixText,
56
85
  prefixText,
57
- resultType,
58
86
  roundingMode,
59
87
  thousandsSeparator,
60
88
  } = format;
61
89
 
62
- let num = value;
63
- if (resultType === 'percent') {
64
- num = value * 100;
65
- }
66
-
67
- const rounded = roundValue(num, decimals, roundingMode);
90
+ const rounded = roundValue(value, decimals, roundingMode);
68
91
  const fixed = rounded.toFixed(decimals);
69
92
  const [intPart, decPart] = fixed.split('.');
70
93
 
@@ -73,7 +96,6 @@ export function formatCalculatedResult(
73
96
  const decSuffix = decimals > 0 ? decimalSep + (decPart ?? '') : '';
74
97
 
75
98
  const numberStr = intFormatted + decSuffix;
76
- const suffix = resultType === 'percent' ? '%' : '';
77
99
 
78
- return `${prefixText}${numberStr}${suffix}${postfixText}`;
100
+ return `${prefixText}${numberStr}${postfixText}`;
79
101
  }
@@ -1,3 +1,4 @@
1
+ import { MAX_DATE_CALC_DAYS } from '../../constants';
1
2
  import { FormulaToken, StructuredFormula } from '../../interface/types';
2
3
 
3
4
  export interface FormulaValidationResult {
@@ -20,6 +21,8 @@ function isOperandToken(t: FormulaToken): boolean {
20
21
  export function validateFormula(
21
22
  formula: StructuredFormula | undefined | null,
22
23
  validPaths: Set<string>,
24
+ knownDateFields?: Set<string>,
25
+ pathToLabel?: Map<string, string>,
23
26
  ): FormulaValidationResult {
24
27
  const errors: string[] = [];
25
28
 
@@ -61,7 +64,8 @@ export function validateFormula(
61
64
  }
62
65
  } else if (t.type === 'field') {
63
66
  if (!validPaths.has(t.path)) {
64
- errors.push(`Unknown field: "${t.label}" (path: ${t.path})`);
67
+ const label = pathToLabel?.get(t.path) ?? t.path;
68
+ errors.push(`Unknown field: "${label}" (path: ${t.path})`);
65
69
  }
66
70
  if (next != null && isOperandToken(next)) {
67
71
  errors.push('Operator required between two fields or values');
@@ -90,6 +94,28 @@ export function validateFormula(
90
94
  errors.push('Formula must end with an operand or closing parenthesis');
91
95
  }
92
96
 
97
+ if (knownDateFields?.size) {
98
+ const hasDateField = tokens.some(t => t.type === 'field' && knownDateFields.has(t.path));
99
+ if (hasDateField) {
100
+ const hasMultiplyOrDivide = tokens.some(
101
+ t => t.type === 'operator' && (t.value === '*' || t.value === '/'),
102
+ );
103
+ if (hasMultiplyOrDivide) {
104
+ errors.push(
105
+ 'Multiply (*) and divide (/) operators cannot be used with date fields. Only +, -, and parentheses are allowed.',
106
+ );
107
+ }
108
+ const hasLargeNumber = tokens.some(
109
+ t => t.type === 'number' && Math.abs(Number(t.value)) > MAX_DATE_CALC_DAYS,
110
+ );
111
+ if (hasLargeNumber) {
112
+ errors.push(
113
+ `Number of days cannot exceed ${MAX_DATE_CALC_DAYS} in date calculations.`,
114
+ );
115
+ }
116
+ }
117
+ }
118
+
93
119
  return {
94
120
  valid: errors.length === 0,
95
121
  errors,
@@ -0,0 +1,6 @@
1
+ export const dateToUtcZero = (dateString: string): string => {
2
+ const [year, month, day] = dateString.split('-').map(Number);
3
+ const utcDate = new Date(Date.UTC(year, month - 1, day));
4
+
5
+ return utcDate.toISOString();
6
+ };
@@ -1 +1,2 @@
1
+ export * from './date.utils';
1
2
  export * from './number.utils';