@admin-layout/tailwind-design-pro 12.0.16-alpha.7 → 12.0.16-alpha.71

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.
Files changed (182) hide show
  1. package/README.md +20 -0
  2. package/lib/cdm-locales/en/menu.json +2 -1
  3. package/lib/cdm-locales/es/menu.json +2 -1
  4. package/lib/cdm-locales/index.d.ts +2 -0
  5. package/lib/cdm-locales/index.d.ts.map +1 -0
  6. package/lib/cdm-locales/index.ts +1 -0
  7. package/lib/components/LanguageMenu/LanguageMenu.js.map +1 -1
  8. package/lib/components/Layout/BasicLayout/index.d.ts +2 -1
  9. package/lib/components/Layout/BasicLayout/index.d.ts.map +1 -1
  10. package/lib/components/Layout/BasicLayout/index.js +134 -44
  11. package/lib/components/Layout/BasicLayout/index.js.map +1 -1
  12. package/lib/components/Layout/BasicLayout/utils.d.ts +2 -0
  13. package/lib/components/Layout/BasicLayout/utils.d.ts.map +1 -1
  14. package/lib/components/Layout/BasicLayout/utils.js +16 -13
  15. package/lib/components/Layout/BasicLayout/utils.js.map +1 -1
  16. package/lib/components/Layout/GlobalFooter/Account.d.ts +4 -0
  17. package/lib/components/Layout/GlobalFooter/Account.d.ts.map +1 -0
  18. package/lib/components/Layout/GlobalFooter/BottomMenuDropdown.d.ts.map +1 -1
  19. package/lib/components/Layout/GlobalFooter/BottomMenuDropdown.js +7 -12
  20. package/lib/components/Layout/GlobalFooter/BottomMenuDropdown.js.map +1 -1
  21. package/lib/components/Layout/GlobalHeader/Header.d.ts.map +1 -1
  22. package/lib/components/Layout/GlobalHeader/Header.js +31 -8
  23. package/lib/components/Layout/GlobalHeader/Header.js.map +1 -1
  24. package/lib/components/Layout/GlobalHeader/MainHeader.d.ts +0 -2
  25. package/lib/components/Layout/GlobalHeader/MainHeader.d.ts.map +1 -1
  26. package/lib/components/Layout/GlobalHeader/MainHeader.js +54 -85
  27. package/lib/components/Layout/GlobalHeader/MainHeader.js.map +1 -1
  28. package/lib/components/Layout/GlobalHeader/OrganizationDropdown.js +1 -1
  29. package/lib/components/Layout/GlobalHeader/OrganizationDropdown.js.map +1 -1
  30. package/lib/components/Layout/GlobalHeader/RightContent.d.ts +1 -0
  31. package/lib/components/Layout/GlobalHeader/RightContent.d.ts.map +1 -1
  32. package/lib/components/Layout/GlobalHeader/RightContent.js +73 -6
  33. package/lib/components/Layout/GlobalHeader/RightContent.js.map +1 -1
  34. package/lib/components/Layout/GlobalHeader/RightMenu.d.ts.map +1 -1
  35. package/lib/components/Layout/GlobalHeader/RightMenu.js +2 -4
  36. package/lib/components/Layout/GlobalHeader/RightMenu.js.map +1 -1
  37. package/lib/components/Layout/GlobalHeader/SearchBar.js.map +1 -1
  38. package/lib/components/Layout/GlobalHeader/index.d.ts +0 -1
  39. package/lib/components/Layout/GlobalHeader/index.d.ts.map +1 -1
  40. package/lib/components/Layout/ProTailwindLayout.d.ts +2 -2
  41. package/lib/components/Layout/ProTailwindLayout.d.ts.map +1 -1
  42. package/lib/components/Layout/ProTailwindLayout.js +69 -7
  43. package/lib/components/Layout/ProTailwindLayout.js.map +1 -1
  44. package/lib/components/Layout/Sidebar/BottomMenu.d.ts +4 -0
  45. package/lib/components/Layout/Sidebar/BottomMenu.d.ts.map +1 -0
  46. package/lib/components/Layout/Sidebar/BottomMenu.js +330 -0
  47. package/lib/components/Layout/Sidebar/BottomMenu.js.map +1 -0
  48. package/lib/components/Layout/Sidebar/Divider.js.map +1 -1
  49. package/lib/components/Layout/Sidebar/DynamicIcon.js.map +1 -1
  50. package/lib/components/Layout/Sidebar/MainSidebar.d.ts +5 -0
  51. package/lib/components/Layout/Sidebar/MainSidebar.d.ts.map +1 -0
  52. package/lib/components/Layout/Sidebar/{Sidebar.js → MainSidebar.js} +94 -34
  53. package/lib/components/Layout/Sidebar/MainSidebar.js.map +1 -0
  54. package/lib/components/Layout/Sidebar/MainSidebarMenu.d.ts +4 -0
  55. package/lib/components/Layout/Sidebar/MainSidebarMenu.d.ts.map +1 -0
  56. package/lib/components/Layout/Sidebar/MainSidebarMenu.js +291 -0
  57. package/lib/components/Layout/Sidebar/MainSidebarMenu.js.map +1 -0
  58. package/lib/components/Layout/Sidebar/PerplexSidebar.d.ts +5 -0
  59. package/lib/components/Layout/Sidebar/PerplexSidebar.d.ts.map +1 -0
  60. package/lib/components/Layout/Sidebar/PerplexSidebar.js +277 -0
  61. package/lib/components/Layout/Sidebar/PerplexSidebar.js.map +1 -0
  62. package/lib/components/Layout/Sidebar/PerplexSidebarMenu.d.ts +4 -0
  63. package/lib/components/Layout/Sidebar/PerplexSidebarMenu.d.ts.map +1 -0
  64. package/lib/components/Layout/Sidebar/PerplexSidebarMenu.js +330 -0
  65. package/lib/components/Layout/Sidebar/PerplexSidebarMenu.js.map +1 -0
  66. package/lib/components/Layout/TailwindLayout.d.ts.map +1 -1
  67. package/lib/components/Layout/TailwindLayout.js +37 -10
  68. package/lib/components/Layout/TailwindLayout.js.map +1 -1
  69. package/lib/components/Layout/getPageTitle.js.map +1 -1
  70. package/lib/components/Layout/slot-fill/AdditionalSettings.js.map +1 -1
  71. package/lib/components/Layout/slot-fill/Footer.js.map +1 -1
  72. package/lib/components/Layout/slot-fill/HeaderSearchBar.js.map +1 -1
  73. package/lib/components/Layout/slot-fill/HeaderSearchButton.js.map +1 -1
  74. package/lib/components/Layout/slot-fill/Logo.d.ts +4 -0
  75. package/lib/components/Layout/slot-fill/Logo.d.ts.map +1 -0
  76. package/lib/components/Layout/slot-fill/Logo.js +7 -0
  77. package/lib/components/Layout/slot-fill/Logo.js.map +1 -0
  78. package/lib/components/Layout/slot-fill/RightContent.js.map +1 -1
  79. package/lib/components/Layout/slot-fill/SideMenuLogo.d.ts +4 -0
  80. package/lib/components/Layout/slot-fill/SideMenuLogo.d.ts.map +1 -0
  81. package/lib/components/Layout/slot-fill/SideMenuLogo.js +7 -0
  82. package/lib/components/Layout/slot-fill/SideMenuLogo.js.map +1 -0
  83. package/lib/components/Layout/slot-fill/index.d.ts +2 -0
  84. package/lib/components/Layout/slot-fill/index.d.ts.map +1 -1
  85. package/lib/components/Layout/util.js +1 -1
  86. package/lib/components/Layout/util.js.map +1 -1
  87. package/lib/components/SettingDrawer/CheckBoxTheme.d.ts.map +1 -1
  88. package/lib/components/SettingDrawer/CheckBoxTheme.js +13 -3
  89. package/lib/components/SettingDrawer/CheckBoxTheme.js.map +1 -1
  90. package/lib/components/SettingDrawer/InvitationSettings.d.ts.map +1 -1
  91. package/lib/components/SettingDrawer/InvitationSettings.js +10 -6
  92. package/lib/components/SettingDrawer/InvitationSettings.js.map +1 -1
  93. package/lib/components/SettingDrawer/LayoutChange.d.ts.map +1 -1
  94. package/lib/components/SettingDrawer/LayoutChange.js +23 -16
  95. package/lib/components/SettingDrawer/LayoutChange.js.map +1 -1
  96. package/lib/components/SettingDrawer/MenuVisibilitySettings.d.ts +20 -0
  97. package/lib/components/SettingDrawer/MenuVisibilitySettings.d.ts.map +1 -0
  98. package/lib/components/SettingDrawer/MenuVisibilitySettings.js +120 -0
  99. package/lib/components/SettingDrawer/MenuVisibilitySettings.js.map +1 -0
  100. package/lib/components/SettingDrawer/NavigationsModes.d.ts +1 -1
  101. package/lib/components/SettingDrawer/NavigationsModes.d.ts.map +1 -1
  102. package/lib/components/SettingDrawer/NavigationsModes.js +90 -39
  103. package/lib/components/SettingDrawer/NavigationsModes.js.map +1 -1
  104. package/lib/components/SettingDrawer/RegionalSettings.d.ts.map +1 -1
  105. package/lib/components/SettingDrawer/RegionalSettings.js +54 -177
  106. package/lib/components/SettingDrawer/RegionalSettings.js.map +1 -1
  107. package/lib/components/SettingDrawer/SettingDrawer.d.ts.map +1 -1
  108. package/lib/components/SettingDrawer/SettingDrawer.js +404 -66
  109. package/lib/components/SettingDrawer/SettingDrawer.js.map +1 -1
  110. package/lib/components/SettingDrawer/Switch/index.js.map +1 -1
  111. package/lib/components/SettingDrawer/ThemeColor.d.ts.map +1 -1
  112. package/lib/components/SettingDrawer/ThemeColor.js +15 -3
  113. package/lib/components/SettingDrawer/ThemeColor.js.map +1 -1
  114. package/lib/components/SettingDrawer/types.d.ts +12 -0
  115. package/lib/components/SettingDrawer/types.d.ts.map +1 -1
  116. package/lib/components/UpdateSettingsResource/UpdateSettingsResource.server.d.ts +10 -5
  117. package/lib/components/UpdateSettingsResource/UpdateSettingsResource.server.d.ts.map +1 -1
  118. package/lib/components/UpdateSettingsResource/UpdateSettingsResource.server.js +97 -4
  119. package/lib/components/UpdateSettingsResource/UpdateSettingsResource.server.js.map +1 -1
  120. package/lib/components/index.js +1 -1
  121. package/lib/components/typings.d.ts +20 -1
  122. package/lib/components/typings.d.ts.map +1 -1
  123. package/lib/compute.d.ts.map +1 -1
  124. package/lib/compute.js +10 -2
  125. package/lib/compute.js.map +1 -1
  126. package/lib/config/constants.js.map +1 -1
  127. package/lib/config/env-config.d.ts +3 -0
  128. package/lib/config/env-config.d.ts.map +1 -1
  129. package/lib/config/env-config.js +10 -0
  130. package/lib/config/env-config.js.map +1 -1
  131. package/lib/helpers/DynamicIcon.js.map +1 -1
  132. package/lib/hooks/useMediaQuery.js.map +1 -1
  133. package/lib/hooks/useScrollThreshold.js.map +1 -1
  134. package/lib/hooks/useTailwindTheme.js +82 -0
  135. package/lib/hooks/useTailwindTheme.js.map +1 -0
  136. package/lib/icons.d.ts +2 -0
  137. package/lib/icons.d.ts.map +1 -1
  138. package/lib/icons.js +4 -2
  139. package/lib/icons.js.map +1 -1
  140. package/lib/index.d.ts +1 -0
  141. package/lib/index.d.ts.map +1 -1
  142. package/lib/index.js +1 -1
  143. package/lib/index.js.map +1 -1
  144. package/lib/machines/selectors.d.ts +118 -0
  145. package/lib/machines/selectors.d.ts.map +1 -0
  146. package/lib/machines/settingsMachine.d.ts +25 -2
  147. package/lib/machines/settingsMachine.d.ts.map +1 -1
  148. package/lib/machines/settingsMachine.js +174 -163
  149. package/lib/machines/settingsMachine.js.map +1 -1
  150. package/lib/machines/settingsMachine.test.d.ts +2 -0
  151. package/lib/machines/settingsMachine.test.d.ts.map +1 -0
  152. package/lib/machines/types.d.ts +106 -9
  153. package/lib/machines/types.d.ts.map +1 -1
  154. package/lib/machines/types.js.map +1 -1
  155. package/lib/module.js.map +1 -1
  156. package/lib/redux/searchReducer.js.map +1 -1
  157. package/lib/routes.json +9 -1
  158. package/lib/tailwindConfig.js.map +1 -1
  159. package/lib/utils/__tests__/configOverrides.test.d.ts +2 -0
  160. package/lib/utils/__tests__/configOverrides.test.d.ts.map +1 -0
  161. package/lib/utils/configOverrides.d.ts +213 -0
  162. package/lib/utils/configOverrides.d.ts.map +1 -0
  163. package/lib/utils/configOverrides.js +91 -0
  164. package/lib/utils/configOverrides.js.map +1 -0
  165. package/lib/utils/isBrowser/index.js.map +1 -1
  166. package/lib/utils/settingsUtils.d.ts +20 -0
  167. package/lib/utils/settingsUtils.d.ts.map +1 -0
  168. package/lib/utils/settingsUtils.js +74 -0
  169. package/lib/utils/settingsUtils.js.map +1 -0
  170. package/lib/utils/utils/index.js.map +1 -1
  171. package/package.json +6 -6
  172. package/lib/components/Layout/GlobalHeader/Logo.d.ts +0 -4
  173. package/lib/components/Layout/GlobalHeader/Logo.d.ts.map +0 -1
  174. package/lib/components/Layout/GlobalHeader/Logo.js +0 -141
  175. package/lib/components/Layout/GlobalHeader/Logo.js.map +0 -1
  176. package/lib/components/Layout/Sidebar/Sidebar.d.ts +0 -5
  177. package/lib/components/Layout/Sidebar/Sidebar.d.ts.map +0 -1
  178. package/lib/components/Layout/Sidebar/Sidebar.js.map +0 -1
  179. package/lib/components/Layout/Sidebar/SidebarMenu.d.ts +0 -4
  180. package/lib/components/Layout/Sidebar/SidebarMenu.d.ts.map +0 -1
  181. package/lib/components/Layout/Sidebar/SidebarMenu.js +0 -176
  182. package/lib/components/Layout/Sidebar/SidebarMenu.js.map +0 -1
