@djangocfg/layouts 1.2.31 โ 1.2.33
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 +5 -5
- package/src/auth/context/AccountsContext.tsx +40 -9
- package/src/auth/context/AuthContext.tsx +43 -13
- package/src/auth/context/types.ts +1 -1
- package/src/auth/hooks/useAuthForm.ts +2 -1
- package/src/auth/hooks/useAutoAuth.ts +2 -1
- package/src/auth/hooks/useLocalStorage.ts +19 -18
- package/src/auth/hooks/useProfileCache.ts +4 -1
- package/src/auth/hooks/useSessionStorage.ts +19 -18
- package/src/index.ts +4 -1
- package/src/layouts/AppLayout/AppLayout.tsx +9 -2
- package/src/layouts/AppLayout/components/ErrorBoundary.tsx +3 -2
- package/src/layouts/AppLayout/components/PackageVersions/packageVersions.config.ts +8 -8
- package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +56 -19
- package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +29 -18
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +65 -19
- package/src/layouts/AppLayout/providers/CoreProviders.tsx +12 -2
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +2 -1
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +2 -1
- package/src/layouts/UILayout/components/layout/Header/Header.tsx +11 -4
- package/src/layouts/UILayout/components/layout/Header/HeaderDesktop.tsx +14 -7
- package/src/layouts/UILayout/components/layout/Header/TestValidationButton.tsx +265 -0
- package/src/layouts/UILayout/components/layout/Header/index.ts +2 -0
- package/src/utils/logger.ts +3 -1
- package/src/validation/README.md +507 -0
- package/src/validation/ValidationErrorContext.tsx +333 -0
- package/src/validation/ValidationErrorToast.tsx +251 -0
- package/src/validation/index.ts +25 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestValidationButton Component
|
|
3
|
+
*
|
|
4
|
+
* Developer tool for testing Zod validation error events
|
|
5
|
+
* Dispatches mock CustomEvent to test ValidationErrorProvider
|
|
6
|
+
*
|
|
7
|
+
* Only visible in development mode
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use client';
|
|
11
|
+
|
|
12
|
+
import React, { useState } from 'react';
|
|
13
|
+
import { Button } from '@djangocfg/ui';
|
|
14
|
+
import {
|
|
15
|
+
DropdownMenu,
|
|
16
|
+
DropdownMenuContent,
|
|
17
|
+
DropdownMenuItem,
|
|
18
|
+
DropdownMenuLabel,
|
|
19
|
+
DropdownMenuSeparator,
|
|
20
|
+
DropdownMenuTrigger,
|
|
21
|
+
} from '@djangocfg/ui';
|
|
22
|
+
import { Bug, AlertCircle } from 'lucide-react';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Mock Zod error factory
|
|
26
|
+
*/
|
|
27
|
+
function createMockZodError(type: 'simple' | 'multiple' | 'nested' | 'array') {
|
|
28
|
+
const errors: Record<string, any> = {
|
|
29
|
+
simple: {
|
|
30
|
+
issues: [
|
|
31
|
+
{
|
|
32
|
+
path: ['email'],
|
|
33
|
+
message: 'Invalid email format',
|
|
34
|
+
code: 'invalid_string',
|
|
35
|
+
expected: 'email',
|
|
36
|
+
received: 'string',
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
multiple: {
|
|
41
|
+
issues: [
|
|
42
|
+
{
|
|
43
|
+
path: ['email'],
|
|
44
|
+
message: 'Invalid email format',
|
|
45
|
+
code: 'invalid_string',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
path: ['age'],
|
|
49
|
+
message: 'Number must be greater than 0',
|
|
50
|
+
code: 'too_small',
|
|
51
|
+
minimum: 0,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
path: ['username'],
|
|
55
|
+
message: 'Required field',
|
|
56
|
+
code: 'invalid_type',
|
|
57
|
+
expected: 'string',
|
|
58
|
+
received: 'undefined',
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
nested: {
|
|
63
|
+
issues: [
|
|
64
|
+
{
|
|
65
|
+
path: ['address', 'street'],
|
|
66
|
+
message: 'Street is required',
|
|
67
|
+
code: 'invalid_type',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
path: ['address', 'zipCode'],
|
|
71
|
+
message: 'Invalid zip code format',
|
|
72
|
+
code: 'invalid_string',
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
array: {
|
|
77
|
+
issues: [
|
|
78
|
+
{
|
|
79
|
+
path: ['items', 0, 'quantity'],
|
|
80
|
+
message: 'Quantity must be at least 1',
|
|
81
|
+
code: 'too_small',
|
|
82
|
+
minimum: 1,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
path: ['items', 1, 'price'],
|
|
86
|
+
message: 'Price is required',
|
|
87
|
+
code: 'invalid_type',
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return errors[type];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Dispatch validation error event
|
|
98
|
+
*/
|
|
99
|
+
function dispatchValidationError(type: 'simple' | 'multiple' | 'nested' | 'array') {
|
|
100
|
+
const operations: Record<string, { operation: string; path: string; method: string; response: any }> = {
|
|
101
|
+
simple: {
|
|
102
|
+
operation: 'createUser',
|
|
103
|
+
path: '/api/users',
|
|
104
|
+
method: 'POST',
|
|
105
|
+
response: { email: 'not-an-email@' },
|
|
106
|
+
},
|
|
107
|
+
multiple: {
|
|
108
|
+
operation: 'updateProfile',
|
|
109
|
+
path: '/api/profile/123',
|
|
110
|
+
method: 'PATCH',
|
|
111
|
+
response: { email: 'bad', age: -5 },
|
|
112
|
+
},
|
|
113
|
+
nested: {
|
|
114
|
+
operation: 'createAddress',
|
|
115
|
+
path: '/api/addresses',
|
|
116
|
+
method: 'POST',
|
|
117
|
+
response: { address: { street: null, zipCode: '12' } },
|
|
118
|
+
},
|
|
119
|
+
array: {
|
|
120
|
+
operation: 'createOrder',
|
|
121
|
+
path: '/api/orders',
|
|
122
|
+
method: 'POST',
|
|
123
|
+
response: { items: [{ quantity: 0 }, { price: undefined }] },
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const config = operations[type];
|
|
128
|
+
const error = createMockZodError(type);
|
|
129
|
+
|
|
130
|
+
const event = new CustomEvent('zod-validation-error', {
|
|
131
|
+
detail: {
|
|
132
|
+
operation: config.operation,
|
|
133
|
+
path: config.path,
|
|
134
|
+
method: config.method,
|
|
135
|
+
error: error,
|
|
136
|
+
response: config.response,
|
|
137
|
+
timestamp: new Date(),
|
|
138
|
+
},
|
|
139
|
+
bubbles: true,
|
|
140
|
+
cancelable: false,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
window.dispatchEvent(event);
|
|
144
|
+
|
|
145
|
+
console.group('๐งช Test Validation Error Dispatched');
|
|
146
|
+
console.log('Type:', type);
|
|
147
|
+
console.log('Operation:', config.operation);
|
|
148
|
+
console.log('Endpoint:', config.method, config.path);
|
|
149
|
+
console.log('Issues:', error.issues);
|
|
150
|
+
console.groupEnd();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface TestValidationButtonProps {
|
|
154
|
+
/** Show in production (default: false - dev only) */
|
|
155
|
+
showInProduction?: boolean;
|
|
156
|
+
/** Button size */
|
|
157
|
+
size?: 'default' | 'sm' | 'lg' | 'icon';
|
|
158
|
+
/** Additional CSS classes */
|
|
159
|
+
className?: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Test Validation Button
|
|
164
|
+
*
|
|
165
|
+
* Dropdown menu with different validation error test cases
|
|
166
|
+
* Only visible in development by default
|
|
167
|
+
*/
|
|
168
|
+
export function TestValidationButton({
|
|
169
|
+
showInProduction = false,
|
|
170
|
+
size = 'sm',
|
|
171
|
+
className,
|
|
172
|
+
}: TestValidationButtonProps) {
|
|
173
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
174
|
+
|
|
175
|
+
// Hide in production unless explicitly enabled
|
|
176
|
+
if (!showInProduction && process.env.NODE_ENV === 'production') {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
|
|
182
|
+
<DropdownMenuTrigger asChild>
|
|
183
|
+
<Button
|
|
184
|
+
variant="outline"
|
|
185
|
+
size={size}
|
|
186
|
+
className={className}
|
|
187
|
+
>
|
|
188
|
+
<Bug className="h-4 w-4" />
|
|
189
|
+
<span className="ml-2 hidden sm:inline">Test Validation</span>
|
|
190
|
+
</Button>
|
|
191
|
+
</DropdownMenuTrigger>
|
|
192
|
+
|
|
193
|
+
<DropdownMenuContent align="end" className="w-[240px]">
|
|
194
|
+
<DropdownMenuLabel className="flex items-center gap-2">
|
|
195
|
+
<AlertCircle className="h-4 w-4 text-orange-500" />
|
|
196
|
+
<span>Test Validation Errors</span>
|
|
197
|
+
</DropdownMenuLabel>
|
|
198
|
+
|
|
199
|
+
<DropdownMenuSeparator />
|
|
200
|
+
|
|
201
|
+
<DropdownMenuItem
|
|
202
|
+
onClick={() => {
|
|
203
|
+
dispatchValidationError('simple');
|
|
204
|
+
setIsOpen(false);
|
|
205
|
+
}}
|
|
206
|
+
>
|
|
207
|
+
<div className="flex flex-col gap-1">
|
|
208
|
+
<div className="font-medium">Simple Error</div>
|
|
209
|
+
<div className="text-xs text-muted-foreground">
|
|
210
|
+
Single field validation
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</DropdownMenuItem>
|
|
214
|
+
|
|
215
|
+
<DropdownMenuItem
|
|
216
|
+
onClick={() => {
|
|
217
|
+
dispatchValidationError('multiple');
|
|
218
|
+
setIsOpen(false);
|
|
219
|
+
}}
|
|
220
|
+
>
|
|
221
|
+
<div className="flex flex-col gap-1">
|
|
222
|
+
<div className="font-medium">Multiple Errors</div>
|
|
223
|
+
<div className="text-xs text-muted-foreground">
|
|
224
|
+
3 validation errors
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</DropdownMenuItem>
|
|
228
|
+
|
|
229
|
+
<DropdownMenuItem
|
|
230
|
+
onClick={() => {
|
|
231
|
+
dispatchValidationError('nested');
|
|
232
|
+
setIsOpen(false);
|
|
233
|
+
}}
|
|
234
|
+
>
|
|
235
|
+
<div className="flex flex-col gap-1">
|
|
236
|
+
<div className="font-medium">Nested Object</div>
|
|
237
|
+
<div className="text-xs text-muted-foreground">
|
|
238
|
+
Nested property errors
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</DropdownMenuItem>
|
|
242
|
+
|
|
243
|
+
<DropdownMenuItem
|
|
244
|
+
onClick={() => {
|
|
245
|
+
dispatchValidationError('array');
|
|
246
|
+
setIsOpen(false);
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
<div className="flex flex-col gap-1">
|
|
250
|
+
<div className="font-medium">Array Validation</div>
|
|
251
|
+
<div className="text-xs text-muted-foreground">
|
|
252
|
+
Array item errors
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</DropdownMenuItem>
|
|
256
|
+
|
|
257
|
+
<DropdownMenuSeparator />
|
|
258
|
+
|
|
259
|
+
<div className="px-2 py-1.5 text-xs text-muted-foreground">
|
|
260
|
+
๐ก Check console and toast notifications
|
|
261
|
+
</div>
|
|
262
|
+
</DropdownMenuContent>
|
|
263
|
+
</DropdownMenu>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
@@ -7,3 +7,5 @@ export type { HeaderProps } from './Header';
|
|
|
7
7
|
export { HeaderMobile } from './HeaderMobile';
|
|
8
8
|
export { HeaderDesktop } from './HeaderDesktop';
|
|
9
9
|
export { CopyAIButton } from './CopyAIButton';
|
|
10
|
+
export { TestValidationButton } from './TestValidationButton';
|
|
11
|
+
export type { TestValidationButtonProps } from './TestValidationButton';
|
package/src/utils/logger.ts
CHANGED
|
@@ -13,9 +13,11 @@ import { createConsola } from 'consola';
|
|
|
13
13
|
* - 5: trace, verbose
|
|
14
14
|
*/
|
|
15
15
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
16
|
+
const isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === 'true';
|
|
17
|
+
const showLogs = isDevelopment || isStaticBuild;
|
|
16
18
|
|
|
17
19
|
export const logger = createConsola({
|
|
18
|
-
level:
|
|
20
|
+
level: showLogs ? 4 : 1, // dev: debug, production: errors only
|
|
19
21
|
}).withTag('layouts');
|
|
20
22
|
|
|
21
23
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|