@admin-layout/client 12.0.16-alpha.62 → 12.0.16-alpha.67

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ConfigurationModelWrapper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigurationModelWrapper.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ConfigurationModelWrapper.test.ts"],"names":[],"mappings":""}
@@ -1,7 +1,62 @@
1
+ /**
2
+ * Cookie configuration for storing user settings
3
+ * - Max age: 30 days
4
+ * - Client-side accessible for dynamic UI updates
5
+ * - Domain-scoped in production for subdomain sharing
6
+ */
1
7
  export declare const settingsCookie: import("@remix-run/server-runtime").Cookie;
8
+ /**
9
+ * Fetches page settings from GraphQL backend with device/route-specific overrides
10
+ *
11
+ * @param request - The incoming HTTP request (used for forwarding cookies and headers)
12
+ * @param overrideIdentifier - Optional override identifier (e.g., "[mobile]", "[desktop]", "[/dashboard][mobile]")
13
+ * @returns GraphQL query result containing merged settings with overrides applied
14
+ *
15
+ * @remarks
16
+ * - The GraphQL backend implements Redis caching for performance
17
+ * - Settings are returned in a flat structure with bracket notation for overrides
18
+ * - Example structure: { logo: '...', theme: '...', '[mobile]': { ... }, '[/dashboard][mobile]': { ... } }
19
+ */
2
20
  export declare function fetchPageSettingsWithFetch(request: Request, overrideIdentifier?: string): Promise<{
3
21
  data: any;
4
22
  }>;
