@lastbrain/module-ai 0.1.17 → 0.1.19

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.
Files changed (38) hide show
  1. package/dist/ai.build.config.d.ts.map +1 -1
  2. package/dist/ai.build.config.js +65 -1
  3. package/dist/api/admin/token-packs/[id].d.ts +28 -0
  4. package/dist/api/admin/token-packs/[id].d.ts.map +1 -0
  5. package/dist/api/admin/token-packs/[id].js +44 -0
  6. package/dist/api/admin/token-packs.d.ts +20 -0
  7. package/dist/api/admin/token-packs.d.ts.map +1 -0
  8. package/dist/api/admin/token-packs.js +57 -0
  9. package/dist/api/admin/user-token.js +2 -1
  10. package/dist/api/auth/create-checkout.d.ts +31 -0
  11. package/dist/api/auth/create-checkout.d.ts.map +1 -0
  12. package/dist/api/auth/create-checkout.js +93 -0
  13. package/dist/api/auth/generate-image.d.ts +3 -3
  14. package/dist/api/auth/generate-image.d.ts.map +1 -1
  15. package/dist/api/auth/generate-image.js +68 -27
  16. package/dist/api/auth/token-checkout.d.ts +12 -0
  17. package/dist/api/auth/token-checkout.d.ts.map +1 -0
  18. package/dist/api/auth/token-checkout.js +79 -0
  19. package/dist/api/auth/token-packs.d.ts +11 -0
  20. package/dist/api/auth/token-packs.d.ts.map +1 -0
  21. package/dist/api/auth/token-packs.js +29 -0
  22. package/dist/api/public/webhook.d.ts +2 -0
  23. package/dist/api/public/webhook.d.ts.map +1 -0
  24. package/dist/api/public/webhook.js +171 -0
  25. package/dist/components/DocUsageCustom.js +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -0
  29. package/dist/web/admin/AdminTokenPacksPage.d.ts +2 -0
  30. package/dist/web/admin/AdminTokenPacksPage.d.ts.map +1 -0
  31. package/dist/web/admin/AdminTokenPacksPage.js +127 -0
  32. package/dist/web/auth/TokenPage.d.ts.map +1 -1
  33. package/dist/web/auth/TokenPage.js +51 -3
  34. package/dist/web/components/ImageGenerative.d.ts +5 -2
  35. package/dist/web/components/ImageGenerative.d.ts.map +1 -1
  36. package/dist/web/components/ImageGenerative.js +51 -7
  37. package/package.json +8 -4
  38. package/supabase/migrations/20251201000000_token_packs.sql +73 -0
