@djangocfg/layouts 1.2.33 → 1.2.35
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/layouts/AppLayout/AppLayout.tsx +10 -11
- package/src/layouts/AppLayout/components/PackageVersions/packageVersions.config.ts +8 -8
- package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +45 -59
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +98 -0
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +149 -0
- package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +2 -2
- package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +2 -0
- package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +48 -0
- package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +2 -0
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +20 -12
- package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +4 -0
- package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +1 -0
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +24 -55
- package/src/layouts/UILayout/config/components/tools.config.tsx +31 -1
- package/src/validation/README.md +88 -2
- package/src/validation/REFACTORING.md +162 -0
- package/src/validation/ValidationErrorButtons.tsx +80 -0
- package/src/validation/ValidationErrorToast.tsx +7 -77
- package/src/validation/curl-generator.ts +118 -0
- package/src/validation/index.ts +12 -1
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import React from 'react';
|
|
11
11
|
import { ToastAction } from '@djangocfg/ui';
|
|
12
12
|
import type { ZodError } from 'zod';
|
|
13
|
+
import { ValidationErrorButtons } from './ValidationErrorButtons';
|
|
13
14
|
|
|
14
15
|
export interface ValidationErrorDetail {
|
|
15
16
|
operation: string;
|
|
@@ -89,34 +90,6 @@ export function formatErrorForClipboard(detail: ValidationErrorDetail): string {
|
|
|
89
90
|
return JSON.stringify(errorData, null, 2);
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
/**
|
|
93
|
-
* Copy text to clipboard
|
|
94
|
-
*/
|
|
95
|
-
async function copyToClipboard(text: string): Promise<boolean> {
|
|
96
|
-
if (typeof window === 'undefined') return false;
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
100
|
-
await navigator.clipboard.writeText(text);
|
|
101
|
-
return true;
|
|
102
|
-
} else {
|
|
103
|
-
// Fallback for older browsers
|
|
104
|
-
const textarea = document.createElement('textarea');
|
|
105
|
-
textarea.value = text;
|
|
106
|
-
textarea.style.position = 'fixed';
|
|
107
|
-
textarea.style.opacity = '0';
|
|
108
|
-
document.body.appendChild(textarea);
|
|
109
|
-
textarea.select();
|
|
110
|
-
const success = document.execCommand('copy');
|
|
111
|
-
document.body.removeChild(textarea);
|
|
112
|
-
return success;
|
|
113
|
-
}
|
|
114
|
-
} catch (error) {
|
|
115
|
-
console.error('Failed to copy to clipboard:', error);
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
93
|
/**
|
|
121
94
|
* Build toast title from validation error
|
|
122
95
|
*/
|
|
@@ -165,49 +138,6 @@ export function buildToastDescription(
|
|
|
165
138
|
return descriptionParts.join(' • ');
|
|
166
139
|
}
|
|
167
140
|
|
|
168
|
-
/**
|
|
169
|
-
* Create copy action button for toast
|
|
170
|
-
*
|
|
171
|
-
* @param detail - Validation error details
|
|
172
|
-
* @param onCopySuccess - Optional callback when copy succeeds
|
|
173
|
-
* @param onCopyError - Optional callback when copy fails
|
|
174
|
-
*/
|
|
175
|
-
export function createCopyAction(
|
|
176
|
-
detail: ValidationErrorDetail,
|
|
177
|
-
onCopySuccess?: () => void,
|
|
178
|
-
onCopyError?: (error: Error) => void
|
|
179
|
-
): React.ReactElement<typeof ToastAction> {
|
|
180
|
-
const handleCopy = async (e: React.MouseEvent) => {
|
|
181
|
-
e.preventDefault();
|
|
182
|
-
e.stopPropagation();
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
const formattedError = formatErrorForClipboard(detail);
|
|
186
|
-
const success = await copyToClipboard(formattedError);
|
|
187
|
-
|
|
188
|
-
if (success) {
|
|
189
|
-
console.log('✅ Validation error copied to clipboard');
|
|
190
|
-
onCopySuccess?.();
|
|
191
|
-
} else {
|
|
192
|
-
throw new Error('Clipboard API failed');
|
|
193
|
-
}
|
|
194
|
-
} catch (error) {
|
|
195
|
-
console.error('❌ Failed to copy validation error:', error);
|
|
196
|
-
onCopyError?.(error as Error);
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
return (
|
|
201
|
-
<ToastAction
|
|
202
|
-
altText="Copy error details"
|
|
203
|
-
onClick={handleCopy}
|
|
204
|
-
className="shrink-0"
|
|
205
|
-
>
|
|
206
|
-
📋 Copy
|
|
207
|
-
</ToastAction>
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
141
|
/**
|
|
212
142
|
* Create complete toast options for validation error
|
|
213
143
|
*
|
|
@@ -239,13 +169,13 @@ export function createValidationErrorToast(
|
|
|
239
169
|
|
|
240
170
|
return {
|
|
241
171
|
title: buildToastTitle(detail, config),
|
|
242
|
-
description:
|
|
172
|
+
description: (
|
|
173
|
+
<div className="flex flex-col gap-2">
|
|
174
|
+
<div>{buildToastDescription(detail, config)}</div>
|
|
175
|
+
<ValidationErrorButtons detail={detail} />
|
|
176
|
+
</div>
|
|
177
|
+
),
|
|
243
178
|
variant: 'destructive' as const,
|
|
244
179
|
duration: options?.duration,
|
|
245
|
-
action: createCopyAction(
|
|
246
|
-
detail,
|
|
247
|
-
options?.onCopySuccess,
|
|
248
|
-
options?.onCopyError
|
|
249
|
-
),
|
|
250
180
|
};
|
|
251
181
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cURL Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates cURL commands from API request details with authentication token
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface CurlOptions {
|
|
8
|
+
method: string;
|
|
9
|
+
path: string;
|
|
10
|
+
token?: string;
|
|
11
|
+
body?: any;
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
queryParams?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get authentication token from localStorage
|
|
19
|
+
*/
|
|
20
|
+
export function getAuthToken(): string | null {
|
|
21
|
+
if (typeof window === 'undefined') return null;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// Priority order: access_token > token > auth_token
|
|
25
|
+
const token = localStorage.getItem('access_token') ||
|
|
26
|
+
localStorage.getItem('token') ||
|
|
27
|
+
localStorage.getItem('auth_token');
|
|
28
|
+
|
|
29
|
+
return token;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Failed to get auth token:', error);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format headers for cURL command
|
|
38
|
+
*/
|
|
39
|
+
function formatHeaders(headers: Record<string, string>): string[] {
|
|
40
|
+
return Object.entries(headers).map(
|
|
41
|
+
([key, value]) => `-H '${key}: ${value}'`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Escape single quotes in string for shell
|
|
47
|
+
*/
|
|
48
|
+
function escapeShell(str: string): string {
|
|
49
|
+
return str.replace(/'/g, "'\\''");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate cURL command from request details
|
|
54
|
+
*/
|
|
55
|
+
export function generateCurl(options: CurlOptions): string {
|
|
56
|
+
const {
|
|
57
|
+
method,
|
|
58
|
+
path,
|
|
59
|
+
token = getAuthToken() || undefined, // Auto-fetch if not provided
|
|
60
|
+
body,
|
|
61
|
+
headers = {},
|
|
62
|
+
baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
|
|
63
|
+
} = options;
|
|
64
|
+
|
|
65
|
+
const curlParts: string[] = ['curl'];
|
|
66
|
+
|
|
67
|
+
// Build URL
|
|
68
|
+
const url = `${baseUrl}${path}`;
|
|
69
|
+
curlParts.push(`'${url}'`);
|
|
70
|
+
|
|
71
|
+
// Add method if not GET
|
|
72
|
+
if (method.toUpperCase() !== 'GET') {
|
|
73
|
+
curlParts.push(`-X ${method.toUpperCase()}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Default headers
|
|
77
|
+
const allHeaders: Record<string, string> = {
|
|
78
|
+
'Accept': '*/*',
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
...headers,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Add Authorization header if token exists
|
|
84
|
+
if (token) {
|
|
85
|
+
allHeaders['Authorization'] = `Bearer ${token}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Add all headers
|
|
89
|
+
const headerStrings = formatHeaders(allHeaders);
|
|
90
|
+
curlParts.push(...headerStrings);
|
|
91
|
+
|
|
92
|
+
// Add body for non-GET requests
|
|
93
|
+
if (body && method.toUpperCase() !== 'GET') {
|
|
94
|
+
const bodyJson = typeof body === 'string'
|
|
95
|
+
? body
|
|
96
|
+
: JSON.stringify(body, null, 2);
|
|
97
|
+
curlParts.push(`-d '${escapeShell(bodyJson)}'`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Join with line continuation
|
|
101
|
+
return curlParts.join(' \\\n ');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Generate cURL from validation error details
|
|
106
|
+
* Auto-fetches token from localStorage
|
|
107
|
+
*/
|
|
108
|
+
export function generateCurlFromError(detail: {
|
|
109
|
+
method: string;
|
|
110
|
+
path: string;
|
|
111
|
+
response?: any;
|
|
112
|
+
}): string {
|
|
113
|
+
return generateCurl({
|
|
114
|
+
method: detail.method,
|
|
115
|
+
path: detail.path,
|
|
116
|
+
// token is auto-fetched in generateCurl
|
|
117
|
+
});
|
|
118
|
+
}
|
package/src/validation/index.ts
CHANGED
|
@@ -16,10 +16,21 @@ export {
|
|
|
16
16
|
|
|
17
17
|
export {
|
|
18
18
|
createValidationErrorToast,
|
|
19
|
-
createCopyAction,
|
|
20
19
|
buildToastTitle,
|
|
21
20
|
buildToastDescription,
|
|
22
21
|
formatZodIssuesForToast,
|
|
23
22
|
formatErrorForClipboard,
|
|
24
23
|
type ValidationErrorToastConfig,
|
|
25
24
|
} from './ValidationErrorToast';
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
ValidationErrorButtons,
|
|
28
|
+
type ValidationErrorButtonsProps,
|
|
29
|
+
} from './ValidationErrorButtons';
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
generateCurl,
|
|
33
|
+
generateCurlFromError,
|
|
34
|
+
getAuthToken,
|
|
35
|
+
type CurlOptions,
|
|
36
|
+
} from './curl-generator';
|