form-craft-package 1.10.13-dev.2 → 1.10.13-dev.4

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.13-dev.2",
3
+ "version": "1.10.13-dev.4",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useCallback } from 'react'
1
+ import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
2
2
  import { useParams } from 'react-router-dom'
3
3
  import { IDynamicForm } from '../../../types'
4
4
  import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
@@ -6,6 +6,7 @@ import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
6
6
  export const useFindDynamiForm = () => {
7
7
  const { formName: formSlug } = useParams<{ formName: string }>()
8
8
  const [foundItem, setFoundItem] = useState<IDynamicForm | null | undefined>(undefined)
9
+ const lastResolvedRef = useRef<{ slug?: string; id?: number | null }>({})
9
10
 
10
11
  useEffect(() => {
11
12
  if (!formSlug) return
@@ -16,8 +17,15 @@ export const useFindDynamiForm = () => {
16
17
  const list: IDynamicForm[] = JSON.parse(stored)
17
18
  let match = list.find((entry) => entry.slug === formSlug)
18
19
  if (!match) match = list.find((entry) => entry.name.split(' ').join('-').toLowerCase() === formSlug)
20
+
21
+ const nextId = match?.id ?? null
22
+ if (lastResolvedRef.current.slug === formSlug && lastResolvedRef.current.id === nextId) return
23
+ lastResolvedRef.current = { slug: formSlug, id: nextId }
24
+
19
25
  setFoundItem(match ?? null)
20
26
  } else {
27
+ if (lastResolvedRef.current.slug === formSlug && lastResolvedRef.current.id === null) return
28
+ lastResolvedRef.current = { slug: formSlug, id: null }
21
29
  setFoundItem(null)
22
30
  }
23
31
  } catch (err) {
@@ -41,5 +49,5 @@ export const useFindDynamiForm = () => {
41
49
  }
42
50
  }, [])
43
51
 
44
- return { formInfo: foundItem, getFormById }
52
+ return useMemo(() => ({ formInfo: foundItem, getFormById }), [foundItem, getFormById])
45
53
  }
