@lastbrain/ai-ui-react 1.0.47 → 1.0.49

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 +1 @@
1
- {"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAkBtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,2CA+9BrB"}
1
+ {"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAoBtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,2CAiiCrB"}
@@ -2,9 +2,10 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useRef, useLayoutEffect } from "react";
4
4
  import { createPortal } from "react-dom";
5
- import { BarChart3, Settings, FileText, History as HistoryIcon, FolderPlus, Power, LogOut, Key, } from "lucide-react";
5
+ import { BarChart3, Settings, FileText, History as HistoryIcon, FolderPlus, Power, LogOut, Key, RefreshCw, } from "lucide-react";
6
6
  import { aiStyles, calculateTooltipPosition } from "../styles/inline";
7
7
  import { useLB } from "../context/LBAuthProvider";
8
+ import { useAiContext } from "../context/AiProvider";
8
9
  import { LBSigninModal } from "./LBSigninModal";
9
10
  import { LBApiKeySelector } from "./LBApiKeySelector";
10
11
  export function AiStatusButton({ status, loading = false, className = "", }) {
@@ -30,6 +31,16 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
30
31
  user = undefined;
31
32
  logout = undefined;
32
33
  }
34
+ // Récupérer refetchProviders depuis AiProvider si disponible
35
+ let refetchProviders;
36
+ try {
37
+ const aiContext = useAiContext();
38
+ refetchProviders = aiContext.refetchProviders;
39
+ }
40
+ catch {
41
+ // AiProvider n'est pas disponible, ignorer
42
+ refetchProviders = undefined;
43
+ }
33
44
  const [showSigninModal, setShowSigninModal] = useState(false);
34
45
  const [showApiKeySelector, setShowApiKeySelector] = useState(false);
35
46
  const formatNumber = (value) => typeof value === "number" ? value.toLocaleString() : "0";
@@ -202,7 +213,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
202
213
  gap: "8px",
203
214
  borderTop: "1px solid var(--ai-border-primary, #374151)",
204
215
  paddingTop: "12px",
