@cmk/fe_utils
Version:
frontend utility library
328 lines (311 loc) • 12.5 kB
text/typescript
import {
EditorRendererControllerType,
EditorStateType,
Element,
} from '../../editorRendererController'
import { replacePlaceholdersInString } from '../placeholder/replacePlaceholder'
import { queryAction } from './queryAction'
import { toast } from 'react-hot-toast'
type EditorRendererController = EditorRendererControllerType
export const createAppAction = (params: {
element: Element
eventName: string
editorState: EditorStateType
currentViewportElements: EditorRendererController['currentViewportElements']
ELEMENT_MODELS: EditorRendererController['ELEMENT_MODELS']
appController: EditorRendererController['appController']
icons?: Record<string, string>
navigate: (to: string) => void
isProduction?: boolean
// formData?: Record<string, any>
}): ((...fnParams: unknown[]) => Promise<void>) | undefined => {
const {
element,
editorState,
currentViewportElements,
ELEMENT_MODELS,
appController,
eventName,
icons,
navigate,
isProduction,
// formData,
} = params
const elementProps = editorState.properties?.filter(
(prop) => prop.element_id === element.element_id
)
const templateProps = editorState.properties?.filter(
(prop) =>
prop.template_id === element.template_id &&
!elementProps.find((elprop) => elprop.prop_name === prop.prop_name)
)
const allElementProps = [...(elementProps ?? []), ...(templateProps ?? [])]
const getPropActionIdsByName = (key: string) =>
allElementProps?.find((prop) => prop.prop_name === key)?.action_ids ?? []
const actionIds = getPropActionIdsByName(eventName)
// const eventProps = getPropByName(eventName) as string[]
return !actionIds?.length
? undefined
: async (...fnParams: unknown[]) => {
console.debug('app action called', eventName, fnParams)
// click actions are currently assosiacted with endpoint events only!
// const actionIds: string[] = eventProps
const navigationActionElements = actionIds
.map(
(actId) =>
currentViewportElements.find((el) => el.element_id === actId) ??
null
)
.filter((el) => el)
const navigationActionElementIds = navigationActionElements.map(
(el) => el?.element_id
) as string[]
for (let a = 0; a < actionIds.length; a++) {
const actionId = actionIds[a]
const endpointAction = editorState.actions.find(
(act) => act.action_id === actionId
)
const navigationAction = navigationActionElementIds.includes(actionId)
const action = editorState.actions.find(
(act) => act.action_id === actionId
)
const isPageNavigation =
action?.internal_action_name === 'navigatePage'
if (!endpointAction && !navigationAction && !isPageNavigation) {
console.warn(
'No ep and no nav -action found for actionId',
actionId
)
continue
}
// console.debug(
// 'app action called WITH',
// eventName,
// fnParams,
// isPageNavigation,
// endpointAction,
// navigationAction
// )
if (isPageNavigation) {
const actionsParmValue = editorState.action_params.find(
(ap) =>
ap.param_name === 'navigatePage' &&
ap.element_id === element.element_id &&
actionId === ap.action_id
)?.param_value
const elementTemplateValuesDict = editorState.action_params
.filter((ap) => ap.element_id === element.element_id)
.reduce<Record<string, string>>((acc, cur) => {
return {
...acc,
[cur.param_name]: cur.param_value,
}
}, {})
const componentModel = ELEMENT_MODELS.find(
(mod) => mod.type === element.element_type
)
const isItemEvent =
componentModel?.schema?.properties[eventName]?.eventType
const elementTemplateValuesDictAdj = isItemEvent
? Object.keys(elementTemplateValuesDict).reduce<
Record<string, string>
>((acc, cur) => {
const value =
elementTemplateValuesDict?.[
cur as keyof typeof elementTemplateValuesDict
]
const replaceValue =
element.element_type === 'TreeView'
? (fnParams?.[1] as string)
: (fnParams?.[0] as string)
const newValue =
typeof value === 'string' &&
['string', 'number'].includes(typeof replaceValue)
? value?.replaceAll?.('{itemId}', replaceValue)
: value
const matches =
typeof newValue === 'string' &&
newValue?.match?.(
/{(_data|form|formData|props|treeviews|buttonStates)\.[^}]*}/g
)
const newValueReplaced = matches
? replacePlaceholdersInString(
newValue,
appController.state,
editorState.composite_component_props,
editorState.properties,
element,
undefined,
undefined,
icons,
undefined,
componentModel?.renderType === 'form'
? (fnParams?.[1] as Record<string, unknown>)
: undefined
)
: newValue
const regexOnlyNumbersOrDecimal = /^[0-9]+(\.[0-9]+)?$/
const isNumberOrDecimal =
regexOnlyNumbersOrDecimal.test(newValueReplaced)
const newValueAdj = isNumberOrDecimal
? parseFloat(newValueReplaced)
: newValueReplaced
return {
...acc,
[cur]: newValueAdj,
}
}, {})
: elementTemplateValuesDict
const actionsParmValueAdj =
isItemEvent && elementTemplateValuesDictAdj?.['navigatePage']
? elementTemplateValuesDictAdj?.['navigatePage']
: actionsParmValue
if (!actionsParmValueAdj) {
console.debug(
'actionsParmValueAdj for pageNavigation is not valid',
actionsParmValueAdj
)
return
}
if (isProduction) {
console.debug(
'Navigate to page ...',
actionsParmValue,
actionsParmValueAdj,
isProduction
)
navigate('/' + actionsParmValueAdj)
} else {
console.warn(
'Navigate to page not implemented in dev mode, will navigate to ' +
actionsParmValue,
actionsParmValueAdj,
isProduction
)
toast.error(
'Navigate to page not implemented in dev mode, navigate to /' +
actionsParmValueAdj
)
}
continue
} else if (endpointAction) {
const endpointId = endpointAction.endpoint_id
const endpoint = editorState.externalApis
.map((api) =>
api.endpoints.map((ep) => ({
...ep,
api_id: api.external_api_id,
}))
)
.flat()
.find((ep) => ep.endpoint_id === endpointId)
const api = editorState.externalApis.find(
(api) => api.external_api_id === endpoint?.api_id
)
const url = (api?.base_url || '') + (endpoint?.url || '')
const action = editorState.actions.find(
(act) => act.endpoint_id === endpoint?.endpoint_id
)
const elementTemplateValuesDict = editorState.action_params
.filter((ap) => ap.element_id === element.element_id)
.reduce<Record<string, string>>((acc, cur) => {
return {
...acc,
[cur.param_name]: cur.param_value,
}
}, {})
const componentModel = ELEMENT_MODELS.find(
(mod) => mod.type === element.element_type
)
const isItemEvent =
componentModel?.schema?.properties[eventName]?.eventType
const elementTemplateValuesDictAdj = isItemEvent
? Object.keys(elementTemplateValuesDict).reduce<
Record<string, string>
>((acc, cur) => {
const value =
elementTemplateValuesDict?.[
cur as keyof typeof elementTemplateValuesDict
]
const replaceValue =
element.element_type === 'TreeView'
? (fnParams?.[1] as string)
: (fnParams?.[0] as string)
const newValue =
typeof value === 'string' &&
['string', 'number'].includes(typeof replaceValue)
? value?.replaceAll?.('{itemId}', replaceValue)
: value
const matches =
typeof newValue === 'string' &&
newValue?.match?.(
/{(_data|form|formData|props|treeviews|buttonStates)\.[^}]*}/g
)
const newValueReplaced = matches
? replacePlaceholdersInString(
newValue,
appController.state,
editorState.composite_component_props,
editorState.properties,
element,
undefined,
undefined,
icons,
undefined,
componentModel?.renderType === 'form'
? (fnParams?.[1] as Record<string, unknown>)
: undefined
)
: newValue
const regexOnlyNumbersOrDecimal = /^[0-9]+(\.[0-9]+)?$/
const isNumberOrDecimal =
regexOnlyNumbersOrDecimal.test(newValueReplaced)
const newValueAdj = isNumberOrDecimal
? parseFloat(newValueReplaced)
: newValueReplaced
return {
...acc,
[cur]: newValueAdj,
}
}, {})
: elementTemplateValuesDict
console.debug(
'elementTemplateValuesDictAdj',
elementTemplateValuesDictAdj,
isItemEvent,
componentModel,
eventName,
componentModel?.schema?.properties[eventName],
fnParams,
['string', 'number'].includes(typeof fnParams?.[1]),
fnParams,
elementTemplateValuesDict
)
await queryAction(
appController,
action?.action_id ?? '', // should never happen -> should always have action
endpoint?.method as string,
url,
!!endpoint?.use_cookies,
endpoint?.body,
endpoint?.headers,
endpoint?.params,
endpoint?.response_type,
undefined,
elementTemplateValuesDictAdj
)
} else if (navigationAction) {
const navElementId = actionId
const actionParam = editorState.action_params.find(
(ap) => ap.param_name === navElementId
)
const elementWithEvent = actionParam?.element_id
if (!elementWithEvent) return
appController.actions.updateProperty(
navElementId,
actionParam.param_value
)
}
}
}
}