23
+ /**
24
+ * Universal settings loader utility for both Remix loaders and actions
25
+ *
26
+ * @param params - Object containing request, context, and params
27
+ * @param params.request - The incoming HTTP request
28
+ * @param params.context - Optional Remix load context
29
+ * @param params.params - Optional route parameters
30
+ *
31
+ * @returns Object containing merged settings and cookie string
32
+ * @returns settings - Fully merged settings (GraphQL + env + cookies + request payload)
33
+ * @returns setCookie - Cookie string to set in response headers
34
+ *
35
+ * @remarks
36
+ * This utility handles both loader (GET) and action (POST/PUT/DELETE) requests:
37
+ * - **Loaders (GET)**: Fetches and merges settings from GraphQL, env, and cookies
38
+ * - **Actions (POST/PUT)**: Additionally processes request payload and updates cookies
39
+ *
40
+ * Settings merge order (later sources override earlier ones):
41
+ * 1. GraphQL backend settings (with device overrides)
42
+ * 2. Environment variable settings
43
+ * 3. Cookie settings (user preferences)
44
+ * 4. Request payload (only for actions)
45
+ *
46
+ * @example
47
+ * // In a Remix loader
48
+ * export const loader = async ({ request }) => {
49
+ * const { settings, setCookie } = await settingsLoaderUtil({ request });
50
+ * return json({ settings }, { headers: { 'Set-Cookie': setCookie } });
51
+ * };
52
+ *
53
+ * @example
54
+ * // In a Remix action
55
+ * export const action = async ({ request }) => {
56
+ * const { settings, setCookie } = await settingsLoaderUtil({ request });
57
+ * return json(settings, { headers: { 'Set-Cookie': setCookie } });
58
+ * };
59
+ */
5
60
  export declare function settingsLoaderUtil({ request, context, params }: {
6
61
  request: any;
7
62
  context: any;
@@ -10,4 +65,5 @@ export declare function settingsLoaderUtil({ request, context, params }: {
10
65
  settings: any;
11
66
  setCookie: string;
12
67
  }>;
68
+ export declare function getDeviceType(userAgent: string): 'mobile' | 'desktop';
13
69
  //# sourceMappingURL=UpdateSettings.server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UpdateSettings.server.d.ts","sourceRoot":"","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,cAAc,4CAMzB,CAAC;AAIH,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,kBAAkB,CAAC,EAAE,MAAM;;GAqF7F;AAED,wBAAsB,kBAAkB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE;;;;CAAA;;;GAqGpE"}
1
+ {"version":3,"file":"UpdateSettings.server.d.ts","sourceRoot":"","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,eAAO,MAAM,cAAc,4CAMzB,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,kBAAkB,CAAC,EAAE,MAAM;;GAuF7F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE;;;;CAAA;;;GA8GpE;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAQrE"}
@@ -1,14 +1,31 @@
1
- import {createCookie}from'@remix-run/node';import {ConfigCollectionName,ConfigFragmentName,ConfigurationSchemaId}from'common';import {GetPageSettingsDocument}from'common/graphql';import {merge}from'lodash-es';import {print}from'graphql';import {config}from'../../config/env-config.js';import'../../config/defaultSettings.js';import'react';import'@cdm-logger/client';import {compareAndSaveSettingsDifferences}from'../../utils/settingsDifference.js';import {generateCdecodeUri,DEFAULT_CONTRIBUTION_TENANT_ID}from'@adminide-stack/core';const settingsCookie = createCookie('settings', {
1
+ import {createCookie}from'@remix-run/node';import {ConfigCollectionName,ConfigFragmentName,ConfigurationSchemaId}from'common';import {GetPageSettingsDocument}from'common/graphql';import {merge}from'lodash-es';import {print}from'graphql';import {parseEnvLayoutSettings}from'../../utils/parseEnvUiLayoutSettings.js';import {config}from'../../config/env-config.js';import'../../config/defaultSettings.js';import {generateCdecodeUri,DEFAULT_CONTRIBUTION_TENANT_ID}from'@adminide-stack/core';/**
2
+ * Cookie configuration for storing user settings
3
+ * - Max age: 30 days
4
+ * - Client-side accessible for dynamic UI updates
5
+ * - Domain-scoped in production for subdomain sharing
6
+ */
7
+ const settingsCookie = createCookie('settings', {
2
8
  maxAge: 60 * 60 * 24 * 30, // 1 month
3
9
  httpOnly: false, // Allow client-side access
4
10
  sameSite: 'lax',
5
11
  path: '/', // Ensure cookie is available for all paths
6
12
  domain: config.isProd ? config.APP_DOMAIN : undefined,
7
13
  });
8
- // Direct fetch-based implementation for fetching settings from GraphQL
9
- // Note: GraphQL backend already has Redis caching, so no need for additional caching here
14
+ /**
15
+ * Fetches page settings from GraphQL backend with device/route-specific overrides
16
+ *
17
+ * @param request - The incoming HTTP request (used for forwarding cookies and headers)
18
+ * @param overrideIdentifier - Optional override identifier (e.g., "[mobile]", "[desktop]", "[/dashboard][mobile]")
19
+ * @returns GraphQL query result containing merged settings with overrides applied
20
+ *
21
+ * @remarks
22
+ * - The GraphQL backend implements Redis caching for performance
23
+ * - Settings are returned in a flat structure with bracket notation for overrides
24
+ * - Example structure: { logo: '...', theme: '...', '[mobile]': { ... }, '[/dashboard][mobile]': { ... } }
25
+ */
10
26
  async function fetchPageSettingsWithFetch(request, overrideIdentifier) {
11
27
  console.log(`🔍 Fetching settings from GraphQL (Redis-cached backend)...`);
28
+ // Generate resource URI for the application settings
12
29
  const resource = generateCdecodeUri(DEFAULT_CONTRIBUTION_TENANT_ID, {
13
30
  resourceType: ConfigCollectionName.Applications,
14
31
  resourceId: config.APPLICATION_ID,
@@ -17,27 +34,26 @@ async function fetchPageSettingsWithFetch(request, overrideIdentifier) {
17
34
  const url = process.env.GRAPHQL_URL || 'http://localhost:8080/graphql';
18
35
  // Convert GraphQL DocumentNode to string
19
36
  const query = print(GetPageSettingsDocument);
20
- // Prepare options with optional overrides
37
+ // Prepare GraphQL query options
21
38
  const options = {
22
39
  schemaId: ConfigurationSchemaId.UiLayout,
23
40
  target: 2 /* ConfigurationTarget.APPLICATION */,
24
41
  configKey: 'uilayout',
25
42
  };
26
- // Add overrides if provided
43
+ // Add device/route overrides if provided
27
44
  if (overrideIdentifier) {
28
45
  options.overrides = {
46
+ strict: true,
29
47
  overrideIdentifier,
30
48
  };
31
49
  }
32
- // Add timestamp to force cache invalidation
33
50
  const variables = {
34
51
  resourceUri: resource,
35
52
  options,
36
- // _timestamp: Date.now(), // Force cache bust
37
53
  };
38
54
  console.log('🔍 Fetching settings with direct fetch to:', url);
39
- console.log('🔍 Query variables:', JSON.stringify(variables, null, 2));
40
- // Forward cookies from the incoming request
55
+ // console.log('🔍 Query variables:', JSON.stringify(variables, null, 2));
56
+ // Forward cookies and headers from the incoming request
41
57
  const headers = {
42
58
  'Content-Type': 'application/json',
43
59
  'Cache-Control': 'no-cache, no-store, must-revalidate',
@@ -78,85 +94,133 @@ async function fetchPageSettingsWithFetch(request, overrideIdentifier) {
78
94
  throw error;
79
95
  }
80
96
  }
97
+ /**
98
+ * Universal settings loader utility for both Remix loaders and actions
99
+ *
100
+ * @param params - Object containing request, context, and params
101
+ * @param params.request - The incoming HTTP request
102
+ * @param params.context - Optional Remix load context
103
+ * @param params.params - Optional route parameters
104
+ *
105
+ * @returns Object containing merged settings and cookie string
106
+ * @returns settings - Fully merged settings (GraphQL + env + cookies + request payload)
107
+ * @returns setCookie - Cookie string to set in response headers
108
+ *
109
+ * @remarks
110
+ * This utility handles both loader (GET) and action (POST/PUT/DELETE) requests:
111
+ * - **Loaders (GET)**: Fetches and merges settings from GraphQL, env, and cookies
112
+ * - **Actions (POST/PUT)**: Additionally processes request payload and updates cookies
113
+ *
114
+ * Settings merge order (later sources override earlier ones):
115
+ * 1. GraphQL backend settings (with device overrides)
116
+ * 2. Environment variable settings
117
+ * 3. Cookie settings (user preferences)
118
+ * 4. Request payload (only for actions)
119
+ *
120
+ * @example
121
+ * // In a Remix loader
122
+ * export const loader = async ({ request }) => {
123
+ * const { settings, setCookie } = await settingsLoaderUtil({ request });
124
+ * return json({ settings }, { headers: { 'Set-Cookie': setCookie } });
125
+ * };
126
+ *
127
+ * @example
128
+ * // In a Remix action
129
+ * export const action = async ({ request }) => {
130
+ * const { settings, setCookie } = await settingsLoaderUtil({ request });
131
+ * return json(settings, { headers: { 'Set-Cookie': setCookie } });
132
+ * };
133
+ */
81
134
  async function settingsLoaderUtil({ request, context, params }) {
82
- const cookieHeader = request.headers.get('Cookie');
83
- // Detect device type from User-Agent (simplified detection)
135
+ // 1. Detect device type from User-Agent header
84
136
  const userAgent = request.headers.get('User-Agent') || '';
85
- const isMobile = /mobile|android|iphone|ipad|ipod/i.test(userAgent);
86
- const deviceType = isMobile ? 'mobile' : 'desktop';
87
- // Fetch ALL settings for this device upfront (includes all route overrides)
88
- // Pass device-level override to get all route-specific overrides for this device
137
+ const deviceType = getDeviceType(userAgent);
138
+ // 2. Fetch settings from GraphQL backend with device-specific overrides
139
+ // Override identifier format: "[desktop]" or "[mobile]"
89
140
  const overrideIdentifier = `[${deviceType}]`;
90
141
  const result = await fetchPageSettingsWithFetch(request, overrideIdentifier);
91
- console.log('RESULT=========>', JSON.stringify(result));
92
- const pageSettings = result?.data?.pageSettings?.settings
142
+ // console.log('ddRESULT=========>', JSON.stringify(result));
143
+ const pageBackendSettings = result?.data?.pageSettings?.settings
93
144
  ? JSON.parse(JSON.stringify(result?.data?.pageSettings?.settings))
94
145
  : null;
95
- // pageSettings now contains flat structure:
96
- // {
97
- // logo: '...', theme: '...', ...base settings,
98
- // [desktop]: { overrides for desktop },
99
- // [/dashboard][mobile]: { overrides for /dashboard on mobile },
100
- // ... etc
101
- // }
102
- // Add required keys that might not be in backend
103
- let pageDefaultSettings = {
104
- ...pageSettings,
105
- };
106
- console.log('🔍 DEBUG - final pageDefaultSettings:', JSON.stringify(pageDefaultSettings, null, 2));
107
- // Fallback to config.LAYOUT_SETTINGS if backend returns empty
108
- const settingsKeys = Object.keys(pageDefaultSettings);
109
- const hasOnlyDefaultKeys = settingsKeys.length === 2 &&
110
- settingsKeys.includes('hiddenMenuKeys') &&
111
- settingsKeys.includes('hiddenMenuCategories');
112
- if (hasOnlyDefaultKeys && config.LAYOUT_SETTINGS) {
113
- const fallbackSettings = typeof config.LAYOUT_SETTINGS === 'string'
114
- ? JSON.parse(config.LAYOUT_SETTINGS)
115
- : { ...config.LAYOUT_SETTINGS };
116
- pageDefaultSettings = {
117
- ...fallbackSettings,
118
- };
146
+ // 3. Parse request payload (only available for POST/PUT requests in actions)
147
+ let requestPayload = {};
148
+ try {
149
+ // Clone request to avoid "body already read" errors
150
+ const clonedRequest = request.clone();
151
+ const contentType = request.headers.get('content-type');
152
+ // Only parse JSON for non-GET requests with JSON content-type
153
+ if (request.method !== 'GET' && contentType?.includes('application/json')) {
154
+ const jsonData = await clonedRequest.json();
155
+ requestPayload = jsonData?.config || {};
156
+ }
157
+ }
158
+ catch (error) {
159
+ // Expected for GET requests (loaders) - no payload to parse
160
+ console.log('ℹ️ No request payload (likely a loader call)');
161
+ requestPayload = {};
119
162
  }
120
- // Parse cookie settings
121
- let settings;
163
+ // console.log('🔍 DEBUG - pageBackendSettings:', JSON.stringify(pageBackendSettings, null, 2));
164
+ // 4. Parse environment-based layout settings (fallback/default values)
165
+ const envLayoutSettings = parseEnvLayoutSettings();
166
+ // 5. Parse and validate existing cookie settings
167
+ const cookieHeader = request.headers.get('Cookie');
168
+ let existingCookieSettings;
169
+ let cookieUpdatedSettings;
122
170
  try {
123
- settings = await settingsCookie.parse(cookieHeader);
124
- // Remove any invalid numeric string keys from cookie
125
- if (settings && typeof settings === 'object') {
126
- Object.keys(settings).forEach((key) => {
171
+ existingCookieSettings = await settingsCookie.parse(cookieHeader);
172
+ // DEFENSIVE: Remove invalid numeric string keys (data corruption prevention)
173
+ if (existingCookieSettings && typeof existingCookieSettings === 'object') {
174
+ Object.keys(existingCookieSettings).forEach((key) => {
175
+ // Remove keys that are numeric strings (like "0", "1", etc.)
127
176
  if (/^\d+$/.test(key)) {
128
- delete settings[key];
177
+ console.warn(`⚠️ Removing invalid numeric key from cookie settings: "${key}" = ${JSON.stringify(existingCookieSettings[key])}`);
178
+ delete existingCookieSettings[key];
129
179
  }
130
180
  });
131
181
  }
182
+ // 6. Merge existing cookie settings with new request payload
183
+ cookieUpdatedSettings = merge({}, existingCookieSettings, requestPayload);
184
+ // Validate merge result - check for numeric key corruption
185
+ const numericKeysAfterMerge = Object.keys(cookieUpdatedSettings).filter((k) => /^\d+$/.test(k));
186
+ if (numericKeysAfterMerge.length > 0) {
187
+ console.error('❌ NUMERIC KEYS APPEARED AFTER MERGE!', numericKeysAfterMerge);
188
+ console.error('❌ Values:', numericKeysAfterMerge.map((k) => `${k}: ${JSON.stringify(cookieUpdatedSettings[k])}`));
189
+ }
132
190
  }
133
191
  catch (error) {
134
- settings = null;
192
+ // If cookie parsing fails, use only the request payload
193
+ console.error('Error parsing settings cookie:', error);
194
+ existingCookieSettings = null;
195
+ cookieUpdatedSettings = requestPayload;
135
196
  }
136
- // Generate default settings if no cookie exists
137
- if (!settings) {
138
- const fullSettings = merge({}, pageDefaultSettings, config.LAYOUT_SETTINGS);
139
- // Only store differences to keep cookie size small
140
- const diffSettings = await compareAndSaveSettingsDifferences(fullSettings, pageDefaultSettings);
141
- const cookie = await settingsCookie.serialize(diffSettings);
142
- return { settings: fullSettings, setCookie: cookie };
197
+ // 7. Merge all settings sources in priority order (later sources override earlier)
198
+ // Priority: GraphQL backend → Environment → Cookie + Request payload
199
+ const fullSettings = merge({}, pageBackendSettings, envLayoutSettings, cookieUpdatedSettings);
200
+ // 8. Validate final merged settings - detect and remove numeric key corruption
201
+ const numericKeysAfterFinalMerge = Object.keys(fullSettings).filter((k) => /^\d+$/.test(k));
202
+ if (numericKeysAfterFinalMerge.length > 0) {
203
+ console.error('❌ NUMERIC KEYS APPEARED AFTER FINAL MERGE!', numericKeysAfterFinalMerge);
204
+ console.error('❌ Values:', numericKeysAfterFinalMerge.map((k) => `${k}: ${JSON.stringify(fullSettings[k])}`));
143
205
  }
144
- // Extract base settings (non-bracket keys) from pageDefaultSettings
145
- const baseSettings = {};
146
- const bracketOverrides = {};
147
- Object.keys(pageDefaultSettings).forEach((key) => {
148
- if (key.startsWith('[') && key.endsWith(']')) {
149
- bracketOverrides[key] = pageDefaultSettings[key];
150
- }
151
- else {
152
- baseSettings[key] = pageDefaultSettings[key];
153
- }
154
- });
155
- // Merge: base settings + config + cookie overrides, then add bracket overrides
156
- const mergedBaseSettings = merge({}, baseSettings, config.LAYOUT_SETTINGS, settings);
157
- const fullSettings = merge({}, mergedBaseSettings, bracketOverrides);
158
- // Refresh the cookie to keep it active
159
- const cookie = await settingsCookie.serialize(settings);
160
- console.log('FULL_SETTINGS', fullSettings);
206
+ // 9. Final cleanup - remove any invalid numeric string keys before returning
207
+ if (fullSettings && typeof fullSettings === 'object') {
208
+ Object.keys(fullSettings).forEach((key) => {
209
+ if (/^\d+$/.test(key)) {
210
+ console.warn(`⚠️ Removing invalid numeric key from fullSettings: "${key}"`);
211
+ delete fullSettings[key];
212
+ }
213
+ });
214
+ }
215
+ // 10. Serialize cookie settings for response headers
216
+ const cookie = await settingsCookie.serialize(cookieUpdatedSettings);
217
+ console.log('✅ FULL_SETTINGS prepared:', Object.keys(fullSettings).length, 'keys');
161
218
  return { settings: fullSettings, setCookie: cookie };
162
- }export{fetchPageSettingsWithFetch,settingsCookie,settingsLoaderUtil};//# sourceMappingURL=UpdateSettings.server.js.map
219
+ }
220
+ function getDeviceType(userAgent) {
221
+ if (!userAgent)
222
+ return 'desktop';
223
+ // Enhanced regex that catches more mobile devices
224
+ const mobileRegex = /mobile|android|iphone|ipad|ipod|blackberry|windows phone|opera mini|iemobile|webos|palm|playbook|kindle|silk|psp|symbian|nokia|nexus|htc|samsung|sony|ericsson|lg|motorola|sgh|sec|sharp|vodafone|benq|sanyo|kyocera|nec|alcatel|denso|jarviss|docomo|emacs|minimo|maemo|blazer|dolfin|dolphin|netfront|openwave|teleca|elaine|bolt|iris|maui|packet|pie|portalmmm|w3c|wig|zte|audiovox|ericsson|kyocera|panasonic|qwerty|sendo|sharp|sie-|sonyericsson|t-mobile|up\.browser|up\.link|verizon|vodafone|wap|winwap|xda|zte/i;
225
+ return mobileRegex.test(userAgent) ? 'mobile' : 'desktop';
226
+ }export{fetchPageSettingsWithFetch,getDeviceType,settingsCookie,settingsLoaderUtil};//# sourceMappingURL=UpdateSettings.server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UpdateSettings.server.js","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.ts"],"sourcesContent":[null],"names":[],"mappings":"qhBAiHqE,MAAA,cAAA,GAAA,YAAA,CAAA,UAAA,EAAA;;;AAqGpE,IAAA,QAAA,EAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"UpdateSettings.server.js","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;AAyCG,IAAA,IAAA,EAAA,GAAA;AACH,IAAA,MAAA,EAAA,MAAA,CAAA,MAAsB,GAAA,MAAA,CAAA,UAAA,GAAA,SAA2B;;AAuFhD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCG,QAAA,WAAA,EAAA,QAAA;AACH,QAAA,OAAA;;;;AAAqE;;;AA8GpE,QAAA,eAAA,EAAA,qCAAA;AAED,QAAA,MAAA,EAAA,UAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=UpdateSettings.server.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdateSettings.server.test.d.ts","sourceRoot":"","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.test.ts"],"names":[],"mappings":""}
@@ -3,4 +3,5 @@ export declare const useLayoutSettings: () => {
3
3
  setSettings: (config: any) => Promise<any>;
4
4
  getSettings: () => Promise<any>;
5
5
  };
6
+ export declare function getClientDeviceType(): 'mobile' | 'desktop';
6
7
  //# sourceMappingURL=useLayoutSettings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useLayoutSettings.d.ts","sourceRoot":"","sources":["../../src/hooks/useLayoutSettings.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,iBAAiB;;0BAgDP,GAAG;;CAmEzB,CAAC"}
1
+ {"version":3,"file":"useLayoutSettings.d.ts","sourceRoot":"","sources":["../../src/hooks/useLayoutSettings.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,iBAAiB;;0BAgDP,GAAG;;CAiEzB,CAAC;AAEF,wBAAgB,mBAAmB,IAAI,QAAQ,GAAG,SAAS,CAY1D"}
@@ -45,9 +45,7 @@ const useLayoutSettings = () => {
45
45
  }
46
46
  }, []);
47
47
  const setSettings = useCallback(async (config) => {
48
- // Settings are now flat (no uilayout wrapper)
49
- const flatConfig = config;
50
- const { colorWeak, contentWidth } = flatConfig;
48
+ const { colorWeak, contentWidth } = config;
51
49
  try {
52
50
  // Handle UI updates for special settings
53
51
  if (settings.contentWidth !== contentWidth) {
@@ -56,18 +54,20 @@ const useLayoutSettings = () => {
56
54
  }
57
55
  }
58
56
  updateColorWeak(!!colorWeak);
59
- const cleanDiffSettings = compareAndSaveSettingsDifferences(flatConfig, settings);
57
+ const cleanDiffSettings = compareAndSaveSettingsDifferences(config, settings);
58
+ if (Object.keys(cleanDiffSettings).length === 0)
59
+ return;
60
60
  const response = await fetch('/resources/settings', {
61
61
  method: 'POST',
62
62
  headers: {
63
63
  'Content-Type': 'application/json',
64
64
  orgName: orgNameFromParams || orgNameFromUrl || '',
65
65
  },
66
- body: JSON.stringify({ config: cleanDiffSettings }),
66
+ body: JSON.stringify({ config: cleanDiffSettings, deviceType: getClientDeviceType() }),
67
67
  });
68
68
  // Check content type before parsing
69
69
  const contentType = response.headers.get('content-type');
70
- console.log('Response content-type:', contentType);
70
+ // console.log('Response content-type:', contentType);
71
71
  if (!response.ok) {
72
72
  const errorText = await response.text();
73
73
  console.error('Server error response:', errorText.substring(0, 500));
@@ -84,7 +84,7 @@ const useLayoutSettings = () => {
84
84
  const result = await response.json();
85
85
  const pageDefaultSettings = result;
86
86
  // Merge the full config with default settings before updating the store
87
- const mergedSettings = merge({}, pageDefaultSettings, flatConfig);
87
+ const mergedSettings = merge({}, pageDefaultSettings, config);
88
88
  dispatch({
89
89
  type: CHANGE_SETTINGS_ACTION,
90
90
  payload: mergedSettings,
@@ -96,4 +96,14 @@ const useLayoutSettings = () => {
96
96
  }
97
97
  }, [dispatch, settings]);
98
98
  return { settings, setSettings, getSettings };
99
- };export{useLayoutSettings};//# sourceMappingURL=useLayoutSettings.js.map
99
+ };
100
+ function getClientDeviceType() {
101
+ if (typeof window === 'undefined') {
102
+ return 'desktop'; // SSR fallback
103
+ }
104
+ const userAgent = window.navigator.userAgent;
105
+ const isMobile = /mobile|android|iphone|ipad|ipod|webos|opera mini|iemobile|windows phone/i.test(userAgent);
106
+ // Also check viewport width as fallback
107
+ const isSmallViewport = window.matchMedia('(max-width: 768px)').matches;
108
+ return isMobile || isSmallViewport ? 'mobile' : 'desktop';
109
+ }export{getClientDeviceType,useLayoutSettings};//# sourceMappingURL=useLayoutSettings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useLayoutSettings.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useLayoutSettings.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/lib/index.js CHANGED
@@ -1 +1 @@
1
- export{SearchBarBehavior}from'./interfaces/settings.js';export{APPLICATION_ERROR_SLOT_FILL,CHANGE_LANGUAGE,CHANGE_SETTINGS_ACTION}from'./constants/constants.js';export{BACKEND_ERROR,CLEAR_APPLICATION_ERRORS,DISMISS_APPLICATION_ERROR,LOG_APPLICATION_ERROR,RESTORE_APPLICATION_ERRORS}from'./constants/error.js';export{HEADER_SEARCHBAR_FILL,HEADER_SEARCH_BUTTON_FILL,RIGHT_CONTENT_FILL,SCROLL_END_FILL}from'./constants/layout.js';export{languages}from'./constants/languages.js';export{dismissApplicationError,restoreApplicationError,setApplicationError}from'./redux/actions/error-actions.js';export{applicationErrors,initialErrorsState}from'./redux/reducers/error.js';export{settingsReducer}from'./redux/reducers/settings.js';export{store}from'./redux/store.js';export{useAppDispatch,useAppSelector}from'./redux/hooks.js';export{getMenuSeparation}from'./utils/seperatedMenus.js';export{useComponentSize,useIsomorphicLayoutEffect}from'./utils/componentSize.js';export{matchParentRoute}from'./utils/parentRoute.js';export{addProLayoutParentKeys,filterRoutesWithLocale,menuDataRender,removeUnnecessaryProperties,setLocale,transformData}from'./utils/menuUtils.js';export{getMatchMenuKeys}from'./utils/matchMenuKeys.js';export{generateMenuPath}from'./utils/generateMenuLink.js';export{compareAndSaveSettingsDifferences}from'./utils/settingsDifference.js';export{ApplicationErrorHandlerCommon}from'./components/ApplicationErrorHandlerCommon.js';export{ErrorBoundaryCommon}from'./components/ErrorBoundaryCommon.js';export{ApplicationErrorFillWrapper}from'./components/ApplicationErrorFillWrapper.js';export{LayoutCookieProvider}from'./components/LayoutCookieProvider.js';export{ErrorLink,errorReduxLink}from'./graphql/link/error-link.js';export{systemFont}from'./themes/systemFont/index.js';export{borderRadius,colors,lightLayoutTheme,lightNavigationBarTheme,lightTabBarTheme,shadows,sizes,spacings,textVariants}from'./themes/templates/lightLayoutTheme.js';export{createLayoutTheme}from'./themes/templates/createLayoutTheme.js';export{darkColors,darkLayoutTheme,darkNavigationBarTheme,darkTabBarTheme}from'./themes/templates/darkLayoutTheme.js';export{config}from'./config/env-config.js';export{AMENITIES,DEFAULT_HEADER,DEFAULT_LAYOUT,SEARCH_TYPES,SHARED_VIEW_CONFIG}from'./config/defaultSettings.js';export{useLayoutSettings}from'./hooks/useLayoutSettings.js';export{usePermissionAutoFetch,useSetting,useSettingsLoader}from'@adminide-stack/platform-client';export{ClientOnly}from'./hooks/Client-only.js';export{blue}from'./colors/presets/blue.js';export{brinkPink}from'./colors/presets/brinkPink.js';export{cyan}from'./colors/presets/cyan.js';export{green}from'./colors/presets/green.js';export{lime}from'./colors/presets/lime.js';export{orange}from'./colors/presets/orange.js';export{pink}from'./colors/presets/pink.js';export{purple}from'./colors/presets/purple.js';export{red}from'./colors/presets/red.js';export{skyBlue}from'./colors/presets/skyBlue.js';export{turquoise}from'./colors/presets/turquoise.js';export{yellow}from'./colors/presets/yellow.js';export{white}from'./colors/presets/white.js';export{black}from'./colors/presets/black.js';export{colorList,getThemeColors}from'./colors/colorsList.js';//# sourceMappingURL=index.js.map
1
+ export{SearchBarBehavior}from'./interfaces/settings.js';export{APPLICATION_ERROR_SLOT_FILL,CHANGE_LANGUAGE,CHANGE_SETTINGS_ACTION}from'./constants/constants.js';export{BACKEND_ERROR,CLEAR_APPLICATION_ERRORS,DISMISS_APPLICATION_ERROR,LOG_APPLICATION_ERROR,RESTORE_APPLICATION_ERRORS}from'./constants/error.js';export{HEADER_SEARCHBAR_FILL,HEADER_SEARCH_BUTTON_FILL,RIGHT_CONTENT_FILL,SCROLL_END_FILL}from'./constants/layout.js';export{languages}from'./constants/languages.js';export{dismissApplicationError,restoreApplicationError,setApplicationError}from'./redux/actions/error-actions.js';export{applicationErrors,initialErrorsState}from'./redux/reducers/error.js';export{settingsReducer}from'./redux/reducers/settings.js';export{store}from'./redux/store.js';export{useAppDispatch,useAppSelector}from'./redux/hooks.js';export{getMenuSeparation}from'./utils/seperatedMenus.js';export{useComponentSize,useIsomorphicLayoutEffect}from'./utils/componentSize.js';export{matchParentRoute}from'./utils/parentRoute.js';export{addProLayoutParentKeys,filterRoutesWithLocale,menuDataRender,removeUnnecessaryProperties,setLocale,transformData}from'./utils/menuUtils.js';export{getMatchMenuKeys}from'./utils/matchMenuKeys.js';export{generateMenuPath}from'./utils/generateMenuLink.js';export{compareAndSaveSettingsDifferences}from'./utils/settingsDifference.js';export{ApplicationErrorHandlerCommon}from'./components/ApplicationErrorHandlerCommon.js';export{ErrorBoundaryCommon}from'./components/ErrorBoundaryCommon.js';export{ApplicationErrorFillWrapper}from'./components/ApplicationErrorFillWrapper.js';export{LayoutCookieProvider}from'./components/LayoutCookieProvider.js';export{ErrorLink,errorReduxLink}from'./graphql/link/error-link.js';export{systemFont}from'./themes/systemFont/index.js';export{borderRadius,colors,lightLayoutTheme,lightNavigationBarTheme,lightTabBarTheme,shadows,sizes,spacings,textVariants}from'./themes/templates/lightLayoutTheme.js';export{createLayoutTheme}from'./themes/templates/createLayoutTheme.js';export{darkColors,darkLayoutTheme,darkNavigationBarTheme,darkTabBarTheme}from'./themes/templates/darkLayoutTheme.js';export{config}from'./config/env-config.js';export{AMENITIES,DEFAULT_HEADER,DEFAULT_LAYOUT,SEARCH_TYPES,SHARED_VIEW_CONFIG}from'./config/defaultSettings.js';export{getClientDeviceType,useLayoutSettings}from'./hooks/useLayoutSettings.js';export{usePermissionAutoFetch,useSetting,useSettingsLoader}from'@adminide-stack/platform-client';export{ClientOnly}from'./hooks/Client-only.js';export{blue}from'./colors/presets/blue.js';export{brinkPink}from'./colors/presets/brinkPink.js';export{cyan}from'./colors/presets/cyan.js';export{green}from'./colors/presets/green.js';export{lime}from'./colors/presets/lime.js';export{orange}from'./colors/presets/orange.js';export{pink}from'./colors/presets/pink.js';export{purple}from'./colors/presets/purple.js';export{red}from'./colors/presets/red.js';export{skyBlue}from'./colors/presets/skyBlue.js';export{turquoise}from'./colors/presets/turquoise.js';export{yellow}from'./colors/presets/yellow.js';export{white}from'./colors/presets/white.js';export{black}from'./colors/presets/black.js';export{colorList,getThemeColors}from'./colors/colorsList.js';//# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Parse LAYOUT_SETTINGS from config (which may be a JSON string from env variable)
3
+ * This is the single place where we handle the parsing logic
4
+ */
5
+ export declare function parseEnvLayoutSettings(): Record<string, any>;
6
+ //# sourceMappingURL=parseEnvUiLayoutSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseEnvUiLayoutSettings.d.ts","sourceRoot":"","sources":["../../src/utils/parseEnvUiLayoutSettings.ts"],"names":[],"mappings":"AACA;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAwB5D"}
@@ -0,0 +1,27 @@
1
+ import {config}from'../config/env-config.js';import'../config/defaultSettings.js';/**
2
+ * Parse LAYOUT_SETTINGS from config (which may be a JSON string from env variable)
3
+ * This is the single place where we handle the parsing logic
4
+ */
5
+ function parseEnvLayoutSettings() {
6
+ try {
7
+ if (!config.LAYOUT_SETTINGS) {
8
+ return {};
9
+ }
10
+ // If it's already an object (parsed by envalid json validator), return it
11
+ if (typeof config.LAYOUT_SETTINGS === 'object' && !Array.isArray(config.LAYOUT_SETTINGS)) {
12
+ return config.LAYOUT_SETTINGS;
13
+ }
14
+ // If it's a string, parse it (this happens when envalid uses str validator)
15
+ if (typeof config.LAYOUT_SETTINGS === 'string') {
16
+ const parsed = JSON.parse(config.LAYOUT_SETTINGS);
17
+ return parsed || {};
18
+ }
19
+ console.warn('⚠️ config.LAYOUT_SETTINGS is unexpected type:', typeof config.LAYOUT_SETTINGS);
20
+ return {};
21
+ }
22
+ catch (error) {
23
+ console.error('❌ Error parsing LAYOUT_SETTINGS:', error);
24
+ console.error('❌ Value was:', config.LAYOUT_SETTINGS);
25
+ return {};
26
+ }
27
+ }export{parseEnvLayoutSettings};//# sourceMappingURL=parseEnvUiLayoutSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseEnvUiLayoutSettings.js","sources":["../../src/utils/parseEnvUiLayoutSettings.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAIG;AACH;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parseEnvUiLayoutSettings.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseEnvUiLayoutSettings.test.d.ts","sourceRoot":"","sources":["../../src/utils/parseEnvUiLayoutSettings.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@admin-layout/client",
3
- "version": "12.0.16-alpha.62",
3
+ "version": "12.0.16-alpha.67",
4
4
  "description": "Sample client for higher packages to depend on",
5
5
  "license": "ISC",
6
6
  "author": "CDMBase LLC",
@@ -26,7 +26,7 @@
26
26
  "serialize-error": "^8.0.0"
27
27
  },
28
28
  "devDependencies": {
29
- "common": "12.0.16-alpha.59",
29
+ "common": "12.0.16-alpha.64",
30
30
  "rc-menu": "^9.16.1"
31
31
  },
32
32
  "peerDependencies": {
@@ -44,5 +44,5 @@
44
44
  "typescript": {
45
45
  "definition": "lib/index.d.ts"
46
46
  },
47
- "gitHead": "0f34e34fa56ef415cd32998b67df42ab784af9a1"
47
+ "gitHead": "d8c9237b79c0bb4b1457cf446ab77dd5b43d8e05"
48
48
  }