@djangocfg/layouts 1.2.39 → 1.2.41

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.
@@ -1,593 +1,191 @@
1
- # Validation Error System
1
+ # Error Tracking System
2
2
 
3
- Automatic Zod validation error tracking with toast notifications and copy-to-clipboard functionality.
3
+ **Universal error tracking for React apps with automatic toast notifications.**
4
4
 
5
- ---
6
-
7
- ## Features
8
-
9
- ✅ **Auto-capture validation errors** from API client via CustomEvent
10
- ✅ **Toast notifications** with destructive styling
11
- ✅ **Copy Error button** - One-click copy full error details JSON to clipboard
12
- ✅ **Copy cURL button** - Generate and copy cURL command with auth token
13
- ✅ **Error history** - Store and manage validation errors
14
- ✅ **Configurable** - Customize display and behavior
15
- ✅ **Type-safe** - Full TypeScript support
16
-
17
- ---
18
-
19
- ## Quick Start
20
-
21
- ### 1. Setup Provider
5
+ ## The Essence
22
6
 
23
- Wrap your app with `ValidationErrorProvider`:
7
+ One provider that listens to browser `CustomEvent`s from API client and shows toast notifications automatically:
24
8
 
25
9
  ```tsx
26
- // apps/admin/src/layouts/RootLayout.tsx
27
- import { ValidationErrorProvider } from '@djangocfg/layouts/validation';
28
-
29
- export default function RootLayout({ children }) {
30
- return (
31
- <ValidationErrorProvider>
32
- {children}
33
- </ValidationErrorProvider>
34
- );
35
- }
36
- ```
37
-
38
- ### 2. Trigger Validation Error
39
-
40
- Use the test button in UILayout header:
41
-
42
- ```tsx
43
- // In UILayout header, click "Test Validation" button
44
- // Select any error type (Simple, Multiple, Nested, Array)
45
- ```
46
-
47
- Or dispatch manually:
48
-
49
- ```typescript
50
- import type { ValidationErrorDetail } from '@djangocfg/layouts/validation';
51
-
52
- const errorDetail: ValidationErrorDetail = {
53
- operation: 'createUser',
54
- path: '/api/users',
55
- method: 'POST',
56
- error: zodError, // ZodError instance
57
- response: responseData,
58
- timestamp: new Date(),
59
- };
60
-
61
- window.dispatchEvent(new CustomEvent('zod-validation-error', {
62
- detail: errorDetail,
63
- bubbles: true,
64
- cancelable: false,
65
- }));
66
- ```
10
+ // Setup once
11
+ <ErrorTrackingProvider>
12
+ <App />
13
+ </ErrorTrackingProvider>
67
14
 
68
- ### 3. See Results
15
+ // API client dispatches events
16
+ window.dispatchEvent(new CustomEvent('zod-validation-error', { detail: {...} }));
17
+ window.dispatchEvent(new CustomEvent('cors-error', { detail: {...} }));
18
+ window.dispatchEvent(new CustomEvent('network-error', { detail: {...} }));
69
19
 
70
- You'll see a toast with:
71
- - **Title**: "❌ Validation Error in operation_name"
72
- - **Description**: Endpoint, error count, first 3 errors
73
- - **Two Buttons** (displayed at bottom):
74
- - **📋 Copy Error** - Copies full error JSON to clipboard
75
- - **🔄 Copy cURL** - Generates and copies cURL command with auth token
76
-
77
- ---
78
-
79
- ## Toast with Copy Button
80
-
81
- The system automatically shows toasts with a copy button for all validation errors.
82
-
83
- ### What Gets Copied
84
-
85
- When you click "📋 Copy", the following JSON is copied to clipboard:
86
-
87
- ```json
88
- {
89
- "timestamp": "2025-11-11T00:39:22.123Z",
90
- "operation": "createUser",
91
- "endpoint": {
92
- "method": "POST",
93
- "path": "/api/users"
94
- },
95
- "validation_errors": [
96
- {
97
- "path": "email",
98
- "message": "Invalid email format",
99
- "code": "invalid_string",
100
- "expected": "email",
101
- "received": "string"
102
- },
103
- {
104
- "path": "age",
105
- "message": "Number must be greater than 0",
106
- "code": "too_small",
107
- "minimum": 0
108
- }
109
- ],
110
- "response": {
111
- "email": "not-an-email",
112
- "age": -5
113
- },
114
- "total_errors": 2
115
- }
20
+ // Provider catches them → shows toast → stores in state
21
+ const { errors } = useErrors();
116
22
  ```
