form-craft-package 1.10.4 → 1.10.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.10.4",
3
+ "version": "1.10.5",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -5,7 +5,14 @@ import { IOnSuccessFunctions } from '.'
5
5
  import { useNotification } from '../../../../common/custom-hooks'
6
6
  import { useFormPreservedItemValues } from '../../../../common/custom-hooks/use-preserved-form-items.hook'
7
7
  import { MongoDbExtendedJsonObjectKeys, REGEX_PATTERNS } from '../../../../../constants'
8
- import { buildImageTag, isNewFormDataPage, renderData, replaceTemplateReportAndUpload, safeNormalizeForEmail } from '../../../../../functions'
8
+ import {
9
+ buildImageTag,
10
+ extractNotFoundBlobNames,
11
+ isNewFormDataPage,
12
+ renderData,
13
+ replaceTemplateReportAndUpload,
14
+ safeNormalizeForEmail,
15
+ } from '../../../../../functions'
9
16
  import {
10
17
  CountryEnum,
11
18
  DataRenderTypeEnum,
@@ -147,7 +154,6 @@ export const useSendNotificationAction = ({
147
154
  typeof notifConfigRes.data.data === 'string' &&
148
155
  formNotif.replacements
149
156
  ) {
150
-
151
157
  let notifConfig = notifConfigRes.data.data || ''
152
158
 
153
159
  for (const [placeholderKey, placeholderReplacement] of Object.entries(formNotif.replacements)) {
@@ -209,7 +215,7 @@ export const useSendNotificationAction = ({
209
215
  ) || []
210
216
 
211
217
  const notifRes = await Promise.all(
212
- notifTypes.map((type) => {
218
+ notifTypes.map(async (type) => {
213
219
  let endpoint = '/api/notification/email'
214
220
  if (type === NotificationTypeEnum.Push) endpoint = '/api/notification/push'
215
221
  else if (type === NotificationTypeEnum.Text) endpoint = '/api/notification/text'
@@ -230,14 +236,23 @@ export const useSendNotificationAction = ({
230
236
  typeReqData.body = safeNormalizeForEmail(typeReqData.body[selectedLanguage])
231
237
  }
232
238
 
233
- if (Array.isArray(typeReqData.attachments))
234
- typeReqData.attachments = [...typeReqData.attachments, ...formTemplateReportAttachments].map(
235
- (att: IEmail_Attachment) => {
236
- const { type, ...restAtt } = att
237
- return restAtt
238
- },
239
- )
239
+ if (Array.isArray(typeReqData.attachments)) {
240
+ console.log('typeReqData.attachments', typeReqData.attachments)
240
241
 
242
+ const blobsNotFound = await extractNotFoundBlobNames(
243
+ typeReqData.attachments.map((att: IEmail_Attachment) => att.blobName),
244
+ )
245
+ console.log('blobsNotFound', blobsNotFound)
246
+ typeReqData.attachments = [
247
+ ...typeReqData.attachments.filter(
248
+ (att: IEmail_Attachment) => !blobsNotFound.includes(att.blobName),
249
+ ),
250
+ ...formTemplateReportAttachments,
251
+ ].map((att: IEmail_Attachment) => {
252
+ const { type, ...restAtt } = att
253
+ return restAtt
254
+ })
255
+ }
241
256
  const emailFields = ['to', 'from', 'cc', 'bcc']
242
257
  emailFields.forEach((f) => {
243
258
  typeReqData[f] = Array.isArray(typeReqData[f])
@@ -292,5 +307,3 @@ export const useSendNotificationAction = ({
292
307
 
293
308
  return onSendNotification
294
309
  }
295
-
296
-
@@ -1,6 +1,6 @@
1
1
  import { LayoutRenderer_FieldElement } from './2-field-element'
2
2
  import { IFormContext } from '../1-row'
3
- import { IPickerElement } from '../../../../types'
3
+ import { IDatePickerElement, ITimePickerElement } from '../../../../types'
4
4
  import { IElementBaseProps } from '.'
5
5
  import { getElementGeneralizedProps } from '../../../../functions/forms/get-element-props'
6
6
  import { ElementTypeEnum, TranslationTextSubTypeEnum, TranslationTextTypeEnum } from '../../../../enums'
@@ -41,7 +41,12 @@ function LayoutRenderer_PickerField({ formContext, formItem, elementData, isDisa
41
41
  })
42
42
 
43
43
  return (
44
- <LayoutRenderer_FieldElement formRef={formContext.formRef} elementKey={elementData.key} formId={formContext.formId} formContext={formContext}>
44
+ <LayoutRenderer_FieldElement
45
+ formRef={formContext.formRef}
46
+ elementKey={elementData.key}
47
+ formId={formContext.formId}
48
+ formContext={formContext}
49
+ >
45
50
  {() => ({
46
51
  ...props,
47
52
  label,
@@ -68,7 +73,5 @@ export default memo(LayoutRenderer_PickerField)
68
73
 
69
74
  type ILayoutRenderer_PickerField = {
70
75
  formContext: IFormContext
71
- elementData: IPickerElement
76
+ elementData: IDatePickerElement | ITimePickerElement
72
77
  } & IElementBaseProps
73
-
74
-
@@ -12,7 +12,7 @@ import { extractConditionalFieldDependencies } from '../../../../functions/forms
12
12
  import { mapToFormItemRules } from '../../../../functions'
13
13
  import { DisabledFieldIndicator } from '../../../common/disabled-field-indicator'
14
14
 
15
- function LayoutRenderer_AutoComplete({ formContext, formItem, elementData, isDisabled }: ILayoutRenderer_PickerField) {
15
+ function LayoutRenderer_AutoComplete({ formContext, formItem, elementData, isDisabled }: ILayoutRenderer_AutoComplete) {
16
16
  const { formRef, formId, formDataId } = formContext
17
17
  const { t } = useTranslation(formId)
18
18
  const currentBreakpoint = useGetCurrentBreakpoint()
@@ -102,7 +102,7 @@ function LayoutRenderer_AutoComplete({ formContext, formItem, elementData, isDis
102
102
 
103
103
  export default memo(LayoutRenderer_AutoComplete)
104
104
 
105
- type ILayoutRenderer_PickerField = {
105
+ type ILayoutRenderer_AutoComplete = {
106
106
  formContext: IFormContext
107
107
  elementData: IInputElement
108
- } & IElementBaseProps
108
+ } & IElementBaseProps
@@ -10,10 +10,23 @@ import { IFormContext } from '../1-row'
10
10
  import CurrencyField from '../../../common/currency-field'
11
11
  import useGetCurrentBreakpoint from '../../../common/custom-hooks/use-window-width.hook'
12
12
  import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
13
- import { Checkbox, Form, Input, Select, Radio, InputNumber, DatePicker, Modal, Switch, FormInstance } from 'antd'
13
+ import { IntRange } from 'rc-picker/lib/interface'
14
14
  import { useBlurEventPublisher } from '../../../common/duplicate-entry-checker/blur-event-bus-provider'
15
15
  import { Rule } from 'antd/es/form'
16
16
  import { translationStore } from '../../../common/custom-hooks/use-translation.hook/store'
17
+ import {
18
+ Checkbox,
19
+ Form,
20
+ Input,
21
+ Select,
22
+ Radio,
23
+ InputNumber,
24
+ DatePicker,
25
+ Modal,
26
+ Switch,
27
+ FormInstance,
28
+ TimePicker,
29
+ } from 'antd'
17
30
  import {
18
31
  CountryEnum,
19
32
  DataSanitizationTypeEnum,
@@ -107,7 +120,7 @@ const FieldFormItemWrapper = ({
107
120
  },
108
121
  formId,
109
122
  ) || 'The new password that you entered do not match!'
110
-
123
+
111
124
  baseRules = [
112
125
  ...baseRules,
113
126
  ({ getFieldValue }) => ({
@@ -307,14 +320,23 @@ const fieldRenderers: Partial<Record<ElementTypeEnum, FieldRenderer>> = {
307
320
  allowEmpty
308
321
  />
309
322
  ),
310
- [ElementTypeEnum.TimePicker]: ({ disabled }) => (
311
- <DatePicker.TimePicker
312
- format={datepickerFormats}
313
- disabled={disabled}
314
- className={`${ELEMENTS_DEFAULT_CLASS.TimePicker} w-full`}
315
- allowClear
316
- />
317
- ),
323
+ [ElementTypeEnum.TimePicker]: ({ disabled, use12Hours, includeSeconds, hourStep, minuteStep, secondStep }) => {
324
+ const baseFormat = includeSeconds ? 'HH:mm:ss' : 'HH:mm'
325
+ return (
326
+ <TimePicker
327
+ format={baseFormat + (use12Hours ? ' A' : '')}
328
+ use12Hours={use12Hours}
329
+ disabled={disabled}
330
+ className={`${ELEMENTS_DEFAULT_CLASS.TimePicker} w-full`}
331
+ allowClear
332
+ changeOnScroll
333
+ needConfirm={false}
334
+ hourStep={hourStep}
335
+ minuteStep={minuteStep}
336
+ secondStep={secondStep}
337
+ />
338
+ )
339
+ },
318
340
  [ElementTypeEnum.ShortInput]: ({ placeholder, disabled, name, onBlur }) => (
319
341
  <Input
320
342
  id={name as string}
@@ -358,6 +380,12 @@ interface IMapperFieldObj {
358
380
  decimalPoint?: number
359
381
  mode?: 'multiple' | 'tags'
360
382
  confirmPwdField?: string
383
+ // Time picker props
384
+ use12Hours?: boolean
385
+ includeSeconds?: boolean
386
+ hourStep?: IntRange<1, 23>
387
+ minuteStep?: IntRange<1, 59>
388
+ secondStep?: IntRange<1, 59>
361
389
  disabledDate?: (date: Dayjs) => boolean
362
390
  onBlur?: (currentValue: any) => void
363
391
  }
@@ -25,7 +25,7 @@ function LayoutRenderer_Signature({
25
25
  const { t } = useTranslation(formId)
26
26
  const sigCanvasRef = useRef<SignatureCanvas>(null)
27
27
  const savedSignatureBlobName = Form.useWatch(formItem.path, formRef)
28
- const { isGeneratingPdf, baseServerUrl } = useFormPreservedItemValues(formRef)
28
+ const { isGeneratingPdf, baseServerUrl, signatureFields } = useFormPreservedItemValues(formRef)
29
29
  const currentBreakpoint = useGetCurrentBreakpoint()
30
30
  const [isReady, setIsReady] = useState(false)
31
31
  const savedSignatureBlobNameRef = useRef<string>('')
@@ -48,10 +48,22 @@ function LayoutRenderer_Signature({
48
48
  else sigCanvasRef.current?.on()
49
49
  }, [isDisabled])
50
50
 
51
- const fieldKeyJoined = useMemo(
52
- () => (Array.isArray(formItem.path) ? formItem.path.join('.') : formItem.path),
53
- [formItem.path],
54
- )
51
+ const fieldKeyJoined = useMemo(
52
+ () => (Array.isArray(formItem.path) ? formItem.path.join('.') : formItem.path),
53
+ [formItem.path],
54
+ )
55
+
56
+ useEffect(() => {
57
+ if (!isNewFormDataPage(formDataId) || !fieldKeyJoined) return
58
+ const stored = signatureFields?.[fieldKeyJoined]
59
+ if (typeof stored === 'string' && stored) {
60
+ if (stored !== savedSignatureBlobNameRef.current) savedSignatureBlobNameRef.current = stored
61
+ if (isReady) sigCanvasRef.current?.fromDataURL(stored)
62
+ } else {
63
+ savedSignatureBlobNameRef.current = ''
64
+ if (isReady) sigCanvasRef.current?.clear()
65
+ }
66
+ }, [fieldKeyJoined, formDataId, isReady, signatureFields])
55
67
 
56
68
  useEffect(() => {
57
69
  setTimeout(() => setIsReady(true), 2000)
@@ -98,11 +110,12 @@ function LayoutRenderer_Signature({
98
110
  {(!elementProps.showClearWhenNew || isNewFormDataPage(formDataId)) && (
99
111
  <Button_FillerPortal
100
112
  link
101
- onClick={() => {
102
- sigCanvasRef.current?.clear()
103
- if (fieldKeyJoined)
104
- formRef?.setFieldsValue({ [FormPreservedItemKeys.SignatureFields]: { [fieldKeyJoined]: null } })
105
- }}
113
+ onClick={() => {
114
+ sigCanvasRef.current?.clear()
115
+ savedSignatureBlobNameRef.current = ''
116
+ if (fieldKeyJoined)
117
+ formRef?.setFieldsValue({ [FormPreservedItemKeys.SignatureFields]: { [fieldKeyJoined]: null } })
118
+ }}
106
119
  >
107
120
  {clearBtnLabel || 'Clear'}
108
121
  </Button_FillerPortal>
@@ -30,11 +30,12 @@ import {
30
30
  ISelectElement,
31
31
  IRadioElement,
32
32
  ICurrencyInputElement,
33
- IPickerElement,
33
+ IDatePickerElement,
34
34
  IInputElementProps,
35
35
  IInputElement,
36
36
  ICustomComponentProps,
37
37
  IUserRoleElement,
38
+ ITimePickerElement,
38
39
  } from '../../../../types'
39
40
 
40
41
  const LayoutRenderer_ReadFieldData = lazy(() => import('./3-read-field-data'))
@@ -269,7 +270,7 @@ function LayoutRendererElement({
269
270
 
270
271
  [ElementTypeEnum.DatePicker]: () => (
271
272
  <LayoutRenderer_PickerField
272
- elementData={{ ...(elementData as IPickerElement), validations: validationsAfterIsHidden }}
273
+ elementData={{ ...(elementData as IDatePickerElement), validations: validationsAfterIsHidden }}
273
274
  formContext={formContext}
274
275
  {...elementBaseProps}
275
276
  />
@@ -277,7 +278,7 @@ function LayoutRendererElement({
277
278
 
278
279
  [ElementTypeEnum.TimePicker]: () => (
279
280
  <LayoutRenderer_PickerField
280
- elementData={{ ...(elementData as IPickerElement), validations: validationsAfterIsHidden }}
281
+ elementData={{ ...(elementData as ITimePickerElement), validations: validationsAfterIsHidden }}
281
282
  formContext={formContext}
282
283
  {...elementBaseProps}
283
284
  />
@@ -285,7 +286,7 @@ function LayoutRendererElement({
285
286
 
286
287
  [ElementTypeEnum.RangePicker]: () => (
287
288
  <LayoutRenderer_PickerField
288
- elementData={{ ...(elementData as IPickerElement), validations: validationsAfterIsHidden }}
289
+ elementData={{ ...(elementData as IDatePickerElement), validations: validationsAfterIsHidden }}
289
290
  formContext={formContext}
290
291
  {...elementBaseProps}
291
292
  />
@@ -1,5 +1,5 @@
1
1
  import { ButtonElementSizeEnum, ButtonElementTypeEnum, ElementTypeEnum } from '../../enums'
2
- import { IButtonPropsBase, IDndLayoutElement, IPickerElementProps } from '../../types'
2
+ import { IButtonPropsBase, IDndLayoutElement, IDatePickerElementProps } from '../../types'
3
3
 
4
4
  // since all the field elements have different type, this function is generalizing the types
5
5
  export const getElementGeneralizedProps = (props: Record<string, any> | undefined): Record<string, any> => ({
@@ -23,7 +23,7 @@ export const getPickerFieldsWithOriginalTz = (elements: { [key: string]: IDndLay
23
23
  Object.values(elements).reduce((curr: string[], el) => {
24
24
  if (
25
25
  [ElementTypeEnum.DatePicker, ElementTypeEnum.TimePicker].includes(el.elementType) &&
26
- (el.props as IPickerElementProps)?.displayInOriginalTz
26
+ (el.props as IDatePickerElementProps)?.displayInOriginalTz
27
27
  )
28
28
  return [...curr, el.key]
29
29
 
@@ -7,7 +7,7 @@ export * from './linked-form-joins'
7
7
  export * from './convert-number-into-words'
8
8
  export * from './deep-swap-form-ids'
9
9
  export * from './email-body-helpers'
10
- import { CancelToken } from 'axios'
10
+ import axios, { CancelToken } from 'axios'
11
11
  import client from '../../api/client'
12
12
  import { renderData } from '..'
13
13
  import { translationStore } from '../../components/common/custom-hooks/use-translation.hook/store'
@@ -21,6 +21,7 @@ import {
21
21
  DataRenderTypeEnum,
22
22
  ElementTypeEnum,
23
23
  FieldElementOptionSourceEnum,
24
+ LOCAL_STORAGE_KEYS_ENUM,
24
25
  TemplateDataReplacementFieldTypesEnum,
25
26
  TranslationTextTypeEnum,
26
27
  } from '../../enums'
@@ -368,4 +369,25 @@ export const replaceTemplateReportAndUpload = async ({
368
369
 
369
370
  /** --------------------------------------------------------------------------------------------------------- */
370
371
 
372
+ export const extractNotFoundBlobNames = async (blobNames: string[]): Promise<string[]> => {
373
+ const baseUrl = import.meta.env.VITE_API_BASE_URL
374
+ const domain = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.Domain)
375
+ const blobsNotFound: string[] = []
376
+ for await (const blobName of blobNames) {
377
+ if (!blobName) continue
378
+
379
+ const fetchBlobStatus = await axios
380
+ .get(`${baseUrl}/api/attachment/${domain}/${blobName}`)
381
+ .then((res) => res.status)
382
+ .catch((err) => err.status)
383
+
384
+ if (fetchBlobStatus !== 200) {
385
+ blobsNotFound.push(blobName)
386
+ }
387
+ }
388
+
389
+ console.log('BLOBS NOT FOUND:', blobsNotFound.join(', '))
390
+ return blobsNotFound
391
+ }
392
+
371
393
  /** --------------------------------------------------------------------------------------------------------- */
@@ -14,6 +14,7 @@ import { IValidationRule } from './validation'
14
14
  import { IFormLayoutElementConditions } from './conditions'
15
15
  import { IDataSanitization } from './sanitization'
16
16
  import { ReportPageBreakEnum } from '../../../enums/report.enum'
17
+ import { IntRange } from 'rc-picker/lib/interface'
17
18
  import {
18
19
  IButtonElementProps,
19
20
  ICssStyle,
@@ -133,11 +134,11 @@ export type IRadioElementProps = IOptionElementProps_Base & {
133
134
 
134
135
  /** -------------------------------------------------------------------------------------------- */
135
136
 
136
- export interface IPickerElement extends BaseFormLayoutElement {
137
- elementType: ElementTypeEnum.DatePicker | ElementTypeEnum.RangePicker | ElementTypeEnum.TimePicker
138
- props: IPickerElementProps
137
+ export interface IDatePickerElement extends BaseFormLayoutElement {
138
+ elementType: ElementTypeEnum.DatePicker | ElementTypeEnum.RangePicker
139
+ props: IDatePickerElementProps
139
140
  }
140
- export interface IPickerElementProps {
141
+ export interface IDatePickerElementProps {
141
142
  hasNoLabel?: boolean
142
143
  allowClear?: boolean
143
144
  filter?: IFilterConfig
@@ -146,6 +147,20 @@ export interface IPickerElementProps {
146
147
 
147
148
  /** -------------------------------------------------------------------------------------------- */
148
149
 
150
+ export interface ITimePickerElement extends BaseFormLayoutElement {
151
+ elementType: ElementTypeEnum.TimePicker
152
+ props: ITimePickerElementProps
153
+ }
154
+ export interface ITimePickerElementProps {
155
+ use12Hours?: boolean
156
+ includeSeconds?: boolean
157
+ hourStep?: IntRange<1, 23>
158
+ minuteStep?: IntRange<1, 59>
159
+ secondStep?: IntRange<1, 59>
160
+ }
161
+
162
+ /** -------------------------------------------------------------------------------------------- */
163
+
149
164
  export interface IFileUploadElement extends BaseFormLayoutElement {
150
165
  elementType: ElementTypeEnum.FileUpload
151
166
  props: IFileUploadElementProps
@@ -18,7 +18,7 @@ import {
18
18
  ILanguageSelector,
19
19
  IListElement,
20
20
  INumberInputElement,
21
- IPickerElement,
21
+ IDatePickerElement,
22
22
  IPlaceholderElement,
23
23
  IRadioElement,
24
24
  IReadFieldDataElement,
@@ -31,6 +31,7 @@ import {
31
31
  ITextElement,
32
32
  IUserPasswordElement,
33
33
  IUserRoleElement,
34
+ ITimePickerElement,
34
35
  } from './forms'
35
36
 
36
37
  export * from './companies'
@@ -70,7 +71,8 @@ export type IDndLayoutElement =
70
71
  | ISearchElement
71
72
  | ISelectElement
72
73
  | IRadioElement
73
- | IPickerElement
74
+ | IDatePickerElement
75
+ | ITimePickerElement
74
76
  | IDividerElement
75
77
  | IReadFieldDataElement
76
78
  | IBooleanElement