@djangocfg/layouts 2.1.234 → 2.1.236
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.236",
|
|
4
4
|
"description": "Simple, straightforward layout components for Next.js - import and use with props",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"layouts",
|
|
@@ -74,14 +74,14 @@
|
|
|
74
74
|
"check": "tsc --noEmit"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
|
-
"@djangocfg/api": "^2.1.
|
|
78
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
79
|
-
"@djangocfg/i18n": "^2.1.
|
|
80
|
-
"@djangocfg/monitor": "^2.1.
|
|
81
|
-
"@djangocfg/debuger": "^2.1.
|
|
82
|
-
"@djangocfg/ui-core": "^2.1.
|
|
83
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
84
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
77
|
+
"@djangocfg/api": "^2.1.236",
|
|
78
|
+
"@djangocfg/centrifugo": "^2.1.236",
|
|
79
|
+
"@djangocfg/i18n": "^2.1.236",
|
|
80
|
+
"@djangocfg/monitor": "^2.1.236",
|
|
81
|
+
"@djangocfg/debuger": "^2.1.236",
|
|
82
|
+
"@djangocfg/ui-core": "^2.1.236",
|
|
83
|
+
"@djangocfg/ui-nextjs": "^2.1.236",
|
|
84
|
+
"@djangocfg/ui-tools": "^2.1.236",
|
|
85
85
|
"@hookform/resolvers": "^5.2.2",
|
|
86
86
|
"consola": "^3.4.2",
|
|
87
87
|
"lucide-react": "^0.545.0",
|
|
@@ -109,15 +109,15 @@
|
|
|
109
109
|
"uuid": "^11.1.0"
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
|
-
"@djangocfg/api": "^2.1.
|
|
113
|
-
"@djangocfg/i18n": "^2.1.
|
|
114
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
115
|
-
"@djangocfg/monitor": "^2.1.
|
|
116
|
-
"@djangocfg/debuger": "^2.1.
|
|
117
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
118
|
-
"@djangocfg/ui-core": "^2.1.
|
|
119
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
120
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
112
|
+
"@djangocfg/api": "^2.1.236",
|
|
113
|
+
"@djangocfg/i18n": "^2.1.236",
|
|
114
|
+
"@djangocfg/centrifugo": "^2.1.236",
|
|
115
|
+
"@djangocfg/monitor": "^2.1.236",
|
|
116
|
+
"@djangocfg/debuger": "^2.1.236",
|
|
117
|
+
"@djangocfg/typescript-config": "^2.1.236",
|
|
118
|
+
"@djangocfg/ui-core": "^2.1.236",
|
|
119
|
+
"@djangocfg/ui-nextjs": "^2.1.236",
|
|
120
|
+
"@djangocfg/ui-tools": "^2.1.236",
|
|
121
121
|
"@types/node": "^24.7.2",
|
|
122
122
|
"@types/react": "^19.1.0",
|
|
123
123
|
"@types/react-dom": "^19.1.0",
|
|
@@ -34,6 +34,7 @@ import { toast } from '@djangocfg/ui-core/hooks';
|
|
|
34
34
|
import { useDebugMode, isProduction } from '@djangocfg/monitor/client';
|
|
35
35
|
|
|
36
36
|
import { createErrorToast } from '../components/ErrorToast';
|
|
37
|
+
import { generateShortErrorId, getProductionToast } from '../utils/formatters';
|
|
37
38
|
import {
|
|
38
39
|
DEFAULT_CENTRIFUGO_CONFIG,
|
|
39
40
|
DEFAULT_CORS_CONFIG,
|
|
@@ -148,6 +149,20 @@ function shouldShowError(detail: ErrorDetail): boolean {
|
|
|
148
149
|
return true;
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
/** Throttle production toasts — max 1 per type within this window */
|
|
153
|
+
const PROD_TOAST_THROTTLE_MS = 3000;
|
|
154
|
+
let lastProdToastByType: Map<string, number> | null = null;
|
|
155
|
+
|
|
156
|
+
function shouldThrottleProdToast(type: string): boolean {
|
|
157
|
+
if (typeof window === 'undefined') return false;
|
|
158
|
+
if (!lastProdToastByType) lastProdToastByType = new Map();
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
const last = lastProdToastByType.get(type);
|
|
161
|
+
if (last && now - last < PROD_TOAST_THROTTLE_MS) return true;
|
|
162
|
+
lastProdToastByType.set(type, now);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
151
166
|
export interface ErrorTrackingProviderProps {
|
|
152
167
|
children: ReactNode;
|
|
153
168
|
validation?: Partial<ValidationErrorConfig>;
|
|
@@ -255,13 +270,19 @@ export function ErrorTrackingProvider({
|
|
|
255
270
|
// Call custom error handler
|
|
256
271
|
const shouldShowToast = onError?.(detail) !== false;
|
|
257
272
|
|
|
258
|
-
// Show toast notification
|
|
259
|
-
if (config.showToast && shouldShowToast && isUnique
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
// Show toast notification (only for unique errors)
|
|
274
|
+
if (config.showToast && shouldShowToast && isUnique) {
|
|
275
|
+
if (!isProduction || isDebugMode) {
|
|
276
|
+
const toastOptions = createErrorToast(detail, config);
|
|
277
|
+
toast.error(toastOptions.title, {
|
|
278
|
+
description: toastOptions.description,
|
|
279
|
+
duration: toastOptions.duration,
|
|
280
|
+
});
|
|
281
|
+
} else if (!shouldThrottleProdToast(detail.type)) {
|
|
282
|
+
const errorId = generateShortErrorId();
|
|
283
|
+
const { title, description } = getProductionToast(detail, errorId);
|
|
284
|
+
toast.error(title, { description, duration: config.duration });
|
|
285
|
+
}
|
|
265
286
|
}
|
|
266
287
|
},
|
|
267
288
|
[onError, onMonitorCapture, isProduction, isDebugMode]
|
|
@@ -154,7 +154,7 @@ export function errorDetailToMonitorEvent(detail: ErrorDetail): MonitorEvent {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
|
-
* Format error title based on type
|
|
157
|
+
* Format error title based on type (dev/debug mode — full details)
|
|
158
158
|
*/
|
|
159
159
|
export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail | RuntimeErrorDetail): string {
|
|
160
160
|
switch (detail.type) {
|
|
@@ -176,3 +176,72 @@ export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail
|
|
|
176
176
|
return '❌ Error';
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Generate short error ID for support reference
|
|
182
|
+
* Format: ERR-XXXXX (base36, derived from timestamp + counter)
|
|
183
|
+
*/
|
|
184
|
+
let prodErrorCounter = 0;
|
|
185
|
+
export function generateShortErrorId(): string {
|
|
186
|
+
const id = ((Date.now() % 1_000_000) + (++prodErrorCounter)).toString(36).toUpperCase().slice(-5);
|
|
187
|
+
return `ERR-${id.padStart(5, '0')}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Production-safe toast content — no internal details exposed
|
|
192
|
+
* Returns user-friendly title + description with error ID for support
|
|
193
|
+
*/
|
|
194
|
+
export function getProductionToast(detail: ErrorDetail, errorId: string): { title: string; description: string } {
|
|
195
|
+
switch (detail.type) {
|
|
196
|
+
case 'validation':
|
|
197
|
+
return {
|
|
198
|
+
title: 'Invalid data received',
|
|
199
|
+
description: `Please try again. Reference: ${errorId}`,
|
|
200
|
+
};
|
|
201
|
+
case 'cors':
|
|
202
|
+
return {
|
|
203
|
+
title: 'Server unavailable',
|
|
204
|
+
description: `Unable to reach the server. Reference: ${errorId}`,
|
|
205
|
+
};
|
|
206
|
+
case 'network': {
|
|
207
|
+
const status = detail.statusCode;
|
|
208
|
+
if (status && status >= 500) {
|
|
209
|
+
return {
|
|
210
|
+
title: 'Server error',
|
|
211
|
+
description: `We're working on it. Reference: ${errorId}`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (status === 403) {
|
|
215
|
+
return {
|
|
216
|
+
title: 'Access denied',
|
|
217
|
+
description: `You don't have permission. Reference: ${errorId}`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (status === 404) {
|
|
221
|
+
return {
|
|
222
|
+
title: 'Not found',
|
|
223
|
+
description: `The requested resource was not found. Reference: ${errorId}`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
title: 'Connection error',
|
|
228
|
+
description: `Please check your connection and try again. Reference: ${errorId}`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
case 'centrifugo':
|
|
232
|
+
return {
|
|
233
|
+
title: 'Real-time connection error',
|
|
234
|
+
description: `Live updates may be delayed. Reference: ${errorId}`,
|
|
235
|
+
};
|
|
236
|
+
case 'runtime':
|
|
237
|
+
return {
|
|
238
|
+
title: 'Something went wrong',
|
|
239
|
+
description: `An unexpected error occurred. Reference: ${errorId}`,
|
|
240
|
+
};
|
|
241
|
+
default:
|
|
242
|
+
return {
|
|
243
|
+
title: 'Something went wrong',
|
|
244
|
+
description: `Please try again. Reference: ${errorId}`,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -99,8 +99,8 @@ export function BaseApp({
|
|
|
99
99
|
const centrifugoUrl = centrifugo?.url || process.env.NEXT_PUBLIC_CENTRIFUGO_URL;
|
|
100
100
|
const centrifugoEnabled = centrifugo?.enabled !== false;
|
|
101
101
|
|
|
102
|
-
// Debug panel —
|
|
103
|
-
const { enabled: debugEnabled
|
|
102
|
+
// Debug panel — always mounted, DebugButton handles visibility itself
|
|
103
|
+
const { enabled: debugEnabled, ...debugProps } = debug ?? {};
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
// Monitor — project prop as default, override with monitor config
|
|
@@ -170,8 +170,8 @@ export function BaseApp({
|
|
|
170
170
|
{/* Auth Dialog - global auth prompt */}
|
|
171
171
|
<AuthDialog authPath={auth?.routes?.auth} />
|
|
172
172
|
|
|
173
|
-
{/* Debug Panel —
|
|
174
|
-
|
|
173
|
+
{/* Debug Panel — auto in dev, ?debug=1 in prod, disable with debug={{ enabled: false }} */}
|
|
174
|
+
<DebugButton enabled={debugEnabled} {...debugProps} />
|
|
175
175
|
</ErrorTrackingProvider>
|
|
176
176
|
</PwaProvider>
|
|
177
177
|
</CentrifugoProvider>
|
package/src/utils/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Frontend configuration from environment variables
|
|
3
3
|
*/
|
|
4
|
-
import { isDev } from '@djangocfg/ui-core/lib';
|
|
4
|
+
import { isDev, isProd } from '@djangocfg/ui-core/lib';
|
|
5
5
|
|
|
6
6
|
export const config = {
|
|
7
7
|
/**
|
|
@@ -9,4 +9,5 @@ export const config = {
|
|
|
9
9
|
* In development mode, any OTP code is accepted for authentication
|
|
10
10
|
*/
|
|
11
11
|
isDevelopment: isDev,
|
|
12
|
+
isProduction: isProd,
|
|
12
13
|
} as const;
|