117
23
 
118
- ### Copy Success/Error Feedback
119
-
120
- - **Success**: Shows "✅ Copied! Error details copied to clipboard" for 2 seconds
121
- - **Error**: Shows "❌ Copy failed - Could not copy error details" for 2 seconds
122
-
123
- ---
124
-
125
- ## Configuration
126
-
127
- ### Provider Config
24
+ ## Quick Start
128
25
 
129
26
  ```tsx
130
- <ValidationErrorProvider
131
- config={{
132
- enableToast: true, // Show toast notifications
133
- maxErrors: 50, // Max errors to store in history
134
- toastSettings: {
135
- duration: 5000, // Toast duration in ms
136
- showOperation: true, // Show operation name in title
137
- showPath: true, // Show endpoint path in description
138
- showErrorCount: true, // Show error count in description
139
- },
140
- onError: (detail) => {
141
- // Custom error handler
142
- console.error('Validation error:', detail);
143
- // Return false to prevent toast
144
- return true;
145
- },
146
- }}
147
- >
148
- {children}
149
- </ValidationErrorProvider>
150
- ```
27
+ import { ErrorTrackingProvider, useErrors } from '@djangocfg/layouts';
151
28
 
152
- ### Custom Toast Config
153
-
154
- Create custom toast programmatically:
155
-
156
- ```typescript
157
- import { useToast } from '@djangocfg/ui';
158
- import { createValidationErrorToast } from '@djangocfg/layouts/validation';
29
+ // 1. Wrap your app
30
+ <ErrorTrackingProvider
31
+ validation={{ enabled: true, showToast: true }}
32
+ cors={{ enabled: true, showToast: true }}
33
+ network={{ enabled: false }}
34
+ >
35
+ <App />
36
+ </ErrorTrackingProvider>
159
37
 
38
+ // 2. Access errors anywhere
160
39
  function MyComponent() {
161
- const { toast } = useToast();
162
-
163
- const handleError = (errorDetail: ValidationErrorDetail) => {
164
- const toastOptions = createValidationErrorToast(errorDetail, {
165
- config: {
166
- showOperation: true,
167
- showPath: true,
168
- showErrorCount: true,
169
- maxIssuesInDescription: 5, // Show up to 5 errors
170
- titlePrefix: '🚨 API Error', // Custom prefix
171
- },
172
- duration: 10000, // 10 seconds
173
- onCopySuccess: () => {
174
- toast({ title: 'Copied!', duration: 1000 });
175
- },
176
- onCopyError: (error) => {
177
- console.error('Copy failed:', error);
178
- },
179
- });
180
-
181
- toast(toastOptions);
182
- };
183
-
184
- return <button onClick={() => handleError(errorDetail)}>Trigger Error</button>;
185
- }
186
- ```
187
-
188
- ---
189
-
190
- ## API Reference
191
-
192
- ### `ValidationErrorProvider`
40
+ const { errors, validationErrors, corsErrors, clearErrors } = useErrors();
193
41
 
194
- Main provider component for validation error tracking.
195
-
196
- **Props:**
197
- ```typescript
198
- interface ValidationErrorProviderProps {
199
- children: ReactNode;
200
- config?: Partial<ValidationErrorConfig>;
201
- }
202
-
203
- interface ValidationErrorConfig {
204
- enableToast: boolean; // Default: true
205
- maxErrors: number; // Default: 50
206
- toastSettings: {
207
- duration: number; // Default: 5000ms
208
- showOperation: boolean; // Default: true
209
- showPath: boolean; // Default: true
210
- showErrorCount: boolean; // Default: true
211
- };
212
- onError?: (detail: ValidationErrorDetail) => boolean | void;
42
+ return <div>Total errors: {errors.length}</div>;
213
43
  }
214
44
  ```
