@djangocfg/layouts 2.1.49 → 2.1.50

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.49",
3
+ "version": "2.1.50",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -92,9 +92,9 @@
92
92
  "check": "tsc --noEmit"
93
93
  },
94
94
  "peerDependencies": {
95
- "@djangocfg/api": "^2.1.49",
96
- "@djangocfg/centrifugo": "^2.1.49",
97
- "@djangocfg/ui-nextjs": "^2.1.49",
95
+ "@djangocfg/api": "^2.1.50",
96
+ "@djangocfg/centrifugo": "^2.1.50",
97
+ "@djangocfg/ui-nextjs": "^2.1.50",
98
98
  "@hookform/resolvers": "^5.2.0",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -116,7 +116,7 @@
116
116
  "uuid": "^11.1.0"
117
117
  },
118
118
  "devDependencies": {
119
- "@djangocfg/typescript-config": "^2.1.49",
119
+ "@djangocfg/typescript-config": "^2.1.50",
120
120
  "@types/node": "^24.7.2",
121
121
  "@types/react": "^19.1.0",
122
122
  "@types/react-dom": "^19.1.0",
@@ -13,18 +13,21 @@ import { Button, useCopy } from '@djangocfg/ui-nextjs';
13
13
 
14
14
  import { generateCurlFromError } from '../utils/curl-generator';
15
15
  import {
16
- formatCORSErrorForClipboard, formatNetworkErrorForClipboard, formatValidationErrorForClipboard
16
+ formatCentrifugoErrorForClipboard,
17
+ formatCORSErrorForClipboard,
18
+ formatNetworkErrorForClipboard,
19
+ formatValidationErrorForClipboard
17
20
  } from '../utils/formatters';
18
21
 
19
- import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail } from '../types';
22
+ import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail, CentrifugoErrorDetail } from '../types';
20
23
  export interface ErrorButtonsProps {
21
- detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail;
24
+ detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail;
22
25
  }
23
26
 
24
27
  /**
25
28
  * Format error for clipboard based on type
26
29
  */