205
- }, children: [_jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/metrics", "_blank"), style: {
216
+ }, children: [_jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/dashboard", "_blank"), style: {
206
217
  flex: 1,
207
218
  background: "transparent",
208
219
  border: "none",
@@ -221,7 +232,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
221
232
  Object.assign(e.currentTarget.style, {
222
233
  background: "transparent",
223
234
  });
224
- }, title: "View Metrics", children: _jsx(BarChart3, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/settings", "_blank"), style: {
235
+ }, title: "View Metrics", children: _jsx(BarChart3, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/settings", "_blank"), style: {
225
236
  flex: 1,
226
237
  background: "transparent",
227
238
  border: "none",
@@ -240,7 +251,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
240
251
  Object.assign(e.currentTarget.style, {
241
252
  background: "transparent",
242
253
  });
243
- }, title: "Settings", children: _jsx(Settings, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/prompts", "_blank"), style: {
254
+ }, title: "Settings", children: _jsx(Settings, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/prompts", "_blank"), style: {
244
255
  flex: 1,
245
256
  background: "transparent",
246
257
  border: "none",
@@ -281,6 +292,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
281
292
  }, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }), _jsx("button", { onClick: async () => {
282
293
  if (logout) {
283
294
  await logout();
295
+ // Refresh provider data after logout
296
+ if (refetchProviders) {
297
+ await refetchProviders();
298
+ }
284
299
  }
285
300
  setShowTooltip(false);
286
301
  }, style: {
@@ -354,12 +369,39 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
354
369
  textOverflow: "ellipsis",
355
370
  }, children: status.user.email })] }) })), _jsxs("div", { style: {
356
371
  ...aiStyles.tooltipSection,
357
- ...(status.user?.email
358
- ? {}
359
- : aiStyles.tooltipSectionFirst),
360
- }, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsx("span", { style: aiStyles.tooltipValue, children: status.apiKey?.name ||
361
- status.api_key?.name ||
362
- "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("span", { style: aiStyles.tooltipValue, children: status.api_key?.env || "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [status.api_key?.rate_limit_rpm || 0, " req/min"] })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Balance" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: ["$", formatFixed(balanceUsed, 6), " / $", formatNumber(balanceTotal)] }), renderUsageCircle(balancePercentage)] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Storage" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [formatStorage(storageUsed), " /", " ", formatStorage(storageAllocated)] }), renderUsageCircle(storagePercentage)] })] }), _jsxs("div", { style: {
372
+ ...(status.user?.email ? {} : aiStyles.tooltipSectionFirst),
373
+ }, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsxs("div", { style: {
374
+ display: "flex",
375
+ alignItems: "center",
376
+ gap: "8px",
377
+ }, children: [_jsx("span", { style: aiStyles.tooltipValue, children: status.apiKey?.name || status.api_key?.name || "Unknown" }), apiKeys.length > 1 && selectApiKeyWithToken && (_jsx("button", { onClick: (e) => {
378
+ e.stopPropagation();
379
+ setShowApiKeySelector(true);
380
+ }, style: {
381
+ background: "rgba(139, 92, 246, 0.1)",
382
+ border: "1px solid rgba(139, 92, 246, 0.3)",
383
+ borderRadius: "50%",
384
+ width: "24px",
385
+ height: "24px",
386
+ display: "flex",
387
+ alignItems: "center",
388
+ justifyContent: "center",
389
+ cursor: "pointer",
390
+ padding: 0,
391
+ transition: "all 0.2s ease",
392
+ }, onMouseEnter: (e) => {
393
+ e.currentTarget.style.background =
394
+ "rgba(139, 92, 246, 0.2)";
395
+ e.currentTarget.style.borderColor =
396
+ "rgba(139, 92, 246, 0.5)";
397
+ }, onMouseLeave: (e) => {
398
+ e.currentTarget.style.background =
399
+ "rgba(139, 92, 246, 0.1)";
400
+ e.currentTarget.style.borderColor =
401
+ "rgba(139, 92, 246, 0.3)";
402
+ }, title: "Change API Key", children: _jsx(RefreshCw, { size: 12, style: { color: "rgba(139, 92, 246, 1)" } }) }))] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("span", { style: aiStyles.tooltipValue, children: status.apiKey?.env || status.api_key?.env || "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [status.apiKey?.rate_limit_rpm ||
403
+ status.api_key?.rate_limit_rpm ||
404
+ 0, " ", "req/min"] })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Balance" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: ["$", formatFixed(balanceUsed, 6), " / $", formatNumber(balanceTotal)] }), renderUsageCircle(balancePercentage)] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Storage" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [formatStorage(storageUsed), " /", " ", formatStorage(storageAllocated)] }), renderUsageCircle(storagePercentage)] })] }), _jsxs("div", { style: {
363
405
  ...aiStyles.tooltipActions,
364
406
  width: "100%",
365
407
  flexDirection: "row",
@@ -479,6 +521,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
479
521
  try {
480
522
  await logout();
481
523
  setShowTooltip(false);
524
+ // Refresh provider data after logout
525
+ if (refetchProviders) {
526
+ await refetchProviders();
527
+ }
482
528
  }
483
529
  catch (error) {
484
530
  console.error("Logout failed:", error);
@@ -506,6 +552,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
506
552
  await selectApiKeyWithToken(keyId);
507
553
  setShowApiKeySelector(false);
508
554
  setShowTooltip(false);
555
+ // Refresh provider data after API key selection
556
+ if (refetchProviders) {
557
+ await refetchProviders();
558
+ }
509
559
  }
510
560
  catch (error) {
511
561
  console.error("Failed to select API key:", error);
@@ -12,6 +12,8 @@ interface LBProviderProps {
12
12
  proxyUrl?: string;
13
13
  /** Fonction appelée lors des changements d'état */
14
14
  onStatusChange?: (status: LBAuthState["status"]) => void;
15
+ /** Fonction appelée après signin/logout pour refresh les providers */
16
+ onAuthChange?: () => void;
15
17
  }
16
18
  interface LBContextValue extends LBAuthState {
17
19
  /** Fonction de connexion */
@@ -35,7 +37,7 @@ interface LBContextValue extends LBAuthState {
35
37
  /** Access token temporaire (après login) */
36
38
  accessToken?: string;
37
39
  }
38
- export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
40
+ export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, onAuthChange, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
39
41
  /**
40
42
  * Hook pour accéder au contexte LastBrain
41
43
  */
@@ -1 +1 @@
1
- {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIT,MAAM,uBAAuB,CAAC;AAE/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;CAC1D;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,GACf,EAAE,eAAe,2CA0UjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
1
+ {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIT,MAAM,uBAAuB,CAAC;AAE/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CA6WjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
@@ -6,7 +6,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
6
6
  */
7
7
  import { createContext, useContext, useEffect, useCallback, useState, } from "react";
8
8
  const LBContext = createContext(undefined);
9
- export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", proxyUrl = "/api/lastbrain", onStatusChange, }) {
9
+ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", proxyUrl = "/api/lastbrain", onStatusChange, onAuthChange, }) {
10
10
  const [state, setState] = useState({
11
11
  status: "loading",
12
12
  });
@@ -22,14 +22,46 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
22
22
  });
23
23
  if (response.ok) {
24
24
  const session = await response.json();
25
- setState({
26
- status: "ready",
27
- session,
28
- user: {
29
- id: session.userId,
30
- email: "", // Sera rempli par une autre requête si nécessaire
31
- },
32
- });
25
+ // Récupérer les infos utilisateur depuis /auth/status
26
+ try {
27
+ const statusResponse = await fetch(`${proxyUrl}/auth/status`, {
28
+ credentials: "include",
29
+ });
30
+ if (statusResponse.ok) {
31
+ const statusData = await statusResponse.json();
32
+ setState({
33
+ status: "ready",
34
+ session,
35
+ user: {
36
+ id: session.userId,
37
+ email: statusData.user?.email || "",
38
+ },
39
+ });
40
+ }
41
+ else {
42
+ // Fallback sans email
43
+ setState({
44
+ status: "ready",
45
+ session,
46
+ user: {
47
+ id: session.userId,
48
+ email: "",
49
+ },
50
+ });
51
+ }
52
+ }
53
+ catch (statusError) {
54
+ console.error("[LBProvider] Failed to fetch status:", statusError);
55
+ // Fallback sans email
56
+ setState({
57
+ status: "ready",
58
+ session,
59
+ user: {
60
+ id: session.userId,
61
+ email: "",
62
+ },
63
+ });
64
+ }
33
65
  onStatusChange?.("ready");
34
66
  }
35
67
  else {
@@ -114,6 +146,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
114
146
  setAccessToken(undefined); // Nettoyer l'access token temporaire
115
147
  setApiKeys([]); // Nettoyer les clés API temporaires
116
148
  onStatusChange?.("ready");
149
+ onAuthChange?.(); // Refresh provider after signin
117
150
  }
118
151
  catch (error) {
119
152
  const message = error instanceof Error ? error.message : "Failed to select API key";
@@ -123,7 +156,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
123
156
  });
124
157
  throw error;
125
158
  }
126
- }, [proxyUrl, state.user, onStatusChange]);
159
+ }, [proxyUrl, state.user, onStatusChange, onAuthChange]);
127
160
  /**
128
161
  * Connexion utilisateur (étape 1 : login)
129
162
  * Retourne le token et les clés API sans créer de session
@@ -248,8 +281,9 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
248
281
  setApiKeys([]);
249
282
  setAccessToken(undefined);
250
283
  onStatusChange?.("needs_auth");
284
+ onAuthChange?.(); // Refresh provider after logout
251
285
  }
252
- }, [proxyUrl, onStatusChange]);
286
+ }, [proxyUrl, onStatusChange, onAuthChange]);
253
287
  /**
254
288
  * Recharge la session
255
289
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "Headless React components for LastBrain AI UI Kit",
5
5
  "private": false,
6
6
  "type": "module",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "lucide-react": "^0.257.0",
51
- "@lastbrain/ai-ui-core": "1.0.38"
51
+ "@lastbrain/ai-ui-core": "1.0.39"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/react": "^19.2.0",
@@ -12,9 +12,11 @@ import {
12
12
  Power,
13
13
  LogOut,
14
14
  Key,
15
+ RefreshCw,
15
16
  } from "lucide-react";
16
17
  import { aiStyles, calculateTooltipPosition } from "../styles/inline";
17
18
  import { useLB } from "../context/LBAuthProvider";
19
+ import { useAiContext } from "../context/AiProvider";
18
20
  import { LBSigninModal } from "./LBSigninModal";
19
21
  import { LBApiKeySelector } from "./LBApiKeySelector";
20
22
 
@@ -35,9 +37,7 @@ export function AiStatusButton({
35
37
  let logout: (() => Promise<void>) | undefined;
36
38
  let apiKeys: any[] = [];
37
39
  let accessToken: string | undefined;
38
- let selectApiKeyWithToken:
39
- | ((apiKeyId: string) => Promise<void>)
40
- | undefined;
40
+ let selectApiKeyWithToken: ((apiKeyId: string) => Promise<void>) | undefined;
41
41
 
42
42
  try {
43
43
  const lbContext = useLB();
@@ -54,6 +54,16 @@ export function AiStatusButton({
54
54
  logout = undefined;
55
55
  }
56
56
 
57
+ // Récupérer refetchProviders depuis AiProvider si disponible
58
+ let refetchProviders: (() => Promise<void>) | undefined;
59
+ try {
60
+ const aiContext = useAiContext();
61
+ refetchProviders = aiContext.refetchProviders;
62
+ } catch {
63
+ // AiProvider n'est pas disponible, ignorer
64
+ refetchProviders = undefined;
65
+ }
66
+
57
67
  const [showSigninModal, setShowSigninModal] = useState(false);
58
68
  const [showApiKeySelector, setShowApiKeySelector] = useState(false);
59
69
 
@@ -407,7 +417,7 @@ export function AiStatusButton({
407
417
  <button
408
418
  onClick={() =>
409
419
  window.open(
410
- "https://prompt.lastbrain.io/metrics",
420
+ "https://prompt.lastbrain.io/auth/dashboard",
411
421
  "_blank"
412
422
  )
413
423
  }
@@ -440,7 +450,7 @@ export function AiStatusButton({
440
450
  <button
441
451
  onClick={() =>
442
452
  window.open(
443
- "https://prompt.lastbrain.io/settings",
453
+ "https://prompt.lastbrain.io/auth/ai/settings",
444
454
  "_blank"
445
455
  )
446
456
  }
@@ -473,7 +483,7 @@ export function AiStatusButton({
473
483
  <button
474
484
  onClick={() =>
475
485
  window.open(
476
- "https://prompt.lastbrain.io/auth/prompts",
486
+ "https://prompt.lastbrain.io/auth/ai/prompts",
477
487
  "_blank"
478
488
  )
479
489
  }
@@ -540,6 +550,10 @@ export function AiStatusButton({
540
550
  onClick={async () => {
541
551
  if (logout) {
542
552
  await logout();
553
+ // Refresh provider data after logout
554
+ if (refetchProviders) {
555
+ await refetchProviders();
556
+ }
543
557
  }
544
558
  setShowTooltip(false);
545
559
  }}
@@ -704,29 +718,75 @@ export function AiStatusButton({
704
718
  <div
705
719
  style={{
706
720
  ...aiStyles.tooltipSection,
707
- ...(status.user?.email
708
- ? {}
709
- : aiStyles.tooltipSectionFirst),
721
+ ...(status.user?.email ? {} : aiStyles.tooltipSectionFirst),
710
722
  }}
711
723
  >
712
724
  <div style={aiStyles.tooltipRow}>
713
725
  <span style={aiStyles.tooltipLabel}>API Key:</span>
714
- <span style={aiStyles.tooltipValue}>
715
- {status.apiKey?.name ||
716
- status.api_key?.name ||
717
- "N/A"}
718
- </span>
726
+ <div
727
+ style={{
728
+ display: "flex",
729
+ alignItems: "center",
730
+ gap: "8px",
731
+ }}
732
+ >
733
+ <span style={aiStyles.tooltipValue}>
734
+ {status.apiKey?.name || status.api_key?.name || "Unknown"}
735
+ </span>
736
+ {apiKeys.length > 1 && selectApiKeyWithToken && (
737
+ <button
738
+ onClick={(e) => {
739
+ e.stopPropagation();
740
+ setShowApiKeySelector(true);
741
+ }}
742
+ style={{
743
+ background: "rgba(139, 92, 246, 0.1)",
744
+ border: "1px solid rgba(139, 92, 246, 0.3)",
745
+ borderRadius: "50%",
746
+ width: "24px",
747
+ height: "24px",
748
+ display: "flex",
749
+ alignItems: "center",
750
+ justifyContent: "center",
751
+ cursor: "pointer",
752
+ padding: 0,
753
+ transition: "all 0.2s ease",
754
+ }}
755
+ onMouseEnter={(e) => {
756
+ e.currentTarget.style.background =
757
+ "rgba(139, 92, 246, 0.2)";
758
+ e.currentTarget.style.borderColor =
759
+ "rgba(139, 92, 246, 0.5)";
760
+ }}
761
+ onMouseLeave={(e) => {
762
+ e.currentTarget.style.background =
763
+ "rgba(139, 92, 246, 0.1)";
764
+ e.currentTarget.style.borderColor =
765
+ "rgba(139, 92, 246, 0.3)";
766
+ }}
767
+ title="Change API Key"
768
+ >
769
+ <RefreshCw
770
+ size={12}
771
+ style={{ color: "rgba(139, 92, 246, 1)" }}
772
+ />
773
+ </button>
774
+ )}
775
+ </div>
719
776
  </div>
720
777
  <div style={aiStyles.tooltipRow}>
721
778
  <span style={aiStyles.tooltipLabel}>Env:</span>
722
779
  <span style={aiStyles.tooltipValue}>
723
- {status.api_key?.env || "N/A"}
780
+ {status.apiKey?.env || status.api_key?.env || "N/A"}
724
781
  </span>
725
782
  </div>
726
783
  <div style={aiStyles.tooltipRow}>
727
784
  <span style={aiStyles.tooltipLabel}>Rate Limit:</span>
728
785
  <span style={aiStyles.tooltipValue}>
729
- {status.api_key?.rate_limit_rpm || 0} req/min
786
+ {status.apiKey?.rate_limit_rpm ||
787
+ status.api_key?.rate_limit_rpm ||
788
+ 0}{" "}
789
+ req/min
730
790
  </span>
731
791
  </div>
732
792
  </div>
@@ -965,6 +1025,10 @@ export function AiStatusButton({
965
1025
  try {
966
1026
  await logout();
967
1027
  setShowTooltip(false);
1028
+ // Refresh provider data after logout
1029
+ if (refetchProviders) {
1030
+ await refetchProviders();
1031
+ }
968
1032
  } catch (error) {
969
1033
  console.error("Logout failed:", error);
970
1034
  }
@@ -1010,6 +1074,10 @@ export function AiStatusButton({
1010
1074
  await selectApiKeyWithToken(keyId);
1011
1075
  setShowApiKeySelector(false);
1012
1076
  setShowTooltip(false);
1077
+ // Refresh provider data after API key selection
1078
+ if (refetchProviders) {
1079
+ await refetchProviders();
1080
+ }
1013
1081
  } catch (error) {
1014
1082
  console.error("Failed to select API key:", error);
1015
1083
  }
@@ -29,6 +29,8 @@ interface LBProviderProps {
29
29
  proxyUrl?: string;
30
30
  /** Fonction appelée lors des changements d'état */
