datastake-daf 0.6.526 → 0.6.528

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.
@@ -33,17 +33,6 @@ const ResizeProvider = ({
33
33
  const [isNestedSidebarCollapsed, setIsNestedSidebarCollapsed] = React.useState(_isNestedSidebarCollapsed);
34
34
  const [loading, setLoading] = React.useState(false);
35
35
  const [dif, setDif] = React.useState();
36
-
37
- // ADD THIS DEBUG
38
- console.log('ResizeProvider render - isCollapsed:', isCollapsed);
39
-
40
- // Wrap setIsCollapsed to add debugging
41
- const setIsCollapsedDebug = React__default["default"].useCallback(value => {
42
- console.log('ResizeProvider: setIsCollapsed called with:', value);
43
- console.log('ResizeProvider: current isCollapsed:', isCollapsed);
44
- setIsCollapsed(value);
45
- console.log('ResizeProvider: setIsCollapsed done');
46
- }, [isCollapsed]);
47
36
  const resizeEvent = React.useCallback(() => {
48
37
  setLoading(true);
49
38
  clearTimeout(timeOut);
@@ -56,20 +45,19 @@ const ResizeProvider = ({
56
45
  }, [windowWidth]);
57
46
  React.useEffect(() => {
58
47
  window.addEventListener('resize', resizeEvent);
48
+ // Fix: Should remove the event listener on cleanup
59
49
  return () => window.removeEventListener('resize', resizeEvent);
60
50
  }, [resizeEvent]);
61
- const value = React__default["default"].useMemo(() => ({
62
- resizeLoading: loading,
63
- windowWidth,
64
- resizeDif: dif,
65
- isCollapsed,
66
- setIsCollapsed: setIsCollapsedDebug,
67
- // Use the debug wrapper
68
- isNestedSidebarCollapsed,
69
- setIsNestedSidebarCollapsed
70
- }), [loading, windowWidth, dif, isCollapsed, isNestedSidebarCollapsed, setIsCollapsedDebug]);
71
51
  return /*#__PURE__*/React__default["default"].createElement(ResizeContext.Provider, {
72
- value
52
+ value: {
53
+ resizeLoading: loading,
54
+ windowWidth,
55
+ resizeDif: dif,
56
+ isCollapsed,
57
+ setIsCollapsed,
58
+ isNestedSidebarCollapsed,
59
+ setIsNestedSidebarCollapsed
60
+ }
73
61
  }, children);
74
62
  };
75
63
  const useResizeContext = () => {
@@ -1 +1 @@
1
- .index_components-layout__BppiJ{display:flex;flex-direction:row;height:100dvh;max-height:100dvh;overflow:hidden;width:100vw}.index_components-layout__BppiJ .index_layout-content-wrapper__ANOdo{display:flex;flex-direction:column;min-height:100dvh;width:100%}.index_layout-children__gWZas{background:#fff;flex:1;min-height:calc(100vh - 64px);overflow-y:auto;padding:24px;position:relative}@media (max-width:768px){.index_layout-children__gWZas{padding:16px}}.index_mod-name__ZAqSo{align-items:center;border-bottom:1px solid #ffffff1a;color:#fff;cursor:pointer;display:flex;gap:12px;padding:20px;transition:all .3s}.index_mod-name__ZAqSo:hover{background:#ffffff1a}.index_mod-name__ZAqSo.index_is-collapsed__4rEBC{justify-content:center;padding:20px 10px}.index_mod-name__ZAqSo.index_is-collapsed__4rEBC h1{display:none}.index_mod-name__ZAqSo .index_icon__tIPRE{align-items:center;display:flex;height:24px;justify-content:center;width:24px}.index_mod-name__ZAqSo .index_icon__tIPRE img,.index_mod-name__ZAqSo .index_icon__tIPRE svg{height:100%;object-fit:contain;width:100%}.index_mod-name__ZAqSo h1{font-size:16px;font-weight:600;line-height:1.2;margin:0}.index_layout-header__uP-hH{align-items:center;background:#1a1d29;box-shadow:0 2px 8px #00000026;display:flex;height:64px;justify-content:space-between;padding:0 24px;position:relative;z-index:1000}.index_layout-header__uP-hH .index_left-sidebar__rf0f7,.index_layout-header__uP-hH .index_right-sidebar__b2OCD{align-items:center;display:flex;gap:16px}.index_layout-header__uP-hH .index_left-sidebar__rf0f7{flex:1}.index_layout-header__uP-hH .index_trigger__UMbTb{color:#fff;cursor:pointer;font-size:18px;line-height:64px;margin:0 -24px 0 0;padding:0 24px;transition:color .3s}.index_layout-header__uP-hH .index_trigger__UMbTb:hover{color:#1890ff}.index_layout-header__uP-hH .index_desktop__sAat1{display:flex}@media (max-width:768px){.index_layout-header__uP-hH .index_desktop__sAat1{display:none}}.index_layout-header__uP-hH .index_mobile__exhDj{display:none}@media (max-width:768px){.index_layout-header__uP-hH .index_mobile__exhDj{align-items:center;cursor:pointer;display:flex}}.index_layout-header__uP-hH .index_mobile__exhDj img{height:32px;width:auto}.index_layout-header__uP-hH .index_mobile__exhDj .index_mobile-burger__HLNGn{color:#fff;font-size:20px}.index_layout-header__uP-hH .index_notification-icon__rzMrK{align-items:center;color:#fff;cursor:pointer;display:flex;font-size:18px;padding:8px;position:relative;transition:color .3s}.index_layout-header__uP-hH .index_notification-icon__rzMrK:hover{color:#1890ff}.index_layout-header__uP-hH .index_notification-icon__rzMrK .index_noti-loader__OF-Nt{animation:index_pulse__tnSN4 2s infinite;background:#52c41a;border-radius:50%;height:8px;position:absolute;right:5px;top:5px;width:8px}.index_layout-header__uP-hH .index_notification-icon__rzMrK .index_noti-more-cont__Glzl6{align-items:center;background:#ff4d4f;border-radius:10px;color:#fff;display:flex;font-size:12px;font-weight:600;height:18px;justify-content:center;min-width:18px;padding:2px 6px;position:absolute;right:5px;top:5px}.index_layout-header__uP-hH .index_language-select__eSaHK{min-width:100px}.index_layout-header__uP-hH .index_language-select__eSaHK .index_ant-select-selector__G-MXN{background:#0000!important;border:none!important;box-shadow:none!important;color:#fff!important}.index_layout-header__uP-hH .index_language-select__eSaHK .index_ant-select-arrow__hcasC{color:#fff}.index_layout-header__uP-hH .index_language-select__eSaHK .index_row-cont__G2wqI{align-items:center;display:flex;gap:8px}.index_layout-header__uP-hH .index_user-details__TajEC{align-items:center;display:flex}.index_dark-select-popup__-UN-9.index_ant-select-dropdown__PAJxh{background:#1a1d29}.index_dark-select-popup__-UN-9.index_ant-select-dropdown__PAJxh .index_ant-select-item__-KnB8{color:#fff}.index_dark-select-popup__-UN-9.index_ant-select-dropdown__PAJxh .index_ant-select-item-option-selected__C7atp,.index_dark-select-popup__-UN-9.index_ant-select-dropdown__PAJxh .index_ant-select-item__-KnB8:hover{background:#ffffff1a}.index_user-dropdown-layout__jsdUj.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g{min-width:200px;padding:0}.index_user-dropdown-layout__jsdUj.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd{padding:0}.index_user-dropdown-layout__jsdUj.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd:hover{background:#0000}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF{border-bottom:1px solid #f0f0f0;padding:16px}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF h4{color:#262626;font-size:14px;font-weight:600;margin:0 0 4px}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF p{color:#8c8c8c;font-size:12px;margin:0}.index_user-dropdown-layout__jsdUj .index_list__JZPtv{padding:8px 0}.index_user-dropdown-layout__jsdUj .index_list__JZPtv .index_list-item__jgxiJ{color:#262626;cursor:pointer;font-size:14px;padding:10px 16px;transition:background .3s}.index_user-dropdown-layout__jsdUj .index_list__JZPtv .index_list-item__jgxiJ:hover{background:#f5f5f5}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g{background:#1a1d29;padding:4px 0}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd{align-items:center;color:#fff;display:flex;gap:8px;padding:8px 12px}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd:hover{background:#ffffff1a}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item-disabled__8Zr1C{color:#ffffff4d;cursor:not-allowed}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item-disabled__8Zr1C:hover{background:#0000}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd .index_anticon__mmT-3{color:#fff}@keyframes index_pulse__tnSN4{0%{box-shadow:0 0 0 0 #52c41ab3}70%{box-shadow:0 0 0 6px #52c41a00}to{box-shadow:0 0 0 0 #52c41a00}}.index_sidenav-sider__hWcZw{background:#1a1d29;display:flex;flex-direction:column;height:100dvh;position:relative;transition:all .3s;z-index:999}@media (max-width:768px){.index_sidenav-sider__hWcZw.index_desktop-sider__qQ7Y5{display:none}}.index_sidenav-sider__hWcZw .index_logo__ThlDP{background:#1a1d29;border-bottom:1px solid #ffffff1a;padding:20px;text-align:center}.index_sidenav-sider__hWcZw .index_logo__ThlDP img{cursor:pointer;height:auto;max-height:40px;max-width:100%;object-fit:contain;transition:all .3s}.index_sidenav-sider__hWcZw .index_logo__ThlDP img:hover{opacity:.8}.index_sidenav-sider__hWcZw.index_sidenav-sider-collapsed__PBdKB{min-width:70px!important;width:70px!important}.index_sidenav-sider__hWcZw.index_sidenav-sider-collapsed__PBdKB .index_logo__ThlDP{padding:20px 10px}.index_sidenav-sider__hWcZw.index_sidenav-sider-collapsed__PBdKB .index_logo__ThlDP img{max-height:32px}.index_sidenav-sider__hWcZw.index_sidenav-sider-opened__tBCYr{min-width:250px;width:250px}.index_sidenav-menu__etWhC{flex:1;overflow-x:hidden;overflow-y:auto}.index_sidenav-menu__etWhC::-webkit-scrollbar{width:6px}.index_sidenav-menu__etWhC::-webkit-scrollbar-track{background:#0000}.index_sidenav-menu__etWhC::-webkit-scrollbar-thumb{background:#fff3;border-radius:3px}.index_sidenav-menu__etWhC::-webkit-scrollbar-thumb:hover{background:#ffffff4d}@media (max-width:768px){.index_components-layout__BppiJ .index_sidenav-sider__hWcZw.index_desktop-sider__qQ7Y5{display:none}.index_components-layout__BppiJ .index_layout-header__uP-hH{padding:0 16px}.index_components-layout__BppiJ .index_layout-header__uP-hH .index_left-sidebar__rf0f7,.index_components-layout__BppiJ .index_layout-header__uP-hH .index_right-sidebar__b2OCD{gap:12px}.index_components-layout__BppiJ .index_layout-children__gWZas,.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF{padding:12px}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF h4{font-size:13px}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF p{font-size:11px}.index_user-dropdown-layout__jsdUj .index_list__JZPtv .index_list-item__jgxiJ{font-size:13px;padding:8px 12px}}@media (min-width:1920px){.index_layout-children__gWZas{margin:0 auto;max-width:1600px}}
1
+ .index_components-layout__BppiJ{display:flex;flex-direction:row;height:100dvh;max-height:100dvh;overflow:hidden;width:100vw}.index_components-layout__BppiJ .index_layout-content-wrapper__ANOdo{display:flex;flex-direction:column;min-height:100dvh;width:100%}.index_layout-children__gWZas{background:#fff;flex:1;min-height:calc(100vh - 64px);overflow-y:auto;padding:24px;position:relative}@media (max-width:768px){.index_layout-children__gWZas{padding:16px}}.index_mod-name__ZAqSo{align-items:center;border-bottom:1px solid var(--layout-border-color,#ffffff1a);color:var(--layout-text-primary,#fff);cursor:pointer;display:flex;gap:12px;padding:20px;transition:all .3s}.index_mod-name__ZAqSo:hover{background:var(--layout-hover-bg,#ffffff1a)}.index_mod-name__ZAqSo.index_is-collapsed__4rEBC{justify-content:center;padding:20px 10px}.index_mod-name__ZAqSo.index_is-collapsed__4rEBC h1{display:none}.index_mod-name__ZAqSo .index_icon__tIPRE{align-items:center;display:flex;height:24px;justify-content:center;width:24px}.index_mod-name__ZAqSo .index_icon__tIPRE img,.index_mod-name__ZAqSo .index_icon__tIPRE svg{height:100%;object-fit:contain;width:100%}.index_mod-name__ZAqSo h1{font-size:16px;font-weight:600;line-height:1.2;margin:0}.index_layout-header__uP-hH{align-items:center;background:var(--layout-primary-bg,#1a1d29);box-shadow:0 2px 8px #00000026;display:flex;height:64px;justify-content:space-between;padding:0 24px;position:relative;z-index:1000}.index_layout-header__uP-hH .index_left-sidebar__rf0f7,.index_layout-header__uP-hH .index_right-sidebar__b2OCD{align-items:center;display:flex;gap:16px}.index_layout-header__uP-hH .index_left-sidebar__rf0f7{flex:1}.index_layout-header__uP-hH .index_trigger__UMbTb{color:var(--layout-text-primary,#fff);cursor:pointer;font-size:18px;line-height:64px;margin:0 -24px 0 0;padding:0 24px;transition:color .3s}.index_layout-header__uP-hH .index_trigger__UMbTb:hover{color:#1890ff}.index_layout-header__uP-hH .index_desktop__sAat1{display:flex}@media (max-width:768px){.index_layout-header__uP-hH .index_desktop__sAat1{display:none}}.index_layout-header__uP-hH .index_mobile__exhDj{display:none}@media (max-width:768px){.index_layout-header__uP-hH .index_mobile__exhDj{align-items:center;cursor:pointer;display:flex}}.index_layout-header__uP-hH .index_mobile__exhDj img{height:32px;width:auto}.index_layout-header__uP-hH .index_mobile__exhDj .index_mobile-burger__HLNGn{color:var(--layout-text-primary,#fff);font-size:20px}.index_layout-header__uP-hH .index_notification-icon__rzMrK{align-items:center;color:var(--layout-text-primary,#fff);cursor:pointer;display:flex;font-size:18px;padding:8px;position:relative;transition:color .3s}.index_layout-header__uP-hH .index_notification-icon__rzMrK:hover{color:#1890ff}.index_layout-header__uP-hH .index_notification-icon__rzMrK .index_noti-loader__OF-Nt{animation:index_pulse__tnSN4 2s infinite;background:#52c41a;border-radius:50%;height:8px;position:absolute;right:5px;top:5px;width:8px}.index_layout-header__uP-hH .index_notification-icon__rzMrK .index_noti-more-cont__Glzl6{align-items:center;background:#ff4d4f;border-radius:10px;color:#fff;display:flex;font-size:12px;font-weight:600;height:18px;justify-content:center;min-width:18px;padding:2px 6px;position:absolute;right:5px;top:5px}.index_layout-header__uP-hH .index_language-select__eSaHK{min-width:100px}.index_layout-header__uP-hH .index_language-select__eSaHK .index_ant-select-selector__G-MXN{background:#0000!important;border:none!important;box-shadow:none!important;color:var(--layout-text-primary,#fff)!important}.index_layout-header__uP-hH .index_language-select__eSaHK .index_ant-select-arrow__hcasC{color:var(--layout-text-primary,#fff)}.index_layout-header__uP-hH .index_language-select__eSaHK .index_row-cont__G2wqI{align-items:center;display:flex;gap:8px}.index_layout-header__uP-hH .index_user-details__TajEC{align-items:center;display:flex}.index_dark-select-popup__-UN-9.index_ant-select-dropdown__PAJxh{background:var(--layout-primary-bg,#1a1d29)}.index_dark-select-popup__-UN-9.index_ant-select-dropdown__PAJxh .index_ant-select-item__-KnB8{color:var(--layout-text-primary,#fff)}.index_dark-select-popup__-UN-9.index_ant-select-dropdown__PAJxh .index_ant-select-item-option-selected__C7atp,.index_dark-select-popup__-UN-9.index_ant-select-dropdown__PAJxh .index_ant-select-item__-KnB8:hover{background:var(--layout-hover-bg,#ffffff1a)}.index_user-dropdown-layout__jsdUj.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g{min-width:200px;padding:0}.index_user-dropdown-layout__jsdUj.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd{padding:0}.index_user-dropdown-layout__jsdUj.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd:hover{background:#0000}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF{border-bottom:1px solid #f0f0f0;padding:16px}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF h4{color:#262626;font-size:14px;font-weight:600;margin:0 0 4px}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF p{color:var(--layout-text-secondary,#8c8c8c);font-size:12px;margin:0}.index_user-dropdown-layout__jsdUj .index_list__JZPtv{padding:8px 0}.index_user-dropdown-layout__jsdUj .index_list__JZPtv .index_list-item__jgxiJ{color:#262626;cursor:pointer;font-size:14px;padding:10px 16px;transition:background .3s}.index_user-dropdown-layout__jsdUj .index_list__JZPtv .index_list-item__jgxiJ:hover{background:#f5f5f5}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g{background:var(--layout-primary-bg,#1a1d29);padding:4px 0}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd{align-items:center;color:var(--layout-text-primary,#fff);display:flex;gap:8px;padding:8px 12px}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd:hover{background:var(--layout-hover-bg,#ffffff1a)}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item-disabled__8Zr1C{color:#ffffff4d;cursor:not-allowed}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item-disabled__8Zr1C:hover{background:#0000}.index_dark-menu__oxCSP.index_ant-dropdown__yk3cn .index_ant-dropdown-menu__-Vi2g .index_ant-dropdown-menu-item__cxyFd .index_anticon__mmT-3{color:var(--layout-text-primary,#fff)}@keyframes index_pulse__tnSN4{0%{box-shadow:0 0 0 0 #52c41ab3}70%{box-shadow:0 0 0 6px #52c41a00}to{box-shadow:0 0 0 0 #52c41a00}}.index_sidenav-sider__hWcZw{background:var(--layout-primary-bg,#1a1d29);display:flex;flex-direction:column;height:100dvh;position:relative;transition:all .3s;z-index:999}@media (max-width:768px){.index_sidenav-sider__hWcZw.index_desktop-sider__qQ7Y5{display:none}}.index_sidenav-sider__hWcZw .index_logo__ThlDP{background:var(--layout-primary-bg,#1a1d29);border-bottom:1px solid var(--layout-border-color,#ffffff1a);padding:20px;text-align:center}.index_sidenav-sider__hWcZw .index_logo__ThlDP img{cursor:pointer;height:auto;max-height:40px;max-width:100%;object-fit:contain;transition:all .3s}.index_sidenav-sider__hWcZw .index_logo__ThlDP img:hover{opacity:.8}.index_sidenav-sider__hWcZw.index_sidenav-sider-collapsed__PBdKB{min-width:70px!important;width:70px!important}.index_sidenav-sider__hWcZw.index_sidenav-sider-collapsed__PBdKB .index_logo__ThlDP{padding:20px 10px}.index_sidenav-sider__hWcZw.index_sidenav-sider-collapsed__PBdKB .index_logo__ThlDP img{max-height:32px}.index_sidenav-sider__hWcZw.index_sidenav-sider-opened__tBCYr{min-width:250px;width:250px}.index_sidenav-menu__etWhC{flex:1;overflow-x:hidden;overflow-y:auto}.index_sidenav-menu__etWhC::-webkit-scrollbar{width:6px}.index_sidenav-menu__etWhC::-webkit-scrollbar-track{background:#0000}.index_sidenav-menu__etWhC::-webkit-scrollbar-thumb{background:#fff3;border-radius:3px}.index_sidenav-menu__etWhC::-webkit-scrollbar-thumb:hover{background:#ffffff4d}@media (max-width:768px){.index_components-layout__BppiJ .index_sidenav-sider__hWcZw.index_desktop-sider__qQ7Y5{display:none}.index_components-layout__BppiJ .index_layout-header__uP-hH{padding:0 16px}.index_components-layout__BppiJ .index_layout-header__uP-hH .index_left-sidebar__rf0f7,.index_components-layout__BppiJ .index_layout-header__uP-hH .index_right-sidebar__b2OCD{gap:12px}.index_components-layout__BppiJ .index_layout-children__gWZas,.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF{padding:12px}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF h4{font-size:13px}.index_user-dropdown-layout__jsdUj .index_drop-header__4udrF p{font-size:11px}.index_user-dropdown-layout__jsdUj .index_list__JZPtv .index_list-item__jgxiJ{font-size:13px;padding:8px 12px}}@media (min-width:1920px){.index_layout-children__gWZas{margin:0 auto;max-width:1600px}}
@@ -5098,73 +5098,53 @@ const PageInterface = ({
5098
5098
  })]
5099
5099
  });
5100
5100
  };
5101
+
5102
+ /**
5103
+ * Renders the module interface based on configuration
5104
+ *
5105
+ * @param {Object} params
5106
+ * @param {boolean} params.isCollapsed - Whether the sidebar is collapsed
5107
+ * @param {Function} params.onClick - Click handler for the module interface
5108
+ * @param {Object|null} params.moduleInterfaceConfig - Configuration for the module interface
5109
+ *
5110
+ * @example
5111
+ * // The app determines the role and passes the appropriate config:
5112
+ * const moduleInterfaceConfig = {
5113
+ * title: "Implementer",
5114
+ * icon: "/assets/images/SVG/implementer.svg" // or icon: <CustomIcon />
5115
+ * }
5116
+ *
5117
+ * // Or for admin:
5118
+ * const moduleInterfaceConfig = {
5119
+ * title: "ADMIN",
5120
+ * icon: <svg>...</svg>
5121
+ * }
5122
+ */
5101
5123
  const renderModule = ({
5102
5124
  isCollapsed,
5103
5125
  onClick,
5104
- user,
5105
- userHelpers = {},
5106
- goTo = () => {}
5126
+ moduleInterfaceConfig = null
5107
5127
  }) => {
5108
- const {
5109
- userIsAdmin,
5110
- userIsSbgImplementor,
5111
- userIsSbgPartner
5112
- } = userHelpers;
5113
- const isAdmin = userIsAdmin?.(user);
5114
- const hasStandardInterface = userIsSbgImplementor?.(user);
5115
- const hasPartnerInterface = userIsSbgPartner?.(user);
5116
- if (isAdmin) {
5117
- return /*#__PURE__*/jsxRuntime.jsx(PageInterface, {
5118
- isCollapsed: isCollapsed,
5119
- onClick: onClick,
5120
- title: "ADMIN",
5121
- children: /*#__PURE__*/jsxRuntime.jsx("svg", {
5122
- viewBox: "0 0 14 14",
5123
- fill: "none",
5124
- xmlns: "http://www.w3.org/2000/svg",
5125
- width: "14",
5126
- height: "14",
5127
- children: /*#__PURE__*/jsxRuntime.jsx("path", {
5128
- d: "M9.03367 5H2.66667C1.74619 5 1 4.25381 1 3.33333C1 2.41286 1.74619 1.66667 2.66667 1.66667H9.03367M4.96633 12.3333H11.3333C12.2538 12.3333 13 11.5871 13 10.6667C13 9.74619 12.2538 9 11.3333 9H4.96633M1 10.6667C1 11.9553 2.04467 13 3.33333 13C4.622 13 5.66667 11.9553 5.66667 10.6667C5.66667 9.378 4.622 8.33333 3.33333 8.33333C2.04467 8.33333 1 9.378 1 10.6667ZM13 3.33333C13 4.622 11.9553 5.66667 10.6667 5.66667C9.378 5.66667 8.33333 4.622 8.33333 3.33333C8.33333 2.04467 9.378 1 10.6667 1C11.9553 1 13 2.04467 13 3.33333Z",
5129
- stroke: "#B9C0D4",
5130
- strokeWidth: "1.2",
5131
- strokeLinecap: "round",
5132
- strokeLinejoin: "round"
5133
- })
5134
- })
5135
- });
5136
- }
5137
- if (hasStandardInterface) {
5138
- return /*#__PURE__*/jsxRuntime.jsx(PageInterface, {
5139
- isCollapsed: isCollapsed,
5140
- onClick: onClick,
5141
- title: "Implementer",
5142
- children: /*#__PURE__*/jsxRuntime.jsx("img", {
5143
- src: "/assets/images/SVG/implementer.svg",
5144
- alt: "implementer"
5145
- })
5146
- });
5147
- } else if (hasPartnerInterface) {
5148
- return /*#__PURE__*/jsxRuntime.jsx(PageInterface, {
5149
- isCollapsed: isCollapsed,
5150
- onClick: onClick,
5151
- title: "Partner",
5152
- children: /*#__PURE__*/jsxRuntime.jsx("img", {
5153
- src: "/assets/images/SVG/partner-building.svg",
5154
- alt: "partner"
5155
- })
5156
- });
5157
- } else if (!hasStandardInterface) {
5158
- return /*#__PURE__*/jsxRuntime.jsx(PageInterface, {
5159
- isCollapsed: isCollapsed,
5160
- onClick: onClick,
5161
- title: "Coordinator",
5162
- children: /*#__PURE__*/jsxRuntime.jsx("img", {
5163
- src: "/assets/images/SVG/coordinator.svg",
5164
- alt: "coordinator"
5165
- })
5166
- });
5128
+ // If no config provided, don't render anything
5129
+ if (!moduleInterfaceConfig) {
5130
+ return null;
5167
5131
  }
5132
+ const {
5133
+ title,
5134
+ icon
5135
+ } = moduleInterfaceConfig;
5136
+
5137
+ // Convert string icon paths to img elements
5138
+ const iconElement = typeof icon === 'string' ? /*#__PURE__*/jsxRuntime.jsx("img", {
5139
+ src: icon,
5140
+ alt: title.toLowerCase()
5141
+ }) : icon;
5142
+ return /*#__PURE__*/jsxRuntime.jsx(PageInterface, {
5143
+ isCollapsed: isCollapsed,
5144
+ onClick: onClick,
5145
+ title: title,
5146
+ children: iconElement
5147
+ });
5168
5148
  };
5169
5149
  const Sidenav = ({
5170
5150
  user,
@@ -5178,6 +5158,7 @@ const Sidenav = ({
5178
5158
  t,
5179
5159
  changeNotificationState,
5180
5160
  userHelpers = {},
5161
+ moduleInterfaceConfig = null,
5181
5162
  isDev = false,
5182
5163
  appName = 'app',
5183
5164
  getRedirectLink = () => {},
@@ -5197,7 +5178,7 @@ const Sidenav = ({
5197
5178
  const {
5198
5179
  pathname
5199
5180
  } = location || {};
5200
- const mod = o.useMemo(() => module, [module, user]);
5181
+ o.useMemo(() => module, [module, user]);
5201
5182
  const checkPath = i => {
5202
5183
  if (!matchPath || !pathname) return false;
5203
5184
  return matchPath({
@@ -5305,17 +5286,14 @@ const Sidenav = ({
5305
5286
  setSelectedKeys([pathname]);
5306
5287
  }
5307
5288
  }, [pathname]);
5308
- const userModule = o.useMemo(() => module, [user, module]);
5289
+ o.useMemo(() => module, [user, module]);
5309
5290
  return /*#__PURE__*/jsxRuntime.jsxs(o__default["default"].Fragment, {
5310
5291
  children: [renderModule({
5311
- module: userModule,
5312
5292
  isCollapsed,
5313
5293
  onClick: () => checkOnClick({
5314
5294
  event: () => goTo(getRedirectLink(`/app`))
5315
5295
  }),
5316
- mod,
5317
- user,
5318
- userHelpers
5296
+ moduleInterfaceConfig
5319
5297
  }), /*#__PURE__*/jsxRuntime.jsx(Sidenav$1, {
5320
5298
  module: module,
5321
5299
  isCollapsed: isCollapsed,
@@ -5893,6 +5871,7 @@ function MobileDrawer({
5893
5871
  isUserDropdown = false,
5894
5872
  user,
5895
5873
  sidenavConfig = {},
5874
+ moduleInterfaceConfig = null,
5896
5875
  goTo = () => {},
5897
5876
  getRedirectLink = () => {},
5898
5877
  t = key => key,
@@ -6012,10 +5991,7 @@ function MobileDrawer({
6012
5991
  className: "flex-1",
6013
5992
  children: mod === 'app' || !user ? null : renderModule({
6014
5993
  isCollapsed,
6015
- mod: mod,
6016
- module: mod,
6017
- user,
6018
- userHelpers
5994
+ moduleInterfaceConfig
6019
5995
  })
6020
5996
  }), /*#__PURE__*/jsxRuntime.jsx("div", {
6021
5997
  className: "cursor-pointer close-icon",
@@ -6947,6 +6923,8 @@ const {
6947
6923
  * @param {Object} props.appLogos - Logo URLs { collapsed, expanded }
6948
6924
  * @param {Object} props.languageConfig - Language selector configuration
6949
6925
  * @param {Function} props.renderChildren - Custom render function for children
6926
+ * @param {Object} props.theme - Theme object with layout color customization
6927
+ * @param {Object|null} props.moduleInterfaceConfig - Module interface config { title, icon }
6950
6928
  */
6951
6929
  function AppLayout({
6952
6930
  // User & Auth
@@ -6986,6 +6964,7 @@ function AppLayout({
6986
6964
  setIsCollapsed,
6987
6965
  // Config
6988
6966
  sidenavConfig = {},
6967
+ moduleInterfaceConfig = null,
6989
6968
  appName = 'app',
6990
6969
  appLogos = {
6991
6970
  collapsed: '/assets/images/logo-small.svg',
@@ -7008,6 +6987,8 @@ function AppLayout({
7008
6987
  onCollapse = () => {},
7009
6988
  // Custom renderers
7010
6989
  renderChildren,
6990
+ // Theme
6991
+ theme = {},
7011
6992
  // Children
7012
6993
  children
7013
6994
  }) {
@@ -7019,9 +7000,6 @@ function AppLayout({
7019
7000
  const {
7020
7001
  logOutPopupVisible = false
7021
7002
  } = generalContext;
7022
- o.useEffect(() => {
7023
- console.log('isCollapsed changed to:', isCollapsed);
7024
- }, [isCollapsed]);
7025
7003
  const canViewNotifications = checkPermission({
7026
7004
  permission: 'notifications.canView',
7027
7005
  permissions: user?.role?.permissions
@@ -7042,13 +7020,8 @@ function AppLayout({
7042
7020
  _fetchPreferences();
7043
7021
  }, [getUserPreference]);
7044
7022
  const setIsCollapse = (val = false) => {
7045
- console.log('setIsCollapse called with:', val);
7046
- console.log('setIsCollapsed function:', setIsCollapsed);
7047
- console.log('typeof setIsCollapsed:', typeof setIsCollapsed);
7048
7023
  localStorage.setItem('is_collapsed', val ? 'true' : 'false');
7049
- console.log('Calling setIsCollapsed with:', val);
7050
7024
  setIsCollapsed(val);
7051
- console.log('setIsCollapsed called');
7052
7025
  if (onCollapse && typeof onCollapse === 'function') {
7053
7026
  onCollapse(val);
7054
7027
  }
@@ -7100,14 +7073,23 @@ function AppLayout({
7100
7073
  onYes: () => goTo(getRedirectLink('/app'))
7101
7074
  });
7102
7075
  };
7076
+
7077
+ // Apply theme CSS custom properties
7078
+ const layoutStyle = o.useMemo(() => ({
7079
+ display: 'flex',
7080
+ flexDirection: 'row',
7081
+ width: '100vw',
7082
+ height: '100dvh',
7083
+ maxHeight: '100dvh',
7084
+ '--layout-primary-bg': theme.layoutPrimaryBg,
7085
+ '--layout-secondary-bg': theme.layoutSecondaryBg,
7086
+ '--layout-border-color': theme.layoutBorderColor,
7087
+ '--layout-text-primary': theme.layoutTextPrimary,
7088
+ '--layout-text-secondary': theme.layoutTextSecondary,
7089
+ '--layout-hover-bg': theme.layoutHoverBg
7090
+ }), [theme]);
7103
7091
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
7104
- style: {
7105
- display: 'flex',
7106
- flexDirection: 'row',
7107
- width: '100vw',
7108
- height: '100dvh',
7109
- maxHeight: '100dvh'
7110
- },
7092
+ style: layoutStyle,
7111
7093
  className: "components-layout",
7112
7094
  children: [!isAppNavigation ? /*#__PURE__*/jsxRuntime.jsxs("div", {
7113
7095
  className: formatClassname([isCollapsed ? 'sidenav-sider-collapsed sidenav-sider desktop-sider' : 'sidenav-sider sidenav-sider-opened desktop-sider', appName]),
@@ -7129,6 +7111,7 @@ function AppLayout({
7129
7111
  isCollapsed: isCollapsed,
7130
7112
  user: user,
7131
7113
  sidenavConfig: sidenavConfig,
7114
+ moduleInterfaceConfig: moduleInterfaceConfig,
7132
7115
  goTo: goTo,
7133
7116
  location: location,
7134
7117
  matchPath: matchPath,
@@ -7159,9 +7142,7 @@ function AppLayout({
7159
7142
  children: isAppNavigation ? null : /*#__PURE__*/o__default["default"].createElement(isCollapsed ? icons.MenuUnfoldOutlined : icons.MenuFoldOutlined, {
7160
7143
  className: 'trigger',
7161
7144
  onClick: () => {
7162
- console.log('Trigger clicked! Current isCollapsed:', isCollapsed);
7163
7145
  const newValue = !isCollapsed;
7164
- console.log('Setting to:', newValue);
7165
7146
  setIsCollapse(newValue);
7166
7147
  }
7167
7148
  })
@@ -7281,6 +7262,7 @@ function AppLayout({
7281
7262
  drawerOpened: drawerOpened,
7282
7263
  user: user,
7283
7264
  sidenavConfig: sidenavConfig,
7265
+ moduleInterfaceConfig: moduleInterfaceConfig,
7284
7266
  navigate: goTo,
7285
7267
  t: t,
7286
7268
  checkPermission: checkPermission,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datastake-daf",
3
- "version": "0.6.526",
3
+ "version": "0.6.528",
4
4
  "dependencies": {
5
5
  "@ant-design/icons": "^5.2.5",
6
6
  "@antv/g2": "^5.1.1",
@@ -25,17 +25,6 @@ export const ResizeProvider = ({ children }) => {
25
25
  const [loading, setLoading] = useState(false);
26
26
  const [dif, setDif] = useState();
27
27
 
28
- // ADD THIS DEBUG
29
- console.log('ResizeProvider render - isCollapsed:', isCollapsed);
30
-
31
- // Wrap setIsCollapsed to add debugging
32
- const setIsCollapsedDebug = React.useCallback((value) => {
33
- console.log('ResizeProvider: setIsCollapsed called with:', value);
34
- console.log('ResizeProvider: current isCollapsed:', isCollapsed);
35
- setIsCollapsed(value);
36
- console.log('ResizeProvider: setIsCollapsed done');
37
- }, [isCollapsed]);
38
-
39
28
  const resizeEvent = useCallback(() => {
40
29
  setLoading(true);
41
30
  clearTimeout(timeOut);
@@ -49,22 +38,23 @@ export const ResizeProvider = ({ children }) => {
49
38
 
50
39
  useEffect(() => {
51
40
  window.addEventListener('resize', resizeEvent);
41
+ // Fix: Should remove the event listener on cleanup
52
42
  return () => window.removeEventListener('resize', resizeEvent);
53
43
  }, [resizeEvent]);
54
44
 
55
- const value = React.useMemo(() => ({
56
- resizeLoading: loading,
57
- windowWidth,
58
- resizeDif: dif,
59
- isCollapsed,
60
- setIsCollapsed: setIsCollapsedDebug, // Use the debug wrapper
61
- isNestedSidebarCollapsed,
62
- setIsNestedSidebarCollapsed,
63
- }), [loading, windowWidth, dif, isCollapsed, isNestedSidebarCollapsed, setIsCollapsedDebug]);
64
-
65
45
  return React.createElement(
66
46
  ResizeContext.Provider,
67
- { value },
47
+ {
48
+ value: {
49
+ resizeLoading: loading,
50
+ windowWidth,
51
+ resizeDif: dif,
52
+ isCollapsed,
53
+ setIsCollapsed,
54
+ isNestedSidebarCollapsed,
55
+ setIsNestedSidebarCollapsed,
56
+ },
57
+ },
68
58
  children
69
59
  );
70
60
  };
@@ -173,8 +173,44 @@ const mockCheckPermission = ({ permission, permissions }) => {
173
173
  // Mock user helpers
174
174
  const mockUserHelpers = {
175
175
  userIsAdmin: (user) => user?.role?.id === "APP_ADMIN",
176
- userIsSbgImplementor: (user) => false,
177
- userIsSbgPartner: (user) => false,
176
+ userIsSbgImplementor: (user) => user?.role?.id === "SBG_IMPLEMENTOR",
177
+ userIsSbgPartner: (user) => user?.role?.id === "SBG_PARTNER",
178
+ };
179
+
180
+ // Module Interface Configurations
181
+ const AdminIcon = () => (
182
+ <svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" width="14" height="14">
183
+ <path d="M9.03367 5H2.66667C1.74619 5 1 4.25381 1 3.33333C1 2.41286 1.74619 1.66667 2.66667 1.66667H9.03367M4.96633 12.3333H11.3333C12.2538 12.3333 13 11.5871 13 10.6667C13 9.74619 12.2538 9 11.3333 9H4.96633M1 10.6667C1 11.9553 2.04467 13 3.33333 13C4.622 13 5.66667 11.9553 5.66667 10.6667C5.66667 9.378 4.622 8.33333 3.33333 8.33333C2.04467 8.33333 1 9.378 1 10.6667ZM13 3.33333C13 4.622 11.9553 5.66667 10.6667 5.66667C9.378 5.66667 8.33333 4.622 8.33333 3.33333C8.33333 2.04467 9.378 1 10.6667 1C11.9953 1 13 2.04467 13 3.33333Z" stroke="#B9C0D4" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round"></path>
184
+ </svg>
185
+ );
186
+
187
+ const getModuleInterfaceConfig = (user, userHelpers) => {
188
+ if (userHelpers.userIsAdmin(user)) {
189
+ return {
190
+ title: "ADMIN",
191
+ icon: <AdminIcon />
192
+ };
193
+ }
194
+
195
+ if (userHelpers.userIsSbgImplementor(user)) {
196
+ return {
197
+ title: "Implementer",
198
+ icon: "/assets/images/SVG/implementer.svg"
199
+ };
200
+ }
201
+
202
+ if (userHelpers.userIsSbgPartner(user)) {
203
+ return {
204
+ title: "Partner",
205
+ icon: "/assets/images/SVG/partner-building.svg"
206
+ };
207
+ }
208
+
209
+ // Default/Coordinator
210
+ return {
211
+ title: "Coordinator",
212
+ icon: "/assets/images/SVG/coordinator.svg"
213
+ };
178
214
  };
179
215
 
180
216
  // Language configuration
@@ -325,6 +361,7 @@ export const Default = {
325
361
 
326
362
  // Config
327
363
  sidenavConfig={mockSidenavConfig}
364
+ moduleInterfaceConfig={getModuleInterfaceConfig(mockUser, mockUserHelpers)}
328
365
  appName="sbg"
329
366
  appLogos={appLogos}
330
367
  languageConfig={languageConfig}
@@ -359,6 +396,7 @@ export const AdminUser = {
359
396
  notificationHandlers={mockNotificationHandlers}
360
397
  NotificationsHistoryProvider={MockNotificationsHistoryProvider}
361
398
  sidenavConfig={mockSidenavConfig}
399
+ moduleInterfaceConfig={getModuleInterfaceConfig(mockAdminUser, mockUserHelpers)}
362
400
  appName="sbg"
363
401
  appLogos={appLogos}
364
402
  languageConfig={languageConfig}
@@ -391,6 +429,7 @@ export const AppNavigation = {
391
429
  notificationHandlers={mockNotificationHandlers}
392
430
  NotificationsHistoryProvider={MockNotificationsHistoryProvider}
393
431
  sidenavConfig={mockSidenavConfig}
432
+ moduleInterfaceConfig={getModuleInterfaceConfig(mockUser, mockUserHelpers)}
394
433
  appName="sbg"
395
434
  appLogos={appLogos}
396
435
  languageConfig={languageConfig}
@@ -429,6 +468,7 @@ export const WithLoginPopup = {
429
468
  notificationHandlers={mockNotificationHandlers}
430
469
  NotificationsHistoryProvider={MockNotificationsHistoryProvider}
431
470
  sidenavConfig={mockSidenavConfig}
471
+ moduleInterfaceConfig={getModuleInterfaceConfig(mockUser, mockUserHelpers)}
432
472
  appName="sbg"
433
473
  appLogos={appLogos}
434
474
  languageConfig={languageConfig}
@@ -474,6 +514,7 @@ export const NoNotifications = {
474
514
  notificationHandlers={mockNotificationHandlers}
475
515
  NotificationsHistoryProvider={MockNotificationsHistoryProvider}
476
516
  sidenavConfig={mockSidenavConfig}
517
+ moduleInterfaceConfig={getModuleInterfaceConfig(userWithoutNotifications, mockUserHelpers)}
477
518
  appName="sbg"
478
519
  appLogos={appLogos}
479
520
  languageConfig={languageConfig}
@@ -506,6 +547,7 @@ export const WithCustomChildren = {
506
547
  notificationHandlers={mockNotificationHandlers}
507
548
  NotificationsHistoryProvider={MockNotificationsHistoryProvider}
508
549
  sidenavConfig={mockSidenavConfig}
550
+ moduleInterfaceConfig={getModuleInterfaceConfig(mockUser, mockUserHelpers)}
509
551
  appName="sbg"
510
552
  appLogos={appLogos}
511
553
  languageConfig={languageConfig}
@@ -529,4 +571,154 @@ export const WithCustomChildren = {
529
571
  </div>
530
572
  </AppLayout>
531
573
  ),
574
+ };
575
+
576
+ // Implementer Role
577
+ export const ImplementerRole = {
578
+ name: "Implementer Role",
579
+ render: () => {
580
+ const implementorUser = {
581
+ ...mockUser,
582
+ role: {
583
+ id: "SBG_IMPLEMENTOR",
584
+ name: "Implementer",
585
+ permissions: {
586
+ "dashboard.canView": true,
587
+ "notifications.canView": true,
588
+ "users.canView": false,
589
+ "settings.canView": false,
590
+ "projects.canView": true,
591
+ }
592
+ }
593
+ };
594
+
595
+ return (
596
+ <AppLayout
597
+ user={implementorUser}
598
+ module="sbg"
599
+ logout={() => console.log("Logout clicked")}
600
+ exitImpersonation={() => console.log("Exit impersonation")}
601
+ updateLanguage={(lng) => console.log("Language changed to:", lng)}
602
+ getUserOptions={(lang) => console.log("Get user options for:", lang)}
603
+ navigate={mockNavigate}
604
+ location={mockLocation}
605
+ matchPath={mockMatchPath}
606
+ Outlet={MockOutlet}
607
+ t={mockT}
608
+ checkPermission={mockCheckPermission}
609
+ generalContext={mockGeneralContext}
610
+ getUserPreference={mockGetUserPreference}
611
+ notificationHandlers={mockNotificationHandlers}
612
+ NotificationsHistoryProvider={MockNotificationsHistoryProvider}
613
+ sidenavConfig={mockSidenavConfig}
614
+ moduleInterfaceConfig={getModuleInterfaceConfig(implementorUser, mockUserHelpers)}
615
+ appName="sbg"
616
+ appLogos={appLogos}
617
+ languageConfig={languageConfig}
618
+ userHelpers={mockUserHelpers}
619
+ isDev={true}
620
+ onCollapse={(collapsed) => console.log("Sidebar collapsed:", collapsed)}
621
+ />
622
+ );
623
+ },
624
+ };
625
+
626
+ // Partner Role
627
+ export const PartnerRole = {
628
+ name: "Partner Role",
629
+ render: () => {
630
+ const partnerUser = {
631
+ ...mockUser,
632
+ role: {
633
+ id: "SBG_PARTNER",
634
+ name: "Partner",
635
+ permissions: {
636
+ "dashboard.canView": true,
637
+ "notifications.canView": true,
638
+ "users.canView": false,
639
+ "settings.canView": false,
640
+ "projects.canView": true,
641
+ }
642
+ }
643
+ };
644
+
645
+ return (
646
+ <AppLayout
647
+ user={partnerUser}
648
+ module="sbg"
649
+ logout={() => console.log("Logout clicked")}
650
+ exitImpersonation={() => console.log("Exit impersonation")}
651
+ updateLanguage={(lng) => console.log("Language changed to:", lng)}
652
+ getUserOptions={(lang) => console.log("Get user options for:", lang)}
653
+ navigate={mockNavigate}
654
+ location={mockLocation}
655
+ matchPath={mockMatchPath}
656
+ Outlet={MockOutlet}
657
+ t={mockT}
658
+ checkPermission={mockCheckPermission}
659
+ generalContext={mockGeneralContext}
660
+ getUserPreference={mockGetUserPreference}
661
+ notificationHandlers={mockNotificationHandlers}
662
+ NotificationsHistoryProvider={MockNotificationsHistoryProvider}
663
+ sidenavConfig={mockSidenavConfig}
664
+ moduleInterfaceConfig={getModuleInterfaceConfig(partnerUser, mockUserHelpers)}
665
+ appName="sbg"
666
+ appLogos={appLogos}
667
+ languageConfig={languageConfig}
668
+ userHelpers={mockUserHelpers}
669
+ isDev={true}
670
+ onCollapse={(collapsed) => console.log("Sidebar collapsed:", collapsed)}
671
+ />
672
+ );
673
+ },
674
+ };
675
+
676
+ // Coordinator Role (Default)
677
+ export const CoordinatorRole = {
678
+ name: "Coordinator Role",
679
+ render: () => {
680
+ const coordinatorUser = {
681
+ ...mockUser,
682
+ role: {
683
+ id: "SBG_COORDINATOR",
684
+ name: "Coordinator",
685
+ permissions: {
686
+ "dashboard.canView": true,
687
+ "notifications.canView": true,
688
+ "users.canView": true,
689
+ "settings.canView": true,
690
+ "projects.canView": true,
691
+ }
692
+ }
693
+ };
694
+
695
+ return (
696
+ <AppLayout
697
+ user={coordinatorUser}
698
+ module="sbg"
699
+ logout={() => console.log("Logout clicked")}
700
+ exitImpersonation={() => console.log("Exit impersonation")}
701
+ updateLanguage={(lng) => console.log("Language changed to:", lng)}
702
+ getUserOptions={(lang) => console.log("Get user options for:", lang)}
703
+ navigate={mockNavigate}
704
+ location={mockLocation}
705
+ matchPath={mockMatchPath}
706
+ Outlet={MockOutlet}
707
+ t={mockT}
708
+ checkPermission={mockCheckPermission}
709
+ generalContext={mockGeneralContext}
710
+ getUserPreference={mockGetUserPreference}
711
+ notificationHandlers={mockNotificationHandlers}
712
+ NotificationsHistoryProvider={MockNotificationsHistoryProvider}
713
+ sidenavConfig={mockSidenavConfig}
714
+ moduleInterfaceConfig={getModuleInterfaceConfig(coordinatorUser, mockUserHelpers)}
715
+ appName="sbg"
716
+ appLogos={appLogos}
717
+ languageConfig={languageConfig}
718
+ userHelpers={mockUserHelpers}
719
+ isDev={true}
720
+ onCollapse={(collapsed) => console.log("Sidebar collapsed:", collapsed)}
721
+ />
722
+ );
723
+ },
532
724
  };
@@ -15,6 +15,7 @@ export default function MobileDrawer({
15
15
  isUserDropdown = false,
16
16
  user,
17
17
  sidenavConfig = {},
18
+ moduleInterfaceConfig = null,
18
19
  goTo = () => {},
19
20
  getRedirectLink = () => {},
20
21
  t = (key) => key,
@@ -165,10 +166,7 @@ export default function MobileDrawer({
165
166
  <div className="flex-1">
166
167
  {mod === 'app' || !user ? null : renderModule({
167
168
  isCollapsed,
168
- mod: mod,
169
- module: mod,
170
- user,
171
- userHelpers
169
+ moduleInterfaceConfig
172
170
  })}
173
171
  </div>
174
172
  <div className="cursor-pointer close-icon" onClick={toggle}>
@@ -21,64 +21,53 @@ const PageInterface = ({ isCollapsed, onClick, title, children }) => {
21
21
  );
22
22
  };
23
23
 
24
+ /**
25
+ * Renders the module interface based on configuration
26
+ *
27
+ * @param {Object} params
28
+ * @param {boolean} params.isCollapsed - Whether the sidebar is collapsed
29
+ * @param {Function} params.onClick - Click handler for the module interface
30
+ * @param {Object|null} params.moduleInterfaceConfig - Configuration for the module interface
31
+ *
32
+ * @example
33
+ * // The app determines the role and passes the appropriate config:
34
+ * const moduleInterfaceConfig = {
35
+ * title: "Implementer",
36
+ * icon: "/assets/images/SVG/implementer.svg" // or icon: <CustomIcon />
37
+ * }
38
+ *
39
+ * // Or for admin:
40
+ * const moduleInterfaceConfig = {
41
+ * title: "ADMIN",
42
+ * icon: <svg>...</svg>
43
+ * }
44
+ */
24
45
  export const renderModule = ({
25
46
  isCollapsed,
26
47
  onClick,
27
- user,
28
- userHelpers = {},
29
- goTo = () => {},
48
+ moduleInterfaceConfig = null,
30
49
  }) => {
31
- const { userIsAdmin, userIsSbgImplementor, userIsSbgPartner } = userHelpers;
32
-
33
- const isAdmin = userIsAdmin?.(user);
34
- const hasStandardInterface = userIsSbgImplementor?.(user);
35
- const hasPartnerInterface = userIsSbgPartner?.(user);
36
-
37
- if (isAdmin) {
38
- return (
39
- <PageInterface
40
- isCollapsed={isCollapsed}
41
- onClick={onClick}
42
- title="ADMIN"
43
- >
44
- <svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" width="14" height="14">
45
- <path d="M9.03367 5H2.66667C1.74619 5 1 4.25381 1 3.33333C1 2.41286 1.74619 1.66667 2.66667 1.66667H9.03367M4.96633 12.3333H11.3333C12.2538 12.3333 13 11.5871 13 10.6667C13 9.74619 12.2538 9 11.3333 9H4.96633M1 10.6667C1 11.9553 2.04467 13 3.33333 13C4.622 13 5.66667 11.9553 5.66667 10.6667C5.66667 9.378 4.622 8.33333 3.33333 8.33333C2.04467 8.33333 1 9.378 1 10.6667ZM13 3.33333C13 4.622 11.9553 5.66667 10.6667 5.66667C9.378 5.66667 8.33333 4.622 8.33333 3.33333C8.33333 2.04467 9.378 1 10.6667 1C11.9553 1 13 2.04467 13 3.33333Z" stroke="#B9C0D4" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round"></path>
46
- </svg>
47
- </PageInterface>
48
- );
50
+ // If no config provided, don't render anything
51
+ if (!moduleInterfaceConfig) {
52
+ return null;
49
53
  }
50
54
 
51
- if (hasStandardInterface) {
52
- return (
53
- <PageInterface
54
- isCollapsed={isCollapsed}
55
- onClick={onClick}
56
- title="Implementer"
57
- >
58
- <img src="/assets/images/SVG/implementer.svg" alt="implementer" />
59
- </PageInterface>
60
- );
61
- } else if (hasPartnerInterface) {
62
- return (
63
- <PageInterface
64
- isCollapsed={isCollapsed}
65
- onClick={onClick}
66
- title="Partner"
67
- >
68
- <img src="/assets/images/SVG/partner-building.svg" alt="partner" />
69
- </PageInterface>
70
- );
71
- } else if (!hasStandardInterface) {
72
- return (
73
- <PageInterface
74
- isCollapsed={isCollapsed}
75
- onClick={onClick}
76
- title="Coordinator"
77
- >
78
- <img src="/assets/images/SVG/coordinator.svg" alt="coordinator" />
79
- </PageInterface>
80
- );
81
- }
55
+ const { title, icon } = moduleInterfaceConfig;
56
+
57
+ // Convert string icon paths to img elements
58
+ const iconElement = typeof icon === 'string'
59
+ ? <img src={icon} alt={title.toLowerCase()} />
60
+ : icon;
61
+
62
+ return (
63
+ <PageInterface
64
+ isCollapsed={isCollapsed}
65
+ onClick={onClick}
66
+ title={title}
67
+ >
68
+ {iconElement}
69
+ </PageInterface>
70
+ );
82
71
  };
83
72
 
84
73
  const Sidenav = ({
@@ -93,6 +82,7 @@ const Sidenav = ({
93
82
  t,
94
83
  changeNotificationState,
95
84
  userHelpers = {},
85
+ moduleInterfaceConfig = null,
96
86
  isDev = false,
97
87
  appName = 'app',
98
88
  getRedirectLink = () => {},
@@ -238,14 +228,11 @@ const Sidenav = ({
238
228
  return (
239
229
  <React.Fragment>
240
230
  {renderModule({
241
- module: userModule,
242
231
  isCollapsed,
243
232
  onClick: () => checkOnClick({
244
233
  event: () => goTo(getRedirectLink(`/app`))
245
234
  }),
246
- mod,
247
- user,
248
- userHelpers
235
+ moduleInterfaceConfig
249
236
  })}
250
237
  <DAFSidenav
251
238
  module={module}
@@ -53,6 +53,8 @@ const { Content } = Layout;
53
53
  * @param {Object} props.appLogos - Logo URLs { collapsed, expanded }
54
54
  * @param {Object} props.languageConfig - Language selector configuration
55
55
  * @param {Function} props.renderChildren - Custom render function for children
56
+ * @param {Object} props.theme - Theme object with layout color customization
57
+ * @param {Object|null} props.moduleInterfaceConfig - Module interface config { title, icon }
56
58
  */
57
59
  function AppLayout({
58
60
  // User & Auth
@@ -101,6 +103,7 @@ function AppLayout({
101
103
 
102
104
  // Config
103
105
  sidenavConfig = {},
106
+ moduleInterfaceConfig = null,
104
107
  appName = 'app',
105
108
  appLogos = {
106
109
  collapsed: '/assets/images/logo-small.svg',
@@ -118,6 +121,9 @@ function AppLayout({
118
121
  // Custom renderers
119
122
  renderChildren,
120
123
 
124
+ // Theme
125
+ theme = {},
126
+
121
127
  // Children
122
128
  children,
123
129
  }) {
@@ -128,10 +134,6 @@ function AppLayout({
128
134
  const [hasPrevious, setPrevious] = useState(false);
129
135
  const { logOutPopupVisible = false } = generalContext;
130
136
 
131
- useEffect(() => {
132
- console.log('isCollapsed changed to:', isCollapsed);
133
- }, [isCollapsed]);
134
-
135
137
  const canViewNotifications = checkPermission({
136
138
  permission: 'notifications.canView',
137
139
  permissions: user?.role?.permissions
@@ -153,15 +155,8 @@ function AppLayout({
153
155
  }, [getUserPreference]);
154
156
 
155
157
  const setIsCollapse = (val = false) => {
156
- console.log('setIsCollapse called with:', val);
157
- console.log('setIsCollapsed function:', setIsCollapsed);
158
- console.log('typeof setIsCollapsed:', typeof setIsCollapsed);
159
-
160
158
  localStorage.setItem('is_collapsed', val ? 'true' : 'false');
161
-
162
- console.log('Calling setIsCollapsed with:', val);
163
159
  setIsCollapsed(val);
164
- console.log('setIsCollapsed called');
165
160
 
166
161
  if (onCollapse && typeof onCollapse === 'function') {
167
162
  onCollapse(val);
@@ -223,15 +218,24 @@ function AppLayout({
223
218
  changeNotificationState({ onYes: () => goTo(getRedirectLink('/app')) });
224
219
  };
225
220
 
221
+ // Apply theme CSS custom properties
222
+ const layoutStyle = useMemo(() => ({
223
+ display: 'flex',
224
+ flexDirection: 'row',
225
+ width: '100vw',
226
+ height: '100dvh',
227
+ maxHeight: '100dvh',
228
+ '--layout-primary-bg': theme.layoutPrimaryBg,
229
+ '--layout-secondary-bg': theme.layoutSecondaryBg,
230
+ '--layout-border-color': theme.layoutBorderColor,
231
+ '--layout-text-primary': theme.layoutTextPrimary,
232
+ '--layout-text-secondary': theme.layoutTextSecondary,
233
+ '--layout-hover-bg': theme.layoutHoverBg,
234
+ }), [theme]);
235
+
226
236
  return (
227
237
  <div
228
- style={{
229
- display: 'flex',
230
- flexDirection: 'row',
231
- width: '100vw',
232
- height: '100dvh',
233
- maxHeight: '100dvh'
234
- }}
238
+ style={layoutStyle}
235
239
  className="components-layout"
236
240
  >
237
241
  {!isAppNavigation ? (
@@ -259,6 +263,7 @@ function AppLayout({
259
263
  isCollapsed={isCollapsed}
260
264
  user={user}
261
265
  sidenavConfig={sidenavConfig}
266
+ moduleInterfaceConfig={moduleInterfaceConfig}
262
267
  goTo={goTo}
263
268
  location={location}
264
269
  matchPath={matchPath}
@@ -292,9 +297,7 @@ function AppLayout({
292
297
  {
293
298
  className: 'trigger',
294
299
  onClick: () => {
295
- console.log('Trigger clicked! Current isCollapsed:', isCollapsed);
296
300
  const newValue = !isCollapsed;
297
- console.log('Setting to:', newValue);
298
301
  setIsCollapse(newValue);
299
302
  },
300
303
  }
@@ -415,6 +418,7 @@ function AppLayout({
415
418
  drawerOpened={drawerOpened}
416
419
  user={user}
417
420
  sidenavConfig={sidenavConfig}
421
+ moduleInterfaceConfig={moduleInterfaceConfig}
418
422
  navigate={goTo}
419
423
  t={t}
420
424
  checkPermission={checkPermission}
@@ -1,10 +1,11 @@
1
- // Colors
2
- $primary-bg: #1a1d29;
3
- $secondary-bg: #262b3d;
4
- $border-color: rgba(255, 255, 255, 0.1);
5
- $text-primary: #ffffff;
6
- $text-secondary: #8c8c8c;
7
- $hover-bg: rgba(255, 255, 255, 0.1);
1
+ // CSS Custom Properties (can be overridden dynamically)
2
+ // These will be set via inline styles in AppLayout component
3
+ $primary-bg: var(--layout-primary-bg, #1a1d29);
4
+ $secondary-bg: var(--layout-secondary-bg, #262b3d);
5
+ $border-color: var(--layout-border-color, rgba(255, 255, 255, 0.1));
6
+ $text-primary: var(--layout-text-primary, #ffffff);
7
+ $text-secondary: var(--layout-text-secondary, #8c8c8c);
8
+ $hover-bg: var(--layout-hover-bg, rgba(255, 255, 255, 0.1));
8
9
 
9
10
  // Spacing
10
11
  $header-height: 64px;