215
45
 
216
- ---
217
-
218
- ### `useValidationErrors()`
46
+ ## Error Types
219
47
 
220
- Hook to access validation error state.
48
+ | Type | Event Name | Source | Toast Content |
49
+ |------|-----------|--------|---------------|
50
+ | **Validation** | `zod-validation-error` | API client Zod schema validation | Field errors, copy buttons |
51
+ | **CORS** | `cors-error` | API client CORS blocked | Causes, solutions, troubleshooting |
52
+ | **Network** | `network-error` | API client network issues | Error details, status code |
221
53
 
222
- **Returns:**
223
- ```typescript
224
- interface ValidationErrorContextValue {
225
- errors: StoredValidationError[]; // All errors in history
226
- clearErrors: () => void; // Clear all errors
227
- clearError: (id: string) => void; // Clear specific error
228
- }
229
- ```
230
-
231
- **Usage:**
232
- ```typescript
233
- import { useValidationErrors } from '@djangocfg/layouts/validation';
234
-
235
- function ErrorList() {
236
- const { errors, clearErrors, clearError } = useValidationErrors();
237
-
238
- return (
239
- <div>
240
- <button onClick={clearErrors}>Clear All</button>
241
- {errors.map((error) => (
242
- <div key={error.id}>
243
- <p>{error.operation}: {error.error.issues.length} errors</p>
244
- <button onClick={() => clearError(error.id)}>Clear</button>
245
- </div>
246
- ))}
247
- </div>
248
- );
249
- }
250
- ```
251
-
252
- ---
253
-
254
- ### `createValidationErrorToast()`
255
-
256
- Create custom toast options with copy button.
257
-
258
- **Signature:**
259
- ```typescript
260
- function createValidationErrorToast(
261
- detail: ValidationErrorDetail,
262
- options?: {
263
- config?: Partial<ValidationErrorToastConfig>;
264
- duration?: number;
265
- onCopySuccess?: () => void;
266
- onCopyError?: (error: Error) => void;
267
- }
268
- ): ToastOptions
269
- ```
270
-
271
- **Returns:** Object with `title`, `description`, `variant`, `duration`, `action` (copy button)
272
-
273
- ---
274
-
275
- ### `formatErrorForClipboard()`
276
-
277
- Format error details as JSON for clipboard.
278
-
279
- **Signature:**
280
- ```typescript
281
- function formatErrorForClipboard(detail: ValidationErrorDetail): string
282
- ```
283
-
284
- **Returns:** Formatted JSON string with all error details
285
-
286
- ---
287
-
288
- ### `createCopyAction()`
289
-
290
- Create standalone copy button for toast.
291
-
292
- **Signature:**
293
- ```typescript
294
- function createCopyAction(
295
- detail: ValidationErrorDetail,
296
- onCopySuccess?: () => void,
297
- onCopyError?: (error: Error) => void
298
- ): React.ReactElement
299
- ```
300
-
301
- **Returns:** `<ToastAction>` component with copy functionality
302
-
303
- ---
304
-
305
- ## Testing
306
-
307
- ### Test Button in UILayout
308
-
309
- The `TestValidationButton` in UILayout header provides 4 test scenarios:
310
-
311
- 1. **Simple Error** - Single field validation (email)
312
- 2. **Multiple Errors** - 3 validation errors (email, age, username)
313
- 3. **Nested Object** - Nested property errors (address.street, address.zipCode)
314
- 4. **Array Validation** - Array item errors (items[0].quantity, items[1].price)
315
-
316
- Each test:
317
- - Dispatches CustomEvent
318
- - Triggers toast with copy button
319
- - Logs to console
320
-
321
- ---
322
-
323
- ## Browser Compatibility
324
-
325
- ### Clipboard API
326
-
327
- **Modern browsers** (Chrome 66+, Firefox 63+, Safari 13.1+):
328
- - Uses `navigator.clipboard.writeText()` (async)
329
-
330
- **Older browsers**:
331
- - Fallback to `document.execCommand('copy')`
332
-
333
- ---
334
-
335
- ## Examples
336
-
337
- ### Example 1: Basic Setup
54
+ ## Props
338
55
 
