xertica-ui 2.3.0 → 2.4.1
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/CHANGELOG.md +22 -2
- package/README.md +33 -22
- package/bin/cli.ts +136 -47
- package/bin/language-config.ts +5 -8
- package/components/assistant/modern-chat-input/ModernChatInput.tsx +17 -7
- package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +1 -3
- package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +13 -3
- package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +10 -6
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +1 -3
- package/components/blocks/card-patterns/FeatureCardSkeleton.tsx +1 -6
- package/components/blocks/card-patterns/ProfileCard.tsx +1 -3
- package/components/blocks/card-patterns/ProjectCardSkeleton.tsx +1 -6
- package/components/brand/language-selector/language-selector.stories.tsx +1 -4
- package/components/brand/theme-toggle/ThemeToggle.tsx +5 -1
- package/components/brand/xertica-provider/XerticaProvider.tsx +1 -4
- package/components/index.ts +1 -5
- package/components/layout/sidebar/sidebar.tsx +9 -3
- package/components/media/audio-player/AudioPlayer.tsx +4 -2
- package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +188 -188
- package/components/pages/home-content/HomeContent.tsx +55 -55
- package/components/pages/home-page/HomePage.tsx +5 -1
- package/components/pages/login-page/LoginPage.tsx +4 -2
- package/components/pages/reset-password-page/ResetPasswordPage.tsx +7 -3
- package/components/pages/template-content/TemplateContent.tsx +268 -149
- package/components/pages/verify-email-page/VerifyEmailPage.tsx +9 -9
- package/components/shared/error-boundary.stories.tsx +114 -132
- package/components/shared/error-boundary.tsx +150 -154
- package/components/shared/error-fallbacks.tsx +222 -226
- package/components/ui/stats-card/stats-card-skeleton.tsx +1 -3
- package/components/ui/stats-card/stats-card.stories.tsx +18 -0
- package/components/ui/stats-card/stats-card.tsx +18 -2
- package/components.json +512 -892
- package/contexts/AuthContext.tsx +121 -118
- package/contexts/LanguageContext.tsx +1 -2
- package/dist/AssistantChart-BKVtGUKF.js +3383 -0
- package/dist/AssistantChart-WeycT5Pd.cjs +3551 -0
- package/dist/VerifyEmailPage-Bp1XXl3H.cjs +3305 -0
- package/dist/VerifyEmailPage-DGhuIqkb.js +3296 -0
- package/dist/XerticaProvider-BErr83Bg.js +42 -0
- package/dist/XerticaProvider-CwOkHxiT.cjs +44 -0
- package/dist/XerticaXLogo-BX3ueACh.js +255 -0
- package/dist/XerticaXLogo-qBPhwK3g.cjs +260 -0
- package/dist/assistant.cjs.js +1 -1
- package/dist/assistant.es.js +1 -1
- package/dist/brand.cjs.js +2 -2
- package/dist/brand.es.js +2 -2
- package/dist/cli.js +90 -37
- package/dist/components/brand/theme-toggle/ThemeToggle.d.ts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/ui/stats-card/stats-card.d.ts +10 -0
- package/dist/index.cjs.js +6 -6
- package/dist/index.es.js +6 -6
- package/dist/layout.cjs.js +1 -1
- package/dist/layout.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/sidebar-B4ZWaMrE.js +792 -0
- package/dist/sidebar-BS1p2V7t.cjs +795 -0
- package/dist/ui.cjs.js +1 -1
- package/dist/ui.es.js +1 -1
- package/dist/xertica-assistant-B1NaSFFj.js +2173 -0
- package/dist/xertica-assistant-CIaUlbIt.cjs +2180 -0
- package/dist/xertica-ui.css +1 -1
- package/docs/architecture-improvements.md +5 -5
- package/docs/architecture.md +16 -10
- package/docs/components/card-patterns.md +19 -17
- package/docs/components/error-boundary.md +201 -191
- package/docs/components/hooks.md +15 -13
- package/docs/components/language-selector.md +20 -16
- package/docs/components/pages.md +323 -309
- package/docs/components/stats-card.md +20 -2
- package/docs/doc-audit.md +12 -11
- package/docs/getting-started.md +41 -28
- package/docs/guidelines.md +14 -12
- package/docs/i18n.md +61 -57
- package/docs/installation.md +268 -267
- package/docs/llms.md +17 -17
- package/docs/state-management.md +17 -17
- package/guidelines/Guidelines.md +17 -14
- package/llms-compact.txt +1 -1
- package/llms-full.txt +11553 -7133
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/styles/xertica/base.css +90 -84
- package/templates/CLAUDE.md +16 -1
- package/templates/guidelines/Guidelines.md +42 -18
- package/templates/package.json +3 -3
- package/templates/src/app/components/AuthGuard.tsx +131 -82
- package/templates/src/features/auth/ui/AuthPageShell.tsx +32 -32
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +1 -3
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +6 -2
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +2 -6
- package/templates/src/features/home/data/mock.ts +41 -35
- package/templates/src/features/home/ui/HomeContent.tsx +62 -64
- package/templates/src/features/template/ui/CrudTemplate.tsx +1 -4
- package/templates/src/features/template/ui/LoginTemplate.tsx +1 -1
- package/templates/src/features/template/ui/TemplateContent.tsx +28 -20
- package/templates/src/locales/en/pages/templates.json +17 -17
- package/templates/src/locales/es/pages/templates.json +17 -17
- package/templates/src/locales/pt-BR/pages/templates.json +17 -17
- package/templates/src/pages/AssistantPage.tsx +26 -20
- package/templates/src/pages/HomePage.tsx +5 -1
- package/templates/src/shared/error-boundary.tsx +150 -154
- package/templates/src/shared/error-fallbacks.tsx +222 -226
- package/templates/vite.config.ts +12 -9
package/contexts/AuthContext.tsx
CHANGED
|
@@ -1,118 +1,121 @@
|
|
|
1
|
-
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
|
2
|
-
import { useNavigate } from 'react-router-dom';
|
|
3
|
-
|
|
4
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
-
// Types
|
|
6
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
-
|
|
8
|
-
export interface AuthUser {
|
|
9
|
-
email: string;
|
|
10
|
-
name?: string;
|
|
11
|
-
avatar?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface AuthContextValue {
|
|
15
|
-
/** Currently authenticated user, or null when logged out */
|
|
16
|
-
user: AuthUser | null;
|
|
17
|
-
/** True while the initial session hydration from localStorage is pending */
|
|
18
|
-
isLoading: boolean;
|
|
19
|
-
/** Attempt login — returns true on success, false on invalid credentials */
|
|
20
|
-
login: (email: string, password: string) => boolean;
|
|
21
|
-
/** Clear session and redirect to /login */
|
|
22
|
-
logout: () => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
26
|
-
// Context
|
|
27
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
const AuthContext = createContext<AuthContextValue | null>(null);
|
|
30
|
-
|
|
31
|
-
const STORAGE_KEY = 'xertica_user';
|
|
32
|
-
const AUTH_PATHS = ['/login', '/forgot-password', '/verify-email', '/reset-password'];
|
|
33
|
-
|
|
34
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
|
-
// Provider
|
|
36
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* `AuthProvider` — manages authentication state for the devops app.
|
|
40
|
-
*
|
|
41
|
-
* Must be rendered **inside** `<BrowserRouter>` so that `useNavigate` is available.
|
|
42
|
-
* Wrap your `<Routes>` tree with this provider to give all pages access to
|
|
43
|
-
* `user`, `login`, and `logout` via `useAuth()` without prop-drilling.
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```tsx
|
|
47
|
-
* <Router>
|
|
48
|
-
* <AuthProvider>
|
|
49
|
-
* <AppRoutes />
|
|
50
|
-
* </AuthProvider>
|
|
51
|
-
* </Router>
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
55
|
-
const [user, setUser] = useState<AuthUser | null>(null);
|
|
56
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
57
|
-
const navigate = useNavigate();
|
|
58
|
-
|
|
59
|
-
// Hydrate session from localStorage on mount
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
try {
|
|
62
|
-
const raw = localStorage.getItem(STORAGE_KEY);
|
|
63
|
-
if (raw) setUser(JSON.parse(raw));
|
|
64
|
-
} catch {
|
|
65
|
-
localStorage.removeItem(STORAGE_KEY);
|
|
66
|
-
} finally {
|
|
67
|
-
setIsLoading(false);
|
|
68
|
-
}
|
|
69
|
-
}, []);
|
|
70
|
-
|
|
71
|
-
// Redirect authenticated users away from auth-only pages
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
if (!isLoading && user && AUTH_PATHS.includes(window.location.pathname)) {
|
|
74
|
-
navigate('/home', { replace: true });
|
|
75
|
-
}
|
|
76
|
-
}, [isLoading, user, navigate]);
|
|
77
|
-
|
|
78
|
-
const login = useCallback(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
1
|
+
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
// Types
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export interface AuthUser {
|
|
9
|
+
email: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
avatar?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface AuthContextValue {
|
|
15
|
+
/** Currently authenticated user, or null when logged out */
|
|
16
|
+
user: AuthUser | null;
|
|
17
|
+
/** True while the initial session hydration from localStorage is pending */
|
|
18
|
+
isLoading: boolean;
|
|
19
|
+
/** Attempt login — returns true on success, false on invalid credentials */
|
|
20
|
+
login: (email: string, password: string) => boolean;
|
|
21
|
+
/** Clear session and redirect to /login */
|
|
22
|
+
logout: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
26
|
+
// Context
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
const AuthContext = createContext<AuthContextValue | null>(null);
|
|
30
|
+
|
|
31
|
+
const STORAGE_KEY = 'xertica_user';
|
|
32
|
+
const AUTH_PATHS = ['/login', '/forgot-password', '/verify-email', '/reset-password'];
|
|
33
|
+
|
|
34
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
|
+
// Provider
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* `AuthProvider` — manages authentication state for the devops app.
|
|
40
|
+
*
|
|
41
|
+
* Must be rendered **inside** `<BrowserRouter>` so that `useNavigate` is available.
|
|
42
|
+
* Wrap your `<Routes>` tree with this provider to give all pages access to
|
|
43
|
+
* `user`, `login`, and `logout` via `useAuth()` without prop-drilling.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* <Router>
|
|
48
|
+
* <AuthProvider>
|
|
49
|
+
* <AppRoutes />
|
|
50
|
+
* </AuthProvider>
|
|
51
|
+
* </Router>
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
55
|
+
const [user, setUser] = useState<AuthUser | null>(null);
|
|
56
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
57
|
+
const navigate = useNavigate();
|
|
58
|
+
|
|
59
|
+
// Hydrate session from localStorage on mount
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
try {
|
|
62
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
63
|
+
if (raw) setUser(JSON.parse(raw));
|
|
64
|
+
} catch {
|
|
65
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
66
|
+
} finally {
|
|
67
|
+
setIsLoading(false);
|
|
68
|
+
}
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
// Redirect authenticated users away from auth-only pages
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!isLoading && user && AUTH_PATHS.includes(window.location.pathname)) {
|
|
74
|
+
navigate('/home', { replace: true });
|
|
75
|
+
}
|
|
76
|
+
}, [isLoading, user, navigate]);
|
|
77
|
+
|
|
78
|
+
const login = useCallback(
|
|
79
|
+
(email: string, password: string): boolean => {
|
|
80
|
+
if (!email.trim() || !password.trim()) return false;
|
|
81
|
+
const userData: AuthUser = { email };
|
|
82
|
+
setUser(userData);
|
|
83
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(userData));
|
|
84
|
+
navigate('/home');
|
|
85
|
+
return true;
|
|
86
|
+
},
|
|
87
|
+
[navigate]
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const logout = useCallback(() => {
|
|
91
|
+
setUser(null);
|
|
92
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
93
|
+
navigate('/login');
|
|
94
|
+
}, [navigate]);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<AuthContext.Provider value={{ user, isLoading, login, logout }}>
|
|
98
|
+
{children}
|
|
99
|
+
</AuthContext.Provider>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
// Hook
|
|
105
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* `useAuth` — consume authentication state anywhere inside `<AuthProvider>`.
|
|
109
|
+
*
|
|
110
|
+
* @throws if called outside `<AuthProvider>`
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```tsx
|
|
114
|
+
* const { user, logout } = useAuth();
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function useAuth(): AuthContextValue {
|
|
118
|
+
const ctx = useContext(AuthContext);
|
|
119
|
+
if (!ctx) throw new Error('useAuth must be used within <AuthProvider>');
|
|
120
|
+
return ctx;
|
|
121
|
+
}
|
|
@@ -160,8 +160,7 @@ export function LanguageProvider({
|
|
|
160
160
|
const resolvedDefault = defaultLanguage ?? languages[0].code;
|
|
161
161
|
|
|
162
162
|
const [language, setLanguageState] = useState<Language>(() => {
|
|
163
|
-
const saved =
|
|
164
|
-
typeof window !== 'undefined' ? window.localStorage?.getItem(STORAGE_KEY) : null;
|
|
163
|
+
const saved = typeof window !== 'undefined' ? window.localStorage?.getItem(STORAGE_KEY) : null;
|
|
165
164
|
|
|
166
165
|
// Direct match against configured languages
|
|
167
166
|
if (saved && languages.some(l => l.code === saved)) {
|