@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.
- package/lib/__tests__/ConfigurationModelWrapper.test.d.ts +2 -0
- package/lib/__tests__/ConfigurationModelWrapper.test.d.ts.map +1 -0
- package/lib/components/UpdateSettings/UpdateSettings.server.d.ts +56 -0
- package/lib/components/UpdateSettings/UpdateSettings.server.d.ts.map +1 -1
- package/lib/components/UpdateSettings/UpdateSettings.server.js +138 -74
- package/lib/components/UpdateSettings/UpdateSettings.server.js.map +1 -1
- package/lib/components/UpdateSettings/UpdateSettings.server.test.d.ts +2 -0
- package/lib/components/UpdateSettings/UpdateSettings.server.test.d.ts.map +1 -0
- package/lib/hooks/useLayoutSettings.d.ts +1 -0
- package/lib/hooks/useLayoutSettings.d.ts.map +1 -1
- package/lib/hooks/useLayoutSettings.js +18 -8
- package/lib/hooks/useLayoutSettings.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/utils/parseEnvUiLayoutSettings.d.ts +6 -0
- package/lib/utils/parseEnvUiLayoutSettings.d.ts.map +1 -0
- package/lib/utils/parseEnvUiLayoutSettings.js +27 -0
- package/lib/utils/parseEnvUiLayoutSettings.js.map +1 -0
- package/lib/utils/parseEnvUiLayoutSettings.test.d.ts +2 -0
- package/lib/utils/parseEnvUiLayoutSettings.test.d.ts.map +1 -0
- package/package.json +3 -3
|
@@ -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;
|
|
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 {
|
|
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
|
-
|
|
9
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
86
|
-
|
|
87
|
-
//
|
|
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('
|
|
92
|
-
const
|
|
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
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
//
|
|
121
|
-
|
|
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
|
-
|
|
124
|
-
// Remove
|
|
125
|
-
if (
|
|
126
|
-
Object.keys(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
}
|
|
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":"
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"UpdateSettings.server.test.d.ts","sourceRoot":"","sources":["../../../src/components/UpdateSettings/UpdateSettings.server.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLayoutSettings.d.ts","sourceRoot":"","sources":["../../src/hooks/useLayoutSettings.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,iBAAiB;;0BAgDP,GAAG;;
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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
|
-
};
|
|
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 @@
|
|
|
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.
|
|
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.
|
|
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": "
|
|
47
|
+
"gitHead": "d8c9237b79c0bb4b1457cf446ab77dd5b43d8e05"
|
|
48
48
|
}
|