@lastbrain/module-ai 0.1.8 → 0.1.11
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/README.md +49 -442
- package/dist/ai.build.config.d.ts.map +1 -1
- package/dist/ai.build.config.js +24 -6
- package/dist/api/admin/user-token/[id].d.ts +6 -1
- package/dist/api/admin/user-token/[id].d.ts.map +1 -1
- package/dist/api/admin/user-token/[id].js +16 -14
- package/dist/api/admin/user-token.d.ts +19 -3
- package/dist/api/admin/user-token.d.ts.map +1 -1
- package/dist/api/admin/user-token.js +85 -26
- package/dist/api/auth/generate-text.js +1 -1
- package/dist/api/auth/user-tokens.d.ts +17 -0
- package/dist/api/auth/user-tokens.d.ts.map +1 -0
- package/dist/api/auth/user-tokens.js +34 -0
- package/dist/components/Doc.d.ts +8 -1
- package/dist/components/Doc.d.ts.map +1 -1
- package/dist/components/Doc.js +17 -3
- package/dist/components/DocUsageCustom.d.ts +9 -0
- package/dist/components/DocUsageCustom.d.ts.map +1 -0
- package/dist/components/DocUsageCustom.js +70 -0
- package/dist/components/admin/UserTokenTab.d.ts +6 -0
- package/dist/components/admin/UserTokenTab.d.ts.map +1 -0
- package/dist/components/admin/UserTokenTab.js +112 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
- package/dist/web/admin/UserTokenPage.js +103 -2
- package/dist/web/auth/TokenPage.d.ts.map +1 -1
- package/dist/web/auth/TokenPage.js +110 -2
- package/dist/web/components/ImageGenerative.d.ts +6 -3
- package/dist/web/components/ImageGenerative.d.ts.map +1 -1
- package/dist/web/components/ImageGenerative.js +51 -12
- package/dist/web/components/TextareaGenerative.d.ts +5 -3
- package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
- package/dist/web/components/TextareaGenerative.js +33 -12
- package/package.json +4 -4
- package/supabase/migrations-down/20251125000000_ai_tokens.sql +45 -0
- package/supabase/migrations/20251121093113_module-ai_init.sql +0 -122
- package/supabase/migrations-down/20251121000000_ai_tokens.sql +0 -23
- package/supabase/migrations-down/20251121093113_module-ai_init.sql +0 -11
- /package/supabase/migrations/{20251121000000_ai_tokens.sql → 20251125000000_ai_tokens.sql} +0 -0
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
-
export declare function GET(
|
|
2
|
+
export declare function GET(request: NextRequest): Promise<NextResponse<{
|
|
3
3
|
users: {
|
|
4
|
+
userId: string;
|
|
5
|
+
email: string;
|
|
6
|
+
fullName: string | undefined;
|
|
7
|
+
avatarUrl: any;
|
|
8
|
+
balance: number;
|
|
9
|
+
totalAdded: number;
|
|
10
|
+
totalUsed: number;
|
|
11
|
+
lastActivity: string | undefined;
|
|
12
|
+
}[];
|
|
13
|
+
transactions: {
|
|
4
14
|
id: any;
|
|
15
|
+
userId: any;
|
|
5
16
|
email: string;
|
|
6
|
-
|
|
7
|
-
|
|
17
|
+
fullName: string | undefined;
|
|
18
|
+
avatarUrl: any;
|
|
19
|
+
amount: any;
|
|
20
|
+
type: any;
|
|
21
|
+
description: any;
|
|
22
|
+
model: any;
|
|
23
|
+
created_at: any;
|
|
8
24
|
}[];
|
|
9
25
|
total: number;
|
|
10
26
|
}> | NextResponse<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-token.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,wBAAsB,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"user-token.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;IA2H7C;AAGD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;IAiD9C"}
|
|
@@ -1,36 +1,96 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
|
-
import {
|
|
2
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
3
3
|
import { addTokens } from "../../server";
|
|
4
|
-
// GET /api/ai/admin/user-token - Liste tous les utilisateurs avec leur balance
|
|
5
|
-
export async function GET(
|
|
4
|
+
// GET /api/ai/admin/user-token - Liste tous les utilisateurs avec leur balance et transactions
|
|
5
|
+
export async function GET(request) {
|
|
6
6
|
try {
|
|
7
|
-
const supabase =
|
|
7
|
+
const supabase = getSupabaseServiceClient();
|
|
8
8
|
// L'authentification et les droits superadmin sont déjà vérifiés par le middleware
|
|
9
|
-
// Récupérer tous les utilisateurs
|
|
10
|
-
const { data:
|
|
11
|
-
.from("user_token_balance_v")
|
|
12
|
-
.select("owner_id, balance")
|
|
13
|
-
.order("balance", { ascending: false });
|
|
14
|
-
if (balanceError)
|
|
15
|
-
throw balanceError;
|
|
16
|
-
// Récupérer les informations des utilisateurs
|
|
17
|
-
const _userIds = balances?.map((b) => b.owner_id) || [];
|
|
18
|
-
const { data: users, error: usersError } = await supabase.auth.admin.listUsers();
|
|
9
|
+
// Récupérer tous les utilisateurs
|
|
10
|
+
const { data: authUsers, error: usersError } = await supabase.auth.admin.listUsers();
|
|
19
11
|
if (usersError)
|
|
20
12
|
throw usersError;
|
|
21
|
-
//
|
|
22
|
-
const
|
|
23
|
-
|
|
13
|
+
// Récupérer toutes les transactions pour calculer les balances et stats
|
|
14
|
+
const { data: transactions, error: txError } = await supabase
|
|
15
|
+
.from("user_token_ledger")
|
|
16
|
+
.select("*")
|
|
17
|
+
.order("ts", { ascending: false })
|
|
18
|
+
.limit(1000);
|
|
19
|
+
if (txError)
|
|
20
|
+
throw txError;
|
|
21
|
+
// Calculer les stats par utilisateur
|
|
22
|
+
const userStatsMap = new Map();
|
|
23
|
+
transactions?.forEach((tx) => {
|
|
24
|
+
const stats = userStatsMap.get(tx.owner_id) || {
|
|
25
|
+
balance: 0,
|
|
26
|
+
totalAdded: 0,
|
|
27
|
+
totalUsed: 0,
|
|
28
|
+
};
|
|
29
|
+
stats.balance += tx.amount;
|
|
30
|
+
if (tx.amount > 0) {
|
|
31
|
+
stats.totalAdded += tx.amount;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
stats.totalUsed += Math.abs(tx.amount);
|
|
35
|
+
}
|
|
36
|
+
if (!stats.lastActivity || tx.ts > stats.lastActivity) {
|
|
37
|
+
stats.lastActivity = tx.ts;
|
|
38
|
+
}
|
|
39
|
+
userStatsMap.set(tx.owner_id, stats);
|
|
40
|
+
});
|
|
41
|
+
// Récupérer les profils utilisateurs pour avatar et nom
|
|
42
|
+
const userIds = authUsers.users.map((u) => u.id);
|
|
43
|
+
const { data: profiles } = await supabase
|
|
44
|
+
.from("user_profil")
|
|
45
|
+
.select("id, first_name, last_name, avatar_url")
|
|
46
|
+
.in("id", userIds);
|
|
47
|
+
const profilesMap = new Map(profiles?.map((p) => [
|
|
48
|
+
p.id,
|
|
49
|
+
{
|
|
50
|
+
fullName: [p.first_name, p.last_name].filter(Boolean).join(" "),
|
|
51
|
+
avatarUrl: p.avatar_url,
|
|
52
|
+
},
|
|
53
|
+
]));
|
|
54
|
+
// Combiner toutes les données
|
|
55
|
+
const usersData = authUsers.users.map((user) => {
|
|
56
|
+
const stats = userStatsMap.get(user.id) || {
|
|
57
|
+
balance: 0,
|
|
58
|
+
totalAdded: 0,
|
|
59
|
+
totalUsed: 0,
|
|
60
|
+
};
|
|
61
|
+
const profile = profilesMap.get(user.id);
|
|
62
|
+
return {
|
|
63
|
+
userId: user.id,
|
|
64
|
+
email: user.email || "Unknown",
|
|
65
|
+
fullName: profile?.fullName,
|
|
66
|
+
avatarUrl: profile?.avatarUrl,
|
|
67
|
+
balance: stats.balance,
|
|
68
|
+
totalAdded: stats.totalAdded,
|
|
69
|
+
totalUsed: stats.totalUsed,
|
|
70
|
+
lastActivity: stats.lastActivity,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
// Préparer les transactions avec infos utilisateurs
|
|
74
|
+
const transactionsWithUsers = transactions?.map((tx) => {
|
|
75
|
+
const user = authUsers.users.find((u) => u.id === tx.owner_id);
|
|
76
|
+
const profile = profilesMap.get(tx.owner_id);
|
|
24
77
|
return {
|
|
25
|
-
id:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
78
|
+
id: tx.id,
|
|
79
|
+
userId: tx.owner_id,
|
|
80
|
+
email: user?.email || "Unknown",
|
|
81
|
+
fullName: profile?.fullName,
|
|
82
|
+
avatarUrl: profile?.avatarUrl,
|
|
83
|
+
amount: tx.amount,
|
|
84
|
+
type: tx.type,
|
|
85
|
+
description: tx.meta?.reason,
|
|
86
|
+
model: tx.model,
|
|
87
|
+
created_at: tx.ts,
|
|
29
88
|
};
|
|
30
89
|
});
|
|
31
90
|
return NextResponse.json({
|
|
32
|
-
users:
|
|
33
|
-
|
|
91
|
+
users: usersData,
|
|
92
|
+
transactions: transactionsWithUsers,
|
|
93
|
+
total: usersData.length,
|
|
34
94
|
});
|
|
35
95
|
}
|
|
36
96
|
catch (error) {
|
|
@@ -41,9 +101,8 @@ export async function GET(_request) {
|
|
|
41
101
|
// POST /api/ai/admin/user-token - Ajouter/retirer des tokens à un utilisateur
|
|
42
102
|
export async function POST(request) {
|
|
43
103
|
try {
|
|
44
|
-
const supabase =
|
|
104
|
+
const supabase = getSupabaseServiceClient();
|
|
45
105
|
// L'authentification et les droits superadmin sont déjà vérifiés par le middleware
|
|
46
|
-
const { data: { user }, } = await supabase.auth.getUser();
|
|
47
106
|
const body = await request.json();
|
|
48
107
|
const { userId, amount, type, reason } = body;
|
|
49
108
|
if (!userId || !amount) {
|
|
@@ -54,7 +113,7 @@ export async function POST(request) {
|
|
|
54
113
|
}
|
|
55
114
|
// Déterminer le type d'opération
|
|
56
115
|
const operationType = type || (amount > 0 ? "adjust" : "adjust");
|
|
57
|
-
const result = await addTokens(userId, amount, operationType, { reason: reason || "Ajustement manuel par admin"
|
|
116
|
+
const result = await addTokens(userId, amount, operationType, { reason: reason || "Ajustement manuel par admin" }, undefined);
|
|
58
117
|
if (!result.success) {
|
|
59
118
|
return NextResponse.json({ error: result.error }, { status: 500 });
|
|
60
119
|
}
|
|
@@ -12,7 +12,7 @@ export async function POST(request) {
|
|
|
12
12
|
const { data: { user }, } = await supabase.auth.getUser();
|
|
13
13
|
// L'utilisateur est déjà authentifié grâce au middleware
|
|
14
14
|
const body = await request.json();
|
|
15
|
-
const { prompt, model = "gpt-4o-mini", context, maxTokens =
|
|
15
|
+
const { prompt, model = "gpt-4o-mini", context, maxTokens = 100, temperature = 0.7, } = body;
|
|
16
16
|
if (!prompt) {
|
|
17
17
|
return NextResponse.json({ error: "Le prompt est requis" }, { status: 400 });
|
|
18
18
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
export declare function GET(request: NextRequest): Promise<NextResponse<{
|
|
3
|
+
userId: string;
|
|
4
|
+
balance: number;
|
|
5
|
+
stats: {
|
|
6
|
+
balance: number;
|
|
7
|
+
totalPurchased: number;
|
|
8
|
+
totalGifted: number;
|
|
9
|
+
totalUsed: number;
|
|
10
|
+
totalAdjusted: number;
|
|
11
|
+
};
|
|
12
|
+
history: import("../../server").TokenLedgerEntry[];
|
|
13
|
+
count: number;
|
|
14
|
+
}> | NextResponse<{
|
|
15
|
+
error: any;
|
|
16
|
+
}>>;
|
|
17
|
+
//# sourceMappingURL=user-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-tokens.d.ts","sourceRoot":"","sources":["../../../src/api/auth/user-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;IA0C7C"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { getSupabaseServerClient } from "@lastbrain/core/server";
|
|
3
|
+
import { getTokenBalance, getTokenHistory, getTokenStats } from "../../server";
|
|
4
|
+
// GET /api/ai/user/tokens - Récupérer le solde et l'historique de l'utilisateur courant
|
|
5
|
+
export async function GET(request) {
|
|
6
|
+
try {
|
|
7
|
+
const supabase = await getSupabaseServerClient();
|
|
8
|
+
// Récupérer l'utilisateur courant
|
|
9
|
+
const { data: { user }, error: authError, } = await supabase.auth.getUser();
|
|
10
|
+
if (authError || !user) {
|
|
11
|
+
return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
12
|
+
}
|
|
13
|
+
const userId = user.id;
|
|
14
|
+
// Récupérer le solde
|
|
15
|
+
const balance = await getTokenBalance(userId);
|
|
16
|
+
// Récupérer les statistiques
|
|
17
|
+
const stats = await getTokenStats(userId);
|
|
18
|
+
// Récupérer l'historique complet (12 derniers mois)
|
|
19
|
+
const searchParams = request.nextUrl.searchParams;
|
|
20
|
+
const limit = parseInt(searchParams.get("limit") || "500");
|
|
21
|
+
const history = await getTokenHistory(userId, limit, 0);
|
|
22
|
+
return NextResponse.json({
|
|
23
|
+
userId,
|
|
24
|
+
balance,
|
|
25
|
+
stats,
|
|
26
|
+
history,
|
|
27
|
+
count: history.length,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error("[GET /api/ai/user/tokens] Error:", error);
|
|
32
|
+
return NextResponse.json({ error: error.message || "Erreur serveur" }, { status: 500 });
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/components/Doc.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Documentation component for @lastbrain/module-ai
|
|
3
|
+
* Auto-generated from module-ai.build.config.ts
|
|
4
|
+
*
|
|
5
|
+
* To regenerate this file, run:
|
|
6
|
+
* pnpm update:module-docs
|
|
7
|
+
*/
|
|
8
|
+
export declare function Doc(): import("react/jsx-runtime").JSX.Element;
|
|
2
9
|
//# sourceMappingURL=Doc.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Doc.d.ts","sourceRoot":"","sources":["../../src/components/Doc.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Doc.d.ts","sourceRoot":"","sources":["../../src/components/Doc.tsx"],"names":[],"mappings":"AAiBA;;;;;;GAMG;AACH,wBAAgB,GAAG,4CAsOlB"}
|
package/dist/components/Doc.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Card, CardBody, CardHeader
|
|
3
|
-
|
|
4
|
-
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-3xl font-bold mb-2", children: "Module AI" }), _jsx("p", { className: "text-slate-600 dark:text-slate-400", children: "G\u00E9n\u00E9ration IA (texte et images) avec syst\u00E8me de tokens et gestion des co\u00FBts" })] }), _jsx(Chip, { color: "primary", variant: "flat", children: "v0.1.0" })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCDD Informations" }) }), _jsxs(CardBody, { className: "space-y-2", children: [_jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Auteur:" }), " LastBrain Team"] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Package:" }), " @lastbrain/module-ai"] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Derni\u00E8re mise \u00E0 jour:" }), " 21 novembre 2025"] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83C\uDFA8 Composants Disponibles" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "TextareaGenerative" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "Textarea avec g\u00E9n\u00E9ration de texte IA int\u00E9gr\u00E9e. Bouton de g\u00E9n\u00E9ration avec s\u00E9lection de prompts." }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("span", { children: ["import ", "{ TextareaGenerative }", " from \"@lastbrain/module-ai\";"] }), _jsx("span", {}), _jsx("span", { children: "<TextareaGenerative" }), _jsx("span", { children: " label=\"Description\"" }), _jsx("span", { children: " placeholder=\"Entrez une description...\"" }), _jsxs("span", { children: [" value=", "{value}"] }), _jsxs("span", { children: [" onChange=", "{setValue}"] }), _jsx("span", { children: "/>" })] }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "ImageGenerative" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "Composant de g\u00E9n\u00E9ration d'images avec DALL-E. Upload ou g\u00E9n\u00E9ration IA." }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("span", { children: ["import ", "{ ImageGenerative }", " from \"@lastbrain/module-ai\";"] }), _jsx("span", {}), _jsx("span", { children: "<ImageGenerative" }), _jsx("span", { children: " label=\"Image du produit\"" }), _jsxs("span", { children: [" value=", "{imageUrl}"] }), _jsxs("span", { children: [" onChange=", "{setImageUrl}"] }), _jsx("span", { children: "/>" })] }) })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCC4 Pages Disponibles" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Pages Prot\u00E9g\u00E9es (Auth)" }), _jsx("div", { className: "space-y-2", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "primary", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/auth/token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Historique et balance des tokens" })] }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Pages Admin" }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "danger", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Gestion des tokens utilisateurs" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "danger", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/admin/user-token/[id]" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- D\u00E9tail d'un utilisateur" })] })] })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDD0C Routes API" }) }), _jsxs(CardBody, { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/ai/generate-text" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- G\u00E9n\u00E9ration de texte" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/ai/generate-image" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- G\u00E9n\u00E9ration d'image" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "success", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/api/ai/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Liste des tokens utilisateurs" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/ai/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Cr\u00E9er/modifier des tokens" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "success", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/api/ai/admin/user-token/[id]" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- D\u00E9tails tokens utilisateur" })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCC1 Structure" }) }), _jsx(CardBody, { children: _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1 text-xs font-mono", children: [_jsx("span", { children: "module-ai/" }), _jsx("span", { children: "\u251C\u2500\u2500 src/" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 web/" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 TextareaGenerative.tsx" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 ImageGenerative.tsx" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 api/" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 generate-text.ts" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 generate-image.ts" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 prompts.ts" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 balance.ts" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 components/" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 Doc.tsx" }), _jsx("span", { children: "\u2502 \u2514\u2500\u2500 ai.build.config.ts" }), _jsx("span", { children: "\u2514\u2500\u2500 supabase/" }), _jsx("span", { children: " \u2514\u2500\u2500 migrations/" }), _jsx("span", { children: " \u251C\u2500\u2500 20251121000000_ai_tokens.sql" }), _jsx("span", { children: " \u2514\u2500\u2500 20251121093113_module-ai_init.sql" })] }) }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDDC4\uFE0F Tables de Donn\u00E9es" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsx(TableStructure, { tableName: "user_token_ledger", title: "user_token_ledger", description: "Comptabilit\u00E9 des tokens (cr\u00E9dits, d\u00E9bits, balance). Vue user_token_balance pour le solde actuel." }), _jsx(TableStructure, { tableName: "user_prompts", title: "user_prompts", description: "Biblioth\u00E8que de prompts r\u00E9utilisables par utilisateur (titre, contenu, type)." })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCE6 Installation" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "pnpm lastbrain add-module ai" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "pnpm build:modules" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "supabase migration up" })] }), _jsxs("div", { className: "border-2 border-danger rounded-lg p-4 mt-6", children: [_jsx("h4", { className: "text-lg font-semibold text-danger mb-3", children: "\u26A0\uFE0F Danger Zone" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "La suppression du module supprimera toutes les pages, routes API et migrations associ\u00E9es. Cette action est irr\u00E9versible." }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Snippet, { symbol: "", color: "danger", size: "sm", children: "pnpm lastbrain remove-module ai" }), _jsx(Snippet, { symbol: "", color: "danger", size: "sm", children: "pnpm build:modules" })] })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\u2699\uFE0F Configuration" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Ajoutez votre cl\u00E9 API OpenAI dans vos variables d'environnement :" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "OPENAI_API_KEY=sk-..." })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCB0 Syst\u00E8me de Tokens" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Co\u00FBts" }), _jsxs("ul", { className: "text-sm text-slate-600 dark:text-slate-400 space-y-1", children: [_jsx("li", { children: "\u2022 G\u00E9n\u00E9ration de texte : 10 tokens" }), _jsx("li", { children: "\u2022 G\u00E9n\u00E9ration d'image : 50 tokens" })] })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "Trigger de validation" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Un trigger emp\u00EAche les transactions si le solde devient n\u00E9gatif." })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "Ajouter des tokens" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("span", { children: "INSERT INTO user_token_ledger" }), _jsx("span", { children: "(user_id, amount, transaction_type, description)" }), _jsx("span", { children: "VALUES" }), _jsx("span", { children: "(\"('user-uuid', 1000, 'credit', 'Achat de tokens');\")" })] }) })] })] })] })] }));
|
|
3
|
+
import { Card, CardBody, CardHeader } from "@lastbrain/ui";
|
|
4
|
+
import { Chip } from "@lastbrain/ui";
|
|
5
|
+
import { Snippet } from "@lastbrain/ui";
|
|
6
|
+
import { Alert } from "@lastbrain/ui";
|
|
7
|
+
import { TableStructure } from "@lastbrain/ui";
|
|
8
|
+
import { FileText, Zap, Database, Package, BookOpen, AlertTriangle, } from "lucide-react";
|
|
9
|
+
import { DocUsageCustom } from "./DocUsageCustom";
|
|
10
|
+
/**
|
|
11
|
+
* Documentation component for @lastbrain/module-ai
|
|
12
|
+
* Auto-generated from module-ai.build.config.ts
|
|
13
|
+
*
|
|
14
|
+
* To regenerate this file, run:
|
|
15
|
+
* pnpm update:module-docs
|
|
16
|
+
*/
|
|
17
|
+
export function Doc() {
|
|
18
|
+
return (_jsxs("div", { className: "container mx-auto p-6 space-y-6", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-2", children: "\uD83D\uDCE6 Module ai" }), _jsx("p", { className: "text-slate-600 dark:text-slate-400", children: "@lastbrain/module-ai" })] }) }), _jsx(CardBody, { children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Package" }), _jsx("code", { className: "text-sm font-semibold", children: "@lastbrain/module-ai" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Slug" }), _jsx("code", { className: "text-sm font-semibold", children: "module-ai" })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Type" }), _jsx("code", { className: "text-sm font-semibold", children: "Module LastBrain" })] })] }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(FileText, { size: 24 }), "Pages Disponibles"] }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Pages Prot\u00E9g\u00E9es (Auth)" }), _jsx("div", { className: "space-y-2", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Chip, { size: "sm", color: "primary", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/token" }), _jsx("span", { className: "text-sm text-slate-600 dark:text-slate-400", children: "- TokenPage" })] }) })] }), _jsxs("div", { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Pages Admin" }), _jsx("div", { className: "space-y-2", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Chip, { size: "sm", color: "secondary", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/user-token" }), _jsx("span", { className: "text-sm text-slate-600 dark:text-slate-400", children: "- UserTokenPage" })] }) })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(Zap, { size: 24 }), "API Routes"] }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: _jsx("code", { children: "/api/auth/user_token_ledger" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Chip, { size: "sm", color: "success", variant: "flat", children: "GET" }), _jsx(Chip, { size: "sm", color: "primary", variant: "flat", children: "POST" }), _jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "PUT" }), _jsx(Chip, { size: "sm", color: "danger", variant: "flat", children: "DELETE" })] })] }), _jsxs("div", { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: _jsx("code", { children: "/api/auth/user_prompts" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Chip, { size: "sm", color: "success", variant: "flat", children: "GET" }), _jsx(Chip, { size: "sm", color: "primary", variant: "flat", children: "POST" }), _jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "PUT" }), _jsx(Chip, { size: "sm", color: "danger", variant: "flat", children: "DELETE" })] })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(Database, { size: 24 }), "Base de Donn\u00E9es"] }) }), _jsxs(CardBody, { className: "space-y-6", children: [_jsx(TableStructure, { tableName: "user_token_ledger", title: "user_token_ledger", description: "Table user_token_ledger du module ai" }), _jsx(TableStructure, { tableName: "user_prompts", title: "user_prompts", description: "Table user_prompts du module ai" })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(Package, { size: 24 }), "Installation"] }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Ajouter le module" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: "pnpm lastbrain add-module ai" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: "pnpm build:modules" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Appliquer les migrations" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: "cd apps/votre-app" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: "supabase migration up" })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(BookOpen, { size: 24 }), "Utilisation"] }) }), _jsx(CardBody, { className: "space-y-4", children: _jsx(DocUsageCustom, {}) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2 text-danger", children: [_jsx(AlertTriangle, { size: 24 }), "Danger Zone"] }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs(Alert, { color: "danger", className: "mb-4", children: [_jsx("p", { className: "text-sm font-semibold", children: "Cette action est irr\u00E9versible" }), _jsx("p", { className: "text-sm mt-2", children: "La suppression du module supprimera toutes les pages, routes API et migrations associ\u00E9es." })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Supprimer le module" }), _jsx(Snippet, { symbol: "", hideSymbol: true, color: "danger", className: "text-sm mb-2", children: "pnpm lastbrain remove-module ai" }), _jsx(Snippet, { symbol: "", hideSymbol: true, color: "danger", className: "text-sm mb-2", children: "pnpm build:modules" })] })] })] })] }));
|
|
5
19
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section personnalisée de documentation pour le module-ai
|
|
3
|
+
* Ce fichier NE SERA JAMAIS écrasé par update:module-docs
|
|
4
|
+
*
|
|
5
|
+
* Utilisez ce composant pour documenter l'utilisation spécifique
|
|
6
|
+
* de vos composants avec des exemples interactifs.
|
|
7
|
+
*/
|
|
8
|
+
export declare function DocUsageCustom(): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=DocUsageCustom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocUsageCustom.d.ts","sourceRoot":"","sources":["../../src/components/DocUsageCustom.tsx"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,wBAAgB,cAAc,4CA2V7B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Card, CardBody, CardHeader, Alert, Tabs, Tab } from "@lastbrain/ui";
|
|
4
|
+
import { TextareaGenerative } from "../web/components/TextareaGenerative";
|
|
5
|
+
import { ImageGenerative } from "../web/components/ImageGenerative";
|
|
6
|
+
import { Lightbulb } from "lucide-react";
|
|
7
|
+
/**
|
|
8
|
+
* Section personnalisée de documentation pour le module-ai
|
|
9
|
+
* Ce fichier NE SERA JAMAIS écrasé par update:module-docs
|
|
10
|
+
*
|
|
11
|
+
* Utilisez ce composant pour documenter l'utilisation spécifique
|
|
12
|
+
* de vos composants avec des exemples interactifs.
|
|
13
|
+
*/
|
|
14
|
+
export function DocUsageCustom() {
|
|
15
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsx(Alert, { hideIcon: true, variant: "flat", color: "primary", className: "mb-6", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Lightbulb, { size: 20, className: "mt-0.5 flex-shrink-0" }), _jsxs("div", { children: [_jsx("p", { className: "font-semibold", children: "Composants IA G\u00E9n\u00E9ratives" }), _jsx("p", { className: "text-sm mt-1", children: "Le module AI fournit des composants pr\u00EAts \u00E0 l'emploi pour int\u00E9grer la g\u00E9n\u00E9ration de texte et d'images par IA dans vos applications." })] })] }) }), _jsxs(Tabs, { "aria-label": "Exemples d'utilisation", color: "primary", variant: "bordered", fullWidth: true, children: [_jsx(Tab, { title: "G\u00E9n\u00E9ration de Texte", children: _jsxs(Card, { className: "mt-4", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "TextareaGenerative" }) }), _jsxs(CardBody, { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "Description" }), _jsx("p", { className: "text-slate-600 dark:text-slate-400", children: "Composant de g\u00E9n\u00E9ration de texte avec gestion automatique des tokens, support de streaming, et affichage du co\u00FBt en temps r\u00E9el." })] }), _jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-3", children: "Exemple interactif" }), _jsx(TextareaGenerative, { defaultPrompt: "\u00C9cris une courte description pour un produit innovant", placeholder: "Le texte g\u00E9n\u00E9r\u00E9 appara\u00EEtra ici...", model: "gpt-4o-mini", showTokenBalance: true })] }), _jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "Code d'utilisation" }), _jsx(Alert, { hideIcon: true, variant: "flat", color: "primary", className: "p-4", children: _jsx("pre", { className: "whitespace-pre-wrap text-sm", children: `import { TextareaGenerative } from "@lastbrain/module-ai";
|
|
16
|
+
|
|
17
|
+
export function MyComponent() {
|
|
18
|
+
return (
|
|
19
|
+
<TextareaGenerative
|
|
20
|
+
defaultPrompt="Votre prompt par défaut ici"
|
|
21
|
+
placeholder="Le texte généré apparaîtra ici..."
|
|
22
|
+
model="gpt-4o-mini"
|
|
23
|
+
showTokenBalance={true}
|
|
24
|
+
onChange={(text, response) => {
|
|
25
|
+
console.log("Texte généré:", text);
|
|
26
|
+
console.log("Tokens utilisés:", response?.tokensUsed);
|
|
27
|
+
}}
|
|
28
|
+
onError={(error) => {
|
|
29
|
+
console.error("Erreur:", error);
|
|
30
|
+
}}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}` }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "Props disponibles" }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "border-b", children: _jsxs("tr", { children: [_jsx("th", { className: "text-left py-2 px-2", children: "Prop" }), _jsx("th", { className: "text-left py-2 px-2", children: "Type" }), _jsx("th", { className: "text-left py-2 px-2", children: "Description" })] }) }), _jsxs("tbody", { className: "divide-y", children: [_jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "prompt" }), _jsx("td", { className: "py-2 px-2", children: "string" }), _jsx("td", { className: "py-2 px-2", children: "Le prompt \u00E0 envoyer \u00E0 l'IA" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "model" }), _jsx("td", { className: "py-2 px-2", children: "string" }), _jsx("td", { className: "py-2 px-2", children: "Mod\u00E8le \u00E0 utiliser (d\u00E9faut: gpt-4o-mini)" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "placeholder" }), _jsx("td", { className: "py-2 px-2", children: "string" }), _jsx("td", { className: "py-2 px-2", children: "Texte placeholder du textarea" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "onChange" }), _jsx("td", { className: "py-2 px-2", children: "function" }), _jsx("td", { className: "py-2 px-2", children: "Callback apr\u00E8s g\u00E9n\u00E9ration r\u00E9ussie" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "onError" }), _jsx("td", { className: "py-2 px-2", children: "function" }), _jsx("td", { className: "py-2 px-2", children: "Callback en cas d'erreur" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "showTokenBalance" }), _jsx("td", { className: "py-2 px-2", children: "boolean" }), _jsx("td", { className: "py-2 px-2", children: "Afficher le solde de tokens" })] })] })] }) })] })] })] }) }, "textarea"), _jsx(Tab, { title: "G\u00E9n\u00E9ration d'Images", children: _jsxs(Card, { className: "mt-4", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "ImageGenerative" }) }), _jsxs(CardBody, { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "Description" }), _jsx("p", { className: "text-slate-600 dark:text-slate-400", children: "Composant de g\u00E9n\u00E9ration d'images avec DALL-E, support de diff\u00E9rentes tailles et qualit\u00E9s, et t\u00E9l\u00E9chargement direct de l'image." })] }), _jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-3", children: "Exemple interactif" }), _jsx(ImageGenerative, { defaultPrompt: "Un chat astronaute dans l'espace, style cartoon color\u00E9", model: "dall-e-3", size: "1024x1024", quality: "standard", showTokenBalance: true, defaultStyle: "digital-art" })] }), _jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "Code d'utilisation" }), _jsx(Alert, { hideIcon: true, variant: "flat", color: "primary", className: "p-4", children: _jsx("pre", { className: "whitespace-pre-wrap text-sm", children: `import { ImageGenerative } from "@lastbrain/module-ai";
|
|
34
|
+
|
|
35
|
+
export function MyComponent() {
|
|
36
|
+
return (
|
|
37
|
+
<ImageGenerative
|
|
38
|
+
defaultPrompt="Un paysage futuriste avec des buildings"
|
|
39
|
+
defaultStyle="realistic"
|
|
40
|
+
model="dall-e-3"
|
|
41
|
+
size="1024x1024"
|
|
42
|
+
quality="hd"
|
|
43
|
+
showTokenBalance={true}
|
|
44
|
+
onChange={(imageUrl, response) => {
|
|
45
|
+
console.log("Image générée:", imageUrl);
|
|
46
|
+
console.log("Tokens utilisés:", response?.tokensUsed);
|
|
47
|
+
}}
|
|
48
|
+
onError={(error) => {
|
|
49
|
+
console.error("Erreur:", error);
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}` }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "Props disponibles" }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "border-b", children: _jsxs("tr", { children: [_jsx("th", { className: "text-left py-2 px-2", children: "Prop" }), _jsx("th", { className: "text-left py-2 px-2", children: "Type" }), _jsx("th", { className: "text-left py-2 px-2", children: "Description" })] }) }), _jsxs("tbody", { className: "divide-y", children: [_jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "prompt" }), _jsx("td", { className: "py-2 px-2", children: "string" }), _jsx("td", { className: "py-2 px-2", children: "Description de l'image \u00E0 g\u00E9n\u00E9rer" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "model" }), _jsx("td", { className: "py-2 px-2", children: "string" }), _jsx("td", { className: "py-2 px-2", children: "Mod\u00E8le (dall-e-2 ou dall-e-3)" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "size" }), _jsx("td", { className: "py-2 px-2", children: "string" }), _jsx("td", { className: "py-2 px-2", children: "Taille: 256x256, 512x512, 1024x1024, etc." })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "quality" }), _jsx("td", { className: "py-2 px-2", children: "string" }), _jsx("td", { className: "py-2 px-2", children: "Qualit\u00E9: standard ou hd" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "onChange" }), _jsx("td", { className: "py-2 px-2", children: "function" }), _jsx("td", { className: "py-2 px-2", children: "Callback apr\u00E8s g\u00E9n\u00E9ration r\u00E9ussie" })] }), _jsxs("tr", { children: [_jsx("td", { className: "py-2 px-2 font-mono", children: "onError" }), _jsx("td", { className: "py-2 px-2", children: "function" }), _jsx("td", { className: "py-2 px-2", children: "Callback en cas d'erreur" })] })] })] }) })] })] })] }) }, "image"), _jsx(Tab, { title: "Utilisation Avanc\u00E9e", children: _jsxs(Card, { className: "mt-4", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "Configuration Avanc\u00E9e" }) }), _jsxs(CardBody, { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "API Endpoints Personnalis\u00E9s" }), _jsx(Alert, { hideIcon: true, variant: "flat", color: "primary", className: "p-4", children: _jsx("pre", { className: "whitespace-pre-wrap text-sm", children: `// Utilisez vos propres endpoints API
|
|
54
|
+
<TextareaGenerative
|
|
55
|
+
prompt="Votre prompt"
|
|
56
|
+
apiEndpoint="/api/custom/generate-text"
|
|
57
|
+
/>
|
|
58
|
+
|
|
59
|
+
<ImageGenerative
|
|
60
|
+
prompt="Votre prompt"
|
|
61
|
+
apiEndpoint="/api/custom/generate-image"
|
|
62
|
+
/>` }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "Gestion des Tokens" }), _jsx("p", { className: "text-slate-600 dark:text-slate-400 mb-3", children: "Les composants utilisent automatiquement le syst\u00E8me de tokens du module. Chaque g\u00E9n\u00E9ration d\u00E9duit le nombre de tokens utilis\u00E9s du solde de l'utilisateur." }), _jsx(Alert, { hideIcon: true, variant: "flat", color: "warning", className: "p-4", children: _jsx("pre", { className: "whitespace-pre-wrap text-sm", children: `// Les tokens sont automatiquement gérés
|
|
63
|
+
// via la table user_token_ledger
|
|
64
|
+
|
|
65
|
+
// Pour recharger les tokens :
|
|
66
|
+
// 1. Accédez à /admin/user-token
|
|
67
|
+
// 2. Créditez les utilisateurs
|
|
68
|
+
// 3. Les composants détectent automatiquement
|
|
69
|
+
// le nouveau solde` }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "text-lg font-semibold mb-2", children: "Best Practices" }), _jsxs("ul", { className: "list-disc list-inside space-y-2 text-slate-600 dark:text-slate-400", children: [_jsxs("li", { children: ["Utilisez", " ", _jsx("code", { className: "text-xs bg-slate-100 dark:bg-slate-800 px-1 py-0.5 rounded", children: "gpt-4o-mini" }), " ", "pour les t\u00E2ches simples (moins cher)"] }), _jsx("li", { children: "Limitez la longueur des prompts pour \u00E9conomiser les tokens" }), _jsxs("li", { children: ["G\u00E9rez les erreurs avec", " ", _jsx("code", { className: "text-xs bg-slate-100 dark:bg-slate-800 px-1 py-0.5 rounded", children: "onError" })] }), _jsxs("li", { children: ["Affichez toujours le solde de tokens avec", " ", _jsx("code", { className: "text-xs bg-slate-100 dark:bg-slate-800 px-1 py-0.5 rounded", children: "showTokenBalance" })] }), _jsxs("li", { children: ["Pour les images, utilisez", " ", _jsx("code", { className: "text-xs bg-slate-100 dark:bg-slate-800 px-1 py-0.5 rounded", children: "standard" }), " ", "par d\u00E9faut,", " ", _jsx("code", { className: "text-xs bg-slate-100 dark:bg-slate-800 px-1 py-0.5 rounded", children: "hd" }), " ", "uniquement si n\u00E9cessaire"] })] })] })] })] }) }, "advanced")] })] }));
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserTokenTab.d.ts","sourceRoot":"","sources":["../../../src/components/admin/UserTokenTab.tsx"],"names":[],"mappings":"AAeA,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB;AAiBD,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CA2OzD"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { Card, CardBody, CardHeader, Button, Input, Spinner, addToast, Chip, } from "@lastbrain/ui";
|
|
5
|
+
import { Plus, Minus, History, Coins } from "lucide-react";
|
|
6
|
+
export function UserTokenTab({ userId }) {
|
|
7
|
+
const [loading, setLoading] = useState(true);
|
|
8
|
+
const [balance, setBalance] = useState(0);
|
|
9
|
+
const [ledger, setLedger] = useState([]);
|
|
10
|
+
const [adjustmentAmount, setAdjustmentAmount] = useState("");
|
|
11
|
+
const [adjustmentDescription, setAdjustmentDescription] = useState("");
|
|
12
|
+
const [processing, setProcessing] = useState(false);
|
|
13
|
+
// Charger le solde et l'historique
|
|
14
|
+
const fetchTokenData = async () => {
|
|
15
|
+
try {
|
|
16
|
+
setLoading(true);
|
|
17
|
+
const res = await fetch(`/api/ai/admin/user-token/${userId}`);
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
throw new Error("Échec chargement détails tokens");
|
|
20
|
+
const data = await res.json();
|
|
21
|
+
const currentBalance = data.balance || 0;
|
|
22
|
+
setBalance(currentBalance);
|
|
23
|
+
const history = data.history || data.data || [];
|
|
24
|
+
// Si l'API ne fournit pas balance_after, le calculer en partant du solde courant (ordre déjà décroissant)
|
|
25
|
+
let running = currentBalance;
|
|
26
|
+
const computed = history.map((entry) => {
|
|
27
|
+
const balanceAfter = running;
|
|
28
|
+
running -= entry.amount; // préparer balance pour l'entrée suivante
|
|
29
|
+
return {
|
|
30
|
+
...entry,
|
|
31
|
+
balance_after: balanceAfter,
|
|
32
|
+
display_amount: entry.amount,
|
|
33
|
+
display_description: entry.meta?.reason ||
|
|
34
|
+
entry.description ||
|
|
35
|
+
entry.type ||
|
|
36
|
+
"Transaction",
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
setLedger(computed);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error("Erreur lors du chargement des tokens:", error);
|
|
43
|
+
addToast({
|
|
44
|
+
color: "danger",
|
|
45
|
+
title: "Erreur lors du chargement des données",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
setLoading(false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
fetchTokenData();
|
|
54
|
+
}, [userId]);
|
|
55
|
+
// Ajuster le solde (crédit ou débit)
|
|
56
|
+
const handleAdjustment = async (type) => {
|
|
57
|
+
const amount = parseInt(adjustmentAmount);
|
|
58
|
+
if (isNaN(amount) || amount <= 0) {
|
|
59
|
+
addToast({
|
|
60
|
+
color: "danger",
|
|
61
|
+
title: "Montant invalide",
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!adjustmentDescription.trim()) {
|
|
66
|
+
addToast({
|
|
67
|
+
color: "danger",
|
|
68
|
+
title: "Description requise",
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
setProcessing(true);
|
|
74
|
+
const response = await fetch(`/api/ai/admin/user-token`, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: { "Content-Type": "application/json" },
|
|
77
|
+
body: JSON.stringify({
|
|
78
|
+
userId,
|
|
79
|
+
amount: type === "credit" ? amount : -amount,
|
|
80
|
+
type: "adjust",
|
|
81
|
+
reason: adjustmentDescription,
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
throw new Error("Échec de l'ajustement");
|
|
86
|
+
}
|
|
87
|
+
addToast({
|
|
88
|
+
color: "success",
|
|
89
|
+
title: type === "credit" ? "Tokens crédités" : "Tokens débités",
|
|
90
|
+
});
|
|
91
|
+
// Reset
|
|
92
|
+
setAdjustmentAmount("");
|
|
93
|
+
setAdjustmentDescription("");
|
|
94
|
+
// Recharger les données
|
|
95
|
+
await fetchTokenData();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error("Erreur:", error);
|
|
99
|
+
addToast({
|
|
100
|
+
color: "danger",
|
|
101
|
+
title: "Erreur lors de l'ajustement",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
setProcessing(false);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
if (loading) {
|
|
109
|
+
return (_jsx("div", { className: "flex justify-center items-center min-h-64", children: _jsx(Spinner, { size: "lg" }) }));
|
|
110
|
+
}
|
|
111
|
+
return (_jsxs("div", { className: "space-y-6 mt-4", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Coins, { size: 20 }), _jsx("h3", { className: "text-lg font-semibold", children: "Solde de tokens" })] }) }), _jsx(CardBody, { children: _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-4xl font-bold text-primary", children: balance.toLocaleString() }), _jsx("p", { className: "text-sm text-gray-500 mt-1", children: "tokens disponibles" })] }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: "Ajuster le solde" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsx(Input, { type: "number", label: "Montant", placeholder: "1000", value: adjustmentAmount, onChange: (e) => setAdjustmentAmount(e.target.value), min: "1", endContent: _jsx("span", { className: "text-sm text-gray-500", children: "tokens" }) }), _jsx(Input, { label: "Description", placeholder: "Raison de l'ajustement...", value: adjustmentDescription, onChange: (e) => setAdjustmentDescription(e.target.value), maxLength: 200 }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { color: "success", startContent: _jsx(Plus, { size: 16 }), onPress: () => handleAdjustment("credit"), isLoading: processing, isDisabled: !adjustmentAmount || !adjustmentDescription.trim(), children: "Cr\u00E9diter" }), _jsx(Button, { color: "danger", variant: "bordered", startContent: _jsx(Minus, { size: 16 }), onPress: () => handleAdjustment("debit"), isLoading: processing, isDisabled: !adjustmentAmount || !adjustmentDescription.trim(), children: "D\u00E9biter" })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(History, { size: 20 }), _jsx("h3", { className: "text-lg font-semibold", children: "Historique des transactions" })] }) }), _jsx(CardBody, { children: ledger.length === 0 ? (_jsx("p", { className: "text-center text-gray-500 py-4", children: "Aucune transaction" })) : (_jsx("div", { className: "space-y-2", children: ledger.map((entry) => (_jsxs("div", { className: "flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg", children: [_jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium", children: entry.display_description }), _jsx("p", { className: "text-xs text-gray-500", children: new Date(entry.created_at).toLocaleString() })] }), _jsxs("div", { className: "text-right", children: [_jsxs(Chip, { size: "sm", color: entry.display_amount > 0 ? "success" : "danger", variant: "flat", children: [entry.display_amount > 0 ? "+" : "", entry.display_amount.toLocaleString()] }), _jsxs("p", { className: "text-xs text-gray-500 mt-1", children: ["Solde: ", entry.balance_after.toLocaleString()] })] })] }, entry.id))) })) })] })] }));
|
|
112
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,9 @@ export { TextareaGenerative } from "./web/components/TextareaGenerative.js";
|
|
|
2
2
|
export { ImageGenerative } from "./web/components/ImageGenerative.js";
|
|
3
3
|
export { TokenPage } from "./web/auth/TokenPage.js";
|
|
4
4
|
export { UserTokenPage } from "./web/admin/UserTokenPage.js";
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
5
|
+
export { UserTokenTab } from "./components/admin/UserTokenTab.js";
|
|
6
|
+
export { Doc } from "./components/Doc.js";
|
|
7
|
+
export { Doc as AiModuleDoc } from "./components/Doc.js";
|
|
7
8
|
export type { GenerativeResponse, TextareaGenerativeProps, } from "./web/components/TextareaGenerative.js";
|
|
8
9
|
export type { GenerativeImageResponse, ImageGenerativeProps, } from "./web/components/ImageGenerative.js";
|
|
9
10
|
export { default as buildConfig } from "./ai.build.config.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAGtE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAGtE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAG7D,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAGlE,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,GAAG,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGzD,YAAY,EACV,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAEhD,YAAY,EACV,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,10 @@ export { ImageGenerative } from "./web/components/ImageGenerative.js";
|
|
|
5
5
|
export { TokenPage } from "./web/auth/TokenPage.js";
|
|
6
6
|
// Pages Admin
|
|
7
7
|
export { UserTokenPage } from "./web/admin/UserTokenPage.js";
|
|
8
|
-
|
|
8
|
+
// Admin Components - User Tabs
|
|
9
|
+
export { UserTokenTab } from "./components/admin/UserTokenTab.js";
|
|
9
10
|
// Documentation
|
|
10
|
-
export {
|
|
11
|
+
export { Doc } from "./components/Doc.js";
|
|
12
|
+
export { Doc as AiModuleDoc } from "./components/Doc.js";
|
|
11
13
|
// Configuration de build
|
|
12
14
|
export { default as buildConfig } from "./ai.build.config.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserTokenPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/UserTokenPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"UserTokenPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/UserTokenPage.tsx"],"names":[],"mappings":"AA+DA,wBAAgB,aAAa,4CAuY5B"}
|