@@ -0,0 +1,11 @@
1
+ import { NextResponse } from "next/server";
2
+ /**
3
+ * GET /api/ai/auth/token-packs
4
+ * Get active token packs available for purchase
5
+ */
6
+ export declare function GET(): Promise<NextResponse<{
7
+ data: any[];
8
+ }> | NextResponse<{
9
+ error: any;
10
+ }>>;
11
+ //# sourceMappingURL=token-packs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-packs.d.ts","sourceRoot":"","sources":["../../../src/api/auth/token-packs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAGxD;;;GAGG;AACH,wBAAsB,GAAG;;;;IA8BxB"}
@@ -0,0 +1,29 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getSupabaseServerClient } from "@lastbrain/core/server";
3
+ /**
4
+ * GET /api/ai/auth/token-packs
5
+ * Get active token packs available for purchase
6
+ */
7
+ export async function GET() {
8
+ try {
9
+ const supabase = await getSupabaseServerClient();
10
+ // Verify authentication
11
+ const { data: { user }, } = await supabase.auth.getUser();
12
+ if (!user) {
13
+ return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
14
+ }
15
+ // Get active packs
16
+ const { data, error } = await supabase
17
+ .from("token_packs")
18
+ .select("*")
19
+ .eq("is_active", true)
20
+ .order("sort_order", { ascending: true });
21
+ if (error)
22
+ throw error;
23
+ return NextResponse.json({ data }, { status: 200 });
24
+ }
25
+ catch (error) {
26
+ console.error("[Token Packs] GET error:", error);
27
+ return NextResponse.json({ error: error.message || "Erreur lors de la récupération des packs" }, { status: 500 });
28
+ }
29
+ }
@@ -0,0 +1,2 @@
1
+ export declare function POST(request: Request): Promise<Response>;
2
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/api/public/webhook.ts"],"names":[],"mappings":"AAyBA,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAkN1C"}
@@ -0,0 +1,171 @@
1
+ // ============================================================================
2
+ // POST /api/ai/public/webhook - Module AI Webhook Handler
3
+ // ============================================================================
4
+ // Receives webhook calls from the central payment webhook when a token purchase
5
+ // payment succeeds. Adds tokens to user balance.
6
+ // CRITICAL: This endpoint must ONLY be callable by service_role to prevent abuse.
7
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
8
+ export async function POST(request) {
9
+ try {
10
+ const supabase = await getSupabaseServiceClient();
11
+ const payload = (await request.json());
12
+ const { payment, metadata } = payload;
13
+ console.log("[AI Webhook] Received webhook for payment", payment.id);
14
+ // Verify this is a token purchase
15
+ if (metadata.purpose !== "token_purchase") {
16
+ console.log("[AI Webhook] Not a token purchase, skipping");
17
+ return Response.json({ received: true }, { status: 200 });
18
+ }
19
+ if (!payment?.owner_id) {
20
+ return Response.json({ error: "Missing owner_id" }, { status: 400 });
21
+ }
22
+ // Extract token pack info from metadata
23
+ const tokenPackId = metadata.token_pack_id;
24
+ const tokensAmount = parseInt(metadata.tokens_amount, 10);
25
+ if (!tokenPackId || !tokensAmount) {
26
+ console.error("[AI Webhook] Missing token pack metadata");
27
+ return Response.json({ error: "Missing token pack metadata" }, { status: 400 });
28
+ }
29
+ // Verify token pack exists
30
+ const { data: pack, error: packError } = await supabase
31
+ .from("token_packs")
32
+ .select("id, name, tokens")
33
+ .eq("id", tokenPackId)
34
+ .single();
35
+ if (packError || !pack) {
36
+ console.error("[AI Webhook] Token pack not found:", tokenPackId);
37
+ return Response.json({ error: "Token pack not found" }, { status: 404 });
38
+ }
39
+ // Idempotency: use payment.id as ledger id and upsert on conflict
40
+ const ledgerOwnerId = payment.user_id || payment.owner_id;
41
+ const meta = {
42
+ token_pack_id: tokenPackId,
43
+ token_pack_name: pack.name,
44
+ payment_id: payment.id,
45
+ provider_payment_id: payment.provider_payment_id,
46
+ amount_cents: payment.amount?.value_cents,
47
+ currency: payment.currency,
48
+ };
49
+ try {
50
+ const { error: upsertErr } = await supabase
51
+ .from("user_token_ledger")
52
+ .upsert({
53
+ id: payment.id, // ensures idempotency across duplicate webhook deliveries
54
+ owner_id: ledgerOwnerId,
55
+ type: "purchase",
56
+ amount: tokensAmount,
57
+ model: null,
58
+ prompt: null,
59
+ meta,
60
+ created_by: null,
61
+ }, { onConflict: "id", ignoreDuplicates: true });
62
+ if (upsertErr) {
63
+ // If it's a unique violation or duplicate, treat as already processed
64
+ console.warn("[AI Webhook] Upsert ledger warning:", upsertErr);
65
+ }
66
+ console.log(`[AI Webhook] Credited (idempotent) ${tokensAmount} tokens to user ${ledgerOwnerId} (payment ${payment.id})`);
67
+ }
68
+ catch (error) {
69
+ console.error("[AI Webhook] Error upserting ledger:", error);
70
+ return Response.json({ error: "Error adding tokens" }, { status: 500 });
71
+ }
72
+ // Create an order for this token purchase so the user can retrieve an invoice/receipt
73
+ try {
74
+ // Check if an order already exists for this payment
75
+ const { data: existingOrder } = await supabase
76
+ .from("orders")
77
+ .select("id")
78
+ .eq("payment_id", payment.id)
79
+ .maybeSingle();
80
+ if (!existingOrder) {
81
+ // Create order record
82
+ const { data: order, error: orderErr } = await supabase
83
+ .from("orders")
84
+ .insert({
85
+ owner_id: payment.owner_id,
86
+ user_id: payment.user_id,
87
+ cart_id: payment.cart_id || null,
88
+ payment_id: payment.id,
89
+ external_invoice_id: payload.externalInvoiceId || null,
90
+ payment_status: "succeeded",
91
+ order_status: "completed",
92
+ currency: payment.currency,
93
+ subtotal_cents: payment.amount?.value_cents || 0,
94
+ total_cents: payment.amount?.value_cents || 0,
95
+ paid_at: new Date().toISOString(),
96
+ })
97
+ .select()
98
+ .single();
99
+ if (orderErr) {
100
+ console.warn("[AI Webhook] Could not create order for token purchase:", orderErr);
101
+ }
102
+ else if (order?.id) {
103
+ // Create a single order item representing the token pack
104
+ const { error: itemErr } = await supabase.from("order_items").insert({
105
+ owner_id: payment.owner_id,
106
+ order_id: order.id,
107
+ product_id: tokenPackId,
108
+ variant_id: null,
109
+ name: pack.name,
110
+ sku: null,
111
+ unit_price_cents: payment.amount?.value_cents || 0,
112
+ currency: payment.currency,
113
+ quantity: 1,
114
+ total_cents: payment.amount?.value_cents || 0,
115
+ snapshot: { token_pack_id: tokenPackId, tokens: pack.tokens },
116
+ });
117
+ if (itemErr) {
118
+ console.warn("[AI Webhook] Could not insert order item:", itemErr);
119
+ }
120
+ // Link payment to order for downstream queries
121
+ const { error: linkErr } = await supabase
122
+ .from("payments")
123
+ .update({ order_id: order.id })
124
+ .eq("id", payment.id)
125
+ .eq("owner_id", payment.owner_id);
126
+ if (linkErr) {
127
+ console.warn("[AI Webhook] Could not link payment to order:", linkErr);
128
+ }
129
+ else {
130
+ console.log(`[AI Webhook] Created order ${order.id} for token purchase and linked payment`);
131
+ }
132
+ // Delete the dedicated token cart (and its items) after order creation
133
+ if (payment.cart_id) {
134
+ try {
135
+ const { error: delItemsErr } = await supabase
136
+ .from("cart_items")
137
+ .delete()
138
+ .eq("cart_id", payment.cart_id)
139
+ .eq("owner_id", payment.owner_id);
140
+ if (delItemsErr) {
141
+ console.warn("[AI Webhook] Could not delete token cart items:", delItemsErr);
142
+ }
143
+ const { error: delCartErr } = await supabase
144
+ .from("carts")
145
+ .delete()
146
+ .eq("id", payment.cart_id)
147
+ .eq("owner_id", payment.owner_id);
148
+ if (delCartErr) {
149
+ console.warn("[AI Webhook] Could not delete token cart:", delCartErr);
150
+ }
151
+ else {
152
+ console.log(`[AI Webhook] Deleted token cart ${payment.cart_id}`);
153
+ }
154
+ }
155
+ catch (cartDeleteErr) {
156
+ console.warn("[AI Webhook] Token cart deletion failed:", cartDeleteErr);
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ catch (orderCreateErr) {
163
+ console.warn("[AI Webhook] Order creation for token purchase failed:", orderCreateErr);
164
+ }
165
+ return Response.json({ received: true, tokens_added: tokensAmount }, { status: 200 });
166
+ }
167
+ catch (error) {
168
+ console.error("[AI Webhook] Error:", error);
169
+ return Response.json({ error: error instanceof Error ? error.message : "Webhook error" }, { status: 400 });
170
+ }
171
+ }
@@ -12,7 +12,7 @@ import { Lightbulb } from "lucide-react";
12
12
  * de vos composants avec des exemples interactifs.
13
13
  */
14
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";
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 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
16
 
17
17
  export function MyComponent() {
18
18
  return (
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ 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 { AdminTokenPacksPage } from "./web/admin/AdminTokenPacksPage.js";
5
6
  export { UserTokenTab } from "./components/admin/UserTokenTab.js";
6
7
  export { Doc } from "./components/Doc.js";
7
8
  export { Doc as AiModuleDoc } from "./components/Doc.js";
@@ -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;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"}
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;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAGzE,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,6 +5,7 @@ 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
+ export { AdminTokenPacksPage } from "./web/admin/AdminTokenPacksPage.js";
8
9
  // Admin Components - User Tabs
9
10
  export { UserTokenTab } from "./components/admin/UserTokenTab.js";
10
11
  // Documentation
@@ -0,0 +1,2 @@
1
+ export declare function AdminTokenPacksPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=AdminTokenPacksPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminTokenPacksPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/AdminTokenPacksPage.tsx"],"names":[],"mappings":"AA0CA,wBAAgB,mBAAmB,4CAyTlC"}
@@ -0,0 +1,127 @@
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, Button, Input, Textarea, Switch, Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Spinner, addToast, Chip, } from "@lastbrain/ui";
5
+ import { Plus, Edit, Trash2 } from "lucide-react";
6
+ export function AdminTokenPacksPage() {
7
+ const [packs, setPacks] = useState([]);
8
+ const [loading, setLoading] = useState(true);
9
+ const [isModalOpen, setIsModalOpen] = useState(false);
10
+ const [editingPack, setEditingPack] = useState(null);
11
+ const [formData, setFormData] = useState({
12
+ name: "",
13
+ description: "",
14
+ tokens: 100,
15
+ price_cents: 1000,
16
+ currency: "EUR",
17
+ stripe_price_id: "",
18
+ is_active: true,
19
+ sort_order: 0,
20
+ });
21
+ useEffect(() => {
22
+ fetchPacks();
23
+ }, []);
24
+ const fetchPacks = async () => {
25
+ try {
26
+ setLoading(true);
27
+ const response = await fetch("/api/ai/admin/token-packs");
28
+ if (!response.ok)
29
+ throw new Error("Erreur lors du chargement");
30
+ const result = await response.json();
31
+ setPacks(result.data || []);
32
+ }
33
+ catch (error) {
34
+ addToast({ color: "danger", title: error.message });
35
+ }
36
+ finally {
37
+ setLoading(false);
38
+ }
39
+ };
40
+ const handleCreate = () => {
41
+ setEditingPack(null);
42
+ setFormData({
43
+ name: "",
44
+ description: "",
45
+ tokens: 100,
46
+ price_cents: 1000,
47
+ currency: "EUR",
48
+ stripe_price_id: "",
49
+ is_active: true,
50
+ sort_order: 0,
51
+ });
52
+ setIsModalOpen(true);
53
+ };
54
+ const handleEdit = (pack) => {
55
+ setEditingPack(pack);
56
+ setFormData({
57
+ name: pack.name,
58
+ description: pack.description || "",
59
+ tokens: pack.tokens,
60
+ price_cents: pack.price_cents,
61
+ currency: pack.currency,
62
+ stripe_price_id: pack.stripe_price_id || "",
63
+ is_active: pack.is_active,
64
+ sort_order: pack.sort_order,
65
+ });
66
+ setIsModalOpen(true);
67
+ };
68
+ const handleSave = async () => {
69
+ try {
70
+ const url = editingPack
71
+ ? `/api/ai/admin/token-packs/${editingPack.id}`
72
+ : "/api/ai/admin/token-packs";
73
+ const method = editingPack ? "PUT" : "POST";
74
+ const response = await fetch(url, {
75
+ method,
76
+ headers: { "Content-Type": "application/json" },
77
+ body: JSON.stringify(formData),
78
+ });
79
+ if (!response.ok)
80
+ throw new Error("Erreur lors de la sauvegarde");
81
+ addToast({
82
+ color: "success",
83
+ title: editingPack ? "Pack mis à jour" : "Pack créé",
84
+ });
85
+ setIsModalOpen(false);
86
+ fetchPacks();
87
+ }
88
+ catch (error) {
89
+ addToast({ color: "danger", title: error.message });
90
+ }
91
+ };
92
+ const handleDelete = async (id) => {
93
+ if (!confirm("Êtes-vous sûr de vouloir supprimer ce pack ?"))
94
+ return;
95
+ try {
96
+ const response = await fetch(`/api/ai/admin/token-packs/${id}`, {
97
+ method: "DELETE",
98
+ });
99
+ if (!response.ok)
100
+ throw new Error("Erreur lors de la suppression");
101
+ addToast({ color: "success", title: "Pack supprimé" });
102
+ fetchPacks();
103
+ }
104
+ catch (error) {
105
+ addToast({ color: "danger", title: error.message });
106
+ }
107
+ };
108
+ const formatPrice = (cents, currency) => {
109
+ return new Intl.NumberFormat("fr-FR", {
110
+ style: "currency",
111
+ currency,
112
+ }).format(cents / 100);
113
+ };
114
+ if (loading) {
115
+ return (_jsx("div", { className: "flex justify-center items-center min-h-96", children: _jsx(Spinner, { size: "lg" }) }));
116
+ }
117
+ return (_jsxs("div", { className: "container mx-auto 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: "Token Packs" }), _jsx("p", { className: "text-gray-500", children: "Gestion des packs de tokens disponibles \u00E0 l'achat" })] }), _jsx(Button, { color: "primary", onPress: handleCreate, startContent: _jsx(Plus, { size: 20 }), children: "Nouveau pack" })] }), _jsx(Card, { children: _jsx(CardBody, { children: _jsxs(Table, { "aria-label": "Token packs", children: [_jsxs(TableHeader, { children: [_jsx(TableColumn, { children: "NOM" }), _jsx(TableColumn, { children: "TOKENS" }), _jsx(TableColumn, { children: "PRIX" }), _jsx(TableColumn, { children: "STATUT" }), _jsx(TableColumn, { children: "ORDRE" }), _jsx(TableColumn, { children: "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(), " 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 ? "Actif" : "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 ? "Modifier le pack" : "Nouveau pack" }), _jsx(ModalBody, { children: _jsxs("div", { className: "space-y-4", children: [_jsx(Input, { label: "Nom", placeholder: "Pack Starter", value: formData.name, onChange: (e) => setFormData({ ...formData, name: e.target.value }) }), _jsx(Textarea, { label: "Description", placeholder: "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: "Nombre de tokens", type: "number", value: formData.tokens.toString(), onChange: (e) => setFormData({
118
+ ...formData,
119
+ tokens: parseInt(e.target.value) || 0,
120
+ }) }), _jsx(Input, { label: "Prix (centimes)", type: "number", value: formData.price_cents.toString(), onChange: (e) => setFormData({
121
+ ...formData,
122
+ 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({
124
+ ...formData,
125
+ sort_order: parseInt(e.target.value) || 0,
126
+ }) })] }), _jsx(Input, { label: "Stripe Price ID (optionnel)", 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: "Pack actif" })] }) }), _jsxs(ModalFooter, { children: [_jsx(Button, { variant: "light", onPress: () => setIsModalOpen(false), children: "Annuler" }), _jsx(Button, { color: "primary", onPress: handleSave, children: editingPack ? "Mettre à jour" : "Créer" })] })] }) })] }));
127
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"TokenPage.d.ts","sourceRoot":"","sources":["../../../src/web/auth/TokenPage.tsx"],"names":[],"mappings":"AAqDA,wBAAgB,SAAS,4CAoWxB"}
1
+ {"version":3,"file":"TokenPage.d.ts","sourceRoot":"","sources":["../../../src/web/auth/TokenPage.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,SAAS,4CAobxB"}
@@ -11,6 +11,8 @@ export function TokenPage() {
11
11
  const [transactions, setTransactions] = useState([]);
12
12
  const [selectedMonth, setSelectedMonth] = useState(new Date().toISOString().slice(0, 7));
13
13
  const [monthlyStats, setMonthlyStats] = useState(null);
14
+ const [tokenPacks, setTokenPacks] = useState([]);
15
+ const [checkoutLoading, setCheckoutLoading] = useState(null);
14
16
  // Générer les 12 derniers mois
15
17
  const availableMonths = Array.from({ length: 12 }, (_, i) => {
16
18
  const date = new Date();
@@ -57,6 +59,12 @@ export function TokenPage() {
57
59
  used,
58
60
  net: added - used,
59
61
  });
62
+ // Récupérer les packs disponibles
63
+ const packsResponse = await fetch("/api/ai/auth/token-packs");
64
+ if (packsResponse.ok) {
65
+ const packsData = await packsResponse.json();
66
+ setTokenPacks(packsData.data || []);
67
+ }
60
68
  }
61
69
  catch (error) {
62
70
  console.error("Erreur:", error);
@@ -99,16 +107,56 @@ export function TokenPage() {
99
107
  };
100
108
  return colors[type] || "default";
101
109
  };
110
+ const handleBuyTokens = async (packId) => {
111
+ try {
112
+ setCheckoutLoading(packId);
113
+ const response = await fetch("/api/ai/auth/token-checkout", {
114
+ method: "POST",
115
+ headers: { "Content-Type": "application/json" },
116
+ body: JSON.stringify({ pack_id: packId }),
117
+ });
118
+ if (!response.ok) {
119
+ const error = await response.json();
120
+ throw new Error(error.error || "Erreur lors du checkout");
121
+ }
122
+ const data = await response.json();
123
+ if (data.checkout_url) {
124
+ window.location.href = data.checkout_url;
125
+ }
126
+ }
127
+ catch (error) {
128
+ addToast({
129
+ color: "danger",
130
+ title: error.message || "Erreur lors du checkout",
131
+ });
132
+ }
133
+ finally {
134
+ setCheckoutLoading(null);
135
+ }
136
+ };
137
+ const formatPrice = (cents, currency) => {
138
+ return new Intl.NumberFormat("fr-FR", {
139
+ style: "currency",
140
+ currency,
141
+ }).format(cents / 100);
142
+ };
143
+ const formatTokensShort = (tokens) => {
144
+ if (tokens >= 10000) {
145
+ const thousands = Math.round(tokens / 1000);
146
+ return `${thousands}K`;
147
+ }
148
+ return tokens.toLocaleString();
149
+ };
102
150
  if (!user) {
103
151
  return (_jsx("div", { className: "flex justify-center items-center min-h-96", children: _jsxs("div", { className: "text-center", children: [_jsx(AlertCircle, { className: "mx-auto mb-4", size: 48 }), _jsx("p", { className: "text-gray-500", children: "Veuillez vous connecter" })] }) }));
104
152
  }
105
153
  if (loading) {
106
154
  return (_jsx("div", { className: "flex justify-center items-center min-h-96", children: _jsx(Spinner, { size: "lg" }) }));
107
155
  }
108
- return (_jsxs("div", { className: "container mx-auto p-6 max-w-7xl", children: [_jsxs("div", { className: "mb-6", children: [_jsx("h1", { className: "text-3xl font-bold mb-2", children: "Mes Tokens IA" }), _jsx("p", { className: "text-gray-500", children: "G\u00E9rez votre solde et consultez votre historique de consommation" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 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: "Solde actuel" })] }), _jsx("p", { className: "text-4xl font-bold text-primary", children: balance?.balance.toLocaleString() }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: "tokens disponibles" })] }) }), _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: "Total ajout\u00E9" })] }), _jsx("p", { className: "text-4xl font-bold text-success", children: balance?.totalAdded.toLocaleString() }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: "tokens re\u00E7us" })] }) }), _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: "Total utilis\u00E9" })] }), _jsx("p", { className: "text-4xl font-bold text-danger", children: balance?.totalUsed.toLocaleString() }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: "tokens consomm\u00E9s" })] }) })] }), monthlyStats && (_jsxs(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: "Statistiques du mois" })] }), _jsx(Select, { size: "sm", selectedKeys: [selectedMonth], onSelectionChange: (keys) => setSelectedMonth(Array.from(keys)[0]), className: "w-48", "aria-label": "S\u00E9lectionner un mois", children: availableMonths.map((month) => (_jsx(SelectItem, { textValue: month, children: new Date(month + "-01").toLocaleDateString("fr-FR", {
156
+ return (_jsxs("div", { className: "container mx-auto p-6 max-w-7xl", children: [_jsxs("div", { className: "mb-6", children: [_jsx("h1", { className: "text-3xl font-bold mb-2", children: "Mes Tokens IA" }), _jsx("p", { className: "text-gray-500", children: "G\u00E9rez votre solde et consultez votre historique de consommation" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 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: "Solde actuel" })] }), _jsx("p", { className: "text-4xl font-bold text-primary", children: formatTokensShort(balance?.balance || 0) }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: "tokens disponibles" })] }) }), _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: "Total" })] }), _jsx("p", { className: "text-4xl font-bold text-success", children: formatTokensShort(balance?.totalAdded || 0) }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: "tokens achet\u00E9" })] }) }), _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: "Total utilis\u00E9" })] }), _jsx("p", { className: "text-4xl font-bold text-danger", children: formatTokensShort(balance?.totalUsed || 0) }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: "tokens consomm\u00E9s" })] }) })] }), monthlyStats && (_jsxs(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: "Statistiques du mois" })] }), _jsx(Select, { size: "sm", selectedKeys: [selectedMonth], onSelectionChange: (keys) => setSelectedMonth(Array.from(keys)[0]), className: "w-48", "aria-label": "S\u00E9lectionner un mois", children: availableMonths.map((month) => (_jsx(SelectItem, { textValue: month, children: new Date(month + "-01").toLocaleDateString("fr-FR", {
109
157
  month: "long",
110
158
  year: "numeric",
111
- }) }, month))) })] }) }), _jsx(CardBody, { children: _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "text-center p-4 bg-success-50 dark:bg-success-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Ajout\u00E9s" }), _jsxs("p", { className: "text-2xl font-bold text-success", children: ["+", monthlyStats.added.toLocaleString()] })] }), _jsxs("div", { className: "text-center p-4 bg-danger-50 dark:bg-danger-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Utilis\u00E9s" }), _jsxs("p", { className: "text-2xl font-bold text-danger", children: ["-", monthlyStats.used.toLocaleString()] })] }), _jsxs("div", { className: "text-center p-4 bg-primary-50 dark:bg-primary-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Solde net" }), _jsxs("p", { className: `text-2xl font-bold ${monthlyStats.net >= 0 ? "text-success" : "text-danger"}`, children: [monthlyStats.net >= 0 ? "+" : "", monthlyStats.net.toLocaleString()] })] })] }) })] })), _jsxs(Card, { className: "mb-6", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: "Historique des transactions" }) }), _jsx(CardBody, { children: transactions.length === 0 ? (_jsxs("div", { className: "text-center py-12 text-gray-500", children: [_jsx(Coins, { size: 48, className: "mx-auto mb-4 opacity-20" }), _jsx("p", { children: "Aucune transaction pour ce mois" })] })) : (_jsxs(Table, { "aria-label": "Historique des transactions", children: [_jsxs(TableHeader, { children: [_jsx(TableColumn, { children: "DATE" }), _jsx(TableColumn, { children: "TYPE" }), _jsx(TableColumn, { children: "DESCRIPTION" }), _jsx(TableColumn, { children: "MONTANT" }), _jsx(TableColumn, { children: "SOLDE APR\u00C8S" })] }), _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: _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
159
+ }) }, month))) })] }) }), _jsx(CardBody, { children: _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "text-center p-4 bg-success-50 dark:bg-success-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Ajout\u00E9s" }), _jsxs("p", { className: "text-2xl font-bold text-success", children: ["+", formatTokensShort(monthlyStats.added)] })] }), _jsxs("div", { className: "text-center p-4 bg-danger-50 dark:bg-danger-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Utilis\u00E9s" }), _jsxs("p", { className: "text-2xl font-bold text-danger", children: ["-", formatTokensShort(monthlyStats.used)] })] }), _jsxs("div", { className: "text-center p-4 bg-primary-50 dark:bg-primary-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Solde net" }), _jsxs("p", { className: `text-2xl font-bold ${monthlyStats.net >= 0 ? "text-success" : "text-danger"}`, children: [monthlyStats.net >= 0 ? "+" : "", formatTokensShort(Math.abs(monthlyStats.net))] })] })] }) })] })), _jsxs(Card, { className: "mb-6", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: "Historique des transactions" }) }), _jsx(CardBody, { children: transactions.length === 0 ? (_jsxs("div", { className: "text-center py-12 text-gray-500", children: [_jsx(Coins, { size: 48, className: "mx-auto mb-4 opacity-20" }), _jsx("p", { children: "Aucune transaction pour ce mois" })] })) : (_jsxs(Table, { "aria-label": "Historique des transactions", children: [_jsxs(TableHeader, { children: [_jsx(TableColumn, { children: "DATE" }), _jsx(TableColumn, { children: "TYPE" }), _jsx(TableColumn, { children: "DESCRIPTION" }), _jsx(TableColumn, { children: "MONTANT" }), _jsx(TableColumn, { children: "SOLDE APR\u00C8S" })] }), _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: _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
112
160
  ? "text-success"
113
- : "text-danger"}`, children: [transaction.amount > 0 ? "+" : "", transaction.amount.toLocaleString()] }) }), _jsx(TableCell, { children: _jsx("span", { className: "text-sm text-gray-600", children: transaction.running_balance.toLocaleString() }) })] }, transaction.id))) })] })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ShoppingCart, { size: 20 }), _jsx("h3", { className: "text-lg font-semibold", children: "Acheter des tokens" })] }) }), _jsx(CardBody, { children: _jsxs("div", { className: "text-center py-8", children: [_jsx(ShoppingCart, { size: 48, className: "mx-auto mb-4 text-gray-300 dark:text-gray-600" }), _jsx("p", { className: "text-gray-500 mb-4", children: "L'achat de tokens sera bient\u00F4t disponible" }), _jsx("p", { className: "text-sm text-gray-400", children: "Module Stripe requis \u2022 Fonctionnalit\u00E9 \u00E0 venir" }), _jsx(Button, { color: "primary", variant: "flat", isDisabled: true, className: "mt-4", startContent: _jsx(ShoppingCart, { size: 16 }), children: "Acheter des tokens" })] }) })] })] }));
161
+ : "text-danger"}`, children: [transaction.amount > 0 ? "+" : "", formatTokensShort(Math.abs(transaction.amount))] }) }), _jsx(TableCell, { children: _jsx("span", { className: "text-sm text-gray-600", children: formatTokensShort(transaction.running_balance) }) })] }, transaction.id))) })] })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ShoppingCart, { size: 20 }), _jsx("h3", { className: "text-lg font-semibold", children: "Acheter des tokens" })] }) }), _jsx(CardBody, { children: tokenPacks.length === 0 ? (_jsxs("div", { className: "text-center py-8", children: [_jsx(ShoppingCart, { size: 48, className: "mx-auto mb-4 text-gray-300 dark:text-gray-600" }), _jsx("p", { className: "text-gray-500 mb-4", children: "Aucun pack disponible pour le moment" })] })) : (_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4", children: tokenPacks.map((pack) => (_jsx(Card, { children: _jsxs(CardBody, { className: "text-center py-6", children: [_jsx("div", { className: "mx-auto", children: _jsx(Coins, { size: 24, className: "mx-auto mb-2 text-gray-400" }) }), _jsx("h4", { className: "text-lg font-bold mb-2", children: pack.name }), pack.description && (_jsx("p", { className: "text-sm text-gray-500 mb-4", children: pack.description })), _jsxs("div", { className: "mb-4", children: [_jsx("p", { className: "text-3xl font-bold text-primary", children: formatTokensShort(pack.tokens) }), _jsx("p", { className: "text-xs text-gray-400", children: "tokens" })] }), _jsx("p", { className: "text-xl font-semibold mb-4", children: formatPrice(pack.price_cents, pack.currency) }), _jsx(Button, { color: "primary", className: "w-full", onPress: () => handleBuyTokens(pack.id), isLoading: checkoutLoading === pack.id, startContent: checkoutLoading !== pack.id && (_jsx(ShoppingCart, { size: 16 })), children: "Acheter" })] }) }, pack.id))) })) })] })] }));
114
162
  }
@@ -1,4 +1,5 @@
1
1
  export interface GenerativeImageResponse {
2
+ supabaseImageUrl?: string;
2
3
  imageUrl: string;
3
4
  tokensUsed: number;
4
5
  tokensRemaining: number;
@@ -11,7 +12,7 @@ export interface ImageGenerativeProps {
11
12
  model?: string;
12
13
  size?: "256x256" | "512x512" | "1024x1024" | "1792x1024" | "1024x1792";
13
14
  quality?: "standard" | "hd";
14
- onChange?: (imageUrl: string, response?: GenerativeImageResponse) => void;
15
+ onChange?: (supabaseImageUrl: string, response?: GenerativeImageResponse) => void;
15
16
  onError?: (error: Error) => void;
16
17
  className?: string;
17
18
  disabled?: boolean;
@@ -22,6 +23,8 @@ export interface ImageGenerativeProps {
22
23
  defaultStyle?: string;
23
24
  hideStyleEditor?: boolean;
24
25
  uploadPath?: string;
26
+ preview?: boolean;
27
+ inline?: boolean;
25
28
  }
26
- export declare function ImageGenerative({ defaultPrompt, model, size, quality, onChange, onError, className, disabled, apiEndpoint, showTokenBalance, label, description, defaultStyle, hideStyleEditor, uploadPath, }: ImageGenerativeProps): import("react/jsx-runtime").JSX.Element;
29
+ export declare function ImageGenerative({ defaultPrompt, model, size, quality, onChange, onError, className, disabled, apiEndpoint, showTokenBalance, label, description, defaultStyle, hideStyleEditor, uploadPath, preview, inline, }: ImageGenerativeProps): import("react/jsx-runtime").JSX.Element;
27
30
  //# sourceMappingURL=ImageGenerative.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImageGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/ImageGenerative.tsx"],"names":[],"mappings":"AA4BA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAiCD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IACvE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAC1E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,aAAkB,EAClB,KAAkB,EAClB,IAAkB,EAClB,OAAoB,EACpB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,WAAsC,EACtC,gBAAuB,EACvB,KAAK,EACL,WAAW,EACX,YAA0B,EAC1B,eAAuB,EACvB,UAAU,GACX,EAAE,oBAAoB,2CA0TtB"}
1
+ {"version":3,"file":"ImageGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/ImageGenerative.tsx"],"names":[],"mappings":"AAyCA,MAAM,WAAW,uBAAuB;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAiCD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IACvE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,CACT,gBAAgB,EAAE,MAAM,EACxB,QAAQ,CAAC,EAAE,uBAAuB,KAC/B,IAAI,CAAC;IACV,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,eAAe,CAAC,EAC9B,aAAkB,EAClB,KAAkB,EAClB,IAAkB,EAClB,OAAoB,EACpB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,WAAsC,EACtC,gBAAuB,EACvB,KAAK,EACL,WAAW,EACX,YAA0B,EAC1B,eAAuB,EACvB,UAAU,EACV,OAAc,EACd,MAAc,GACf,EAAE,oBAAoB,2CAiiBtB"}