form-craft-package 1.10.7 → 1.10.8-dev.1

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.7",
3
+ "version": "1.10.8-dev.1",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -1,88 +1,131 @@
1
- import { FormInstance } from 'antd'
2
- import { useEffect, useMemo, useRef } from 'react'
3
- import { useBlurEventBus } from '../duplicate-entry-checker/blur-event-bus-provider'
4
- import { IDuplicateCheckConfigGroup } from '../../../types'
5
- import apiClient from '../../../api/client'
6
- import { FormPreservedItemKeys } from '../../../enums'
7
-
8
- interface IUseDuplicateOnBlur {
9
- formRef: FormInstance
10
- formKey?: string
11
- formDataId?: string
12
- groups?: IDuplicateCheckConfigGroup[]
13
- debounceMs?: number
14
- }
15
-
16
- export function useDuplicateOnBlur(
17
- { formRef, formKey, formDataId, groups, debounceMs = 0 }: IUseDuplicateOnBlur,
18
- onResult: (isDuplicateFound: boolean) => void,
19
- ) {
20
- const { subscribeBlurEvent: subscribe } = useBlurEventBus()
21
-
22
- const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
23
- const abortRef = useRef<AbortController | null>(null)
24
-
25
- const relevantFieldGroups = useMemo(() => {
26
- if (!Array.isArray(groups)) return []
27
-
28
- return groups.map((group) => group.fields)
29
- }, [groups])
30
-
31
- useEffect(() => {
32
- if (!formKey) return
33
-
34
- const unsub = subscribe((blurredField) => {
35
- const fieldClone: string = Array.isArray(blurredField) ? blurredField.join('.') : (blurredField as string)
36
-
37
- const fieldGroup = relevantFieldGroups.find((g) => g.includes(`Data.${fieldClone}`))
38
-
39
- if (!fieldGroup) return
40
-
41
- const formValues = formRef.getFieldsValue(true)
42
-
43
- if (fieldGroup.some((f) => !formValues[f.replace('Data.', '')])) return
44
-
45
- if (timerRef.current) clearTimeout(timerRef.current)
46
-
47
- timerRef.current = setTimeout(async () => {
48
- formRef.setFieldValue([FormPreservedItemKeys.DuplicateCheckPending], true)
49
-
50
- // cancel previous request
51
- if (abortRef.current) abortRef.current.abort()
52
- abortRef.current = new AbortController()
53
-
54
- const matchKeyValuePairs = {
55
- DeletedDate: null,
56
- ...fieldGroup.reduce((acc, f) => ({ ...acc, [f]: formValues[f.replace('Data.', '')] }), {}),
57
- }
58
- const reqData = { project: JSON.stringify({ _id: '$_id' }), match: JSON.stringify(matchKeyValuePairs) }
59
-
60
- try {
61
- const res = await apiClient.post(`/api/site/${formKey}/get`, reqData)
62
- if (res.status === 200) {
63
- const isRecordFound = res.data.data.filter(({ _id }: { _id: string }) => _id !== formDataId).length > 0
64
-
65
- const matchFilterKeysOnly = Object.keys(matchKeyValuePairs).join('::')
66
-
67
- // set the match filter with the result to display error upon submit button
68
- formRef.setFieldsValue({
69
- [FormPreservedItemKeys.DuplicateDataFound]: { [matchFilterKeysOnly]: isRecordFound },
70
- })
71
-
72
- onResult(isRecordFound)
73
- }
74
- } catch (e: any) {
75
- if (e?.name === 'AbortError') return
76
- }
77
-
78
- formRef.setFieldValue([FormPreservedItemKeys.DuplicateCheckPending], false)
79
- }, debounceMs)
80
- })
81
-
82
- return () => {
83
- unsub()
84
- if (timerRef.current) clearTimeout(timerRef.current)
85
- if (abortRef.current) abortRef.current.abort()
86
- }
87
- }, [subscribe, relevantFieldGroups, formKey, formDataId, formRef, debounceMs])
88
- }
1
+ import { FormInstance } from 'antd'
2
+ import { useEffect, useMemo, useRef } from 'react'
3
+ import { useBlurEventBus } from '../duplicate-entry-checker/blur-event-bus-provider'
4
+ import { IDuplicateCheckConfigGroup } from '../../../types'
5
+ import apiClient from '../../../api/client'
6
+ import { FormPreservedItemKeys } from '../../../enums'
7
+
8
+ interface IUseDuplicateOnBlur {
9
+ formRef: FormInstance
10
+ formKey?: string
11
+ formDataId?: string
12
+ groups?: IDuplicateCheckConfigGroup[]
13
+ debounceMs?: number
14
+ functionName?: string
15
+ onCustomFunctionCall?: CustomFunctionCall
16
+ }
17
+
18
+ export function useDuplicateOnBlur(
19
+ { formRef, formKey, formDataId, groups, debounceMs = 0, functionName, onCustomFunctionCall }: IUseDuplicateOnBlur,
20
+ onResult: (isDuplicateFound: boolean, data?: unknown) => void,
21
+ ) {
22
+ const { subscribeBlurEvent: subscribe } = useBlurEventBus()
23
+
24
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
25
+ const abortRef = useRef<AbortController | null>(null)
26
+
27
+ const relevantFieldGroups = useMemo(() => {
28
+ if (!Array.isArray(groups)) return []
29
+
30
+ return groups.map((group) => group.fields)
31
+ }, [groups])
32
+
33
+ useEffect(() => {
34
+ const shouldUseCustomFunction = Boolean(functionName && onCustomFunctionCall)
35
+ if (!formKey && !shouldUseCustomFunction) return
36
+
37
+ const unsub = subscribe((blurredField) => {
38
+ const fieldClone: string = Array.isArray(blurredField) ? blurredField.join('.') : (blurredField as string)
39
+
40
+ const fieldGroup = relevantFieldGroups.find((g) => g.includes(`Data.${fieldClone}`))
41
+
42
+ if (!fieldGroup) return
43
+
44
+ const formValues = formRef.getFieldsValue(true)
45
+
46
+ if (fieldGroup.some((f) => !formValues[f.replace('Data.', '')])) return
47
+
48
+ if (timerRef.current) clearTimeout(timerRef.current)
49
+
50
+ timerRef.current = setTimeout(async () => {
51
+ formRef.setFieldValue([FormPreservedItemKeys.DuplicateCheckPending], true)
52
+
53
+ // cancel previous request
54
+ if (abortRef.current) abortRef.current.abort()
55
+ abortRef.current = shouldUseCustomFunction ? null : new AbortController()
56
+
57
+ const matchKeyValuePairs = {
58
+ DeletedDate: null,
59
+ ...fieldGroup.reduce((acc, f) => ({ ...acc, [f]: formValues[f.replace('Data.', '')] }), {}),
60
+ }
61
+
62
+ try {
63
+ const duplicateRecords = shouldUseCustomFunction
64
+ ? normalizeDuplicateRecords(
65
+ await onCustomFunctionCall?.({
66
+ fnName: functionName as string,
67
+ formData: { ...formValues },
68
+ }),
69
+ )
70
+ : normalizeDuplicateRecords(
71
+ (
72
+ await apiClient.post(
73
+ `/api/site/${formKey}/get`,
74
+ {
75
+ project: JSON.stringify({ _id: '$_id' }),
76
+ match: JSON.stringify(matchKeyValuePairs),
77
+ },
78
+ abortRef.current ? { signal: abortRef.current.signal } : undefined,
79
+ )
80
+ ).data,
81
+ )
82
+
83
+ const filteredRecords = filterDuplicateRecords(duplicateRecords, formDataId)
84
+ const isRecordFound = filteredRecords.length > 0
85
+
86
+ const matchFilterKeysOnly = Object.keys(matchKeyValuePairs).join('::')
87
+
88
+ formRef.setFieldsValue({
89
+ [FormPreservedItemKeys.DuplicateDataFound]: { [matchFilterKeysOnly]: isRecordFound },
90
+ })
91
+
92
+ onResult(isRecordFound, filteredRecords.length ? filteredRecords[0] : undefined)
93
+ } catch (e: any) {
94
+ if (e?.name === 'AbortError') return
95
+ }
96
+
97
+ formRef.setFieldValue([FormPreservedItemKeys.DuplicateCheckPending], false)
98
+ }, debounceMs)
99
+ })
100
+
101
+ return () => {
102
+ unsub()
103
+ if (timerRef.current) clearTimeout(timerRef.current)
104
+ if (abortRef.current) abortRef.current.abort()
105
+ }
106
+ }, [subscribe, relevantFieldGroups, formKey, formDataId, formRef, debounceMs, functionName, onCustomFunctionCall])
107
+ }
108
+
109
+ const normalizeDuplicateRecords = (payload: any): any[] => {
110
+ if (!payload) return []
111
+ const data = payload?.data?.data ?? payload?.data ?? payload
112
+ if (!data) return []
113
+ return Array.isArray(data) ? data : [data]
114
+ }
115
+
116
+ const filterDuplicateRecords = (records: any[], formDataId?: string) => {
117
+ if (!Array.isArray(records)) return []
118
+ return records.filter((record) => {
119
+ if (!record) return false
120
+ if (!formDataId) return true
121
+ if (record._id && record._id === formDataId) return false
122
+ if (record.id && String(record.id) === formDataId) return false
123
+ return true
124
+ })
125
+ }
126
+
127
+ type CustomFunctionCall = (params: {
128
+ fnName: string
129
+ messages?: { success?: string; error?: string }
130
+ formData?: { [key: string]: any }
131
+ }) => Promise<any> | any
@@ -7,7 +7,12 @@ import { FaExclamationTriangle } from 'react-icons/fa'
7
7
  import { useNavigate } from 'react-router-dom'