31
31
  onStatusChange?: (status: LBAuthState["status"]) => void;
32
+ /** Fonction appelée après signin/logout pour refresh les providers */
33
+ onAuthChange?: () => void;
32
34
  }
33
35
 
34
36
  interface LBContextValue extends LBAuthState {
@@ -64,6 +66,7 @@ export function LBProvider({
64
66
  baseUrl: _baseUrl = "/api/lastbrain",
65
67
  proxyUrl = "/api/lastbrain",
66
68
  onStatusChange,
69
+ onAuthChange,
67
70
  }: LBProviderProps) {
68
71
  const [state, setState] = useState<LBAuthState>({
69
72
  status: "loading",
@@ -82,14 +85,47 @@ export function LBProvider({
82
85
 
83
86
  if (response.ok) {
84
87
  const session: LBSession = await response.json();
85
- setState({
86
- status: "ready",
87
- session,
88
- user: {
89
- id: session.userId,
90
- email: "", // Sera rempli par une autre requête si nécessaire
91
- },
92
- });
88
+
89
+ // Récupérer les infos utilisateur depuis /auth/status
90
+ try {
91
+ const statusResponse = await fetch(`${proxyUrl}/auth/status`, {
92
+ credentials: "include",
93
+ });
94
+
95
+ if (statusResponse.ok) {
96
+ const statusData = await statusResponse.json();
97
+ setState({
98
+ status: "ready",
99
+ session,
100
+ user: {
101
+ id: session.userId,
102
+ email: statusData.user?.email || "",
103
+ },
104
+ });
105
+ } else {
106
+ // Fallback sans email
107
+ setState({
108
+ status: "ready",
109
+ session,
110
+ user: {
111
+ id: session.userId,
112
+ email: "",
113
+ },
114
+ });
115
+ }
116
+ } catch (statusError) {
117
+ console.error("[LBProvider] Failed to fetch status:", statusError);
118
+ // Fallback sans email
119
+ setState({
120
+ status: "ready",
121
+ session,
122
+ user: {
123
+ id: session.userId,
124
+ email: "",
125
+ },
126
+ });
127
+ }
128
+
93
129
  onStatusChange?.("ready");
94
130
  } else {
95
131
  setState({ status: "needs_auth" });
@@ -195,6 +231,7 @@ export function LBProvider({
195
231
  setAccessToken(undefined); // Nettoyer l'access token temporaire
196
232
  setApiKeys([]); // Nettoyer les clés API temporaires
197
233
  onStatusChange?.("ready");
234
+ onAuthChange?.(); // Refresh provider after signin
198
235
  } catch (error) {
199
236
  const message =
200
237
  error instanceof Error ? error.message : "Failed to select API key";
@@ -205,7 +242,7 @@ export function LBProvider({
205
242
  throw error;
206
243
  }
207
244
  },
208
- [proxyUrl, state.user, onStatusChange]
245
+ [proxyUrl, state.user, onStatusChange, onAuthChange]
209
246
  );
210
247
 
211
248
  /**
@@ -371,8 +408,9 @@ export function LBProvider({
371
408
  setApiKeys([]);
372
409
  setAccessToken(undefined);
373
410
  onStatusChange?.("needs_auth");
411
+ onAuthChange?.(); // Refresh provider after logout
374
412
  }
375
- }, [proxyUrl, onStatusChange]);
413
+ }, [proxyUrl, onStatusChange, onAuthChange]);
376
414
 
377
415
  /**
378
416
  * Recharge la session