form-craft-package 1.7.11-dev.0 → 1.7.12-dev.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/package.json +6 -1
- package/src/api/client.ts +9 -3
- package/src/components/common/custom-hooks/use-breadcrumb.hook.ts +2 -2
- package/src/components/common/custom-hooks/use-cache-form-layout-config.hook.ts +111 -0
- package/src/components/common/loading-skeletons/table.tsx +1 -1
- package/src/components/form/1-list/index.tsx +79 -64
- package/src/components/form/1-list/table.tsx +16 -17
- package/src/components/form/2-details/index.tsx +62 -65
- package/src/components/form/index.tsx +0 -0
- package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +4 -4
- package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +2 -2
- package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +53 -60
- package/src/constants.ts +22 -1
- package/src/enums/form.enum.ts +1 -1
- package/src/types/companies/site-layout/authenticated/index.tsx +4 -2
- package/src/types/forms/index.ts +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "form-craft-package",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.12-dev.0",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
@@ -11,10 +11,13 @@
|
|
|
11
11
|
"license": "ISC",
|
|
12
12
|
"description": "",
|
|
13
13
|
"dependencies": {
|
|
14
|
+
"@tanstack/query-sync-storage-persister": "^5.77.2",
|
|
15
|
+
"@tanstack/react-query-persist-client": "^5.77.2",
|
|
14
16
|
"ajv": "^8.17.1",
|
|
15
17
|
"axios": "^1.9.0",
|
|
16
18
|
"crypto-js": "^4.2.0",
|
|
17
19
|
"js-cookie": "^3.0.5",
|
|
20
|
+
"lodash.isequal": "^4.5.0",
|
|
18
21
|
"pdfmake": "^0.2.18",
|
|
19
22
|
"qs": "^6.14.0",
|
|
20
23
|
"quill": "^2.0.3",
|
|
@@ -24,6 +27,7 @@
|
|
|
24
27
|
"react-signature-canvas": "^1.0.7"
|
|
25
28
|
},
|
|
26
29
|
"peerDependencies": {
|
|
30
|
+
"@tanstack/react-query": "^5.76.1",
|
|
27
31
|
"antd": ">=5.21.6",
|
|
28
32
|
"dayjs": ">=1.11.13",
|
|
29
33
|
"react": ">=18.0.0 <19.0.0",
|
|
@@ -34,6 +38,7 @@
|
|
|
34
38
|
"devDependencies": {
|
|
35
39
|
"@types/crypto-js": "^4.2.2",
|
|
36
40
|
"@types/js-cookie": "^3.0.6",
|
|
41
|
+
"@types/lodash.isequal": "^4.5.8",
|
|
37
42
|
"@types/pdfmake": "^0.2.11",
|
|
38
43
|
"@types/qs": "^6.9.18",
|
|
39
44
|
"@types/react": "^18.3.18",
|
package/src/api/client.ts
CHANGED
|
@@ -10,7 +10,7 @@ import axios, {
|
|
|
10
10
|
InternalAxiosRequestConfig,
|
|
11
11
|
} from 'axios'
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { PageViewTypEnum, LOCAL_STORAGE_KEYS_ENUM, SHARED_COOKIE_KEYS } from '../enums'
|
|
14
14
|
import { IDynamicForm } from '../types'
|
|
15
15
|
import { constructDynamicFormHref, fetchDynamicForms } from '../functions'
|
|
16
16
|
import { CLIENT_ID, CLIENT_SECRET } from '../constants'
|
|
@@ -27,7 +27,12 @@ apiClient.interceptors.request.use((config) => {
|
|
|
27
27
|
return config
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
const authResHandler = (resData: {
|
|
30
|
+
const authResHandler = (resData: {
|
|
31
|
+
access_token: string
|
|
32
|
+
refresh_token: string
|
|
33
|
+
companyKey: string
|
|
34
|
+
expires_in: number
|
|
35
|
+
}) => {
|
|
31
36
|
const expiry = new Date(Date.now() + resData.expires_in * 1000)
|
|
32
37
|
Cookies.set(SHARED_COOKIE_KEYS.AccessToken, resData.access_token, {
|
|
33
38
|
expires: expiry,
|
|
@@ -37,6 +42,7 @@ const authResHandler = (resData: { access_token: string; refresh_token: string;
|
|
|
37
42
|
expires: expiry,
|
|
38
43
|
path: '/',
|
|
39
44
|
})
|
|
45
|
+
Cookies.set(SHARED_COOKIE_KEYS.CompanyKey, resData.companyKey, { expires: expiry, path: '/' })
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
let isRefreshing = false
|
|
@@ -180,7 +186,7 @@ export const auth = async (
|
|
|
180
186
|
breadcrumbStore.push({
|
|
181
187
|
label: splittedFormName,
|
|
182
188
|
href: constructDynamicFormHref(firstForm.name),
|
|
183
|
-
type:
|
|
189
|
+
type: PageViewTypEnum.List,
|
|
184
190
|
})
|
|
185
191
|
}
|
|
186
192
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useSyncExternalStore } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { PageViewTypEnum, LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
|
|
3
3
|
import { NEW_FORM_DATA_IDENTIFIER } from '../../../constants'
|
|
4
4
|
|
|
5
|
-
export type IBreadcrumb = { label: string; href: string; [key: string]: any; type:
|
|
5
|
+
export type IBreadcrumb = { label: string; href: string; [key: string]: any; type: PageViewTypEnum }
|
|
6
6
|
|
|
7
7
|
let crumbs: IBreadcrumb[] = loadFromStorage()
|
|
8
8
|
const listeners = new Set<() => void>()
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useMemo } from 'react'
|
|
2
|
+
import { useQuery, useQueryClient, keepPreviousData } from '@tanstack/react-query'
|
|
3
|
+
import isEqual from 'lodash.isequal'
|
|
4
|
+
import { IFormConfigDetails, IFormSchema } from '../../../types'
|
|
5
|
+
import client from '../../../api/client'
|
|
6
|
+
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'
|
|
7
|
+
import { persistQueryClient } from '@tanstack/react-query-persist-client'
|
|
8
|
+
|
|
9
|
+
export interface UseCacheFormLayoutConfig {
|
|
10
|
+
cachedConfig?: IFormConfigDetails & IFormSchema & { formId?: number }
|
|
11
|
+
isConfigLoading: boolean
|
|
12
|
+
isUpdateFound: boolean
|
|
13
|
+
applyUpdate: () => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function useCacheFormLayoutConfig(formId?: number): UseCacheFormLayoutConfig {
|
|
17
|
+
const queryClient = useQueryClient()
|
|
18
|
+
|
|
19
|
+
const [currentSchema, setCurrentSchema] = useState<IFormConfigDetails & IFormSchema>()
|
|
20
|
+
const [pendingSchema, setPendingSchema] = useState<IFormConfigDetails & IFormSchema>()
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setCurrentSchema(undefined)
|
|
24
|
+
setPendingSchema(undefined)
|
|
25
|
+
}, [formId])
|
|
26
|
+
|
|
27
|
+
const queryKey = useMemo(() => ['layoutConfig', formId ? String(formId) : 'default'] as const, [formId])
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
data: fetchedSchema,
|
|
31
|
+
isLoading,
|
|
32
|
+
isSuccess,
|
|
33
|
+
} = useQuery<(IFormConfigDetails & IFormSchema) | undefined, Error>({
|
|
34
|
+
queryKey,
|
|
35
|
+
queryFn: () => fetchLayoutConfig(formId),
|
|
36
|
+
gcTime: Infinity,
|
|
37
|
+
enabled: !!formId,
|
|
38
|
+
placeholderData: keepPreviousData,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const localStoragePersistor = createSyncStoragePersister({
|
|
43
|
+
storage: window.localStorage,
|
|
44
|
+
})
|
|
45
|
+
persistQueryClient({
|
|
46
|
+
queryClient,
|
|
47
|
+
persister: localStoragePersistor,
|
|
48
|
+
// maxAge: 1000 * 60 * 60 * 24, // 24h
|
|
49
|
+
dehydrateOptions: {
|
|
50
|
+
shouldDehydrateQuery: (query) => {
|
|
51
|
+
const [key, id] = query.queryKey
|
|
52
|
+
if (key === 'layoutConfig' && id === String(formId)) return false
|
|
53
|
+
|
|
54
|
+
return true
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
}, [queryClient, formId])
|
|
59
|
+
|
|
60
|
+
// ─── on new network data: queue diffs ───
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!isSuccess || !fetchedSchema || !formId) return
|
|
63
|
+
|
|
64
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
65
|
+
|
|
66
|
+
// first-ever load
|
|
67
|
+
if (!currentSchema) {
|
|
68
|
+
timer = setTimeout(() => {
|
|
69
|
+
setCurrentSchema(fetchedSchema)
|
|
70
|
+
}, 250)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// later loads with real changes → queue up
|
|
74
|
+
else if (!isEqual(currentSchema, fetchedSchema)) setPendingSchema(fetchedSchema)
|
|
75
|
+
|
|
76
|
+
return () => {
|
|
77
|
+
if (timer) clearTimeout(timer)
|
|
78
|
+
}
|
|
79
|
+
}, [isSuccess, fetchedSchema, currentSchema, formId])
|
|
80
|
+
|
|
81
|
+
// ─── apply any queued updates ──
|
|
82
|
+
const applyUpdate = useCallback(() => {
|
|
83
|
+
if (!pendingSchema) return
|
|
84
|
+
|
|
85
|
+
// overwrite the cached schema
|
|
86
|
+
queryClient.setQueryData(queryKey, pendingSchema)
|
|
87
|
+
|
|
88
|
+
setCurrentSchema(pendingSchema)
|
|
89
|
+
setPendingSchema(undefined)
|
|
90
|
+
}, [pendingSchema, queryClient, queryKey])
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
cachedConfig: currentSchema?.id !== formId ? pendingSchema : currentSchema,
|
|
94
|
+
isConfigLoading: !!formId ? isLoading && !currentSchema : false,
|
|
95
|
+
isUpdateFound: Boolean(pendingSchema),
|
|
96
|
+
applyUpdate,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function fetchLayoutConfig(formId?: number): Promise<(IFormConfigDetails & IFormSchema) | undefined> {
|
|
101
|
+
if (!formId) return undefined
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const res = await client.get<{ data: string }>(`/api/form/${formId}`)
|
|
105
|
+
if (res.status === 200) {
|
|
106
|
+
const { data: jsonData, ...restResData } = res.data
|
|
107
|
+
return { ...restResData, ...JSON.parse(jsonData) } as IFormConfigDetails & IFormSchema
|
|
108
|
+
}
|
|
109
|
+
} catch {}
|
|
110
|
+
return
|
|
111
|
+
}
|
|
@@ -3,7 +3,7 @@ import SkeletonBlock from '.'
|
|
|
3
3
|
export default function FormDataListSkeleton_Table({ small = false }: { small?: boolean }) {
|
|
4
4
|
return (
|
|
5
5
|
<>
|
|
6
|
-
<div className={`
|
|
6
|
+
<div className={`flex items-center justify-between rounded-md mb-2`}>
|
|
7
7
|
<SkeletonBlock width="200px" height={small ? 20 : undefined} />
|
|
8
8
|
<div className="flex items-center gap-2">
|
|
9
9
|
<SkeletonBlock width="150px" height={small ? 20 : undefined} />
|
|
@@ -5,22 +5,36 @@ import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-butt
|
|
|
5
5
|
import FormDataListTableComponent, { IDataListLayoutConfig } from './table'
|
|
6
6
|
import { IFormJoin, IFormDataListData, IFormDataListConfig } from '../../../types'
|
|
7
7
|
import { getProjectionKey, mergeJoins } from '../../../functions'
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import { useCacheFormLayoutConfig } from '../../common/custom-hooks/use-cache-form-layout-config.hook'
|
|
9
|
+
import { useNotification } from '../../common/custom-hooks'
|
|
10
|
+
|
|
11
|
+
export function FormDataListComponent({
|
|
12
|
+
formId,
|
|
13
|
+
companyKey,
|
|
14
|
+
baseServerUrl,
|
|
15
|
+
onCustomFunctionCall,
|
|
16
|
+
}: IFormDataListComponent) {
|
|
17
|
+
const [dataLoading, setDataLoading] = useState(true)
|
|
11
18
|
const [dataList, setDataList] = useState<{ data: IFormDataListData[]; total: number }>({ data: [], total: 0 })
|
|
12
19
|
const [listLayoutConfig, setListLayoutConfig] = useState<IDataListLayoutConfig>()
|
|
20
|
+
const { warning, destroy } = useNotification()
|
|
13
21
|
|
|
14
22
|
const reportDataApiCancelFuncRef = useRef<() => void | undefined>()
|
|
15
23
|
const dataProjectRef = useRef<{ [key: string]: string }>({})
|
|
16
24
|
const formJoinsRef = useRef<IFormJoin[]>([])
|
|
17
25
|
const defaultFilterReqDataRef = useRef<IDataListReqData | undefined>()
|
|
26
|
+
const apiCallCounterRef = useRef(0)
|
|
18
27
|
|
|
19
28
|
const attachmentBaseUrl = useMemo(() => `${baseServerUrl}/api/attachment/${companyKey}`, [baseServerUrl, companyKey])
|
|
20
29
|
const headerLayoutContext = { formId, attachmentBaseUrl, companyKey, onCustomFunctionCall }
|
|
21
30
|
|
|
31
|
+
const { cachedConfig, isConfigLoading } = useCacheFormLayoutConfig(formId)
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
reportDataApiCancelFuncRef.current?.()
|
|
35
|
+
}, [formId])
|
|
36
|
+
|
|
22
37
|
useEffect(() => {
|
|
23
|
-
setLoadings({ initial: true, data: true })
|
|
24
38
|
setDataList({ data: [], total: 0 })
|
|
25
39
|
dataProjectRef.current = {}
|
|
26
40
|
formJoinsRef.current = []
|
|
@@ -28,62 +42,58 @@ function FormDataListComponent({ formId, companyKey, baseServerUrl, onCustomFunc
|
|
|
28
42
|
}, [location.pathname])
|
|
29
43
|
|
|
30
44
|
useEffect(() => {
|
|
31
|
-
if (!formId) return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
columns: columns.map((c) => (c.key ? { ...c, key: getProjectionKey(c.key) } : c)),
|
|
76
|
-
},
|
|
77
|
-
})
|
|
78
|
-
}
|
|
45
|
+
if (!formId || !cachedConfig) return
|
|
46
|
+
|
|
47
|
+
const { columns, pagination, defaultFilter, defaultSorter, header } =
|
|
48
|
+
cachedConfig.dataListConfig as IFormDataListConfig
|
|
49
|
+
|
|
50
|
+
if (Array.isArray(columns)) {
|
|
51
|
+
dataProjectRef.current = columns.reduce(
|
|
52
|
+
(curr, c) => (c.key ? { ...curr, [getProjectionKey(c.key)]: `$${c.key}` } : curr),
|
|
53
|
+
{},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
formJoinsRef.current = mergeJoins([
|
|
57
|
+
...columns.map((c) => c.joins),
|
|
58
|
+
...Object.values(header.elements).reduce((c: IFormJoin[], el) => {
|
|
59
|
+
if (el.props && 'filter' in el.props && Array.isArray(el.props.filter?.joins)) {
|
|
60
|
+
return [...c, el.props.filter.joins]
|
|
61
|
+
}
|
|
62
|
+
return c
|
|
63
|
+
}, []),
|
|
64
|
+
])
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const defaultReqData: IDataListReqData = {}
|
|
68
|
+
|
|
69
|
+
if (!pagination?.hasNoPagination) {
|
|
70
|
+
defaultReqData.current = 1
|
|
71
|
+
defaultReqData.limit = pagination?.defaultPageSize ?? 10
|
|
72
|
+
}
|
|
73
|
+
if (defaultSorter) defaultReqData.sort = JSON.stringify({ [defaultSorter.field]: defaultSorter.order })
|
|
74
|
+
if (defaultFilter?.config && Object.values(defaultFilter.config).length)
|
|
75
|
+
// The operators are stored using @, and here it replaces @ with $. Otherwise, MongoDB's JSON-to-BSON conversion driver was detecting certain operators, especially $regex, and automatically modified them. To prevent data manipulation by the MongoDB driver, @ is used instead of $.
|
|
76
|
+
defaultReqData.match = JSON.stringify(defaultFilter.config).replaceAll('@', '$')
|
|
77
|
+
|
|
78
|
+
defaultFilterReqDataRef.current = defaultReqData
|
|
79
|
+
|
|
80
|
+
fetchFormDataList(defaultReqData)
|
|
81
|
+
|
|
82
|
+
// configForFormId is passed to ensure that the table shows the correct data list when the forms are switched quickly
|
|
83
|
+
setListLayoutConfig({
|
|
84
|
+
configForFormId: formId,
|
|
85
|
+
dataListConfig: {
|
|
86
|
+
...cachedConfig.dataListConfig,
|
|
87
|
+
columns: columns.map((c) => (c.key ? { ...c, key: getProjectionKey(c.key) } : c)),
|
|
88
|
+
} as IFormDataListConfig,
|
|
79
89
|
})
|
|
80
|
-
|
|
81
|
-
return () => cancel()
|
|
82
|
-
}, [formId])
|
|
90
|
+
}, [formId, cachedConfig?.dataListConfig])
|
|
83
91
|
|
|
84
92
|
const fetchFormDataList = useCallback(
|
|
85
93
|
(reqData?: IDataListReqData & { skip?: number }) => {
|
|
86
94
|
if (!reqData || !formId) return
|
|
95
|
+
apiCallCounterRef.current += 1
|
|
96
|
+
setDataLoading(true)
|
|
87
97
|
|
|
88
98
|
if (!reqData.current && defaultFilterReqDataRef.current?.current)
|
|
89
99
|
reqData.current = defaultFilterReqDataRef.current.current
|
|
@@ -101,17 +111,25 @@ function FormDataListComponent({ formId, companyKey, baseServerUrl, onCustomFunc
|
|
|
101
111
|
joins: formJoinsRef.current,
|
|
102
112
|
project: JSON.stringify(dataProjectRef.current),
|
|
103
113
|
...restReqData,
|
|
104
|
-
// match: JSON.stringify({"Data.phaseId": 16673})
|
|
105
114
|
})
|
|
106
115
|
reportDataApiCancelFuncRef.current = cancel
|
|
107
116
|
|
|
108
117
|
request
|
|
109
118
|
.then((res) => {
|
|
110
|
-
if (res.status === 200)
|
|
119
|
+
if (res.status === 200) {
|
|
120
|
+
setDataList({ data: res.data.data, total: res.data.totalRecords })
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
.catch((err) => {
|
|
124
|
+
if (err?.response?.status === 500) {
|
|
125
|
+
destroy()
|
|
126
|
+
warning({ message: 'There has been an error fetching the data!' })
|
|
127
|
+
}
|
|
111
128
|
})
|
|
112
129
|
.finally(() => {
|
|
113
|
-
|
|
130
|
+
apiCallCounterRef.current -= 1
|
|
114
131
|
reportDataApiCancelFuncRef.current = undefined
|
|
132
|
+
if (apiCallCounterRef.current === 0) setDataLoading(false)
|
|
115
133
|
})
|
|
116
134
|
},
|
|
117
135
|
[formId],
|
|
@@ -122,20 +140,17 @@ function FormDataListComponent({ formId, companyKey, baseServerUrl, onCustomFunc
|
|
|
122
140
|
layoutsConfigs={listLayoutConfig}
|
|
123
141
|
dataList={dataList}
|
|
124
142
|
updateDataList={(reqData) => {
|
|
125
|
-
if (reportDataApiCancelFuncRef.current)
|
|
126
|
-
// prev request
|
|
127
|
-
reportDataApiCancelFuncRef.current()
|
|
143
|
+
if (reportDataApiCancelFuncRef.current) reportDataApiCancelFuncRef.current()
|
|
128
144
|
|
|
129
145
|
fetchFormDataList(reqData)
|
|
130
146
|
}}
|
|
131
|
-
parentLoadings={
|
|
147
|
+
parentLoadings={{ data: dataLoading, initial: isConfigLoading || !cachedConfig }}
|
|
132
148
|
loadingBlock={<FormDataListSkeleton_Table />}
|
|
133
|
-
setParentLoading={(bool) =>
|
|
149
|
+
setParentLoading={(bool) => setDataLoading(bool)}
|
|
134
150
|
headerLayoutContext={headerLayoutContext}
|
|
135
151
|
/>
|
|
136
152
|
)
|
|
137
153
|
}
|
|
138
|
-
export { FormDataListComponent }
|
|
139
154
|
|
|
140
155
|
type IFormDataListComponent = {
|
|
141
156
|
baseServerUrl?: string
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
2
2
|
import FormDataListHeaderComponent from './table-header'
|
|
3
3
|
import useDebounced from '../../common/custom-hooks/use-debounce.hook'
|
|
4
|
-
import { Table } from 'antd'
|
|
4
|
+
import { Spin, Table } from 'antd'
|
|
5
5
|
import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
|
|
6
6
|
import { SorterResult } from 'antd/es/table/interface'
|
|
7
7
|
import { constructDynamicFormHref, renderTableColumns, revertProjectionKey } from '../../../functions/forms'
|
|
@@ -100,26 +100,25 @@ export default function FormDataListTableComponent({
|
|
|
100
100
|
[tableColumns, currentBreakpoint],
|
|
101
101
|
)
|
|
102
102
|
|
|
103
|
+
// if (loading) return
|
|
103
104
|
if (loading && loadingBlock) return loadingBlock
|
|
104
105
|
|
|
105
106
|
return (
|
|
106
107
|
<>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
/>
|
|
122
|
-
)}
|
|
108
|
+
<FormDataListHeaderComponent
|
|
109
|
+
layoutConfig={layoutsConfigs?.dataListConfig.header}
|
|
110
|
+
updateDynamicFilter={(match) => {
|
|
111
|
+
setParentLoading(true)
|
|
112
|
+
setFilterReqData((c) => ({ ...c, match, current: 1 }))
|
|
113
|
+
}}
|
|
114
|
+
titleComponent={
|
|
115
|
+
<>
|
|
116
|
+
<span className="pr-1">{otherConfigs?.title}</span>
|
|
117
|
+
{otherConfigs?.showCount && <span>({parentLoadings.data ? <Spin size="small" /> : dataList.total})</span>}
|
|
118
|
+
</>
|
|
119
|
+
}
|
|
120
|
+
headerLayoutContext={headerLayoutContext}
|
|
121
|
+
/>
|
|
123
122
|
{(!otherConfigs || otherConfigs.listType === FormDataListViewTypeEnum.Table) && (
|
|
124
123
|
<Table<IFormDataListData>
|
|
125
124
|
dataSource={dataList.data}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Form } from 'antd'
|
|
1
|
+
import { Form, Spin } from 'antd'
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
3
|
-
import { IDndLayoutStructure_Responsive
|
|
3
|
+
import { IDndLayoutStructure_Responsive } from '../../../types'
|
|
4
4
|
import { LayoutRendererRow } from '../layout-renderer/1-row'
|
|
5
5
|
import { DynamicFormButtonRender, ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
|
|
6
6
|
import FormDataListSkeleton_Details from '../../common/loading-skeletons/details'
|
|
@@ -10,12 +10,7 @@ import client from '../../../api/client'
|
|
|
10
10
|
import { useBreadcrumb } from '../../common/custom-hooks/use-breadcrumb.hook'
|
|
11
11
|
import NotFound from '../../common/not-found'
|
|
12
12
|
import { useManyToManyConnector } from '../../common/custom-hooks/use-many-to-many-connector.hook'
|
|
13
|
-
import {
|
|
14
|
-
BreadcrumbTypeEnum,
|
|
15
|
-
DeviceBreakpointEnum,
|
|
16
|
-
FormPreservedItemKeys,
|
|
17
|
-
LOCAL_STORAGE_KEYS_ENUM,
|
|
18
|
-
} from '../../../enums'
|
|
13
|
+
import { PageViewTypEnum, DeviceBreakpointEnum, FormPreservedItemKeys, LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
|
|
19
14
|
import {
|
|
20
15
|
fromMongoDbExtendedJSON,
|
|
21
16
|
getPickerFieldsWithOriginalTz,
|
|
@@ -23,6 +18,8 @@ import {
|
|
|
23
18
|
isValidMongoDbId,
|
|
24
19
|
queryParamsToObject,
|
|
25
20
|
} from '../../../functions/forms'
|
|
21
|
+
import { UserAuth } from '../../../api/user'
|
|
22
|
+
import { useCacheFormLayoutConfig } from '../../common/custom-hooks/use-cache-form-layout-config.hook'
|
|
26
23
|
|
|
27
24
|
export default function FormDataDetailsComponent({
|
|
28
25
|
isPublic,
|
|
@@ -38,14 +35,15 @@ export default function FormDataDetailsComponent({
|
|
|
38
35
|
const { push } = useBreadcrumb()
|
|
39
36
|
const location = useLocation()
|
|
40
37
|
const [formDataRef] = Form.useForm()
|
|
41
|
-
const [
|
|
42
|
-
const [layoutConfig, setLayoutConfig] = useState<IDndLayoutStructure_Responsive>({ elements: {}, layouts: {} })
|
|
38
|
+
const [loading, setLoading] = useState(true)
|
|
43
39
|
const [isNotFound, setIsNotFound] = useState(false)
|
|
44
40
|
const originalTzFieldsRef = useRef<string[]>([])
|
|
45
41
|
useManyToManyConnector(formDataRef)
|
|
46
42
|
|
|
47
43
|
const currentBreakpoint = useGetCurrentBreakpoint()
|
|
48
44
|
|
|
45
|
+
const { cachedConfig, isConfigLoading } = useCacheFormLayoutConfig(formId)
|
|
46
|
+
|
|
49
47
|
useEffect(() => {
|
|
50
48
|
// for public forms, setting the details form into localStorage, so that buttons work correctly
|
|
51
49
|
if (isPublic)
|
|
@@ -61,15 +59,10 @@ export default function FormDataDetailsComponent({
|
|
|
61
59
|
push({
|
|
62
60
|
label: isNewFormDataPage(formDataId) ? splittedFormName.toLowerCase() : splittedFormName,
|
|
63
61
|
href: location.pathname,
|
|
64
|
-
type: isNewFormDataPage(formDataId) ?
|
|
62
|
+
type: isNewFormDataPage(formDataId) ? PageViewTypEnum.New : PageViewTypEnum.Details,
|
|
65
63
|
})
|
|
66
64
|
}, [location.pathname, formName, formDataId])
|
|
67
65
|
|
|
68
|
-
const layout = useMemo(
|
|
69
|
-
() => layoutConfig.layouts[currentBreakpoint] ?? layoutConfig.layouts[DeviceBreakpointEnum.Default] ?? [],
|
|
70
|
-
[layoutConfig.layouts, currentBreakpoint],
|
|
71
|
-
)
|
|
72
|
-
|
|
73
66
|
// Apply initialValues from parent
|
|
74
67
|
useEffect(() => {
|
|
75
68
|
if (initialValues && isNewFormDataPage(formDataId)) {
|
|
@@ -96,7 +89,7 @@ export default function FormDataDetailsComponent({
|
|
|
96
89
|
|
|
97
90
|
const fetchFormData = useCallback(
|
|
98
91
|
(dFormId?: number) => {
|
|
99
|
-
if (isNewFormDataPage(formDataId) || !isValidMongoDbId(formDataId))
|
|
92
|
+
if (isNewFormDataPage(formDataId) || !isValidMongoDbId(formDataId)) setLoading(false)
|
|
100
93
|
else {
|
|
101
94
|
if (!dFormId) {
|
|
102
95
|
console.error('Form ID is required to fetch form data')
|
|
@@ -119,69 +112,73 @@ export default function FormDataDetailsComponent({
|
|
|
119
112
|
}
|
|
120
113
|
} else setIsNotFound(true)
|
|
121
114
|
})
|
|
122
|
-
.finally(() =>
|
|
115
|
+
.finally(() => setLoading(false))
|
|
123
116
|
}
|
|
124
117
|
},
|
|
125
118
|
[formDataId, formDataRef],
|
|
126
119
|
)
|
|
127
120
|
|
|
128
121
|
useEffect(() => {
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
.finally(() => setLoadings((c) => ({ ...c, layout: false })))
|
|
155
|
-
}
|
|
156
|
-
}, [formId, formKey, isPublic, formDataRef, fetchFormData])
|
|
122
|
+
if (!cachedConfig?.detailsConfig) return
|
|
123
|
+
|
|
124
|
+
const { elements } = cachedConfig.detailsConfig as IDndLayoutStructure_Responsive
|
|
125
|
+
originalTzFieldsRef.current = getPickerFieldsWithOriginalTz(elements)
|
|
126
|
+
|
|
127
|
+
if (isPublic) {
|
|
128
|
+
if (cachedConfig.generateConfig?.submissionPdf?.enabled)
|
|
129
|
+
formDataRef.setFieldValue(FormPreservedItemKeys.SubmissionPdfConfig, cachedConfig.generateConfig.submissionPdf)
|
|
130
|
+
setLoading(false)
|
|
131
|
+
} else fetchFormData(formId)
|
|
132
|
+
}, [cachedConfig, formId, isPublic, formDataRef, fetchFormData])
|
|
133
|
+
|
|
134
|
+
const layout = useMemo(() => {
|
|
135
|
+
if (!cachedConfig?.detailsConfig?.layouts) return []
|
|
136
|
+
|
|
137
|
+
const deviceLayout =
|
|
138
|
+
cachedConfig.detailsConfig.layouts[currentBreakpoint] ??
|
|
139
|
+
cachedConfig.detailsConfig.layouts[DeviceBreakpointEnum.Default]
|
|
140
|
+
|
|
141
|
+
if (!deviceLayout) return []
|
|
142
|
+
|
|
143
|
+
return deviceLayout
|
|
144
|
+
}, [cachedConfig?.detailsConfig, currentBreakpoint])
|
|
157
145
|
|
|
158
146
|
const formContext = useMemo(
|
|
159
|
-
() => ({
|
|
147
|
+
() => ({
|
|
148
|
+
detailPageFormId: formId,
|
|
149
|
+
formId,
|
|
150
|
+
formKey,
|
|
151
|
+
formDataId,
|
|
152
|
+
formRef: formDataRef,
|
|
153
|
+
companyKey: UserAuth.getCompanyKey(),
|
|
154
|
+
}),
|
|
160
155
|
[formDataId, formDataRef, formId, formKey, companyKey],
|
|
161
156
|
)
|
|
162
157
|
|
|
163
|
-
if (
|
|
158
|
+
if (isConfigLoading || !cachedConfig) return <FormDataListSkeleton_Details />
|
|
164
159
|
|
|
165
160
|
if (isNotFound) return <NotFound />
|
|
166
161
|
|
|
167
162
|
return (
|
|
168
|
-
<
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
163
|
+
<Spin spinning={loading}>
|
|
164
|
+
<Form layout="vertical" name="dynamic_form_data_form" form={formDataRef}>
|
|
165
|
+
{layout.map((row, rowIdx) => (
|
|
166
|
+
<LayoutRendererRow
|
|
167
|
+
key={rowIdx}
|
|
168
|
+
rowData={row}
|
|
169
|
+
formContext={formContext}
|
|
170
|
+
elements={cachedConfig.detailsConfig.elements}
|
|
171
|
+
renderButton={(btnProps, conditions) => (
|
|
172
|
+
<DynamicFormButtonRender
|
|
173
|
+
displayStateProps={{ btnProps, conditions, defaultDisabled: true }}
|
|
174
|
+
formContext={formContext}
|
|
175
|
+
onCustomFunctionCall={onCustomFunctionCall}
|
|
176
|
+
/>
|
|
177
|
+
)}
|
|
178
|
+
/>
|
|
179
|
+
))}
|
|
180
|
+
</Form>
|
|
181
|
+
</Spin>
|
|
185
182
|
)
|
|
186
183
|
}
|
|
187
184
|
|
|
File without changes
|
|
@@ -3,7 +3,7 @@ import { Button_FillerPortal } from '../../../common/button'
|
|
|
3
3
|
import { useNavigate } from 'react-router-dom'
|
|
4
4
|
import { FaChevronRight } from 'react-icons/fa6'
|
|
5
5
|
import React from 'react'
|
|
6
|
-
import {
|
|
6
|
+
import { PageViewTypEnum } from '../../../../enums'
|
|
7
7
|
import { IBreadcrumbElementProps } from '../../../../types'
|
|
8
8
|
|
|
9
9
|
export default function LayoutRenderer_Breadcrumb({ elementProps }: { elementProps: IBreadcrumbElementProps }) {
|
|
@@ -23,11 +23,11 @@ export default function LayoutRenderer_Breadcrumb({ elementProps }: { elementPro
|
|
|
23
23
|
}}
|
|
24
24
|
>
|
|
25
25
|
<span className="font-normal italic">
|
|
26
|
-
{bc.type ===
|
|
26
|
+
{bc.type === PageViewTypEnum.New ? `${elementProps.newText} ` : ''}
|
|
27
27
|
{bc.label}
|
|
28
|
-
{bc.type ===
|
|
28
|
+
{bc.type === PageViewTypEnum.Details
|
|
29
29
|
? ` ${elementProps.detailText}`
|
|
30
|
-
: bc.type ===
|
|
30
|
+
: bc.type === PageViewTypEnum.List
|
|
31
31
|
? ` ${elementProps.listText}`
|
|
32
32
|
: ''}
|
|
33
33
|
</span>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { PageViewTypEnum, FieldElementOptionSourceEnum, FilterConfigTypeEnum } from '../../../../enums'
|
|
3
3
|
import { IElementBaseProps } from '.'
|
|
4
4
|
import { LayoutRenderer_FieldElement } from './2-field-element'
|
|
5
5
|
import { getElementGeneralizedProps } from '../../../../functions/forms/get-element-props'
|
|
@@ -275,7 +275,7 @@ export default function LayoutRenderer_FieldsWithOptions({
|
|
|
275
275
|
const formInfo = getFormById(props.optionSource.formId)
|
|
276
276
|
if (!formInfo) return
|
|
277
277
|
|
|
278
|
-
push({ label: formInfo.name, href: location.pathname, type:
|
|
278
|
+
push({ label: formInfo.name, href: location.pathname, type: PageViewTypEnum.Details })
|
|
279
279
|
navigate(`${constructDynamicFormHref(formInfo.name)}/${selectedValue}`)
|
|
280
280
|
}}
|
|
281
281
|
>
|
|
@@ -6,13 +6,13 @@ import { getIdEqualsQuery, getProjectionKey, isNewFormDataPage, mergeJoins } fro
|
|
|
6
6
|
import { cancelableClient } from '../../../../api/client'
|
|
7
7
|
import { IFormContext } from '../1-row'
|
|
8
8
|
import { FilterConfigTypeEnum, FormRelationshipEnum } from '../../../../enums'
|
|
9
|
+
import { useCacheFormLayoutConfig } from '../../../common/custom-hooks/use-cache-form-layout-config.hook'
|
|
9
10
|
import {
|
|
10
11
|
IFormDataListData,
|
|
11
12
|
IFormDataLoadElementProps,
|
|
12
13
|
IFormJoin,
|
|
13
14
|
IFormRelationshipConfig,
|
|
14
15
|
IFormRelationshipConfig_OneToMany,
|
|
15
|
-
IFormSchema,
|
|
16
16
|
} from '../../../../types'
|
|
17
17
|
|
|
18
18
|
export default function LayoutRenderer_LoadFormData({ formContext, elementProps }: ILayoutRenderer_LoadFormData) {
|
|
@@ -22,9 +22,11 @@ export default function LayoutRenderer_LoadFormData({ formContext, elementProps
|
|
|
22
22
|
formName: '',
|
|
23
23
|
parentFormJoins: [],
|
|
24
24
|
})
|
|
25
|
-
const [
|
|
25
|
+
const [loading, setLoading] = useState(true)
|
|
26
26
|
const [listLayoutConfig, setListLayoutConfig] = useState<IDataListLayoutConfig>()
|
|
27
27
|
|
|
28
|
+
const { cachedConfig, isConfigLoading } = useCacheFormLayoutConfig(baseFormId)
|
|
29
|
+
|
|
28
30
|
const [dataList, setDataList] = useState<{ data: IFormDataListData[]; total: number }>({ data: [], total: 0 })
|
|
29
31
|
const dataProjectRef = useRef<{ [key: string]: string }>({})
|
|
30
32
|
const reportDataApiCancelFuncRef = useRef<() => void | undefined>(undefined)
|
|
@@ -38,7 +40,7 @@ export default function LayoutRenderer_LoadFormData({ formContext, elementProps
|
|
|
38
40
|
: '',
|
|
39
41
|
) => {
|
|
40
42
|
if (!baseFormId) return
|
|
41
|
-
|
|
43
|
+
setLoading(true)
|
|
42
44
|
|
|
43
45
|
const matchFilters: { [key: string]: any }[] = [getIdEqualsQuery(formId, formDataId)]
|
|
44
46
|
if (headerAppliedFilters) matchFilters.push(JSON.parse(headerAppliedFilters))
|
|
@@ -61,72 +63,63 @@ export default function LayoutRenderer_LoadFormData({ formContext, elementProps
|
|
|
61
63
|
setDataList({ data: res.data.data, total: res.data.totalRecords })
|
|
62
64
|
}
|
|
63
65
|
})
|
|
64
|
-
.finally(() =>
|
|
66
|
+
.finally(() => setLoading(false))
|
|
65
67
|
},
|
|
66
68
|
[joins, baseFormId, initialFilter],
|
|
67
69
|
)
|
|
68
70
|
|
|
69
71
|
useEffect(() => {
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const { request, cancel } = cancelableClient.get(`/api/form/${baseFormId}`)
|
|
74
|
-
request.then((res) => {
|
|
75
|
-
if (res.status === 200) {
|
|
76
|
-
const parsedData: IFormSchema | null = JSON.parse(res.data.data)
|
|
77
|
-
if (!parsedData) return
|
|
78
|
-
const { relationships = [] } = parsedData
|
|
79
|
-
|
|
80
|
-
const relationship: IFormRelationshipConfig | undefined = relationships.find((r: IFormRelationshipConfig) =>
|
|
81
|
-
r.displayConfigs?.some((dc) => dc.id === displayConfigId),
|
|
82
|
-
)
|
|
83
|
-
const displayConfig = relationship?.displayConfigs?.find((dc) => dc.id === displayConfigId)
|
|
84
|
-
|
|
85
|
-
if (!displayConfig || displayConfig.isRenderChildForm) return
|
|
86
|
-
|
|
87
|
-
if (relationship!.type === FormRelationshipEnum.OneToMany && relationship?.m2mTargetFormFK)
|
|
88
|
-
m2mForeignKeysRef.current = {
|
|
89
|
-
currentForm: relationship!.foreignKey,
|
|
90
|
-
otherForm: (relationship as IFormRelationshipConfig_OneToMany)!.m2mTargetFormFK!,
|
|
91
|
-
}
|
|
72
|
+
if (isNewFormDataPage(formDataId) || !cachedConfig) return
|
|
73
|
+
|
|
74
|
+
const { relationships = [] } = cachedConfig
|
|
92
75
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
])
|
|
105
|
-
if (Array.isArray(displayConfig.config.columns))
|
|
106
|
-
dataProjectRef.current = displayConfig.config.columns.reduce((curr, c) => {
|
|
107
|
-
if (!c.key) return curr
|
|
108
|
-
|
|
109
|
-
return { ...curr, [getProjectionKey(c.key)]: `$${c.key}` }
|
|
110
|
-
}, {})
|
|
111
|
-
|
|
112
|
-
setListLayoutConfig({
|
|
113
|
-
configForFormId: baseFormId,
|
|
114
|
-
dataListConfig: {
|
|
115
|
-
...displayConfig.config,
|
|
116
|
-
columns: displayConfig.config.columns.map((c) => (c.key ? { ...c, key: getProjectionKey(c.key) } : c)),
|
|
117
|
-
},
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
fetchDataList()
|
|
121
|
-
}
|
|
76
|
+
const relationship: IFormRelationshipConfig | undefined = relationships.find((r: IFormRelationshipConfig) =>
|
|
77
|
+
r.displayConfigs?.some((dc) => dc.id === displayConfigId),
|
|
78
|
+
)
|
|
79
|
+
const displayConfig = relationship?.displayConfigs?.find((dc) => dc.id === displayConfigId)
|
|
80
|
+
|
|
81
|
+
if (!displayConfig || displayConfig.isRenderChildForm) return
|
|
82
|
+
|
|
83
|
+
if (relationship!.type === FormRelationshipEnum.OneToMany && relationship?.m2mTargetFormFK)
|
|
84
|
+
m2mForeignKeysRef.current = {
|
|
85
|
+
currentForm: relationship!.foreignKey,
|
|
86
|
+
otherForm: (relationship as IFormRelationshipConfig_OneToMany)!.m2mTargetFormFK!,
|
|
122
87
|
}
|
|
123
|
-
|
|
88
|
+
|
|
89
|
+
lastChildInfo.current = { formName: cachedConfig.name, parentFormJoins: joins }
|
|
90
|
+
|
|
91
|
+
if (displayConfig.config) {
|
|
92
|
+
formJoinsRef.current = mergeJoins([
|
|
93
|
+
...displayConfig.config.columns.map((c) => c.joins),
|
|
94
|
+
...Object.values(displayConfig.config.header.elements).reduce((c: IFormJoin[], el) => {
|
|
95
|
+
if (el.props && 'filter' in el.props && Array.isArray(el.props.filter?.joins)) {
|
|
96
|
+
return [...c, el.props.filter.joins]
|
|
97
|
+
}
|
|
98
|
+
return c
|
|
99
|
+
}, []),
|
|
100
|
+
])
|
|
101
|
+
if (Array.isArray(displayConfig.config.columns))
|
|
102
|
+
dataProjectRef.current = displayConfig.config.columns.reduce((curr, c) => {
|
|
103
|
+
if (!c.key) return curr
|
|
104
|
+
|
|
105
|
+
return { ...curr, [getProjectionKey(c.key)]: `$${c.key}` }
|
|
106
|
+
}, {})
|
|
107
|
+
|
|
108
|
+
setListLayoutConfig({
|
|
109
|
+
configForFormId: baseFormId,
|
|
110
|
+
dataListConfig: {
|
|
111
|
+
...displayConfig.config,
|
|
112
|
+
columns: displayConfig.config.columns.map((c) => (c.key ? { ...c, key: getProjectionKey(c.key) } : c)),
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
fetchDataList()
|
|
117
|
+
}
|
|
124
118
|
|
|
125
119
|
return () => {
|
|
126
|
-
cancel()
|
|
127
120
|
reportDataApiCancelFuncRef.current?.()
|
|
128
121
|
}
|
|
129
|
-
}, [
|
|
122
|
+
}, [cachedConfig, displayConfigId, formDataId, fetchDataList])
|
|
130
123
|
|
|
131
124
|
const dataListHeaderContext = {
|
|
132
125
|
detailPageFormId: formId,
|
|
@@ -156,9 +149,9 @@ export default function LayoutRenderer_LoadFormData({ formContext, elementProps
|
|
|
156
149
|
updateDataList={(filter) => {
|
|
157
150
|
fetchDataList(filter.match)
|
|
158
151
|
}}
|
|
159
|
-
parentLoadings={
|
|
152
|
+
parentLoadings={{ data: loading, initial: isConfigLoading }}
|
|
160
153
|
loadingBlock={<FormDataListSkeleton_Table small />}
|
|
161
|
-
setParentLoading={(bool) =>
|
|
154
|
+
setParentLoading={(bool) => setLoading(bool)}
|
|
162
155
|
headerLayoutContext={dataListHeaderContext}
|
|
163
156
|
/>
|
|
164
157
|
)
|
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { QueryClient } from '@tanstack/react-query'
|
|
2
|
+
import { CountryEnum, CSSLayoutType, DeviceBreakpointEnum, FormDataListViewTypeEnum } from './enums'
|
|
3
|
+
import { IFormSchema } from './types'
|
|
2
4
|
|
|
3
5
|
export const CLIENT_ID = 'admin'
|
|
4
6
|
export const CLIENT_SECRET = '3e9e418c-8bd7-4128-90c2-8db3e586a283'
|
|
@@ -170,3 +172,22 @@ export const BSON_DATA_IDENTIFIER_PREFIXES = {
|
|
|
170
172
|
Date: 'Date__',
|
|
171
173
|
Number: 'Number__',
|
|
172
174
|
}
|
|
175
|
+
export const DEFAULT_FORM_SCHEMA_DATA: IFormSchema = {
|
|
176
|
+
detailsConfig: { elements: {}, layouts: { [DeviceBreakpointEnum.Default]: [] } },
|
|
177
|
+
dataListConfig: {
|
|
178
|
+
listType: FormDataListViewTypeEnum.Table,
|
|
179
|
+
columns: [],
|
|
180
|
+
header: { elements: {}, layouts: { [DeviceBreakpointEnum.Default]: [] } },
|
|
181
|
+
},
|
|
182
|
+
generateConfig: { submissionPdf: { enabled: false } },
|
|
183
|
+
relationships: [],
|
|
184
|
+
}
|
|
185
|
+
export const REACT_QUERY_CLIENT = new QueryClient({
|
|
186
|
+
defaultOptions: {
|
|
187
|
+
queries: {
|
|
188
|
+
staleTime: 10 * 60 * 60 * 1000, // 10 hours
|
|
189
|
+
// refetchOnWindowFocus: true,
|
|
190
|
+
refetchOnMount: 'always',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
})
|
package/src/enums/form.enum.ts
CHANGED
|
@@ -9,7 +9,8 @@ import * as FaIcons from 'react-icons/fa'
|
|
|
9
9
|
import { cookieHandler } from '../../../../functions/cookie-handler'
|
|
10
10
|
import { UserAuth } from '../../../../api/user'
|
|
11
11
|
import { useBreadcrumb } from '../../../../components/common/custom-hooks/use-breadcrumb.hook'
|
|
12
|
-
import {
|
|
12
|
+
import { PageViewTypEnum } from '../../../../enums'
|
|
13
|
+
import { REACT_QUERY_CLIENT } from '../../../../constants'
|
|
13
14
|
|
|
14
15
|
const { Header, Content, Sider } = Layout
|
|
15
16
|
|
|
@@ -26,6 +27,7 @@ export const useMenuItems = () => {
|
|
|
26
27
|
icon: FaIcons.FaKey,
|
|
27
28
|
onClick: async () => {
|
|
28
29
|
await cookieHandler.empty()
|
|
30
|
+
REACT_QUERY_CLIENT.clear()
|
|
29
31
|
localStorage.clear()
|
|
30
32
|
navigate('/login')
|
|
31
33
|
},
|
|
@@ -492,7 +494,7 @@ export const layoutTemplates = [
|
|
|
492
494
|
onClick={() => {
|
|
493
495
|
reset()
|
|
494
496
|
const splittedFormName = form.name.split(' ')?.[0] ?? ''
|
|
495
|
-
push({ label: splittedFormName, href, type:
|
|
497
|
+
push({ label: splittedFormName, href, type: PageViewTypEnum.List })
|
|
496
498
|
|
|
497
499
|
if (isParentMenu) toggleMenu(form.id)
|
|
498
500
|
}}
|
package/src/types/forms/index.ts
CHANGED
|
@@ -10,6 +10,16 @@ import { IFormRelationshipConfig } from './relationship'
|
|
|
10
10
|
import { IButtonElementProps } from './layout-elements'
|
|
11
11
|
import { IDndLayoutStructure_Responsive } from '..'
|
|
12
12
|
|
|
13
|
+
export interface IFormConfigDetails {
|
|
14
|
+
description: string
|
|
15
|
+
id: number
|
|
16
|
+
isUser: boolean
|
|
17
|
+
keepVersionHistory: boolean
|
|
18
|
+
key: string
|
|
19
|
+
name: string
|
|
20
|
+
order: number
|
|
21
|
+
version: number
|
|
22
|
+
}
|
|
13
23
|
export interface IFormSchema {
|
|
14
24
|
isRelationshipForm?: boolean
|
|
15
25
|
detailsConfig: IDndLayoutStructure_Responsive
|