339
56
  ```tsx
340
- import { ValidationErrorProvider } from '@djangocfg/layouts/validation';
341
-
342
- function App() {
343
- return (
344
- <ValidationErrorProvider>
345
- <YourApp />
346
- </ValidationErrorProvider>
347
- );
348
- }
349
- ```
350
-
351
- ### Example 2: Custom Error Handler
352
-
353
- ```tsx
354
- <ValidationErrorProvider
355
- config={{
356
- onError: (detail) => {
357
- // Log to external service
358
- logToSentry(detail);
359
-
360
- // Only show toast for POST/PUT/PATCH errors
361
- return ['POST', 'PUT', 'PATCH'].includes(detail.method);
362
- },
57
+ <ErrorTrackingProvider
58
+ validation={{
59
+ enabled: true, // Enable tracking
60
+ showToast: true, // Show toast
61
+ maxErrors: 50, // Max history
62
+ duration: 8000, // Toast duration (ms)
63
+ showOperation: true, // Show operation name
64
+ showPath: true, // Show API path
65
+ showErrorCount: true, // Show error count
66
+ maxIssuesInToast: 3 // Max issues in toast
363
67
  }}
364
- >
365
- {children}
366
- </ValidationErrorProvider>
367
- ```
368
-
369
- ### Example 3: Manual Toast Trigger
370
-
371
- ```typescript
372
- import { useToast } from '@djangocfg/ui';
373
- import { createValidationErrorToast } from '@djangocfg/layouts/validation';
374
-
375
- function MyForm() {
376
- const { toast } = useToast();
377
-
378
- const handleSubmit = async (data) => {
379
- try {
380
- await api.createUser(data);
381
- } catch (error) {
382
- if (error instanceof ZodError) {
383
- const toastOptions = createValidationErrorToast({
384
- operation: 'createUser',
385
- path: '/api/users',
386
- method: 'POST',
387
- error: error,
388
- response: data,
389
- timestamp: new Date(),
390
- }, {
391
- onCopySuccess: () => console.log('Copied!'),
392
- });
393
-
394
- toast(toastOptions);
395
- }
396
- }
397
- };
398
-
399
- return <form onSubmit={handleSubmit}>...</form>;
400
- }
401
- ```
402
-
403
- ### Example 4: Error History Display
404
-
405
- ```tsx
406
- import { useValidationErrors } from '@djangocfg/layouts/validation';
407
-
408
- function ErrorHistory() {
409
- const { errors, clearErrors } = useValidationErrors();
410
-
411
- return (
412
- <div className="space-y-4">
413
- <div className="flex justify-between items-center">
414
- <h2>Validation Error History ({errors.length})</h2>
415
- <Button onClick={clearErrors} variant="outline" size="sm">
416
- Clear All
417
- </Button>
418
- </div>
419
-
420
- {errors.map((error) => (
421
- <div key={error.id} className="border p-4 rounded">
422
- <div className="flex justify-between">
423
- <div>
424
- <p className="font-semibold">{error.operation}</p>
425
- <p className="text-sm text-muted-foreground">
426
- {error.method} {error.path}
427
- </p>
428
- </div>
429
- <p className="text-xs text-muted-foreground">
430
- {error.timestamp.toLocaleString()}
431
- </p>
432
- </div>
433
- <div className="mt-2">
434
- <p className="text-sm">
435
- {error.error.issues.length} validation errors
436
- </p>
437
- </div>
438
- </div>
439
- ))}
440
- </div>
441
- );
442
- }
68
+ cors={{
69
+ enabled: true,
70
+ showToast: true,
71
+ duration: 0, // 0 = no auto-dismiss
72
+ showUrl: true,
73
+ showMethod: true
74
+ }}
75
+ network={{
76
+ enabled: false, // Disabled by default
77
+ showToast: true,
78
+ showUrl: true,
79
+ showMethod: true,
80
+ showStatusCode: true
81
+ }}
82
+ onError={(error) => {
83
+ // Custom handler
84
+ // Return false to prevent toast
85
+ }}
86
+ />
443
87
  ```
