@lastbrain/module-ai 2.0.12 → 2.0.18
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/dist/ai.build.config.js +7 -7
- package/dist/api/auth/token-checkout.d.ts +2 -2
- package/dist/api/auth/token-checkout.d.ts.map +1 -1
- package/dist/api/auth/token-checkout.js +21 -43
- package/dist/components/admin/UserTokenTab.d.ts.map +1 -1
- package/dist/components/admin/UserTokenTab.js +25 -10
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/web/admin/AdminTokenPacksPage.d.ts.map +1 -1
- package/dist/web/admin/AdminTokenPacksPage.js +27 -10
- package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
- package/dist/web/admin/UserTokenPage.js +21 -10
- package/dist/web/auth/TokenPage.d.ts.map +1 -1
- package/dist/web/auth/TokenPage.js +31 -18
- package/dist/web/components/ImageGenerative.d.ts.map +1 -1
- package/dist/web/components/ImageGenerative.js +29 -9
- package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
- package/dist/web/components/TextareaGenerative.js +23 -7
- package/package.json +4 -4
- package/supabase/migrations/20251125000000_ai_tokens.sql +15 -13
- package/supabase/migrations/20251201000000_token_packs.sql +103 -36
package/dist/ai.build.config.js
CHANGED
|
@@ -126,8 +126,8 @@ const buildConfig = {
|
|
|
126
126
|
menu: {
|
|
127
127
|
auth: [
|
|
128
128
|
{
|
|
129
|
-
title: "
|
|
130
|
-
description: "
|
|
129
|
+
title: "module-ai.menu.my_tokens",
|
|
130
|
+
description: "module-ai.menu.my_tokens_desc",
|
|
131
131
|
icon: "Coins",
|
|
132
132
|
path: "/auth/ai/token",
|
|
133
133
|
order: 100,
|
|
@@ -137,8 +137,8 @@ const buildConfig = {
|
|
|
137
137
|
],
|
|
138
138
|
admin: [
|
|
139
139
|
{
|
|
140
|
-
title: "
|
|
141
|
-
description: "
|
|
140
|
+
title: "module-ai.menu.tokens_admin",
|
|
141
|
+
description: "module-ai.menu.tokens_admin_desc",
|
|
142
142
|
icon: "Coins",
|
|
143
143
|
path: "/admin/ai/user-token",
|
|
144
144
|
order: 100,
|
|
@@ -146,8 +146,8 @@ const buildConfig = {
|
|
|
146
146
|
shortcutDisplay: "⌘⇧G",
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
|
-
title: "
|
|
150
|
-
description: "
|
|
149
|
+
title: "module-ai.menu.token_packs",
|
|
150
|
+
description: "module-ai.menu.token_packs_desc",
|
|
151
151
|
icon: "Package",
|
|
152
152
|
path: "/admin/ai/token-packs",
|
|
153
153
|
order: 101,
|
|
@@ -157,7 +157,7 @@ const buildConfig = {
|
|
|
157
157
|
userTabs: [
|
|
158
158
|
{
|
|
159
159
|
key: "tokens",
|
|
160
|
-
title: "
|
|
160
|
+
title: "module-ai.user_tabs.tokens",
|
|
161
161
|
icon: "Coins",
|
|
162
162
|
componentExport: "UserTokenTab",
|
|
163
163
|
entryPoint: "components/admin/UserTokenTab",
|
|
@@ -4,8 +4,8 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
4
4
|
* Create a Stripe checkout session for token purchase
|
|
5
5
|
*/
|
|
6
6
|
export declare function POST(request: NextRequest): Promise<NextResponse<{
|
|
7
|
-
checkout_url:
|
|
8
|
-
session_id:
|
|
7
|
+
checkout_url: string;
|
|
8
|
+
session_id: string;
|
|
9
9
|
}> | NextResponse<{
|
|
10
10
|
error: any;
|
|
11
11
|
}>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-checkout.d.ts","sourceRoot":"","sources":["../../../src/api/auth/token-checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"token-checkout.d.ts","sourceRoot":"","sources":["../../../src/api/auth/token-checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIxD;;;GAGG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;IAiE9C"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
|
-
import { getSupabaseServerClient,
|
|
2
|
+
import { getSupabaseServerClient, getApiBaseUrl } from "@lastbrain/core/server";
|
|
3
|
+
import { createOneTimeCheckout } from "@lastbrain-labs/module-core-payment-pro/server";
|
|
3
4
|
/**
|
|
4
5
|
* POST /api/ai/auth/token-checkout
|
|
5
6
|
* Create a Stripe checkout session for token purchase
|
|
@@ -27,49 +28,26 @@ export async function POST(request) {
|
|
|
27
28
|
if (packError || !pack) {
|
|
28
29
|
return NextResponse.json({ error: "Pack non trouvé" }, { status: 404 });
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
quantity: 1,
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
currency: pack.currency,
|
|
51
|
-
mode: "one_time",
|
|
52
|
-
success_url: successUrl,
|
|
53
|
-
cancel_url: cancelUrl,
|
|
54
|
-
metadata: {
|
|
55
|
-
purpose: "token_purchase",
|
|
56
|
-
module: "@lastbrain-labs/module-ai",
|
|
57
|
-
token_pack_id: pack.id,
|
|
58
|
-
tokens_amount: pack.tokens.toString(),
|
|
59
|
-
user_id: user.id,
|
|
60
|
-
},
|
|
61
|
-
}),
|
|
62
|
-
}, request);
|
|
63
|
-
if (!checkoutResponse.ok) {
|
|
64
|
-
const error = await checkoutResponse.json();
|
|
65
|
-
throw new Error(error.error || "Erreur lors de la création du checkout");
|
|
66
|
-
}
|
|
67
|
-
const checkoutData = await checkoutResponse.json();
|
|
68
|
-
const url = checkoutData.url || checkoutData.checkoutUrl || checkoutData.checkout_url;
|
|
69
|
-
const sessionId = checkoutData.session_id || checkoutData.sessionId;
|
|
31
|
+
// Build absolute URLs with a fully-qualified base (works on Vercel, custom domains, local)
|
|
32
|
+
const baseUrl = getApiBaseUrl();
|
|
33
|
+
const successUrl = `${baseUrl.replace(/\/$/, "")}${"/cart/success"}`;
|
|
34
|
+
const cancelUrl = `${baseUrl.replace(/\/$/, "")}${"/cart/cancel"}`;
|
|
35
|
+
// Use central payment service directly instead of fetching internally
|
|
36
|
+
const checkoutResult = await createOneTimeCheckout({
|
|
37
|
+
purpose: "token",
|
|
38
|
+
module: "module-ai",
|
|
39
|
+
mode: "one_time",
|
|
40
|
+
owner_id: user.id,
|
|
41
|
+
user_id: user.id,
|
|
42
|
+
resourceType: "token_packs",
|
|
43
|
+
resourceId: pack.id,
|
|
44
|
+
successPath: "/cart/success",
|
|
45
|
+
cancelPath: "/cart/cancel",
|
|
46
|
+
description: `${pack.tokens} tokens IA`,
|
|
47
|
+
});
|
|
70
48
|
return NextResponse.json({
|
|
71
|
-
checkout_url: url,
|
|
72
|
-
session_id: sessionId,
|
|
49
|
+
checkout_url: checkoutResult.url,
|
|
50
|
+
session_id: checkoutResult.sessionId,
|
|
73
51
|
}, { status: 200 });
|
|
74
52
|
}
|
|
75
53
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserTokenTab.d.ts","sourceRoot":"","sources":["../../../src/components/admin/UserTokenTab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"UserTokenTab.d.ts","sourceRoot":"","sources":["../../../src/components/admin/UserTokenTab.tsx"],"names":[],"mappings":"AAgBA,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB;AAiBD,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CA0RzD"}
|
|
@@ -3,7 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
4
|
import { Card, CardBody, CardHeader, Button, Input, Spinner, addToast, Chip, } from "@lastbrain/ui";
|
|
5
5
|
import { Plus, Minus, History, Coins } from "lucide-react";
|
|
6
|
+
import { useModuleTranslation } from "@lastbrain/core";
|
|
6
7
|
export function UserTokenTab({ userId }) {
|
|
8
|
+
const t = useModuleTranslation("ai");
|
|
7
9
|
const [loading, setLoading] = useState(true);
|
|
8
10
|
const [balance, setBalance] = useState(0);
|
|
9
11
|
const [ledger, setLedger] = useState([]);
|
|
@@ -16,7 +18,8 @@ export function UserTokenTab({ userId }) {
|
|
|
16
18
|
setLoading(true);
|
|
17
19
|
const res = await fetch(`/api/ai/admin/user-token/${userId}`);
|
|
18
20
|
if (!res.ok)
|
|
19
|
-
throw new Error("
|
|
21
|
+
throw new Error(t("admin.user_token.error.loading") ||
|
|
22
|
+
"Échec chargement détails tokens");
|
|
20
23
|
const data = await res.json();
|
|
21
24
|
const currentBalance = data.balance || 0;
|
|
22
25
|
setBalance(currentBalance);
|
|
@@ -33,16 +36,19 @@ export function UserTokenTab({ userId }) {
|
|
|
33
36
|
display_description: entry.meta?.reason ||
|
|
34
37
|
entry.description ||
|
|
35
38
|
entry.type ||
|
|
39
|
+
t("admin.user_token.transaction_label") ||
|
|
36
40
|
"Transaction",
|
|
37
41
|
};
|
|
38
42
|
});
|
|
39
43
|
setLedger(computed);
|
|
40
44
|
}
|
|
41
45
|
catch (error) {
|
|
42
|
-
console.error("
|
|
46
|
+
console.error(t("admin.user_token.error.loading_tokens") ||
|
|
47
|
+
"Erreur lors du chargement des tokens:", error);
|
|
43
48
|
addToast({
|
|
44
49
|
color: "danger",
|
|
45
|
-
title: "
|
|
50
|
+
title: t("admin.user_token.error.loading_data") ||
|
|
51
|
+
"Erreur lors du chargement des données",
|
|
46
52
|
});
|
|
47
53
|
}
|
|
48
54
|
finally {
|
|
@@ -58,14 +64,15 @@ export function UserTokenTab({ userId }) {
|
|
|
58
64
|
if (isNaN(amount) || amount <= 0) {
|
|
59
65
|
addToast({
|
|
60
66
|
color: "danger",
|
|
61
|
-
title: "Montant invalide",
|
|
67
|
+
title: t("admin.user_token.error.invalid_amount") || "Montant invalide",
|
|
62
68
|
});
|
|
63
69
|
return;
|
|
64
70
|
}
|
|
65
71
|
if (!adjustmentDescription.trim()) {
|
|
66
72
|
addToast({
|
|
67
73
|
color: "danger",
|
|
68
|
-
title: "
|
|
74
|
+
title: t("admin.user_token.error.description_required") ||
|
|
75
|
+
"Description requise",
|
|
69
76
|
});
|
|
70
77
|
return;
|
|
71
78
|
}
|
|
@@ -82,11 +89,14 @@ export function UserTokenTab({ userId }) {
|
|
|
82
89
|
}),
|
|
83
90
|
});
|
|
84
91
|
if (!response.ok) {
|
|
85
|
-
throw new Error("
|
|
92
|
+
throw new Error(t("admin.user_token.error.adjustment_failed") ||
|
|
93
|
+
"Échec de l'ajustement");
|
|
86
94
|
}
|
|
87
95
|
addToast({
|
|
88
96
|
color: "success",
|
|
89
|
-
title: type === "credit"
|
|
97
|
+
title: type === "credit"
|
|
98
|
+
? t("admin.user_token.success.credited") || "Tokens crédités"
|
|
99
|
+
: t("admin.user_token.success.debited") || "Tokens débités",
|
|
90
100
|
});
|
|
91
101
|
// Reset
|
|
92
102
|
setAdjustmentAmount("");
|
|
@@ -95,10 +105,11 @@ export function UserTokenTab({ userId }) {
|
|
|
95
105
|
await fetchTokenData();
|
|
96
106
|
}
|
|
97
107
|
catch (error) {
|
|
98
|
-
console.error("Erreur:", error);
|
|
108
|
+
console.error(t("admin.user_token.error.adjustment") || "Erreur:", error);
|
|
99
109
|
addToast({
|
|
100
110
|
color: "danger",
|
|
101
|
-
title: "
|
|
111
|
+
title: t("admin.user_token.error.adjustment_error") ||
|
|
112
|
+
"Erreur lors de l'ajustement",
|
|
102
113
|
});
|
|
103
114
|
}
|
|
104
115
|
finally {
|
|
@@ -108,5 +119,9 @@ export function UserTokenTab({ userId }) {
|
|
|
108
119
|
if (loading) {
|
|
109
120
|
return (_jsx("div", { className: "flex justify-center items-center min-h-64", children: _jsx(Spinner, { size: "lg" }) }));
|
|
110
121
|
}
|
|
111
|
-
return (_jsxs("div", { className: "w-full space-y-6 mt-4", children: [_jsxs("div", { className: "w-full flex flex-col md:flex-row gap-6", children: [_jsxs(Card, { className: "min-w-72", 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, { className: "flex items-center justify-center", 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:
|
|
122
|
+
return (_jsxs("div", { className: "w-full space-y-6 mt-4", children: [_jsxs("div", { className: "w-full flex flex-col md:flex-row gap-6", children: [_jsxs(Card, { className: "min-w-72", 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: t("admin.user_token.balance.title") || "Solde de tokens" })] }) }), _jsx(CardBody, { className: "flex items-center justify-center", 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: t("admin.user_token.balance.available") ||
|
|
123
|
+
"tokens disponibles" })] }) })] }), _jsxs(Card, { className: "flex-1 ", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: t("admin.user_token.adjustment.title") || "Ajuster le solde" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsx(Input, { type: "number", label: t("admin.user_token.adjustment.amount") || "Montant", placeholder: t("admin.user_token.adjustment.amount_placeholder") || "1000", value: adjustmentAmount, onChange: (e) => setAdjustmentAmount(e.target.value), min: "1", endContent: _jsx("span", { className: "text-sm text-gray-500", children: t("chip.tokens") || "tokens" }) }), _jsx(Input, { label: t("admin.user_token.adjustment.description") || "Description", placeholder: t("admin.user_token.adjustment.description_placeholder") ||
|
|
124
|
+
"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: t("admin.user_token.adjustment.credit_button") || "Créditer" }), _jsx(Button, { color: "danger", variant: "bordered", startContent: _jsx(Minus, { size: 16 }), onPress: () => handleAdjustment("debit"), isLoading: processing, isDisabled: !adjustmentAmount || !adjustmentDescription.trim(), children: t("admin.user_token.adjustment.debit_button") || "Débiter" })] })] })] })] }), _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: t("admin.user_token.history.title") ||
|
|
125
|
+
"Historique des transactions" })] }) }), _jsx(CardBody, { children: ledger.length === 0 ? (_jsx("p", { className: "text-center text-gray-500 py-4", children: t("admin.user_token.history.no_transactions") ||
|
|
126
|
+
"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: [t("admin.user_token.history.balance_label") || "Solde", ":", " ", entry.balance_after.toLocaleString()] })] })] }, entry.id))) })) })] })] }));
|
|
112
127
|
}
|
package/dist/server.d.ts
CHANGED
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,UAAU,GAAG,MAAM,GAAG,QAAiB,EAC7C,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAC9B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA+B/B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAoD/B;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBrE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU,GACjB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiB7B;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM;;;;;;GA4CjD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU;;;KAiBnB"}
|
package/dist/server.js
CHANGED
|
@@ -124,7 +124,7 @@ export async function getTokenHistory(userId, limit = 50, offset = 0) {
|
|
|
124
124
|
try {
|
|
125
125
|
const { data, error } = await supabase
|
|
126
126
|
.from("user_token_ledger")
|
|
127
|
-
.select("
|
|
127
|
+
.select("amount,created_at,created_by,id,model,owner_id,prompt,ts,type")
|
|
128
128
|
.eq("owner_id", userId)
|
|
129
129
|
.order("ts", { ascending: false })
|
|
130
130
|
.range(offset, offset + limit - 1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminTokenPacksPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/AdminTokenPacksPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminTokenPacksPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/AdminTokenPacksPage.tsx"],"names":[],"mappings":"AA0CA,wBAAgB,mBAAmB,4CAkXlC"}
|
|
@@ -3,7 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
4
|
import { Card, CardBody, Button, Input, Textarea, Switch, Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Spinner, addToast, Chip, } from "@lastbrain/ui";
|
|
5
5
|
import { Plus, Edit, Trash2 } from "lucide-react";
|
|
6
|
+
import { useModuleTranslation } from "@lastbrain/core";
|
|
6
7
|
export function AdminTokenPacksPage() {
|
|
8
|
+
const t = useModuleTranslation("ai");
|
|
7
9
|
const [packs, setPacks] = useState([]);
|
|
8
10
|
const [loading, setLoading] = useState(true);
|
|
9
11
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
@@ -26,7 +28,7 @@ export function AdminTokenPacksPage() {
|
|
|
26
28
|
setLoading(true);
|
|
27
29
|
const response = await fetch("/api/ai/admin/token-packs");
|
|
28
30
|
if (!response.ok)
|
|
29
|
-
throw new Error("Erreur lors du chargement");
|
|
31
|
+
throw new Error(t("admin.packs.error.loading") || "Erreur lors du chargement");
|
|
30
32
|
const result = await response.json();
|
|
31
33
|
setPacks(result.data || []);
|
|
32
34
|
}
|
|
@@ -77,10 +79,12 @@ export function AdminTokenPacksPage() {
|
|
|
77
79
|
body: JSON.stringify(formData),
|
|
78
80
|
});
|
|
79
81
|
if (!response.ok)
|
|
80
|
-
throw new Error("Erreur lors de la sauvegarde");
|
|
82
|
+
throw new Error(t("admin.packs.error.saving") || "Erreur lors de la sauvegarde");
|
|
81
83
|
addToast({
|
|
82
84
|
color: "success",
|
|
83
|
-
title: editingPack
|
|
85
|
+
title: editingPack
|
|
86
|
+
? t("admin.packs.success.updated") || "Pack mis à jour"
|
|
87
|
+
: t("admin.packs.success.created") || "Pack créé",
|
|
84
88
|
});
|
|
85
89
|
setIsModalOpen(false);
|
|
86
90
|
fetchPacks();
|
|
@@ -90,15 +94,19 @@ export function AdminTokenPacksPage() {
|
|
|
90
94
|
}
|
|
91
95
|
};
|
|
92
96
|
const handleDelete = async (id) => {
|
|
93
|
-
if (!confirm("
|
|
97
|
+
if (!confirm(t("admin.packs.confirm.delete") ||
|
|
98
|
+
"Êtes-vous sûr de vouloir supprimer ce pack ?"))
|
|
94
99
|
return;
|
|
95
100
|
try {
|
|
96
101
|
const response = await fetch(`/api/ai/admin/token-packs/${id}`, {
|
|
97
102
|
method: "DELETE",
|
|
98
103
|
});
|
|
99
104
|
if (!response.ok)
|
|
100
|
-
throw new Error("Erreur lors de la suppression");
|
|
101
|
-
addToast({
|
|
105
|
+
throw new Error(t("admin.packs.error.deleting") || "Erreur lors de la suppression");
|
|
106
|
+
addToast({
|
|
107
|
+
color: "success",
|
|
108
|
+
title: t("admin.packs.success.deleted") || "Pack supprimé",
|
|
109
|
+
});
|
|
102
110
|
fetchPacks();
|
|
103
111
|
}
|
|
104
112
|
catch (error) {
|
|
@@ -114,14 +122,23 @@ export function AdminTokenPacksPage() {
|
|
|
114
122
|
if (loading) {
|
|
115
123
|
return (_jsx("div", { className: "flex justify-center items-center min-h-96", children: _jsx(Spinner, { size: "lg" }) }));
|
|
116
124
|
}
|
|
117
|
-
return (_jsxs("div", { className: "container mx-auto p-2 md:p-6 max-w-7xl", children: [_jsxs("div", { className: "flex justify-between items-center mb-6", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-2", children:
|
|
125
|
+
return (_jsxs("div", { className: "container mx-auto p-2 md:p-6 max-w-7xl", children: [_jsxs("div", { className: "flex justify-between items-center mb-6", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-2", children: t("admin.packs.page.title") || "Token Packs" }), _jsx("p", { className: "text-gray-500", children: t("admin.packs.page.description") ||
|
|
126
|
+
"Gestion des packs de tokens disponibles à l'achat" })] }), _jsx(Button, { color: "primary", onPress: handleCreate, startContent: _jsx(Plus, { size: 20 }), children: t("admin.packs.button.new") || "Nouveau pack" })] }), _jsx(Card, { children: _jsx(CardBody, { children: _jsxs(Table, { "aria-label": t("admin.packs.table.aria_label") || "Token packs", children: [_jsxs(TableHeader, { children: [_jsx(TableColumn, { children: t("admin.packs.table.column_name") || "NOM" }), _jsx(TableColumn, { children: t("admin.packs.table.column_tokens") || "TOKENS" }), _jsx(TableColumn, { children: t("admin.packs.table.column_price") || "PRIX" }), _jsx(TableColumn, { children: t("admin.packs.table.column_status") || "STATUT" }), _jsx(TableColumn, { children: t("admin.packs.table.column_order") || "ORDRE" }), _jsx(TableColumn, { children: t("admin.packs.table.column_actions") || "ACTIONS" })] }), _jsx(TableBody, { children: packs.map((pack) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { children: [_jsx("p", { className: "font-semibold", children: pack.name }), pack.description && (_jsx("p", { className: "text-sm text-gray-500", children: pack.description }))] }) }), _jsx(TableCell, { children: _jsxs(Chip, { size: "sm", variant: "flat", color: "primary", children: [pack.tokens.toLocaleString(), " ", t("chip.tokens") || "tokens"] }) }), _jsx(TableCell, { children: _jsx("span", { className: "font-semibold", children: formatPrice(pack.price_cents, pack.currency) }) }), _jsx(TableCell, { children: _jsx(Chip, { size: "sm", variant: "flat", color: pack.is_active ? "success" : "default", children: pack.is_active
|
|
127
|
+
? t("admin.packs.status.active") || "Actif"
|
|
128
|
+
: t("admin.packs.status.inactive") || "Inactif" }) }), _jsx(TableCell, { children: pack.sort_order }), _jsx(TableCell, { children: _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { isIconOnly: true, size: "sm", variant: "light", onPress: () => handleEdit(pack), children: _jsx(Edit, { size: 16 }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "light", color: "danger", onPress: () => handleDelete(pack.id), children: _jsx(Trash2, { size: 16 }) })] }) })] }, pack.id))) })] }) }) }), _jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), size: "2xl", children: _jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: editingPack
|
|
129
|
+
? t("admin.packs.modal.edit_title") || "Modifier le pack"
|
|
130
|
+
: t("admin.packs.modal.new_title") || "Nouveau pack" }), _jsx(ModalBody, { children: _jsxs("div", { className: "space-y-4", children: [_jsx(Input, { label: t("admin.packs.form.name") || "Nom", placeholder: t("admin.packs.form.name_placeholder") || "Pack Starter", value: formData.name, onChange: (e) => setFormData({ ...formData, name: e.target.value }) }), _jsx(Textarea, { label: t("admin.packs.form.description") || "Description", placeholder: t("admin.packs.form.description_placeholder") ||
|
|
131
|
+
"Description du pack", value: formData.description, onChange: (e) => setFormData({ ...formData, description: e.target.value }) }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: t("admin.packs.form.tokens") || "Nombre de tokens", type: "number", value: formData.tokens.toString(), onChange: (e) => setFormData({
|
|
118
132
|
...formData,
|
|
119
133
|
tokens: parseInt(e.target.value) || 0,
|
|
120
|
-
}) }), _jsx(Input, { label: "Prix (centimes)", type: "number", value: formData.price_cents.toString(), onChange: (e) => setFormData({
|
|
134
|
+
}) }), _jsx(Input, { label: t("admin.packs.form.price") || "Prix (centimes)", type: "number", value: formData.price_cents.toString(), onChange: (e) => setFormData({
|
|
121
135
|
...formData,
|
|
122
136
|
price_cents: parseInt(e.target.value) || 0,
|
|
123
|
-
}) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: "Devise", value: formData.currency, onChange: (e) => setFormData({ ...formData, currency: e.target.value }) }), _jsx(Input, { label: "Ordre d'affichage", type: "number", value: formData.sort_order.toString(), onChange: (e) => setFormData({
|
|
137
|
+
}) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Input, { label: t("admin.packs.form.currency") || "Devise", value: formData.currency, onChange: (e) => setFormData({ ...formData, currency: e.target.value }) }), _jsx(Input, { label: t("admin.packs.form.sort_order") || "Ordre d'affichage", type: "number", value: formData.sort_order.toString(), onChange: (e) => setFormData({
|
|
124
138
|
...formData,
|
|
125
139
|
sort_order: parseInt(e.target.value) || 0,
|
|
126
|
-
}) })] }), _jsx(Input, { label:
|
|
140
|
+
}) })] }), _jsx(Input, { label: t("admin.packs.form.stripe_id") ||
|
|
141
|
+
"Stripe Price ID (optionnel)", placeholder: t("admin.packs.form.stripe_id_placeholder") || "price_...", value: formData.stripe_price_id, onChange: (e) => setFormData({ ...formData, stripe_price_id: e.target.value }) }), _jsx(Switch, { isSelected: formData.is_active, onValueChange: (checked) => setFormData({ ...formData, is_active: checked }), children: t("admin.packs.form.active") || "Pack actif" })] }) }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "light", onPress: () => setIsModalOpen(false), children: t("admin.packs.button.cancel") || "Annuler" }), _jsx(Button, { color: "primary", onPress: handleSave, children: editingPack
|
|
142
|
+
? t("admin.packs.button.update") || "Mettre à jour"
|
|
143
|
+
: t("admin.packs.button.create") || "Créer" })] })] }) })] }));
|
|
127
144
|
}
|
|
@@ -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":"AAgEA,wBAAgB,aAAa,4CAwc5B"}
|
|
@@ -3,7 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useEffect, useCallback } from "react";
|
|
4
4
|
import { Card, CardBody, CardHeader, Spinner, Chip, Select, SelectItem, Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, Avatar, Input, addToast, } from "@lastbrain/ui";
|
|
5
5
|
import { Coins, TrendingUp, TrendingDown, Calendar, Search, Users, AlertCircle, } from "lucide-react";
|
|
6
|
+
import { useModuleTranslation } from "@lastbrain/core";
|
|
6
7
|
export function UserTokenPage() {
|
|
8
|
+
const t = useModuleTranslation("ai");
|
|
7
9
|
const [loading, setLoading] = useState(true);
|
|
8
10
|
const [users, setUsers] = useState([]);
|
|
9
11
|
const [transactions, setTransactions] = useState([]);
|
|
@@ -22,7 +24,8 @@ export function UserTokenPage() {
|
|
|
22
24
|
// Récupérer toutes les données tokens de tous les utilisateurs
|
|
23
25
|
const response = await fetch("/api/ai/admin/user-token");
|
|
24
26
|
if (!response.ok) {
|
|
25
|
-
throw new Error("
|
|
27
|
+
throw new Error(t("admin.tokens.error.loading") ||
|
|
28
|
+
"Erreur lors du chargement des données");
|
|
26
29
|
}
|
|
27
30
|
const data = await response.json();
|
|
28
31
|
// Traiter les données utilisateurs
|
|
@@ -50,10 +53,11 @@ export function UserTokenPage() {
|
|
|
50
53
|
});
|
|
51
54
|
}
|
|
52
55
|
catch (error) {
|
|
53
|
-
console.error("Erreur:", error);
|
|
56
|
+
console.error(t("admin.tokens.error.loading") || "Erreur:", error);
|
|
54
57
|
addToast({
|
|
55
58
|
color: "danger",
|
|
56
|
-
title: "
|
|
59
|
+
title: t("admin.tokens.error.loading") ||
|
|
60
|
+
"Erreur lors du chargement des données",
|
|
57
61
|
});
|
|
58
62
|
}
|
|
59
63
|
finally {
|
|
@@ -74,10 +78,10 @@ export function UserTokenPage() {
|
|
|
74
78
|
};
|
|
75
79
|
const getTypeLabel = (type) => {
|
|
76
80
|
const labels = {
|
|
77
|
-
purchase: "Achat",
|
|
78
|
-
gift: "Cadeau",
|
|
79
|
-
use: "Utilisation",
|
|
80
|
-
adjust: "Ajustement",
|
|
81
|
+
purchase: t("tokens.type.purchase") || "Achat",
|
|
82
|
+
gift: t("tokens.type.gift") || "Cadeau",
|
|
83
|
+
use: t("tokens.type.use") || "Utilisation",
|
|
84
|
+
adjust: t("tokens.type.adjust") || "Ajustement",
|
|
81
85
|
};
|
|
82
86
|
return labels[type] || type;
|
|
83
87
|
};
|
|
@@ -96,12 +100,19 @@ export function UserTokenPage() {
|
|
|
96
100
|
if (loading) {
|
|
97
101
|
return (_jsx("div", { className: "flex justify-center items-center min-h-96", children: _jsx(Spinner, { size: "lg" }) }));
|
|
98
102
|
}
|
|
99
|
-
return (_jsxs("div", { className: "container mx-auto p-2 md:p-6 max-w-7xl", children: [_jsxs("div", { className: "mb-6", children: [_jsx("h1", { className: "text-3xl font-bold mb-2", children:
|
|
103
|
+
return (_jsxs("div", { className: "container mx-auto p-2 md:p-6 max-w-7xl", children: [_jsxs("div", { className: "mb-6", children: [_jsx("h1", { className: "text-3xl font-bold mb-2", children: t("admin.tokens.page.title") || "Gestion des Tokens IA" }), _jsx("p", { className: "text-gray-500", children: t("admin.tokens.page.description") ||
|
|
104
|
+
"Vue d'ensemble de la consommation des tokens par utilisateur" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-4 mb-6", children: [_jsx(Card, { children: _jsxs(CardBody, { className: "text-center py-6", children: [_jsxs("div", { className: "flex items-center justify-center gap-2 mb-2", children: [_jsx(Coins, { size: 24, className: "text-primary" }), _jsx("h3", { className: "text-sm font-medium text-gray-500", children: t("admin.tokens.stats.total_balance") || "Solde total" })] }), _jsx("p", { className: "text-3xl font-bold text-primary", children: monthlyStats?.totalBalance.toLocaleString() }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: t("admin.tokens.stats.in_circulation") ||
|
|
105
|
+
"tokens en circulation" })] }) }), _jsx(Card, { children: _jsxs(CardBody, { className: "text-center py-6", children: [_jsxs("div", { className: "flex items-center justify-center gap-2 mb-2", children: [_jsx(TrendingUp, { size: 24, className: "text-success" }), _jsx("h3", { className: "text-sm font-medium text-gray-500", children: t("admin.tokens.stats.monthly_credits") || "Crédits du mois" })] }), _jsx("p", { className: "text-3xl font-bold text-success", children: monthlyStats?.totalCredit.toLocaleString() }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: t("admin.tokens.stats.tokens_added") || "tokens ajoutés" })] }) }), _jsx(Card, { children: _jsxs(CardBody, { className: "text-center py-6", children: [_jsxs("div", { className: "flex items-center justify-center gap-2 mb-2", children: [_jsx(TrendingDown, { size: 24, className: "text-danger" }), _jsx("h3", { className: "text-sm font-medium text-gray-500", children: t("admin.tokens.stats.monthly_debits") || "Débits du mois" })] }), _jsx("p", { className: "text-3xl font-bold text-danger", children: monthlyStats?.totalDebit.toLocaleString() }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: t("admin.tokens.stats.tokens_consumed") || "tokens consommés" })] }) }), _jsx(Card, { children: _jsxs(CardBody, { className: "text-center py-6", children: [_jsxs("div", { className: "flex items-center justify-center gap-2 mb-2", children: [_jsx(Users, { size: 24, className: "text-warning" }), _jsx("h3", { className: "text-sm font-medium text-gray-500", children: t("admin.tokens.stats.active_users") || "Utilisateurs actifs" })] }), _jsx("p", { className: "text-3xl font-bold text-warning", children: monthlyStats?.activeUsers }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: t("admin.tokens.stats.this_month") || "ce mois-ci" })] }) })] }), _jsx(Card, { className: "mb-6", children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between w-full", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { size: 20 }), _jsx("h3", { className: "text-lg font-semibold", children: t("admin.tokens.period.title") || "Période d'analyse" })] }), _jsx(Select, { size: "sm", selectedKeys: [selectedMonth], onSelectionChange: (keys) => setSelectedMonth(Array.from(keys)[0]), className: "w-48", "aria-label": t("admin.tokens.period.select") || "Sélectionner un mois", children: availableMonths.map((month) => (_jsx(SelectItem, { textValue: month, children: new Date(month + "-01").toLocaleDateString("fr-FR", {
|
|
100
106
|
month: "long",
|
|
101
107
|
year: "numeric",
|
|
102
|
-
}) }, month))) })] }) }) }), _jsxs(Card, { className: "mb-6", children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between w-full", children: [_jsx("h3", { className: "text-lg font-semibold", children: "Soldes par utilisateur" }), _jsx(Input, { size: "sm", placeholder: "Rechercher un utilisateur...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), startContent: _jsx(Search, { size: 16 }), className: "w-64" })] }) }), _jsx(CardBody, { children: filteredUsers.length === 0 ? (_jsxs("div", { className: "text-center py-12 text-gray-500", children: [_jsx(Users, { size: 48, className: "mx-auto mb-4 opacity-20" }), _jsx("p", { children: "Aucun utilisateur
|
|
108
|
+
}) }, month))) })] }) }) }), _jsxs(Card, { className: "mb-6", children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between w-full", children: [_jsx("h3", { className: "text-lg font-semibold", children: t("admin.tokens.users.title") || "Soldes par utilisateur" }), _jsx(Input, { size: "sm", placeholder: t("admin.tokens.users.search") || "Rechercher un utilisateur...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), startContent: _jsx(Search, { size: 16 }), className: "w-64" })] }) }), _jsx(CardBody, { children: filteredUsers.length === 0 ? (_jsxs("div", { className: "text-center py-12 text-gray-500", children: [_jsx(Users, { size: 48, className: "mx-auto mb-4 opacity-20" }), _jsx("p", { children: t("admin.tokens.users.no_users") || "Aucun utilisateur trouvé" })] })) : (_jsxs(Table, { "aria-label": t("admin.tokens.users.aria_label") || "Soldes par utilisateur", children: [_jsxs(TableHeader, { children: [_jsx(TableColumn, { children: t("admin.tokens.users.column_user") || "UTILISATEUR" }), _jsx(TableColumn, { children: t("admin.tokens.users.column_balance") || "SOLDE ACTUEL" }), _jsx(TableColumn, { children: t("admin.tokens.users.column_added") || "TOTAL AJOUTÉ" }), _jsx(TableColumn, { children: t("admin.tokens.users.column_used") || "TOTAL UTILISÉ" }), _jsx(TableColumn, { children: t("admin.tokens.users.column_activity") ||
|
|
109
|
+
"DERNIÈRE ACTIVITÉ" })] }), _jsx(TableBody, { children: filteredUsers.map((user) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Avatar, { src: user.avatarUrl, name: user.fullName || user.email, size: "sm" }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: user.fullName || user.email }), user.fullName && (_jsx("p", { className: "text-xs text-gray-500", children: user.email }))] })] }) }), _jsx(TableCell, { children: _jsx("span", { className: "font-semibold text-primary", children: user.balance.toLocaleString() }) }), _jsx(TableCell, { children: _jsxs("span", { className: "text-success", children: ["+", user.totalAdded.toLocaleString()] }) }), _jsx(TableCell, { children: _jsxs("span", { className: "text-danger", children: ["-", user.totalUsed.toLocaleString()] }) }), _jsx(TableCell, { children: _jsx("span", { className: "text-sm text-gray-600", children: user.lastActivity
|
|
103
110
|
? formatDate(user.lastActivity)
|
|
104
|
-
: "-" }) })] }, user.userId))) })] })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children:
|
|
111
|
+
: "-" }) })] }, user.userId))) })] })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: t("admin.tokens.transactions.title") ||
|
|
112
|
+
"Transactions du mois sélectionné" }) }), _jsx(CardBody, { children: transactions.length === 0 ? (_jsxs("div", { className: "text-center py-12 text-gray-500", children: [_jsx(AlertCircle, { size: 48, className: "mx-auto mb-4 opacity-20" }), _jsx("p", { children: t("admin.tokens.transactions.no_transactions") ||
|
|
113
|
+
"Aucune transaction pour ce mois" })] })) : (_jsxs(Table, { "aria-label": t("admin.tokens.transactions.aria_label") ||
|
|
114
|
+
"Historique des transactions", children: [_jsxs(TableHeader, { children: [_jsx(TableColumn, { children: t("admin.tokens.transactions.column_date") || "DATE" }), _jsx(TableColumn, { children: t("admin.tokens.transactions.column_user") || "UTILISATEUR" }), _jsx(TableColumn, { children: t("admin.tokens.transactions.column_type") || "TYPE" }), _jsx(TableColumn, { children: t("admin.tokens.transactions.column_description") ||
|
|
115
|
+
"DESCRIPTION" }), _jsx(TableColumn, { children: t("admin.tokens.transactions.column_amount") || "MONTANT" })] }), _jsx(TableBody, { children: transactions.map((transaction) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx("span", { className: "text-sm text-gray-600", children: formatDate(transaction.created_at) }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Avatar, { src: transaction.avatarUrl, name: transaction.fullName || transaction.email, size: "sm" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium", children: transaction.fullName || transaction.email }), transaction.fullName && (_jsx("p", { className: "text-xs text-gray-500", children: transaction.email }))] })] }) }), _jsx(TableCell, { children: _jsx(Chip, { size: "sm", variant: "flat", color: getTypeColor(transaction.type), children: getTypeLabel(transaction.type) }) }), _jsx(TableCell, { children: _jsx("div", { className: "max-w-md", children: _jsx("p", { className: "text-sm truncate", children: transaction.description || transaction.model || "-" }) }) }), _jsx(TableCell, { children: _jsxs("span", { className: `font-semibold ${transaction.amount > 0
|
|
105
116
|
? "text-success"
|
|
106
117
|
: "text-danger"}`, children: [transaction.amount > 0 ? "+" : "", transaction.amount.toLocaleString()] }) })] }, transaction.id))) })] })) })] })] }));
|
|
107
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenPage.d.ts","sourceRoot":"","sources":["../../../src/web/auth/TokenPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TokenPage.d.ts","sourceRoot":"","sources":["../../../src/web/auth/TokenPage.tsx"],"names":[],"mappings":"AAoEA,wBAAgB,SAAS,4CAkhBxB"}
|