8
8
  import { DUPLICATE_CHECK_TRANSLATION_KEY } from '../../../constants'
9
9
 
10
- export default function DuplicateWarningModal({ formRef, formId, closeModal }: IDuplicateWarningModal) {
10
+ export default function DuplicateWarningModal({
11
+ formRef,
12
+ formId,
13
+ duplicateRecord,
14
+ closeModal,
15
+ }: IDuplicateWarningModal) {
11
16
  const navigate = useNavigate()
12
17
  const { t } = useTranslation(formId)
13
18
 
@@ -92,9 +97,11 @@ export default function DuplicateWarningModal({ formRef, formId, closeModal }: I
92
97
  <div className="text-16 text-center">
93
98
  {primaryMessage || 'We’ve found an existing record with the same details.'}
94
99
  </div>
95
- <div className="text-gray-500 text-[15px] text-center">
96
- {secondaryMessage || 'Duplicate entries are not allowed in the system.'}
97
- </div>
100
+ {secondaryMessage && (
101
+ <div className="text-gray-500 text-[15px] text-center">
102
+ {secondaryMessage || 'Duplicate entries are not allowed in the system.'}
103
+ </div>
104
+ )}
98
105
  </div>
99
106
  {fieldLabels.length > 0 && (
100
107
  <>
@@ -102,6 +109,7 @@ export default function DuplicateWarningModal({ formRef, formId, closeModal }: I
102
109
  <div className="bg-background w-full rounded-md p-2 flex flex-col gap-1">
103
110
  <span className="font-bold text-primary">Duplicate data found on the following field(s):</span>
104
111
  <div className="flex gap-2">{fieldLabels.join(', ')}</div>
112
+ {typeof duplicateRecord === 'object' && <pre>{JSON.stringify(duplicateRecord, null, 2)}</pre>}
105
113
  </div>
106
114
  </>
107
115
  )}
@@ -112,7 +120,6 @@ export default function DuplicateWarningModal({ formRef, formId, closeModal }: I
112
120
  interface IDuplicateWarningModal {
113
121
  formRef?: FormInstance
114
122
  formId?: number
123
+ duplicateRecord?: unknown
115
124
  closeModal: () => void
116
125
  }
117
-
118
-
@@ -1,34 +1,62 @@
1
- import { FormInstance } from 'antd'
2
- import { useLazyModalOpener } from '../custom-hooks'
3
- import { IDuplicateCheckConfig } from '../../../types'
4
- import { useDuplicateOnBlur } from '../custom-hooks/use-duplicate-on-blur.hook'
5
- import { lazy } from 'react'
6
-
7
- const DuplicateWarningModal = lazy(() => import('./duplicate-warning.modal'))
8
-
9
- export default function DuplicateEntryChecker({
10
- formRef,
11
- formId,
12
- formKey,
13
- formDataId,
14
- duplicateCheckConfig,
15
- }: IDuplicateEntryChecker) {
16
- const { isModalOpen, openModal, closeModal } = useLazyModalOpener()
17
-
18
- useDuplicateOnBlur({ formRef, formKey, formDataId, groups: duplicateCheckConfig?.groups }, (isDuplicateFound) => {
19
- if (isDuplicateFound) openModal()
20
- else closeModal()
21
- })
22
-
23
- if (!isModalOpen) return
24
-
25
- return <DuplicateWarningModal formRef={formRef} formId={formId} closeModal={closeModal} />
26
- }
27
-
28
- interface IDuplicateEntryChecker {
29
- formRef: FormInstance
30
- formId?: number
31
- formKey?: string
32
- formDataId: string
33
- duplicateCheckConfig?: IDuplicateCheckConfig
34
- }
1
+ import { FormInstance } from 'antd'
2
+ import { useLazyModalOpener } from '../custom-hooks'
3
+ import { IDuplicateCheckConfig } from '../../../types'
4
+ import { useDuplicateOnBlur } from '../custom-hooks/use-duplicate-on-blur.hook'
5
+ import { lazy, useRef } from 'react'
6
+
7
+ const DuplicateWarningModal = lazy(() => import('./duplicate-warning.modal'))
8
+
9
+ export default function DuplicateEntryChecker({
10
+ formRef,
11
+ formId,
12
+ formKey,
13
+ formDataId,
14
+ duplicateCheckConfig,
15
+ onCustomFunctionCall,
16
+ }: IDuplicateEntryChecker) {
17
+ const { isModalOpen, openModal, closeModal } = useLazyModalOpener()
18
+ const duplicateDataRef = useRef<unknown>()
19
+
20
+ useDuplicateOnBlur(
21
+ {
22
+ formRef,
23
+ formKey,
24
+ formDataId,
25
+ groups: duplicateCheckConfig?.groups,
26
+ functionName: duplicateCheckConfig?.functionName,
27
+ onCustomFunctionCall,
28
+ },
29
+ (isDuplicateFound, data) => {
30
+ if (isDuplicateFound) {
31
+ duplicateDataRef.current = data
32
+ openModal()
33
+ } else closeModal()
34
+ },
35
+ )
36
+
37
+ if (!isModalOpen) return
38
+
39
+ return (
40
+ <DuplicateWarningModal
41
+ formRef={formRef}
42
+ formId={formId}
43
+ closeModal={closeModal}
44
+ duplicateRecord={duplicateDataRef.current}
45
+ />
46
+ )
47
+ }
48
+
49
+ interface IDuplicateEntryChecker {
50
+ formRef: FormInstance
51
+ formId?: number
52
+ formKey?: string
53
+ formDataId: string
54
+ duplicateCheckConfig?: IDuplicateCheckConfig
55
+ onCustomFunctionCall?: CustomFunctionCall
56
+ }
57
+
58
+ type CustomFunctionCall = (params: {
59
+ fnName: string
60
+ messages?: { success?: string; error?: string }
61
+ formData?: { [key: string]: any }
62
+ }) => Promise<any> | any
@@ -45,18 +45,18 @@ export default function FormDataDetailsComponent(props: IFormDataDetailsComponen
45
45
  )
46
46
  }
47
47
 
48
- function FormDataDetailsComponentChild({
49
- isPublic,
50
- formId,
51
- formKey,
52
- formDataId,
53
- formName,
54
- baseServerUrl,
55
- initialValues,
56
- companyKey,
57
- customComponents,
58
- onCustomFunctionCall,
59
- }: IFormDataDetailsComponent) {
48
+ function FormDataDetailsComponentChild({
49
+ isPublic,
50
+ formId,
51
+ formKey,
52
+ formDataId,
53
+ formName,
54
+ baseServerUrl,
55
+ initialValues,
56
+ companyKey,
57
+ customComponents,
58
+ onCustomFunctionCall,
59
+ }: IFormDataDetailsComponent) {
60
60
  const { pushCrumb, breadcrumbs } = useBreadcrumb()
61
61
 
62
62
  const location = useLocation()
@@ -242,19 +242,20 @@ function FormDataDetailsComponentChild({
242
242
  return deviceLayout
243
243
  }, [cachedConfig?.detailsConfig, currentBreakpoint])
244
244
 
245
- if (isConfigLoading || !cachedConfig) return <FormDataListSkeleton_Details />
245
+ if (isConfigLoading || !cachedConfig) return <FormDataListSkeleton_Details />
246
246
 
247
247
  if (isNotFound) return <NotFound />
248
248
 
249
249
  return (
250
250
  <>
251
- <DuplicateEntryChecker
252
- formRef={formDataRef}
253
- formId={formId}
254
- formKey={formKey}
255
- formDataId={formDataId}
256
- duplicateCheckConfig={cachedConfig.detailsConfig.duplicateCheckConfig}
257
- />
251
+ <DuplicateEntryChecker
252
+ formRef={formDataRef}
253
+ formId={formId}
254
+ formKey={formKey}
255
+ formDataId={formDataId}
256
+ duplicateCheckConfig={cachedConfig.detailsConfig.duplicateCheckConfig}
257
+ onCustomFunctionCall={onCustomFunctionCall}
258
+ />
258
259
  <Form
259
260
  layout="vertical"
260
261
  name="dynamic_form_data_form"
@@ -301,15 +302,15 @@ function FormDataDetailsComponentChild({
301
302
  )
302
303
  }
303
304
 
304
- type IFormDataDetailsComponent = {
305
- baseServerUrl?: string
306
- companyKey?: string
307
- formDataId: string
308
- formName?: string
309
- initialValues?: Record<string, any>
310
- } & (IDataDetailsPublicProps | IDataDetailsPrivateProps) &
311
- ICustomFunctionCall &
312
- ICustomComponents
305
+ type IFormDataDetailsComponent = {
306
+ baseServerUrl?: string
307
+ companyKey?: string
308
+ formDataId: string
309
+ formName?: string
310
+ initialValues?: Record<string, any>
311
+ } & (IDataDetailsPublicProps | IDataDetailsPrivateProps) &
312
+ ICustomFunctionCall &
313
+ ICustomComponents
313
314
 
314
315
  interface IDataDetailsPublicProps {
315
316
  isPublic: true
@@ -513,13 +513,13 @@ export interface IDynamicButton_DisplayStateProps {
513
513
  btnProps: IButtonElementProps
514
514
  }
515
515
 
516
- export interface ICustomFunctionCall {
517
- onCustomFunctionCall?: (params: {
518
- fnName: string
519
- messages?: { success?: string; error?: string }
520
- formData?: { [key: string]: any }
521
- }) => void
522
- }
516
+ export interface ICustomFunctionCall {
517
+ onCustomFunctionCall?: (params: {
518
+ fnName: string
519
+ messages?: { success?: string; error?: string }
520
+ formData?: { [key: string]: any }
521
+ }) => Promise<any> | any
522
+ }
523
523
  export interface IOnSuccessFunctions {
524
524
  onSuccess: (formDataId?: string) => void
525
525
  onError: () => void
@@ -45,10 +45,11 @@ export interface IFormSchema {
45
45
  }
46
46
  }
47
47
 
48
- export interface IDuplicateCheckConfig {
49
- isEnabled?: boolean
50
- groups?: IDuplicateCheckConfigGroup[]
51
- }
48
+ export interface IDuplicateCheckConfig {
49
+ isEnabled?: boolean
50
+ functionName?: string
51
+ groups?: IDuplicateCheckConfigGroup[]
52
+ }
52
53
 
53
54
  export interface ISubmissionTemplateReportConfig {
54
55
  enabled?: boolean