diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx b/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx index b0302452f3..7126d404ca 100644 --- a/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx @@ -54,7 +54,11 @@ import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext'; import { useElementGuidelines } from './hooks/useElementGuidelines'; import { RIGHT_SIDE_BAR_TAB } from '../../RightSideBar/rightSidebarConstants'; import ConfigHandleButton from '@/_components/ConfigHandleButton'; -import { RESTRICTED_WIDGETS_CONFIG } from '@/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig'; +import { getDropTargetLabel } from '../appCanvasUtils'; +import { + RESTRICTED_WIDGETS_CONFIG, + RESTRICTED_WIDGET_SLOTS_CONFIG, +} from '@/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig'; // Lazy load editor-only component to reduce viewer bundle size const MentionComponentInChat = lazy(() => import('../ConfigHandle/MentionComponentInChat')); @@ -547,11 +551,13 @@ export default function Grid({ gridWidth, currentLayout, mainCanvasWidth }) { const parentId = targetSlotId?.length > 36 ? targetSlotId.slice(0, 36) : targetSlotId; const parentWidgetType = getComponentTypeFromId(parentId); + const parentSlotType = targetSlotId ? targetSlotId.split('-').pop() : undefined; let restrictedWidgetsTobeDropped = - RESTRICTED_WIDGETS_CONFIG?.[parentWidgetType]?.filter((widgetType) => - widgetsTypeToBeDropped.includes(widgetType) - ) || []; + [ + ...(RESTRICTED_WIDGETS_CONFIG?.[parentWidgetType] || []), + ...(['header', 'footer'].includes(parentSlotType) ? RESTRICTED_WIDGET_SLOTS_CONFIG : []), + ].filter((widgetType) => widgetsTypeToBeDropped.includes(widgetType)) || []; // Check nesting depth restrictions for all widget types in NESTING_LEVEL_LIMITS let nestingDepthExceeded = false; @@ -602,7 +608,12 @@ export default function Grid({ gridWidth, currentLayout, mainCanvasWidth }) { } else if (isParentModuleContainer) { toast.error('Modules cannot be edited inside an app'); } else if (!isParentChangeAllowed) { - toast.error(`${restrictedWidgetsTobeDropped} is not compatible as a child component of ${parentWidgetType}`); + toast.error( + `${restrictedWidgetsTobeDropped} is not compatible as a child component of ${getDropTargetLabel( + parentWidgetType, + parentSlotType + )}` + ); } } @@ -1069,7 +1080,12 @@ export default function Grid({ gridWidth, currentLayout, mainCanvasWidth }) { if (isParentModuleContainer) { toast.error('Modules cannot be edited inside an app'); } else if (!isModalToCanvas) { - toast.error(`${dragged.widgetType} is not compatible as a child component of ${target.widgetType}`); + toast.error( + `${dragged.widgetType} is not compatible as a child component of ${getDropTargetLabel( + target.widgetType, + target.slotType + )}` + ); } } diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/gridUtils.js b/frontend/src/AppBuilder/AppCanvas/Grid/gridUtils.js index f7f59c4c25..0fb5494b2d 100644 --- a/frontend/src/AppBuilder/AppCanvas/Grid/gridUtils.js +++ b/frontend/src/AppBuilder/AppCanvas/Grid/gridUtils.js @@ -728,7 +728,9 @@ export const updateDashedBordersOnDragResize = (targetId, moveableControlBoxClas export const isDroppingRestrictedWidget = (target, dragged) => { const restrictedWidgetsOnTarget = RESTRICTED_WIDGETS_CONFIG?.[target.widgetType] || []; - const restrictedWidgetsOnTargetSlot = RESTRICTED_WIDGET_SLOTS_CONFIG?.[target.slotType] || []; + const restrictedWidgetsOnTargetSlot = ['header', 'footer'].includes(target.slotType) + ? RESTRICTED_WIDGET_SLOTS_CONFIG + : []; const restrictedWidgets = [...restrictedWidgetsOnTarget, ...restrictedWidgetsOnTargetSlot]; return restrictedWidgets.includes(dragged.widgetType); diff --git a/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js b/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js index 597ebd6c94..129591e8fe 100644 --- a/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js +++ b/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js @@ -340,6 +340,9 @@ export const getParentWidgetFromId = (parentType, parentId) => { return parentType; }; +export const getDropTargetLabel = (widgetType, slotType) => + slotType === 'header' || slotType === 'footer' ? slotType : widgetType; + export const getTabId = (parentId) => { return parentId.split('-').slice(0, -1).join('-'); }; diff --git a/frontend/src/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig.js b/frontend/src/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig.js index 51e12c1d18..692a080070 100644 --- a/frontend/src/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig.js +++ b/frontend/src/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig.js @@ -12,7 +12,31 @@ export const RESTRICTED_WIDGETS_CONFIG = { Table: ['Kanban'], }; -export const RESTRICTED_WIDGET_SLOTS_CONFIG = { - header: ['Calendar', 'Kanban', 'Table', 'Listview', 'Container', 'Accordion'], - footer: ['Calendar', 'Kanban', 'Table', 'Listview', 'Container', 'Accordion'], -}; +export const RESTRICTED_WIDGET_SLOTS_CONFIG = [ + 'Form', + 'Container', + 'Listview', + 'Tabs', + 'Kanban', + 'Calendar', + 'Chart', + 'Accordion', + 'Map', + 'IFrame', + 'KeyValuePair', + 'RichTextEditor', + 'Timeline', + 'JSONExplorer', + 'JSONEditor', + 'Html', + 'CodeEditor', + 'Chat', + 'TreeSelect', + 'PDF', + 'Camera', + 'QrScanner', + 'Text', + 'TextArea', + 'AudioRecorder', + 'CustomComponent', +]; diff --git a/frontend/src/AppBuilder/_stores/slices/componentsSlice.js b/frontend/src/AppBuilder/_stores/slices/componentsSlice.js index 84700791e5..623bfed749 100644 --- a/frontend/src/AppBuilder/_stores/slices/componentsSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/componentsSlice.js @@ -11,6 +11,7 @@ import { deepClone } from '@/_helpers/utilities/utils.helpers'; import { cloneDeep, merge, set as lodashSet, isEmpty } from 'lodash'; import { computeComponentName, + getDropTargetLabel, getAllChildComponents, getParentWidgetFromId, } from '@/AppBuilder/AppCanvas/appCanvasUtils'; @@ -19,7 +20,10 @@ import { RIGHT_SIDE_BAR_TAB } from '@/AppBuilder/RightSideBar/rightSidebarConsta import { DEFAULT_COMPONENT_STRUCTURE } from './resolvedSlice'; import { savePageChanges } from './pageMenuSlice'; import { toast } from 'react-hot-toast'; -import { RESTRICTED_WIDGETS_CONFIG } from '@/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig'; +import { + RESTRICTED_WIDGETS_CONFIG, + RESTRICTED_WIDGET_SLOTS_CONFIG, +} from '@/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig'; import moment from 'moment'; import { getDateTimeFormat } from '@/_helpers/appUtils'; import { findHighestLevelofSelection } from '@/AppBuilder/AppCanvas/Grid/gridUtils'; @@ -1225,10 +1229,16 @@ export const createComponentsSlice = (set, get) => ({ const transformedParentId = parentId?.length > 36 ? parentId.slice(0, 36) : parentId; let parentType = getComponentTypeFromId(transformedParentId, moduleId); const parentWidget = getParentWidgetFromId(parentType, parentId); - const restrictedWidgets = RESTRICTED_WIDGETS_CONFIG?.[parentWidget] || []; + const parentSlotType = parentId ? parentId.split('-').pop() : undefined; + const restrictedWidgets = [ + ...(RESTRICTED_WIDGETS_CONFIG?.[parentWidget] || []), + ...(['header', 'footer'].includes(parentSlotType) ? RESTRICTED_WIDGET_SLOTS_CONFIG : []), + ]; const isParentChangeAllowed = !restrictedWidgets.includes(currentWidget); if (!isParentChangeAllowed) { - toast.error(`${currentWidget} is not compatible as a child component of ${parentWidget}`); + toast.error( + `${currentWidget} is not compatible as a child component of ${getDropTargetLabel(parentWidget, parentSlotType)}` + ); return false; } @@ -1280,7 +1290,8 @@ export const createComponentsSlice = (set, get) => ({ moduleId ) === false ) { - return false; + resolve(false); + return; } const newComponents = buildComponentDefinition(componentDefinitions, moduleId);