@djangocfg/layouts 1.2.32 → 1.2.34

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,593 @@
1
+ # Validation Error System
2
+
3
+ Automatic Zod validation error tracking with toast notifications and copy-to-clipboard functionality.
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
22
+
23
+ Wrap your app with `ValidationErrorProvider`:
24
+
25
+ ```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
+ ```
67
+
68
+ ### 3. See Results
69
+
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
+ }
116
+ ```
117
+
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
128
+
129
+ ```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
+ ```
151
+
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';
159
+
160
+ 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`
193
+
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;
213
+ }
214
+ ```
215
+
216
+ ---
217
+
218
+ ### `useValidationErrors()`
219
+
220
+ Hook to access validation error state.
221
+
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
338
+
339
+ ```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
+ },
363
+ }}
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
+ }
443
+ ```
444
+
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
476
+
477
+ ```
478
+ validation/
479
+ ├── index.ts # Public exports
480
+ ├── ValidationErrorContext.tsx # Provider & hook
481
+ ├── ValidationErrorToast.tsx # Toast utilities & copy button
482
+ └── README.md # This file
483
+ ```
484
+
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)
493
+
494
+ ---
495
+
496
+ ## Copy as cURL Feature
497
+
498
+ ### What Gets Copied
499
+
500
+ When you click **🔄 Copy cURL**, a ready-to-use cURL command is generated:
501
+
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...'
507
+ ```
508
+
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
523
+
524
+ You can also generate cURL commands programmatically:
525
+
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
+ ```
541
+
542
+ ### API Reference - cURL
543
+
544
+ #### `generateCurl(options: CurlOptions): string`
545
+
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
+ }
556
+ ```
557
+
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
+ ---
571
+
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