444
88
 
445
- ---
446
-
447
- ## Troubleshooting
448
-
449
- ### Toast not showing
450
-
451
- 1. **Check Toaster is rendered**:
452
- ```tsx
453
- import { Toaster } from '@djangocfg/ui';
454
- // In your root layout
455
- <Toaster />
456
- ```
457
-
458
- 2. **Check ValidationErrorProvider is wrapping your app**
459
-
460
- 3. **Check config.enableToast is true**
461
-
462
- ### Copy button not working
463
-
464
- 1. **Check HTTPS**: Clipboard API requires secure context (HTTPS or localhost)
465
- 2. **Check browser permissions**: Some browsers may block clipboard access
466
- 3. **Check console**: Look for clipboard errors
467
-
468
- ### Test button not visible
469
-
470
- 1. **Check NODE_ENV**: Button only shows in development by default
471
- 2. **Override in production**: Pass `showInProduction={true}` to TestValidationButton
472
-
473
- ---
474
-
475
- ## File Structure
89
+ ## Structure
476
90
 
477
91
  ```
478
92
  validation/
479
- ├── index.ts # Public exports
480
- ├── ValidationErrorContext.tsx # Provider & hook
481
- ├── ValidationErrorToast.tsx # Toast utilities & copy button
482
- └── README.md # This file
93
+ ├── providers/ErrorTrackingProvider.tsx # Single provider
94
+ ├── components/
95
+ ├── ErrorToast.tsx # Universal toast
96
+ └── ErrorButtons.tsx # Copy buttons
97
+ ├── utils/
98
+ │ ├── formatters.ts # Format errors
99
+ │ └── curl-generator.ts # Generate cURL
100
+ ├── types.ts # TypeScript types
101
+ ├── hooks.ts # useErrors()
102
+ └── index.ts # Public API
103
+ ```
104
+
105
+ ## How It Works
106
+
107
+ ```
108
+ API Client (TypeScript)
109
+
110
+ Throws Error
111
+
112
+ Dispatches CustomEvent → window.dispatchEvent('zod-validation-error')
113
+
114
+ ErrorTrackingProvider (React)
115
+
116
+ Listens via window.addEventListener()
117
+
118
+ 1. Stores error in state
119
+ 2. Shows toast notification
120
+ 3. Provides via useErrors()
483
121
  ```
484
122
 
485
- ---
486
-
487
- ## Related Components
488
-
489
- - **TestValidationButton** - Test button in UILayout header
490
- - **UILayout** - Layout with validation test button
491
- - **Toaster** - Toast notification container (@djangocfg/ui)
492
- - **useToast** - Toast hook (@djangocfg/ui)
123
+ ## Features
493
124
 
494
- ---
125
+ - ✅ **Single provider** for all error types
126
+ - ✅ **Automatic toast** notifications with copy buttons
127
+ - ✅ **Type-safe** TypeScript throughout
128
+ - ✅ **Theme-aware** buttons (no color issues)
129
+ - ✅ **Lucide icons** instead of emojis
130
+ - ✅ **Error history** with filtering by type
131
+ - ✅ **Custom handlers** for logging to Sentry/etc
132
+ - ✅ **SSR-safe** (checks `typeof window`)
495
133
 
496
- ## Copy as cURL Feature
134
+ ## Migration from v1
497
135
 