@@ -1,5 +1,4 @@
1
- import {jsx,Fragment,jsxs}from'react/jsx-runtime';import {useState,useRef,useEffect}from'react';import {useSelector}from'@xstate/react';import {useTranslation}from'react-i18next';import isBrowser from'../../utils/isBrowser/index.js';import {parse}from'qs';import clsx from'clsx';import {defaultSettings,compareAndSaveSettingsDifferences}from'@admin-layout/client';import ThemeColor from'./ThemeColor.js';import CheckBoxTheme from'./CheckBoxTheme.js';import LayoutChange from'./LayoutChange.js';import RegionalSettings from'./RegionalSettings.js';import InvitationSettings from'./InvitationSettings.js';import {ThemeSelector}from'./ThemeSelector.js';import NavigationModes from'./NavigationsModes.js';import {icons}from'../../icons.js';import'@common-stack/components-pro';import {AdditionalSettingsSlot}from'../Layout/slot-fill/AdditionalSettings.js';import'reflect-metadata';import'@admin-layout/client/lib/constants/layout.js';import {WebFontsSelector}from'./WebFontsSelector.js';import'@react-icons/all-files/fa/FaMoon.js';import'@react-icons/all-files/fa/FaSun.js';import {useMediaQuery}from'../../hooks/useMediaQuery.js';let oldSetting = {};
2
- const updateTheme = (dark, color, publicPath = '/theme') => {
1
+ import {jsx,Fragment,jsxs}from'react/jsx-runtime';import {useSelector}from'@xstate/react';import clsx from'clsx';import {parse}from'qs';import*as React from'react';import {useState,useRef,useEffect}from'react';import {useTranslation}from'react-i18next';import'@react-icons/all-files/fa/FaMoon.js';import'@react-icons/all-files/fa/FaSun.js';import {useMediaQuery}from'../../hooks/useMediaQuery.js';import {icons}from'../../icons.js';import isBrowser from'../../utils/isBrowser/index.js';import {getDefaultApplyToAllRoutes,getSettingValue}from'../../utils/settingsUtils.js';import'@common-stack/components-pro';import {AdditionalSettingsSlot}from'../Layout/slot-fill/AdditionalSettings.js';import'reflect-metadata';import'@admin-layout/client/lib/constants/layout.js';import CheckBoxTheme from'./CheckBoxTheme.js';import InvitationSettings from'./InvitationSettings.js';import LayoutChange from'./LayoutChange.js';import MenuVisibilitySettings from'./MenuVisibilitySettings.js';import NavigationModes from'./NavigationsModes.js';import RegionalSettings from'./RegionalSettings.js';import ThemeColor from'./ThemeColor.js';import {ThemeSelector}from'./ThemeSelector.js';import {WebFontsSelector}from'./WebFontsSelector.js';import {useWriteSettingsMutation,useResetApplicationUiSettingsMutation}from'common/lib/generated/generated.js';import {ConfigCollectionName,ConfigFragmentName}from'common/lib/generated/generated-models.js';import {generateCdecodeUri,DEFAULT_CONTRIBUTION_TENANT_ID}from'@adminide-stack/core';import {config}from'../../config/env-config.js';const updateTheme = (dark, color, publicPath = '/theme') => {
3
2
  // ssr
4
3
  if (typeof window === 'undefined' || !window.umi_plugin_ant_themeVar) {
5
4
  return;
@@ -42,7 +41,7 @@ const updateTheme = (dark, color, publicPath = '/theme') => {
42
41
  }
43
42
  // localStorage.setItem('site-theme', dark ? 'dark' : 'light');
44
43
  };
45
- const initState = (settings, onSettingChange, publicPath) => {
44
+ const initState = (settings, onSettingChange, publicPath, oldSetting) => {
46
45
  if (!isBrowser()) {
47
46
  return;
48
47
  }
@@ -51,21 +50,25 @@ const initState = (settings, onSettingChange, publicPath) => {
51
50
  const params = parse(window.location.search.replace('?', ''));
52
51
  const replaceSetting = {};
53
52
  Object.keys(params).forEach(key => {
54
- if (defaultSettings[key] || defaultSettings[key] === undefined) {
53
+ if (settings[key] !== undefined || settings[key] === undefined) {
55
54
  replaceSetting[key] = params[key];
56
55
  if (key.includes('Render')) {
57
56
  replaceSetting[key] = params[key] === 'false' ? false : undefined;
58
57
  }
59
58
  }
60
59
  });
61
- if (onSettingChange) {
60
+ // Only call onSettingChange if we have actual URL parameters to apply
61
+ if (onSettingChange && Object.keys(replaceSetting).length > 0) {
62
+ console.log('✅ initState: Calling onSettingChange with URL params:', replaceSetting);
62
63
  onSettingChange({
63
64
  ...settings,
64
65
  ...replaceSetting
65
66
  });
67
+ } else {
68
+ console.log('⏭️ initState: Skipping onSettingChange - no valid URL params to apply');
66
69
  }
67
70
  // Set the topic in the URL and load it once.
68
- if (oldSetting.navTheme !== params.navTheme && params.navTheme) {
71
+ if (oldSetting?.navTheme !== params.navTheme && params.navTheme) {
69
72
  updateTheme(settings.navTheme === 'realDark', params.primaryColor, publicPath);
70
73
  loadedStyle = true;
71
74
  }
@@ -74,21 +77,37 @@ const initState = (settings, onSettingChange, publicPath) => {
74
77
  return;
75
78
  }
76
79
  // If there is no topic in the URL, and no load in the URL is loaded.
77
- if (defaultSettings.navTheme !== settings.navTheme && settings.navTheme) {
80
+ if (oldSetting?.navTheme !== settings.navTheme && settings.navTheme) {
78
81
  updateTheme(settings.navTheme === 'realDark', settings.primaryColor, publicPath);
79
82
  }
80
83
  };
81
- const getThemeList = (settings, location) => {
84
+ const getThemeList = (settings, location, routePattern) => {
82
85
  const {
83
86
  t
84
87
  } = useTranslation('settings');
85
88
  const {
86
89
  isMobile
87
90
  } = useMediaQuery();
88
- const currentRoute = location?.pathname || '/';
89
- const routeSettings = settings?.routeSettings?.[currentRoute] || settings?.routeSettings?.['/'] || settings;
91
+ const currentRoute = routePattern || location?.pathname || '/';
90
92
  const deviceType = isMobile ? 'mobile' : 'desktop';
91
- const deviceLayout = routeSettings?.layout?.[deviceType] || {};
93
+ // Helper function to get value from settings with route/device override support
94
+ const getUILayoutValue = path => {
95
+ const routeDeviceKey = `[${currentRoute}][${deviceType}]`;
96
+ const uiOverride = settings?.uiSettings?.[routeDeviceKey];
97
+ if (uiOverride) {
98
+ const value = path.split('.').reduce((obj, key) => obj?.[key], uiOverride);
99
+ if (value !== undefined) return value;
100
+ }
101
+ return path.split('.').reduce((obj, key) => obj?.[key], settings);
102
+ };
103
+ const deviceLayout = {
104
+ navigationMode: getUILayoutValue('navigationMode'),
105
+ sideMenuType: getUILayoutValue('sideMenuType'),
106
+ contentWidth: getUILayoutValue('contentWidth'),
107
+ fixedHeader: getUILayoutValue('fixedHeader'),
108
+ fixedSidebar: getUILayoutValue('fixedSidebar'),
109
+ splitMenus: getUILayoutValue('splitMenus')
110
+ };
92
111
  let list = [];
93
112
  const defaultList = [
94
113
  //@sri following are needed otherwise we need make above plugin to work
@@ -264,7 +283,6 @@ const fontOptions = [{
264
283
  }];
265
284
  const SettingDrawer = props => {
266
285
  const {
267
- settings,
268
286
  onSettingChange,
269
287
  hideLoading,
270
288
  hideColors,
@@ -275,8 +293,12 @@ const SettingDrawer = props => {
275
293
  colorList,
276
294
  navTheme,
277
295
  pathname,
278
- location
296
+ location,
297
+ fullMenuData,
298
+ routePattern
279
299
  } = props;
300
+ // Don't use useLayoutSettings() - it pulls from Redux which may be stale
301
+ // Use settingState from XState actor instead (line 393)
280
302
  const {
281
303
  CloseIcon,
282
304
  Settings5FillIcon,
@@ -289,41 +311,122 @@ const SettingDrawer = props => {
289
311
  } = useMediaQuery();
290
312
  const [copied, setCopied] = useState(false);
291
313
  const [copiedModified, setCopiedModified] = useState(false);
314
+ const [activeDeviceTab, setActiveDeviceTab] = useState(isMobile ? 'mobile' : 'desktop');
315
+ const [applyToAllRoutes, setApplyToAllRoutes] = useState(false);
292
316
  const {
293
317
  t
294
318
  } = useTranslation('settings');
295
- const state = useSelector(actor, state => state);
319
+ // Track if we've already initialized to prevent loops
320
+ const hasInitializedRef = useRef(false);
321
+ const resource = generateCdecodeUri(DEFAULT_CONTRIBUTION_TENANT_ID, {
322
+ resourceType: ConfigCollectionName.Applications,
323
+ resourceId: config.APPLICATION_ID,
324
+ idField: 'appId'
325
+ }, {}, ConfigFragmentName.UiSettings);
326
+ const [writeSettingsMutation, {
327
+ client
328
+ }] = useWriteSettingsMutation();
329
+ const [resetApplicationSettings] = useResetApplicationUiSettingsMutation();
296
330
  const btnRef = useRef();
297
- const settingState = state?.context;
298
- const currentRoute = location?.pathname || '/';
299
- // const routeSettings =
300
- // settingState?.routeSettings?.[currentRoute] || settingState?.routeSettings?.['/'] || settingState;
301
- const deviceType = isMobile ? 'mobile' : 'desktop';
302
- // const deviceLayout = routeSettings?.layout?.[deviceType] || {};
303
- const themeList = getThemeList(settingState, location);
331
+ // REVOLUTIONARY APPROACH: Use stable selectors instead of raw context access
332
+ // This completely eliminates the infinite loop problem because:
333
+ // 1. selectPublicSettings only returns context.public (no internal state)
334
+ // 2. useSelector memoizes the result - only re-renders if PUBLIC settings actually change
335
+ // 3. configModel reference changes are isolated in context.internal, never seen here
336
+ // 4. No filtering logic needed - separation is built into the machine
337
+ const settingState = useSelector(actor, state => state.context.public);
338
+ const changedSettings = useSelector(actor, state => state.context.internal.changedSettings);
339
+ useSelector(actor, state => state.context.internal.deviceType);
340
+ // Track previous settings to detect meaningful changes
341
+ const prevSettingsRef = useRef();
342
+ useEffect(() => {
343
+ const prevSettings = prevSettingsRef.current;
344
+ const currentSettings = settingState;
345
+ if (prevSettings) {
346
+ const prevString = JSON.stringify(prevSettings);
347
+ const currentString = JSON.stringify(currentSettings);
348
+ const hasChanged = prevString !== currentString;
349
+ console.log('⚡ SettingDrawer - PUBLIC settings update:', {
350
+ hasChanged,
351
+ prevKeys: prevSettings ? Object.keys(prevSettings).length : 0,
352
+ currentKeys: currentSettings ? Object.keys(currentSettings).length : 0
353
+ });
354
+ if (hasChanged) {
355
+ console.log('🔍 What changed in PUBLIC settings:', {
356
+ changedKeys: Object.keys(currentSettings).filter(key => {
357
+ const prevValue = JSON.stringify(prevSettings[key]);
358
+ const currentValue = JSON.stringify(currentSettings[key]);
359
+ return prevValue !== currentValue;
360
+ })
361
+ });
362
+ }
363
+ }
364
+ prevSettingsRef.current = JSON.parse(JSON.stringify(currentSettings));
365
+ }, [settingState]);
366
+ const currentRoute = routePattern || location?.pathname || '/';
367
+ // Use activeDeviceTab instead of isMobile for device type
368
+ const deviceType = activeDeviceTab;
369
+ // Calculate the default value for applyToAllRoutes based on what overrides exist
370
+ const defaultApplyToAllRoutes = React.useMemo(() => {
371
+ return getDefaultApplyToAllRoutes(settingState, currentRoute, deviceType);
372
+ }, [settingState, currentRoute, deviceType]);
373
+ // Update applyToAllRoutes when route or device changes
374
+ useEffect(() => {
375
+ setApplyToAllRoutes(defaultApplyToAllRoutes);
376
+ }, [defaultApplyToAllRoutes]);
377
+ // Pass settingState directly - child components will use getSettingValue internally
378
+ // with the applyToAllRoutes prop to get the correct scoped values
379
+ const settingsForComponents = settingState;
380
+ // Helper to get theme value with overrides applied
381
+ const getThemeValue = () => getSettingValue(settingState, 'theme', applyToAllRoutes, currentRoute, deviceType, 'default');
382
+ // Helper to get font family value with overrides applied
383
+ const getFontFamilyValue = () => getSettingValue(settingState, 'fontFamily', applyToAllRoutes, currentRoute, deviceType, 'Poppins, sans-serif');
384
+ const themeList = getThemeList(settingState, location, routePattern);
304
385
  /**
305
386
  * Modify settings
306
387
  * @param key
307
388
  * @param value
308
389
  */
309
390
  const handleSettingChange = (key, value) => {
310
- // If the key is a layout setting, update it in the device-specific path
311
- if (key.startsWith('layout.')) {
312
- const layoutKey = key.replace('layout.', '');
313
- actor.send({
314
- type: 'UPDATE',
315
- value: {
316
- [`routeSettings.${currentRoute}.layout.${deviceType}.${layoutKey}`]: value
317
- }
391
+ // Menu visibility and applyToAllRoutes are always global
392
+ const isGlobalSetting = key === 'hiddenMenuKeys' || key === 'hiddenMenuCategories' || key === 'applyToAllRoutes';
393
+ // If this is a global setting, save as global
394
+ if (isGlobalSetting) {
395
+ console.log('🌐 Sending GLOBAL setting to actor:', {
396
+ key,
397
+ value
318
398
  });
319
- } else {
320
399
  actor.send({
321
- type: 'UPDATE',
400
+ type: 'UISETTING_UPDATE',
322
401
  value: {
323
402
  [key]: value
324
403
  }
325
404
  });
405
+ return;
406
+ }
407
+ // Extract the setting name from the key
408
+ // Key is just "settingName" (no uilayout wrapper)
409
+ const settingName = key;
410
+ // Build the correct bracket pattern based on applyToAllRoutes
411
+ let fullKey;
412
+ if (applyToAllRoutes) {
413
+ // Apply to all routes: [device].settingName
414
+ fullKey = `[${deviceType}].${settingName}`;
415
+ } else {
416
+ // Route-specific: [route][device].settingName
417
+ fullKey = `[${currentRoute}][${deviceType}].${settingName}`;
326
418
  }
419
+ console.log('📤 Sending SCOPED setting to actor:', {
420
+ fullKey,
421
+ value,
422
+ eventType: 'UISETTING_UPDATE'
423
+ });
424
+ actor.send({
425
+ type: 'UISETTING_UPDATE',
426
+ value: {
427
+ [fullKey]: value
428
+ }
429
+ });
327
430
  };
328
431
  const handleCopySettings = async () => {
329
432
  if (!isBrowser()) return;
@@ -339,38 +442,152 @@ const SettingDrawer = props => {
339
442
  const handleCopyModifiedSettings = async () => {
340
443
  if (!isBrowser()) return;
341
444
  try {
342
- const modifiedSettings = compareAndSaveSettingsDifferences(settingState, defaultSettings);
343
- const settings = JSON.stringify(modifiedSettings, null, 2);
344
- await navigator.clipboard.writeText(settings);
345
- setCopiedModified(true);
346
- setTimeout(() => setCopiedModified(false), 2000);
445
+ const settingsCookie = document.cookie.split(';').find(cookie => cookie.startsWith(' settings='));
446
+ const settingsValue = settingsCookie.split('=')[1];
447
+ const decodedComponent = decodeURIComponent(settingsValue);
448
+ const parsedSettings = JSON.parse(atob(decodedComponent));
449
+ console.log('Settings in cookies : ', parsedSettings);
450
+ navigator.clipboard.writeText(JSON.stringify(parsedSettings, null, 2));
347
451
  } catch (err) {
348
452
  console.error('Failed to copy modified settings:', err);
349
453
  }
350
454
  };
351
- useEffect(() => {
352
- // Only update if there's a meaningful difference
353
- if (JSON.stringify(settingState) !== JSON.stringify(settings)) {
354
- oldSetting = {
355
- ...defaultSettings,
356
- ...settings
357
- };
358
- if (isBrowser()) {
359
- // Compare settings with oldSetting to determine if initialization is needed
360
- if (JSON.stringify(settings) !== JSON.stringify(oldSetting)) {
361
- initState(settings, onSettingChange, props.publicPath);
455
+ const handleSaveSettings = async () => {
456
+ if (!isBrowser()) return;
457
+ try {
458
+ // Transform changedSettings to wrap in uilayout structure for backend
459
+ const wrappedSettings = {};
460
+ Object.keys(changedSettings || {}).forEach(key => {
461
+ const value = changedSettings[key];
462
+ // Skip route paths
463
+ if (key.startsWith('/')) return;
464
+ // Check if it's a bracket key like [device].settingName or [route][device].settingName
465
+ if (key.startsWith('[')) {
466
+ // Extract bracket pattern and setting name
467
+ // e.g., "[desktop].theme" or "[/route][desktop].navTheme"
468
+ const match = key.match(/^(\[.+?\](?:\[.+?\])?)\.(.+)$/);
469
+ if (match) {
470
+ const bracketKey = match[1]; // e.g., "[desktop]" or "[/route][desktop]"
471
+ const settingName = match[2]; // e.g., "theme"
472
+ // Wrap in uilayout structure: [desktop].uilayout.theme
473
+ const wrappedKey = `${bracketKey}.uilayout.${settingName}`;
474
+ wrappedSettings[wrappedKey] = value;
475
+ } else {
476
+ // If pattern doesn't match, keep as is
477
+ wrappedSettings[key] = value;
478
+ }
479
+ } else {
480
+ // Non-bracket keys (like hiddenMenuKeys) - keep as is
481
+ wrappedSettings[key] = value;
362
482
  }
483
+ });
484
+ // Prepare mutation variables
485
+ const variables = {
486
+ editingUri: resource,
487
+ settings: wrappedSettings
488
+ };
489
+ const response = await writeSettingsMutation({
490
+ variables
491
+ });
492
+ if (response.data) {
493
+ console.log('Settings saved successfully');
494
+ // Reset XState changedSettings (this will disable save button)
495
+ actor.send({
496
+ type: 'UISETTING_RESET_CHANGED_SETTINGS'
497
+ });
498
+ }
499
+ } catch (err) {
500
+ console.error('Failed to save settings:', err);
501
+ }
502
+ };
503
+ const handleResetSettings = async () => {
504
+ if (!isBrowser()) return;
505
+ const confirmed = window.confirm('Are you sure you want to reset all settings? This will delete all customizations from database and cookies, then reload the page.');
506
+ if (!confirmed) return;
507
+ try {
508
+ console.log('🔥 Resetting all settings...');
509
+ // Step 1: Delete uiSettings from database using the resetApplicationSettings mutation
510
+ try {
511
+ await resetApplicationSettings({
512
+ variables: {
513
+ appId: config.APPLICATION_ID
514
+ }
515
+ });
516
+ console.log('✅ Database uiSettings deleted');
517
+ } catch (dbErr) {
518
+ console.error('⚠️ Failed to delete database uiSettings:', dbErr);
519
+ // Continue anyway to clear cookies
520
+ }
521
+ // Step 2: Clear cookies by sending DELETE request
522
+ try {
523
+ const response = await fetch('/resources/settings', {
524
+ method: 'DELETE',
525
+ headers: {
526
+ 'Content-Type': 'application/json'
527
+ }
528
+ });
529
+ const result = await response.json();
530
+ console.log('✅ Settings cookies cleared');
531
+ } catch (cookieErr) {
532
+ console.error('⚠️ Failed to clear cookies:', cookieErr);
533
+ }
534
+ // Step 3: Reset XState machine
535
+ actor.send({
536
+ type: 'UISETTING_RESET_CHANGED_SETTINGS'
537
+ });
538
+ } catch (err) {
539
+ console.error('❌ Error resetting settings:', err);
540
+ alert('Failed to reset settings. Please try again.');
541
+ }
542
+ };
543
+ useEffect(() => {
544
+ // REVOLUTIONARY: No filtering needed! settingState already contains ONLY public settings
545
+ // The XState machine now has a separated structure:
546
+ // - context.public = user-facing settings (what we're using)
547
+ // - context.internal = machine state (never exposed)
548
+ //
549
+ // This completely eliminates the loop problem at the architectural level.
550
+ if (isBrowser()) {
551
+ const hasUrlParams = !!window.location.search;
552
+ console.log('🔄 SettingDrawer useEffect - PUBLIC settings:', {
553
+ hasUrlParams,
554
+ hasInitialized: hasInitializedRef.current,
555
+ settingsKeys: settingState ? Object.keys(settingState).length : 0
556
+ });
557
+ // Only call initState on first mount OR if there are URL params
558
+ const shouldInit = !hasInitializedRef.current || hasUrlParams;
559
+ if (shouldInit) {
560
+ console.log('🔥 Calling initState:', {
561
+ reason: !hasInitializedRef.current ? 'first mount' : 'has URL params'
562
+ });
563
+ initState(settingState, onSettingChange, props.publicPath, undefined);
564
+ hasInitializedRef.current = true;
565
+ } else {
566
+ console.log('⏭️ Skipping initState - already initialized and no URL params');
363
567
  }
364
568
  }
365
- }, [settings]); // Only depend on settings
569
+ }, [settingState, props.publicPath]); // settingState only changes when PUBLIC settings change
570
+ // Update activeDeviceTab when isMobile changes
571
+ useEffect(() => {
572
+ setActiveDeviceTab(isMobile ? 'mobile' : 'desktop');
573
+ }, [isMobile]);
574
+ // Update machine when device tab changes
575
+ useEffect(() => {
576
+ if (actor) {
577
+ actor.send({
578
+ type: 'UISETTING_UPDATE_DEVICE_TYPE',
579
+ deviceType: activeDeviceTab
580
+ });
581
+ }
582
+ }, [activeDeviceTab, actor]);
366
583
  const currentPathname = isBrowser() ? window.location.pathname : pathname || '';
367
584
  return jsx(Fragment, {
368
- children: settings?.showSettingPanel && jsxs(Fragment, {
585
+ children: settingState?.showSettingPanel && jsxs(Fragment, {
369
586
  children: [jsx("button", {
370
587
  ref: btnRef,
371
588
  onClick: () => setShow(!show),
372
589
  style: {
373
- backgroundColor: settings?.primaryColor,
590
+ backgroundColor: settingState?.primaryColor,
374
591
  display: 'flex',
375
592
  alignItems: 'center',
376
593
  justifyContent: 'center'
@@ -399,6 +616,107 @@ const SettingDrawer = props => {
399
616
  fontSize: '36px'
400
617
  })
401
618
  })]
619
+ }), jsxs("div", {
620
+ className: "mt-8 pb-8 border-b border-black dark:border-[#2a2b2a]",
621
+ children: [jsx(MenuVisibilitySettings, {
622
+ fullMenuData: fullMenuData,
623
+ settings: settingState,
624
+ changeSetting: handleSettingChange
625
+ }), jsxs("div", {
626
+ className: "flex items-center justify-between",
627
+ children: [jsxs("div", {
628
+ children: [jsx("p", {
629
+ className: "text-sm text-black dark:text-white font-medium",
630
+ children: "Apply to All Routes"
631
+ }), jsx("p", {
632
+ className: "text-xs text-gray-600 dark:text-gray-400",
633
+ children: "When enabled, route-specific settings will apply to all routes instead of just the current route"
634
+ })]
635
+ }), jsxs("label", {
636
+ className: "relative inline-flex items-center cursor-pointer",
637
+ children: [jsx("input", {
638
+ type: "checkbox",
639
+ className: "sr-only peer",
640
+ checked: applyToAllRoutes,
641
+ onChange: e => setApplyToAllRoutes(e.target.checked)
642
+ }), jsx("div", {
643
+ className: "w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"
644
+ })]
645
+ })]
646
+ }), jsx("div", {
647
+ className: "mt-4",
648
+ children: jsxs("div", {
649
+ className: "flex mb-4",
650
+ children: [jsx("button", {
651
+ className: `flex-1 py-2 px-4 text-sm font-medium transition-colors duration-200 rounded-l-lg border border-r-0 border-gray-300 ${activeDeviceTab === 'desktop' ? 'bg-blue-500 text-white border-blue-500' : 'bg-white text-gray-700 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700'}`,
652
+ onClick: () => setActiveDeviceTab('desktop'),
653
+ children: jsxs("span", {
654
+ className: "flex items-center justify-center",
655
+ children: [jsxs("svg", {
656
+ xmlns: "http://www.w3.org/2000/svg",
657
+ width: "16",
658
+ height: "16",
659
+ viewBox: "0 0 24 24",
660
+ fill: "none",
661
+ stroke: "currentColor",
662
+ strokeWidth: "2",
663
+ strokeLinecap: "round",
664
+ strokeLinejoin: "round",
665
+ className: "mr-1",
666
+ children: [jsx("rect", {
667
+ x: "2",
668
+ y: "3",
669
+ width: "20",
670
+ height: "14",
671
+ rx: "2",
672
+ ry: "2"
673
+ }), jsx("line", {
674
+ x1: "8",
675
+ y1: "21",
676
+ x2: "16",
677
+ y2: "21"
678
+ }), jsx("line", {
679
+ x1: "12",
680
+ y1: "17",
681
+ x2: "12",
682
+ y2: "21"
683
+ })]
684
+ }), t('desktop_tab')]
685
+ })
686
+ }), jsx("button", {
687
+ className: `flex-1 py-2 px-4 text-sm font-medium transition-colors duration-200 rounded-r-lg border border-gray-300 ${activeDeviceTab === 'mobile' ? 'bg-blue-500 text-white border-blue-500' : 'bg-white text-gray-700 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700'}`,
688
+ onClick: () => setActiveDeviceTab('mobile'),
689
+ children: jsxs("span", {
690
+ className: "flex items-center justify-center",
691
+ children: [jsxs("svg", {
692
+ xmlns: "http://www.w3.org/2000/svg",
693
+ width: "16",
694
+ height: "16",
695
+ viewBox: "0 0 24 24",
696
+ fill: "none",
697
+ stroke: "currentColor",
698
+ strokeWidth: "2",
699
+ strokeLinecap: "round",
700
+ strokeLinejoin: "round",
701
+ className: "mr-1",
702
+ children: [jsx("rect", {
703
+ x: "5",
704
+ y: "2",
705
+ width: "14",
706
+ height: "20",
707
+ rx: "2",
708
+ ry: "2"
709
+ }), jsx("line", {
710
+ x1: "12",
711
+ y1: "18",
712
+ x2: "12",
713
+ y2: "18"
714
+ })]
715
+ }), t('mobile_tab')]
716
+ })
717
+ })]
718
+ })
719
+ })]
402
720
  }), jsxs("div", {
403
721
  className: "mt-8 pb-8 border-b border-black dark:border-[#2a2b2a]",
404
722
  children: [jsx("p", {
@@ -408,7 +726,9 @@ const SettingDrawer = props => {
408
726
  children: jsx(CheckBoxTheme, {
409
727
  list: themeList.themeList,
410
728
  changeSetting: handleSettingChange,
411
- settings: settingState,
729
+ settings: settingsForComponents,
730
+ location: location,
731
+ applyToAllRoutes: applyToAllRoutes,
412
732
  ...props
413
733
  })
414
734
  }), jsxs("div", {
@@ -417,7 +737,7 @@ const SettingDrawer = props => {
417
737
  className: "text-black dark:text-white",
418
738
  children: t('Select Theme')
419
739
  }), jsx(ThemeSelector, {
420
- value: settingState.theme || 'default',
740
+ value: getThemeValue(),
421
741
  onChange: value => handleSettingChange('theme', value),
422
742
  options: themeOptions
423
743
  })]
@@ -429,7 +749,10 @@ const SettingDrawer = props => {
429
749
  }), jsx(ThemeColor, {
430
750
  color: colorList || themeList.colorList[navTheme === 'realDark' ? 'dark' : 'light'],
431
751
  changeSetting: handleSettingChange,
432
- settings: settingState
752
+ settings: settingsForComponents,
753
+ location: location,
754
+ routePattern: routePattern,
755
+ applyToAllRoutes: applyToAllRoutes
433
756
  })]
434
757
  }), jsxs("div", {
435
758
  className: "mt-8",
@@ -437,7 +760,7 @@ const SettingDrawer = props => {
437
760
  className: "text-black dark:text-white",
438
761
  children: t('Font Family')
439
762
  }), jsx(WebFontsSelector, {
440
- value: settingState.fontFamily || 'Poppins, sans-serif',
763
+ value: getFontFamilyValue(),
441
764
  onChange: value => handleSettingChange('fontFamily', value),
442
765
  options: fontOptions
443
766
  })]
@@ -445,38 +768,40 @@ const SettingDrawer = props => {
445
768
  }), jsxs("div", {
446
769
  className: "mt-8 pb-8 border-b border-black dark:border-[#2a2b2a]",
447
770
  children: [jsx(NavigationModes, {
448
- settings: settingState,
771
+ settings: settingsForComponents,
449
772
  changeSetting: handleSettingChange,
773
+ applyToAllRoutes: applyToAllRoutes,
450
774
  ...props
451
775
  }), jsx("div", {
452
776
  className: "mt-8",
453
777
  children: jsx(LayoutChange, {
454
- settings: settingState,
778
+ settings: settingsForComponents,
455
779
  changeSetting: handleSettingChange,
780
+ applyToAllRoutes: applyToAllRoutes,
456
781
  ...props
457
782
  })
458
783
  }), jsx("div", {
459
784
  className: "mt-8",
460
785
  children: jsx(InvitationSettings, {
461
- settings: settingState,
786
+ settings: settingsForComponents,
462
787
  changeSetting: handleSettingChange,
463
- location: location
788
+ location: location,
789
+ routePattern: routePattern,
790
+ applyToAllRoutes: applyToAllRoutes
464
791
  })
465
792
  })]
466
- }), jsxs("div", {
793
+ }), jsx("div", {
467
794
  className: "mt-8 pb-8 border-b border-black dark:border-[#2a2b2a]",
468
- children: [jsx("p", {
469
- className: "mb-2 text-black dark:text-white",
470
- children: t('regional_rettings')
471
- }), jsx(RegionalSettings, {
472
- settings: settingState,
795
+ children: jsx(RegionalSettings, {
796
+ settings: settingsForComponents,
473
797
  changeSetting: handleSettingChange,
798
+ applyToAllRoutes: applyToAllRoutes,
474
799
  ...props
475
- })]
800
+ })
476
801
  }), jsx("div", {
477
802
  className: "mt-8 pb-8 border-b border-black dark:border-[#2a2b2a]",
478
803
  children: jsx(AdditionalSettingsSlot, {
479
- settings: settingState,
804
+ settings: settingsForComponents,
480
805
  onSettingsChange: handleSettingChange,
481
806
  pathname: currentPathname
482
807
  })
@@ -488,6 +813,13 @@ const SettingDrawer = props => {
488
813
  }), jsxs("div", {
489
814
  className: "flex flex-wrap gap-2 mt-2",
490
815
  children: [jsx("button", {
816
+ disabled: !changedSettings || Object.keys(changedSettings).length === 0,
817
+ className: clsx('flex-1 min-w-[140px] sm:flex-none sm:w-auto px-4 py-2 rounded-lg border text-sm font-medium shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2', !changedSettings || Object.keys(changedSettings).length === 0 ? 'border-gray-300 text-gray-500 bg-gray-50 cursor-not-allowed dark:border-gray-600 dark:text-gray-400 dark:bg-gray-800' : 'border-yellow-300 text-yellow-700 bg-yellow-50 hover:bg-yellow-100 active:bg-yellow-200 dark:border-yellow-600 dark:text-yellow-200 dark:bg-yellow-900/50 dark:hover:bg-yellow-900'),
818
+ onClick: handleSaveSettings,
819
+ children: jsx("span", {
820
+ children: !changedSettings || Object.keys(changedSettings).length === 0 ? 'No Changes' : 'Save Settings'
821
+ })
822
+ }), jsx("button", {
491
823
  className: clsx('flex-1 min-w-[140px] sm:flex-none sm:w-auto px-4 py-2 rounded-lg border text-sm font-medium shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2', copied ? 'border-green-300 text-green-700 bg-green-50 dark:border-green-600 dark:text-green-200 dark:bg-green-900' : 'border-yellow-300 text-yellow-700 bg-yellow-50 hover:bg-yellow-100 active:bg-yellow-200 dark:border-yellow-600 dark:text-yellow-200 dark:bg-yellow-900/50 dark:hover:bg-yellow-900'),
492
824
  onClick: handleCopySettings,
493
825
  children: copied ? jsxs(Fragment, {
@@ -519,6 +851,12 @@ const SettingDrawer = props => {
519
851
  children: "Copy Modified Settings"
520
852
  })]
521
853
  })
854
+ }), jsx("button", {
855
+ className: "flex-1 min-w-[140px] sm:flex-none sm:w-auto px-4 py-2 rounded-lg border border-red-300 text-red-700 bg-red-50 hover:bg-red-100 active:bg-red-200 dark:border-red-600 dark:text-red-200 dark:bg-red-900/50 dark:hover:bg-red-900 text-sm font-medium shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500",
856
+ onClick: handleResetSettings,
857
+ children: jsx("span", {
858
+ children: "\uD83D\uDD04 Reset All Settings"
859
+ })
522
860
  })]
523
861
  })]
524
862
  })]