@@ -83,7 +83,15 @@ export default function FormDataListTableComponent({
83
83
  processOptions(layoutsConfigs.dataListConfig.columns).then((optionValues) => {
84
84
  setTableColumns((curr) =>
85
85
  curr.map((col) => {
86
- if ('optionFieldPath' in col) return { ...col, render: (optionKey) => optionValues[optionKey] ?? '-' }
86
+ if ('optionFieldPath' in col)
87
+ return {
88
+ ...col,
89
+ render: (optionKey) => {
90
+ if (Array.isArray(optionKey)) return optionKey.map((k) => optionValues[k]).join(', ') ?? '-'
91
+
92
+ return optionValues[optionKey] ?? '-'
93
+ },
94
+ }
87
95
  return col
88
96
  }),
89
97
  )
@@ -1,5 +1,5 @@
1
1
  import { Form, Spin } from 'antd'
2
- import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'
2
+ import { memo, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'
3
3
  import { IDndLayoutStructure_Responsive, IFormDataDetailsFetchConfig } from '../../../types'
4
4
  import { LayoutRendererRow } from '../layout-renderer/1-row'
5
5
  import FormDataListSkeleton_Details from '../../common/loading-skeletons/details'
@@ -49,13 +49,15 @@ import {
49
49
  queryParamsToObject,
50
50
  } from '../../../functions/forms'
51
51
 
52
- export default function FormDataDetailsComponent(props: IFormDataDetailsComponent) {
52
+ const FormDataDetailsComponent = memo(function FormDataDetailsComponent(props: IFormDataDetailsComponent) {
53
53
  return (
54
54
  <BlurEventBusProvider>
55
55
  <FormDataDetailsComponentChild {...props} />
56
56
  </BlurEventBusProvider>
57
57
  )
58
- }
58
+ })
59
+
60
+ export default FormDataDetailsComponent
59
61
 
60
62
  function FormDataDetailsComponentChild({
61
63
  isPublic,
@@ -441,3 +443,4 @@ interface IDataDetailsPrivateProps {
441
443
  formId: number
442
444
  formKey?: string
443
445
  }
446
+
@@ -1,127 +1,132 @@
1
- import { IBreadcrumb, useBreadcrumb } from '../../../../common/custom-hooks/use-breadcrumb.hook'
2
- import { Button_FillerPortal } from '../../../../common/button'
3
- import { useNavigate } from 'react-router-dom'
4
- import { FaChevronLeft, FaChevronRight } from 'react-icons/fa6'
5
- import React, { memo, useMemo, useSyncExternalStore } from 'react'
6
- import { ELEMENTS_DEFAULT_CLASS } from '../../../../../constants'
7
- import { IElementBaseProps } from '../'
8
- import useGetCurrentBreakpoint from '../../../../common/custom-hooks/use-window-width.hook'
9
- import { Select } from 'antd'
10
- import { CountryEnum, DeviceBreakpointEnum, PageViewTypEnum } from '../../../../../enums'
11
- import { useConfigContext } from '../../../../companies/context'
12
- import { buildBreadcrumbTranslationKey } from '../../../../../functions'
13
- import { translationStore } from '../../../../common/custom-hooks/use-translation.hook/store'
14
-
15
- import './index.scss'
16
-
17
- function LayoutRenderer_Breadcrumb(_: { formId?: number } & IElementBaseProps) {
18
- const currentBreakpoint = useGetCurrentBreakpoint()
19
- const navigate = useNavigate()
20
- const { breadcrumbs, sliceCrumbAt } = useBreadcrumb()
21
- const { config } = useConfigContext()
22
- const { selectedLanguage } = useSyncExternalStore(
23
- translationStore.subscribe.bind(translationStore),
24
- translationStore.getSnapshot.bind(translationStore),
25
- )
26
-
27
- const translations = config?.translations
28
- const fallbackTexts = { Detail: 'detail', List: 'list', New: 'New' }
29
- const getTranslation = useMemo(
30
- () => (key: string) => (selectedLanguage ? translations?.[key]?.[selectedLanguage as CountryEnum] ?? '' : ''),
31
- [selectedLanguage, translations],
32
- )
33
- const getFormName = useMemo(
34
- () =>
35
- (bc: IBreadcrumb) =>
36
- bc.formId
37
- ? getTranslation(buildBreadcrumbTranslationKey(bc.formId, 'Name')) || bc.formName?.split(' ')?.[0] || bc.label
38
- : bc.label,
39
- [getTranslation],
40
- )
41
- const getBreadcrumbText = useMemo(
42
- () =>
43
- (bc: IBreadcrumb, field: 'Detail' | 'List' | 'New') => {
44
- const formText = bc.formId ? getTranslation(buildBreadcrumbTranslationKey(bc.formId, field)) : ''
45
- const defaultText = getTranslation(buildBreadcrumbTranslationKey('Default', field))
46
- return formText || defaultText || fallbackTexts[field]
47
- },
48
- [getTranslation],
49
- )
50
-
51
- const showAsSelect = useMemo(
52
- () =>
53
- ([DeviceBreakpointEnum.Mobile, DeviceBreakpointEnum.TabletPortrait].includes(currentBreakpoint) &&
54
- breadcrumbs.length > 1) ||
55
- breadcrumbs.length > 3,
56
- [currentBreakpoint, breadcrumbs],
57
- )
58
-
59
- if (showAsSelect)
60
- return (
61
- <Select
62
- suffixIcon={null}
63
- value={breadcrumbs[breadcrumbs.length - 1].href}
64
- optionFilterProp="label"
65
- allowClear={false}
66
- className={`${ELEMENTS_DEFAULT_CLASS.Breadcrumb} responsive max-w-[250px]`}
67
- onChange={(bc) => {
68
- const decodedUri = decodeURIComponent(bc)
69
- const bcIdx = breadcrumbs.findIndex((bc) => bc.href === decodedUri)
70
-
71
- sliceCrumbAt(bcIdx)
72
- navigate(decodedUri)
73
- }}
74
- options={breadcrumbs.map((bc) => ({
75
- value: bc.href,
76
- label: (
77
- <div className="flex gap-2 items-center">
78
- <FaChevronLeft className="text-primary" size={10} />
79
- {renderText(bc, getFormName, getBreadcrumbText)}
80
- </div>
81
- ),
82
- }))}
83
- />
84
- )
85
-
86
- return (
87
- <div className={`${ELEMENTS_DEFAULT_CLASS.Breadcrumb} flex items-center text-12`}>
88
- {breadcrumbs.map((bc, bcIdx) => (
89
- <React.Fragment key={bcIdx}>
90
- <Button_FillerPortal
91
- link
92
- disabled={breadcrumbs.length - 1 === bcIdx}
93
- onClick={() => {
94
- sliceCrumbAt(bcIdx)
95
- navigate(bc.href)
96
- }}
97
- >
98
- {renderText(bc, getFormName, getBreadcrumbText)}
99
- </Button_FillerPortal>
100
- {breadcrumbs.length - 1 !== bcIdx && <FaChevronRight className="text-primary" />}
101
- </React.Fragment>
102
- ))}
103
- </div>
104
- )
1
+ import { IBreadcrumb, useBreadcrumb } from '../../../../common/custom-hooks/use-breadcrumb.hook'
2
+ import { Button_FillerPortal } from '../../../../common/button'
3
+ import { useNavigate } from 'react-router-dom'
4
+ import { FaChevronLeft, FaChevronRight } from 'react-icons/fa6'
5
+ import React, { memo, useMemo, useSyncExternalStore } from 'react'
6
+ import { ELEMENTS_DEFAULT_CLASS } from '../../../../../constants'
7
+ import { IElementBaseProps } from '../'
8
+ import useGetCurrentBreakpoint from '../../../../common/custom-hooks/use-window-width.hook'
9
+ import { Select } from 'antd'
10
+ import { CountryEnum, DeviceBreakpointEnum, PageViewTypEnum } from '../../../../../enums'
11
+ import { useConfigContext } from '../../../../companies/context'
12
+ import { buildBreadcrumbTranslationKey } from '../../../../../functions'
13
+ import { translationStore } from '../../../../common/custom-hooks/use-translation.hook/store'
14
+
15
+ import './index.scss'
16
+
17
+ function LayoutRenderer_Breadcrumb(_: { formId?: number } & IElementBaseProps) {
18
+ const currentBreakpoint = useGetCurrentBreakpoint()
19
+ const navigate = useNavigate()
20
+ const { breadcrumbs, sliceCrumbAt } = useBreadcrumb()
21
+ const { config } = useConfigContext()
22
+ const { selectedLanguage } = useSyncExternalStore(
23
+ translationStore.subscribe.bind(translationStore),
24
+ translationStore.getSnapshot.bind(translationStore),
25
+ )
26
+
27
+ const translations = config?.translations
28
+ const fallbackTexts = { Detail: 'detail', List: 'list', New: 'New' }
29
+ const getTranslation = useMemo(
30
+ () => (key: string) => (selectedLanguage ? translations?.[key]?.[selectedLanguage as CountryEnum] ?? '' : ''),
31
+ [selectedLanguage, translations],
32
+ )
33
+ const getFormName = useMemo(
34
+ () =>
35
+ (bc: IBreadcrumb, field: 'Detail' | 'List' | 'New') =>
36
+ bc.formId
37
+ ? getTranslation(buildBreadcrumbTranslationKey(bc.formId, field)) || bc.formName?.split(' ')?.[0] || bc.label
38
+ : bc.label,
39
+ [getTranslation],
40
+ )
41
+ const getBreadcrumbText = useMemo(
42
+ () =>
43
+ (field: 'Detail' | 'List' | 'New') => {
44
+ const defaultText = getTranslation(buildBreadcrumbTranslationKey('Default', field))
45
+ return defaultText || fallbackTexts[field]
46
+ },
47
+ [getTranslation],
48
+ )
49
+
50
+ const showAsSelect = useMemo(
51
+ () =>
52
+ ([DeviceBreakpointEnum.Mobile, DeviceBreakpointEnum.TabletPortrait].includes(currentBreakpoint) &&
53
+ breadcrumbs.length > 1) ||
54
+ breadcrumbs.length > 3,
55
+ [currentBreakpoint, breadcrumbs],
56
+ )
57
+
58
+ if (showAsSelect)
59
+ return (
60
+ <Select
61
+ suffixIcon={null}
62
+ value={breadcrumbs[breadcrumbs.length - 1].href}
63
+ optionFilterProp="label"
64
+ allowClear={false}
65
+ className={`${ELEMENTS_DEFAULT_CLASS.Breadcrumb} responsive max-w-[250px]`}
66
+ onChange={(bc) => {
67
+ const decodedUri = decodeURIComponent(bc)
68
+ const bcIdx = breadcrumbs.findIndex((bc) => bc.href === decodedUri)
69
+
70
+ sliceCrumbAt(bcIdx)
71
+ navigate(decodedUri)
72
+ }}
73
+ options={breadcrumbs.map((bc) => ({
74
+ value: bc.href,
75
+ label: (
76
+ <div className="flex gap-2 items-center">
77
+ <FaChevronLeft className="text-primary" size={10} />
78
+ {renderText(bc, getFormName, getBreadcrumbText)}
79
+ </div>
80
+ ),
81
+ }))}
82
+ />
83
+ )
84
+
85
+ return (
86
+ <div className={`${ELEMENTS_DEFAULT_CLASS.Breadcrumb} flex items-center text-12`}>
87
+ {breadcrumbs.map((bc, bcIdx) => (
88
+ <React.Fragment key={bcIdx}>
89
+ <Button_FillerPortal
90
+ link
91
+ disabled={breadcrumbs.length - 1 === bcIdx}
92
+ onClick={() => {
93
+ sliceCrumbAt(bcIdx)
94
+ navigate(bc.href)
95
+ }}
96
+ >
97
+ {renderText(bc, getFormName, getBreadcrumbText)}
98
+ </Button_FillerPortal>
99
+ {breadcrumbs.length - 1 !== bcIdx && <FaChevronRight className="text-primary" />}
100
+ </React.Fragment>
101
+ ))}
102
+ </div>
103
+ )
104
+ }
105
+
106
+ export default memo(LayoutRenderer_Breadcrumb)
107
+
108
+ const renderText = (
109
+ bc: IBreadcrumb,
110
+ getFormName: (bc: IBreadcrumb, field: 'Detail' | 'List' | 'New') => string,
111
+ getBreadcrumbText: (field: 'Detail' | 'List' | 'New') => string,
112
+ ) => {
113
+ const label =
114
+ bc.type === PageViewTypEnum.Details
115
+ ? getFormName(bc, 'Detail')
116
+ : bc.type === PageViewTypEnum.List
117
+ ? getFormName(bc, 'List')
118
+ : bc.type === PageViewTypEnum.New
119
+ ? getFormName(bc, 'New')
120
+ : bc.label
121
+ const detailText = getBreadcrumbText('Detail')
122
+ const listText = getBreadcrumbText('List')
123
+ const newText = getBreadcrumbText('New')
124
+
125
+ return (
126
+ <span className="font-normal italic">
127
+ {bc.type === PageViewTypEnum.New ? `${newText} ` : ''}
128
+ {label}
129
+ {bc.type === PageViewTypEnum.Details ? ` ${detailText}` : bc.type === PageViewTypEnum.List ? ` ${listText}` : ''}
130
+ </span>
131
+ )
105
132
  }
106
-
107
- export default memo(LayoutRenderer_Breadcrumb)
108
-
109
- const renderText = (
110
- bc: IBreadcrumb,
111
- getFormName: (bc: IBreadcrumb) => string,
112
- getBreadcrumbText: (bc: IBreadcrumb, field: 'Detail' | 'List' | 'New') => string,
113
- ) => {
114
- const label = getFormName(bc)
115
- const detailText = getBreadcrumbText(bc, 'Detail')
116
- const listText = getBreadcrumbText(bc, 'List')
117
- const newText = getBreadcrumbText(bc, 'New')
118
-
119
- return (
120
- <span className="font-normal italic">
121
- {bc.type === PageViewTypEnum.New ? `${newText} ` : ''}
122
- {label}
123
- {bc.type === PageViewTypEnum.Details ? ` ${detailText}` : bc.type === PageViewTypEnum.List ? ` ${listText}` : ''}
124
- </span>
125
- )
126
- }
127
-
@@ -2,8 +2,14 @@ import { Form } from 'antd'
2
2
  import { renderData, resolveConditionalText } from '../../../../functions/forms'
3
3
  import { useFormPreservedItemValues } from '../../../common/custom-hooks/use-preserved-form-items.hook'
4
4
  import { memo, useMemo } from 'react'
5
- import { DataRenderTypeEnum, ReadFieldDataValueTypeEnum, TranslationTextTypeEnum } from '../../../../enums'
6
- import { IReadFieldDataElementProps } from '../../../../types'
5
+ import {
6
+ DataRenderTypeEnum,
7
+ LOCAL_STORAGE_KEYS_ENUM,
8
+ ReadFieldDataValueTypeEnum,
9
+ TranslationTextTypeEnum,
10
+ UserFormPlaceholders,
11
+ } from '../../../../enums'
12
+ import { IReadFieldDataElementProps, ISystemRole } from '../../../../types'
7
13
  import { IFormContext } from '../1-row'
8
14
  import { evaluateValue } from '../../../../functions/forms/evaluate-value'
9
15
  import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
@@ -59,6 +65,27 @@ function LayoutRenderer_ReadFieldData({
59
65
  [elementKey, textConditions, t, formValues, formContext.formDataId, currentBreakpoint],
60
66
  )
61
67
 
68
+ const roles = useMemo(() => {
69
+ if (
70
+ elementProps.valueType !== ReadFieldDataValueTypeEnum.Evaluated &&
71
+ elementProps.field === UserFormPlaceholders.LoginUserFieldKey_Roles &&
72
+ Array.isArray(fieldValue)
73
+ ) {
74
+ const storageRoles: ISystemRole[] = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.UserRoles)
75
+ ? (JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.UserRoles) as string) ?? [])
76
+ : []
77
+
78
+ return fieldValue
79
+ .map((selectedR) => {
80
+ const rInfo = storageRoles.find((r) => r.id === selectedR)?.name
81
+ return rInfo
82
+ })
83
+ .filter(Boolean)
84
+ .join(', ')
85
+ }
86
+ return null
87
+ }, [elementProps])
88
+
62
89
  return (
63
90
  <div className={`${ELEMENTS_DEFAULT_CLASS.ReadFieldData} flex items-center gap-2`}>
64
91
  {label && <span>{label}: </span>}
@@ -67,10 +94,12 @@ function LayoutRenderer_ReadFieldData({
67
94
  elementProps.renderConfig?.type === DataRenderTypeEnum.Image
68
95
  ? attachmentUrl
69
96
  : elementProps.renderConfig?.type === DataRenderTypeEnum.Conditional
70
- ? formValues
71
- : elementProps.valueType === ReadFieldDataValueTypeEnum.Evaluated
72
- ? evaluatedValue
73
- : fieldValue,
97
+ ? formValues
98
+ : elementProps.valueType === ReadFieldDataValueTypeEnum.Evaluated
99
+ ? evaluatedValue
100
+ : roles
101
+ ? roles
102
+ : fieldValue,
74
103
  elementProps.renderConfig ?? { type: DataRenderTypeEnum.Default },
75
104
  ) ?? 'N/A'}
76
105
  </span>
@@ -85,4 +114,3 @@ type ILayoutRenderer_ReadFieldData = {
85
114
  elementProps: IReadFieldDataElementProps
86
115
  style?: { [key: string]: any }
87
116
  } & IElementBaseProps
88
-
@@ -80,6 +80,7 @@ export enum FieldElementOptionSourceEnum {
80
80
  Static = 'Static',
81
81
  DynamicForm = 'DynamicForm',
82
82
  ReadFromDetails = 'ReadFromDetails', // used in data list header
83
+ Roles = 'Roles',
83
84
  }
84
85
  export enum FormDataListViewTypeEnum {
85
86
  Table = 'Table',
@@ -207,7 +208,8 @@ export enum PatternPlacholdersEnum {
207
208
  CyrillicLowercase = 'c',
208
209
  Digit = 'D',
209
210
  }
210
- export enum TemplateDataReplacementFieldTypesEnum { // used for /api/template/{key}/{blobName}
211
+ export enum TemplateDataReplacementFieldTypesEnum {
212
+ // used for /api/template/{key}/{blobName}
211
213
  Text = 1,
212
214
  Html,
213
215
  Image,
@@ -70,8 +70,8 @@ export const renderTableColumns = (
70
70
  colRaw.align === AlignTypeEnum.Center
71
71
  ? 'justify-center'
72
72
  : colRaw.align === AlignTypeEnum.Right
73
- ? 'justify-end'
74
- : ''
73
+ ? 'justify-end'
74
+ : ''
75
75
  }`}
76
76
  onClick={(e) => e.stopPropagation()}
77
77
  >
@@ -144,6 +144,7 @@ export const renderData = (data?: any, renderConfig?: IDataRenderConfig): ReactN
144
144
  case DataRenderTypeEnum.Conditional:
145
145
  return renderConditionalData(data, renderConfig)
146
146
  case DataRenderTypeEnum.Default:
147
+ return data
147
148
  case DataRenderTypeEnum.Buttons:
148
149
  default:
149
150
  return data
@@ -1,6 +1,6 @@
1
1
  import { fetchFormDataAsLookup } from '.'
2
- import { FieldElementOptionSourceEnum, TranslationTextTypeEnum } from '../../enums'
3
- import { IFormDataListColumn } from '../../types'
2
+ import { FieldElementOptionSourceEnum, LOCAL_STORAGE_KEYS_ENUM, TranslationTextTypeEnum } from '../../enums'
3
+ import { IFormDataListColumn, ISystemRole } from '../../types'
4
4
  import client from '../../api/client'
5
5
  import { REACT_QUERY_CLIENT } from '../../constants'
6
6
  import { translationStore } from '../../components/common/custom-hooks/use-translation.hook/store'
@@ -27,7 +27,8 @@ export async function processOptions(options: IFormDataListColumn[]): Promise<{
27
27
  groupedRequests.forEach((paths, formId) => {
28
28
  const cachedConfig = REACT_QUERY_CLIENT.getQueryData(['layoutConfig', String(formId)])
29
29
 
30
- if (cachedConfig) apiCallPromises.push(Promise.resolve({ _id: formId, Data: extractByDotPaths(paths, cachedConfig) }))
30
+ if (cachedConfig)
31
+ apiCallPromises.push(Promise.resolve({ _id: formId, Data: extractByDotPaths(paths, cachedConfig) }))
31
32
  else
32
33
  apiCallPromises.push(
33
34
  client.post(`/api/form/${formId}`, { project: JSON.stringify(paths) }).then((res) => res.data),
@@ -74,6 +75,14 @@ export async function processOptions(options: IFormDataListColumn[]): Promise<{
74
75
  })
75
76
  }),
76
77
  )
78
+ } else if (optionSource.type === FieldElementOptionSourceEnum.Roles) {
79
+ const storageRoles: ISystemRole[] = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.UserRoles)
80
+ ? (JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.UserRoles) as string) ?? [])
81
+ : []
82
+
83
+ storageRoles.forEach((r) => {
84
+ finalResult[r.id as string] = r.name
85
+ })
77
86
  }
78
87
  })
79
88
  })
@@ -58,10 +58,10 @@ export const extractFiltersFromLayout = (elements: { [key: string]: IDndLayoutEl
58
58
  bsonDataType: el.key.includes(BSON_DATA_IDENTIFIER_PREFIXES.ObjectId)
59
59
  ? BSON_DATA_IDENTIFIER_PREFIXES.ObjectId
60
60
  : el.key.includes(BSON_DATA_IDENTIFIER_PREFIXES.Number)
61
- ? BSON_DATA_IDENTIFIER_PREFIXES.Number
62
- : el.key.includes(BSON_DATA_IDENTIFIER_PREFIXES.Date)
63
- ? BSON_DATA_IDENTIFIER_PREFIXES.Date
64
- : undefined,
61
+ ? BSON_DATA_IDENTIFIER_PREFIXES.Number
62
+ : el.key.includes(BSON_DATA_IDENTIFIER_PREFIXES.Date)
63
+ ? BSON_DATA_IDENTIFIER_PREFIXES.Date
64
+ : undefined,
65
65
  }
66
66
  }
67
67
  })
@@ -153,11 +153,14 @@ export const queryParamsToObject = (query: string): Record<string, string | unde
153
153
  return query
154
154
  .replace(/^\?/, '') // Remove leading "?" if present
155
155
  .split('&')
156
- .reduce((acc, param) => {
157
- const [key, value] = param.split('=').map(decodeURIComponent) // Decode and split by "="
158
- if (key) acc[key] = !['null', 'undefined'].includes(value) ? value : undefined // Add key-value to the object
159
- return acc
160
- }, {} as Record<string, string | undefined>)
156
+ .reduce(
157
+ (acc, param) => {
158
+ const [key, value] = param.split('=').map(decodeURIComponent) // Decode and split by "="
159
+ if (key) acc[key] = !['null', 'undefined'].includes(value) ? value : undefined // Add key-value to the object
160
+ return acc
161
+ },
162
+ {} as Record<string, string | undefined>,
163
+ )
161
164
  }
162
165
 
163
166
  /** --------------------------------------------------------------------------------------------------------- */
@@ -284,6 +287,10 @@ export const replaceAndGetFileBlob = async (
284
287
  }
285
288
 
286
289
  if (rep.type === TemplateDataReplacementFieldTypesEnum.Image) {
290
+ const isEmpty = typeof repValue !== 'string' || repValue.length === 0
291
+ if (isEmpty)
292
+ return { placeholder: rep.placeholder, value: '-', type: TemplateDataReplacementFieldTypesEnum.Text }
293
+
287
294
  const imageRenderConfig = rep.renderConfig.type === DataRenderTypeEnum.Image ? rep.renderConfig : undefined
288
295
  const imageRenderOptions: { imageWidth?: number; imageHeight?: number } = {
289
296
  imageWidth: 624, // Page width minus margins