498
- ### What Gets Copied
136
+ ```tsx
137
+ // Before
138
+ <ValidationErrorProvider>
139
+ <CORSErrorProvider>
140
+ <App />
141
+ </CORSErrorProvider>
142
+ </ValidationErrorProvider>
499
143
 
500
- When you click **🔄 Copy cURL**, a ready-to-use cURL command is generated:
144
+ // After
145
+ <ErrorTrackingProvider
146
+ validation={{ ... }}
147
+ cors={{ ... }}
148
+ >
149
+ <App />
150
+ </ErrorTrackingProvider>
501
151
 
502
- ```bash
503
- curl 'http://localhost:8000/api/proxies/proxies/?page=1&page_size=100' \
504
- -H 'Accept: */*' \
505
- -H 'Content-Type: application/json' \
506
- -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
152
+ // Hooks
153
+ useValidationErrors() useErrors().validationErrors
154
+ useCORSErrors() useErrors().corsErrors
507
155
  ```
508
156
 
509
- The generator automatically:
510
- - ✅ Adds your auth token from localStorage
511
- - ✅ Formats headers properly
512
- - ✅ Uses correct HTTP method
513
- - ✅ Escapes special characters
514
-
515
- ### Token Auto-Detection
516
-
517
- The cURL generator looks for your token in localStorage:
518
- - `access_token` (default)
519
- - `token`
520
- - `auth_token`
521
-
522
- ### Manual cURL Generation
157
+ ## API
523
158
 
524
- You can also generate cURL commands programmatically:
159
+ ### `useErrors()`
525
160
 
526
- ```typescript
527
- import { generateCurl, copyCurlToClipboard } from '@djangocfg/layouts/validation';
528
-
529
- // Generate cURL
530
- const curl = generateCurl({
531
- method: 'POST',
532
- path: '/api/users/',
533
- token: 'your-token',
534
- body: { name: 'John' },
535
- headers: { 'X-Custom': 'value' },
536
- });
537
-
538
- // Copy to clipboard
539
- await copyCurlToClipboard(curl);
540
- ```
161
+ ```tsx
162
+ const {
163
+ errors, // All errors
164
+ validationErrors, // Validation only
165
+ corsErrors, // CORS only
166
+ networkErrors, // Network only
167
+ clearErrors, // Clear all
168
+ clearErrorsByType, // Clear by type
169
+ clearError, // Clear one
170
+ errorCount, // Total count
171
+ latestError, // Latest error
172
+ config // Current config
173
+ } = useErrors();
174
+ ```
175
+
176
+ ### Event Details
541
177
 
542
- ### API Reference - cURL
178
+ ```tsx
179
+ // Validation
180
+ { type: 'validation', operation, path, method, error: ZodError, response, timestamp }
543
181
 
544
- #### `generateCurl(options: CurlOptions): string`
182
+ // CORS
183
+ { type: 'cors', url, method, error: string, timestamp }
545
184
 
546
- **Options:**
547
- ```typescript
548
- interface CurlOptions {
549
- method: string; // HTTP method
550
- path: string; // API path
551
- token?: string; // Auth token (auto-fetched if omitted)
552
- body?: any; // Request body
553
- headers?: Record<string, string>; // Custom headers
554
- baseUrl?: string; // API base URL (from env by default)
555
- }
185
+ // Network
186
+ { type: 'network', url, method, error: string, statusCode?, timestamp }
556
187
  ```
557
188
 
558
- #### `generateCurlFromError(detail): string`
559
-
560
- Generate cURL from validation error details. Auto-fetches token from localStorage.
561
-
562
- #### `getAuthToken(): string | null`
563
-
564
- Get authentication token from localStorage.
565
-
566
- #### `copyCurlToClipboard(curl: string): Promise<boolean>`
567
-
568
- Copy cURL command to clipboard. Returns `true` on success.
569
-
570
189
  ---
571
190
 
572
- ## Changelog
573
-
574
- ### 2025-11-11 - v2.1.0
575
- - ✨ Added **Copy cURL button** with automatic token injection
576
- - ✨ Added `curl-generator.ts` utilities
577
- - ✨ Added two buttons layout at bottom of toast
578
- - ✨ Auto-detect auth token from localStorage
579
- - 📝 Updated documentation with cURL examples
580
-
581
- ### 2025-11-11 - v2.0.0
582
- - ✨ Added copy-to-clipboard button in toast
583
- - ✨ Added `ValidationErrorToast` utilities
584
- - ✨ Added `formatErrorForClipboard()` function
585
- - ✨ Added success/error feedback for copy action
586
- - 📝 Added comprehensive documentation
587
-
588
- ### 2025-11-10 - v1.0.0
589
- - 🎉 Initial release
590
- - ✨ ValidationErrorProvider component
591
- - ✨ useValidationErrors hook
592
- - ✨ CustomEvent-based error capture
593
- - ✨ Toast notifications
191
+ **Built with React Context + Browser CustomEvents**