27
- function formatErrorForClipboard(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail): string {
30
+ function formatErrorForClipboard(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail): string {
28
31
  switch (detail.type) {
29
32
  case 'validation':
30
33
  return formatValidationErrorForClipboard(detail);
@@ -32,6 +35,8 @@ function formatErrorForClipboard(detail: ValidationErrorDetail | CORSErrorDetail
32
35
  return formatCORSErrorForClipboard(detail);
33
36
  case 'network':
34
37
  return formatNetworkErrorForClipboard(detail);
38
+ case 'centrifugo':
39
+ return formatCentrifugoErrorForClipboard(detail);
35
40
  default:
36
41
  return JSON.stringify(detail, null, 2);
37
42
  }
@@ -40,7 +45,7 @@ function formatErrorForClipboard(detail: ValidationErrorDetail | CORSErrorDetail
40
45
  /**
41
46
  * Check if error supports cURL generation
42
47
  */
43
- function supportsCurl(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail): boolean {
48
+ function supportsCurl(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail): boolean {
44
49
  return detail.type === 'validation';
45
50
  }
46
51
 
@@ -15,9 +15,11 @@ import type {
15
15
  ValidationErrorDetail,
16
16
  CORSErrorDetail,
17
17
  NetworkErrorDetail,
18
+ CentrifugoErrorDetail,
18
19
  ValidationErrorConfig,
19
20
  CORSErrorConfig,
20
21
  NetworkErrorConfig,
22
+ CentrifugoErrorConfig,
21
23
  } from '../types';
22
24
  /**
23
25
  * Build validation error description
@@ -133,12 +135,46 @@ function buildNetworkDescription(
133
135
  );
134
136
  }
135
137
 
138
+ /**
139
+ * Build centrifugo error description
140
+ */
141
+ function buildCentrifugoDescription(
142
+ detail: CentrifugoErrorDetail,
143
+ config: Required<CentrifugoErrorConfig>
144
+ ): React.ReactNode {
145
+ const parts: string[] = [];
146
+
147
+ // Add method info
148
+ if (config.showMethod) {
149
+ parts.push(`RPC: ${detail.method}`);
150
+ }
151
+
152
+ // Add error code
153
+ if (config.showCode && detail.code !== undefined) {
154
+ parts.push(`Code: ${detail.code}`);
155
+ }
156
+
157
+ return (
158
+ <div className="flex flex-col gap-2 text-sm">
159
+ {parts.length > 0 && (
160
+ <div className="font-mono text-xs opacity-90">
161
+ {parts.join(' • ')}
162
+ </div>
163
+ )}
164
+
165
+ <div className="opacity-90">{detail.error}</div>
166
+
167
+ <ErrorButtons detail={detail} />
168
+ </div>
169
+ );
170
+ }
171
+
136
172
  /**
137
173
  * Create toast options for any error type
138
174
  */
139
175
  export function createErrorToast(
140
- detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail,
141
- config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig>
176
+ detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail,
177
+ config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig>
142
178
  ) {
143
179
  let description: React.ReactNode;
144
180
 
@@ -147,6 +183,8 @@ export function createErrorToast(
147
183
  description = buildValidationDescription(detail, config as Required<ValidationErrorConfig>);
148
184
  } else if (detail.type === 'cors') {
149
185
  description = buildCORSDescription(detail, config as Required<CORSErrorConfig>);
186
+ } else if (detail.type === 'centrifugo') {
187
+ description = buildCentrifugoDescription(detail, config as Required<CentrifugoErrorConfig>);
150
188
  } else {
151
189
  description = buildNetworkDescription(detail, config as Required<NetworkErrorConfig>);
152
190
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Error Tracking - Unified error tracking for all error types
3
3
  *
4
- * Single provider and hook for validation, CORS, and network errors
4
+ * Single provider and hook for validation, CORS, network, and Centrifugo errors
5
5
  */
6
6
 
7
7
  // Main provider and hook
@@ -14,11 +14,13 @@ export type {
14
14
  ValidationErrorDetail,
15
15
  CORSErrorDetail,
16
16
  NetworkErrorDetail,
17
+ CentrifugoErrorDetail,
17
18
  StoredError,
18
19
  ErrorTrackingConfig,
19
20
  ValidationErrorConfig,
20
21
  CORSErrorConfig,
21
22
  NetworkErrorConfig,
23
+ CentrifugoErrorConfig,
22
24
  ErrorTrackingContextValue,
23
25
  } from './types';
24
26
 
@@ -32,6 +34,7 @@ export {
32
34
  formatValidationErrorForClipboard,
33
35
  formatCORSErrorForClipboard,
34
36
  formatNetworkErrorForClipboard,
37
+ formatCentrifugoErrorForClipboard,
35
38
  formatErrorTitle,
36
39
  extractDomain,
37
40
  } from './utils/formatters';
@@ -34,7 +34,11 @@ import { toast } from 'sonner';
34
34
 
35
35
  import { createErrorToast } from '../components/ErrorToast';
36
36
  import {
37
- DEFAULT_CORS_CONFIG, DEFAULT_NETWORK_CONFIG, DEFAULT_VALIDATION_CONFIG, ERROR_EVENTS
37
+ DEFAULT_CENTRIFUGO_CONFIG,
38
+ DEFAULT_CORS_CONFIG,
39
+ DEFAULT_NETWORK_CONFIG,
40
+ DEFAULT_VALIDATION_CONFIG,
41
+ ERROR_EVENTS
38
42
  } from '../types';
39
43
 
40
44
  import type {
@@ -44,9 +48,11 @@ import type {
44
48
  ValidationErrorConfig,
45
49
  CORSErrorConfig,
46
50
  NetworkErrorConfig,
51
+ CentrifugoErrorConfig,
47
52
  ValidationErrorDetail,
48
53
  CORSErrorDetail,
49
54
  NetworkErrorDetail,
55
+ CentrifugoErrorDetail,
50
56
  ErrorTrackingContextValue,
51
57
  } from '../types';
52
58
  const ErrorTrackingContext = createContext<ErrorTrackingContextValue | undefined>(undefined);
@@ -64,6 +70,7 @@ export interface ErrorTrackingProviderProps {
64
70
  validation?: Partial<ValidationErrorConfig>;
65
71
  cors?: Partial<CORSErrorConfig>;
66
72
  network?: Partial<NetworkErrorConfig>;
73
+ centrifugo?: Partial<CentrifugoErrorConfig>;
67
74
  onError?: (error: ErrorDetail) => boolean | void;
68
75
  }
69
76
 
@@ -77,6 +84,7 @@ export function ErrorTrackingProvider({
77
84
  validation: userValidationConfig,
78
85
  cors: userCorsConfig,
79
86
  network: userNetworkConfig,
87
+ centrifugo: userCentrifugoConfig,
80
88
  onError,
81
89
  }: ErrorTrackingProviderProps) {
82
90
  const [errors, setErrors] = useState<StoredError[]>([]);
@@ -97,6 +105,11 @@ export function ErrorTrackingProvider({
97
105
  ...userNetworkConfig,
98
106
  };
99
107
 
108
+ const centrifugoConfig: Required<CentrifugoErrorConfig> = {
109
+ ...DEFAULT_CENTRIFUGO_CONFIG,
110
+ ...userCentrifugoConfig,
111
+ };
112
+
100
113
  /**
101
114
  * Clear all errors
102
115
  */
@@ -122,7 +135,7 @@ export function ErrorTrackingProvider({
122
135
  * Handle any error event
123
136
  */
124
137
  const handleError = useCallback(
125
- (detail: ErrorDetail, config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig>) => {
138
+ (detail: ErrorDetail, config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig>) => {
126
139
  // Create stored error with ID
127
140
  const storedError: StoredError = {
128
141
  ...detail,
@@ -201,24 +214,40 @@ export function ErrorTrackingProvider({
201
214
  handlers.push({ event: ERROR_EVENTS.NETWORK, handler });
202
215
  }
203
216
 
217
+ // Centrifugo errors
218
+ if (centrifugoConfig.enabled) {
219
+ const handler = (event: Event) => {
220
+ if (!(event instanceof CustomEvent)) return;
221
+ const detail: CentrifugoErrorDetail = {
222
+ ...event.detail,
223
+ type: 'centrifugo' as const,
224
+ };
225
+ handleError(detail, centrifugoConfig);
226
+ };
227
+ window.addEventListener(ERROR_EVENTS.CENTRIFUGO, handler);
228
+ handlers.push({ event: ERROR_EVENTS.CENTRIFUGO, handler });
229
+ }
230
+
204
231
  // Cleanup
205
232
  return () => {
206
233
  handlers.forEach(({ event, handler }) => {
207
234
  window.removeEventListener(event, handler);
208
235
  });
209
236
  };
210
- }, [handleError, validationConfig, corsConfig, networkConfig]);
237
+ }, [handleError, validationConfig, corsConfig, networkConfig, centrifugoConfig]);
211
238
 
212
239
  // Filter errors by type
213
240
  const validationErrors = errors.filter((e) => e.type === 'validation') as StoredError<ValidationErrorDetail>[];
214
241
  const corsErrors = errors.filter((e) => e.type === 'cors') as StoredError<CORSErrorDetail>[];
215
242
  const networkErrors = errors.filter((e) => e.type === 'network') as StoredError<NetworkErrorDetail>[];
243
+ const centrifugoErrors = errors.filter((e) => e.type === 'centrifugo') as StoredError<CentrifugoErrorDetail>[];
216
244
 
217
245
  const value: ErrorTrackingContextValue = {
218
246
  errors,
219
247
  validationErrors,
220
248
  corsErrors,
221
249
  networkErrors,
250
+ centrifugoErrors,
222
251
  clearErrors,
223
252
  clearErrorsByType,
224
253
  clearError,
@@ -228,6 +257,7 @@ export function ErrorTrackingProvider({
228
257
  validation: validationConfig,
229
258
  cors: corsConfig,
230
259
  network: networkConfig,
260
+ centrifugo: centrifugoConfig,
231
261
  },
232
262
  };
233
263
 
@@ -59,10 +59,25 @@ export interface NetworkErrorDetail extends BaseErrorDetail {
59
59
  statusCode?: number;
60
60
  }
61
61
 
62
+ /**
63
+ * Centrifugo error detail (from centrifugo-error event)
64
+ */
65
+ export interface CentrifugoErrorDetail extends BaseErrorDetail {
66
+ type: 'centrifugo';
67
+ /** RPC method that failed */
68
+ method: string;
69
+ /** Error message */
70
+ error: string;
71
+ /** Error code from Centrifugo */
72
+ code?: number;
73
+ /** Additional data sent with the request */
74
+ data?: any;
75
+ }
76
+
62
77
  /**
63
78
  * Union type of all error details
64
79
  */
65
- export type ErrorDetail = ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail;
80
+ export type ErrorDetail = ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail;
66
81
 
67
82
  /**
68
83
  * Stored error with unique ID
@@ -170,6 +185,23 @@ export interface NetworkErrorConfig extends ErrorTypeConfig {
170
185
  showStatusCode?: boolean;
171
186
  }
172
187
 
188
+ /**
189
+ * Centrifugo error specific config
190
+ */
191
+ export interface CentrifugoErrorConfig extends ErrorTypeConfig {
192
+ /**
193
+ * Show RPC method in toast
194
+ * @default true
195
+ */
196
+ showMethod?: boolean;
197
+
198
+ /**
199
+ * Show error code in toast
200
+ * @default true
201
+ */
202
+ showCode?: boolean;
203
+ }
204
+
173
205
  /**
174
206
  * Complete error tracking configuration
175
207
  */
@@ -189,6 +221,11 @@ export interface ErrorTrackingConfig {
189
221
  */
190
222
  network?: NetworkErrorConfig;
191
223
 
224
+ /**
225
+ * Centrifugo error tracking configuration
226
+ */
227
+ centrifugo?: CentrifugoErrorConfig;
228
+
192
229
  /**
193
230
  * Custom error handler (called before toast for all errors)
194
231
  * Return false to prevent default toast notification
@@ -212,11 +249,14 @@ export interface ErrorTrackingContextValue {
212
249
  /** Network errors only */
213
250
  networkErrors: StoredError<NetworkErrorDetail>[];
214
251
 
252
+ /** Centrifugo errors only */
253
+ centrifugoErrors: StoredError<CentrifugoErrorDetail>[];
254
+
215
255
  /** Clear all errors */
216
256
  clearErrors: () => void;
217
257
 
218
258
  /** Clear errors by type */
219
- clearErrorsByType: (type: 'validation' | 'cors' | 'network') => void;
259
+ clearErrorsByType: (type: 'validation' | 'cors' | 'network' | 'centrifugo') => void;
220
260
 
221
261
  /** Clear specific error by ID */
222
262
  clearError: (id: string) => void;
@@ -232,6 +272,7 @@ export interface ErrorTrackingContextValue {
232
272
  validation: Required<ValidationErrorConfig>;
233
273
  cors: Required<CORSErrorConfig>;
234
274
  network: Required<NetworkErrorConfig>;
275
+ centrifugo: Required<CentrifugoErrorConfig>;
235
276
  };
236
277
  }
237
278
 
@@ -242,6 +283,7 @@ export const ERROR_EVENTS = {
242
283
  VALIDATION: 'zod-validation-error',
243
284
  CORS: 'cors-error',
244
285
  NETWORK: 'network-error',
286
+ CENTRIFUGO: 'centrifugo-error',
245
287
  } as const;
246
288
 
247
289
  /**
@@ -276,3 +318,10 @@ export const DEFAULT_NETWORK_CONFIG: Required<NetworkErrorConfig> = {
276
318
  showMethod: true,
277
319
  showStatusCode: true,
278
320
  };
321
+
322
+ export const DEFAULT_CENTRIFUGO_CONFIG: Required<CentrifugoErrorConfig> = {
323
+ ...DEFAULT_ERROR_CONFIG,
324
+ duration: 0, // Don't auto-dismiss centrifugo errors
325
+ showMethod: true,
326
+ showCode: true,
327
+ };
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { ZodError } from 'zod';
8
- import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail } from '../types';
8
+ import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail, CentrifugoErrorDetail } from '../types';
9
9
 
10
10
  /**
11
11
  * Format Zod error issues for display
@@ -83,6 +83,22 @@ export function formatNetworkErrorForClipboard(detail: NetworkErrorDetail): stri
83
83
  return JSON.stringify(errorData, null, 2);
84
84
  }
85
85
 
86
+ /**
87
+ * Format centrifugo error for clipboard
88
+ */
89
+ export function formatCentrifugoErrorForClipboard(detail: CentrifugoErrorDetail): string {
90
+ const errorData = {
91
+ type: 'centrifugo',
92
+ timestamp: detail.timestamp.toISOString(),
93
+ method: detail.method,
94
+ error: detail.error,
95
+ ...(detail.code !== undefined && { code: detail.code }),
96
+ ...(detail.data && { data: detail.data }),
97
+ };
98
+
99
+ return JSON.stringify(errorData, null, 2);
100
+ }
101
+
86
102
  /**
87
103
  * Extract domain from URL
88
104
  */
@@ -98,7 +114,7 @@ export function extractDomain(url: string): string {
98
114
  /**
99
115
  * Format error title based on type
100
116
  */
101
- export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail): string {
117
+ export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail): string {
102
118
  switch (detail.type) {
103
119
  case 'validation':
104
120
  return `❌ Validation Error in ${detail.operation}`;
@@ -108,6 +124,10 @@ export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail
108
124
  return detail.statusCode
109
125
  ? `⚠️ Network Error (${detail.statusCode})`
110
126
  : '⚠️ Network Error';
127
+ case 'centrifugo':
128
+ return detail.code !== undefined
129
+ ? `🔌 Centrifugo Error (${detail.code})`
130
+ : '🔌 Centrifugo Error';
111
131
  default:
112
132
  return '❌ Error';
113
133
  }