@lastbrain/module-ai 2.0.26 → 2.0.30

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 (365) hide show
  1. package/README.md +52 -1
  2. package/dist/ai.build.config.d.ts.map +1 -1
  3. package/dist/ai.build.config.js +508 -9
  4. package/dist/api/admin/ai-provider-models/[id].d.ts +18 -0
  5. package/dist/api/admin/ai-provider-models/[id].d.ts.map +1 -0
  6. package/dist/api/admin/ai-provider-models/[id].js +58 -0
  7. package/dist/api/admin/ai-provider-models.d.ts +20 -0
  8. package/dist/api/admin/ai-provider-models.d.ts.map +1 -0
  9. package/dist/api/admin/ai-provider-models.js +26 -0
  10. package/dist/api/admin/ai-providers/[key].d.ts +18 -0
  11. package/dist/api/admin/ai-providers/[key].d.ts.map +1 -0
  12. package/dist/api/admin/ai-providers/[key].js +55 -0
  13. package/dist/api/admin/ai-providers.d.ts +20 -0
  14. package/dist/api/admin/ai-providers.d.ts.map +1 -0
  15. package/dist/api/admin/ai-providers.js +26 -0
  16. package/dist/api/admin/billing-analytics.d.ts +43 -0
  17. package/dist/api/admin/billing-analytics.d.ts.map +1 -0
  18. package/dist/api/admin/billing-analytics.js +144 -0
  19. package/dist/api/admin/global-ai-settings.d.ts +14 -0
  20. package/dist/api/admin/global-ai-settings.d.ts.map +1 -0
  21. package/dist/api/admin/global-ai-settings.js +63 -0
  22. package/dist/api/admin/token-packs/[id].d.ts.map +1 -1
  23. package/dist/api/admin/token-packs/[id].js +3 -2
  24. package/dist/api/admin/token-packs.d.ts.map +1 -1
  25. package/dist/api/admin/token-packs.js +3 -2
  26. package/dist/api/admin/user-monthly-details.d.ts +49 -0
  27. package/dist/api/admin/user-monthly-details.d.ts.map +1 -0
  28. package/dist/api/admin/user-monthly-details.js +140 -0
  29. package/dist/api/admin/user-quota.d.ts +21 -0
  30. package/dist/api/admin/user-quota.d.ts.map +1 -0
  31. package/dist/api/admin/user-quota.js +59 -0
  32. package/dist/api/admin/user-token/[id].d.ts.map +1 -1
  33. package/dist/api/admin/user-token/[id].js +2 -1
  34. package/dist/api/admin/user-token.d.ts +5 -2
  35. package/dist/api/admin/user-token.d.ts.map +1 -1
  36. package/dist/api/admin/user-token.js +91 -17
  37. package/dist/api/admin/user-usage-by-model.d.ts +22 -0
  38. package/dist/api/admin/user-usage-by-model.d.ts.map +1 -0
  39. package/dist/api/admin/user-usage-by-model.js +78 -0
  40. package/dist/api/admin/user-wallet-analytics.d.ts +15 -0
  41. package/dist/api/admin/user-wallet-analytics.d.ts.map +1 -0
  42. package/dist/api/admin/user-wallet-analytics.js +67 -0
  43. package/dist/api/admin/wallet-repair/route.d.ts +30 -0
  44. package/dist/api/admin/wallet-repair/route.d.ts.map +1 -0
  45. package/dist/api/admin/wallet-repair/route.js +63 -0
  46. package/dist/api/auth/ai-model-settings.d.ts +21 -0
  47. package/dist/api/auth/ai-model-settings.d.ts.map +1 -0
  48. package/dist/api/auth/ai-model-settings.js +86 -0
  49. package/dist/api/auth/ai-settings.d.ts +17 -0
  50. package/dist/api/auth/ai-settings.d.ts.map +1 -0
  51. package/dist/api/auth/ai-settings.js +87 -0
  52. package/dist/api/auth/api-keys/[id].d.ts +17 -0
  53. package/dist/api/auth/api-keys/[id].d.ts.map +1 -0
  54. package/dist/api/auth/api-keys/[id].js +66 -0
  55. package/dist/api/auth/api-keys.d.ts +19 -0
  56. package/dist/api/auth/api-keys.d.ts.map +1 -0
  57. package/dist/api/auth/api-keys.js +94 -0
  58. package/dist/api/auth/create-checkout.d.ts +1 -1
  59. package/dist/api/auth/create-checkout.d.ts.map +1 -1
  60. package/dist/api/auth/create-checkout.js +8 -6
  61. package/dist/api/auth/generate-image.d.ts +2 -2
  62. package/dist/api/auth/generate-image.d.ts.map +1 -1
  63. package/dist/api/auth/generate-image.js +404 -104
  64. package/dist/api/auth/generate-text.d.ts +3 -2
  65. package/dist/api/auth/generate-text.d.ts.map +1 -1
  66. package/dist/api/auth/generate-text.js +130 -58
  67. package/dist/api/auth/process-ocr.d.ts +9 -0
  68. package/dist/api/auth/process-ocr.d.ts.map +1 -0
  69. package/dist/api/auth/process-ocr.js +43 -0
  70. package/dist/api/auth/prompts/stats.d.ts +14 -0
  71. package/dist/api/auth/prompts/stats.d.ts.map +1 -0
  72. package/dist/api/auth/prompts/stats.js +46 -0
  73. package/dist/api/auth/prompts.d.ts +26 -0
  74. package/dist/api/auth/prompts.d.ts.map +1 -0
  75. package/dist/api/auth/prompts.js +175 -0
  76. package/dist/api/auth/token-balance.d.ts +26 -0
  77. package/dist/api/auth/token-balance.d.ts.map +1 -0
  78. package/dist/api/auth/token-balance.js +47 -0
  79. package/dist/api/auth/token-checkout.d.ts.map +1 -1
  80. package/dist/api/auth/token-checkout.js +22 -4
  81. package/dist/api/auth/token-packs.d.ts.map +1 -1
  82. package/dist/api/auth/token-packs.js +2 -1
  83. package/dist/api/auth/usage-by-model.d.ts +25 -0
  84. package/dist/api/auth/usage-by-model.d.ts.map +1 -0
  85. package/dist/api/auth/usage-by-model.js +95 -0
  86. package/dist/api/auth/usage.d.ts +26 -0
  87. package/dist/api/auth/usage.d.ts.map +1 -0
  88. package/dist/api/auth/usage.js +127 -0
  89. package/dist/api/auth/user-tokens.d.ts.map +1 -1
  90. package/dist/api/auth/user-tokens.js +36 -2
  91. package/dist/api/auth/wallet/route.d.ts +17 -0
  92. package/dist/api/auth/wallet/route.d.ts.map +1 -0
  93. package/dist/api/auth/wallet/route.js +68 -0
  94. package/dist/api/auth/wallet.d.ts +16 -0
  95. package/dist/api/auth/wallet.d.ts.map +1 -0
  96. package/dist/api/auth/wallet.js +71 -0
  97. package/dist/api/public/gateway-models.d.ts +25 -0
  98. package/dist/api/public/gateway-models.d.ts.map +1 -0
  99. package/dist/api/public/gateway-models.js +49 -0
  100. package/dist/api/public/pricing-summary.d.ts +46 -0
  101. package/dist/api/public/pricing-summary.d.ts.map +1 -0
  102. package/dist/api/public/pricing-summary.js +70 -0
  103. package/dist/api/public/prompts/[id]/stats.d.ts +15 -0
  104. package/dist/api/public/prompts/[id]/stats.d.ts.map +1 -0
  105. package/dist/api/public/prompts/[id]/stats.js +37 -0
  106. package/dist/api/public/prompts/[id]/view.d.ts +15 -0
  107. package/dist/api/public/prompts/[id]/view.d.ts.map +1 -0
  108. package/dist/api/public/prompts/[id]/view.js +41 -0
  109. package/dist/api/public/prompts/slug/[slug].d.ts +15 -0
  110. package/dist/api/public/prompts/slug/[slug].d.ts.map +1 -0
  111. package/dist/api/public/prompts/slug/[slug].js +40 -0
  112. package/dist/api/public/prompts/user/[username].d.ts +17 -0
  113. package/dist/api/public/prompts/user/[username].d.ts.map +1 -0
  114. package/dist/api/public/prompts/user/[username].js +51 -0
  115. package/dist/api/public/prompts.d.ts +15 -0
  116. package/dist/api/public/prompts.d.ts.map +1 -0
  117. package/dist/api/public/prompts.js +45 -0
  118. package/dist/api/public/token-packs.d.ts +11 -0
  119. package/dist/api/public/token-packs.d.ts.map +1 -0
  120. package/dist/api/public/token-packs.js +25 -0
  121. package/dist/api/public/token-pricing.d.ts +44 -0
  122. package/dist/api/public/token-pricing.d.ts.map +1 -0
  123. package/dist/api/public/token-pricing.js +168 -0
  124. package/dist/api/public/v1/_lib/artifacts.d.ts +52 -0
  125. package/dist/api/public/v1/_lib/artifacts.d.ts.map +1 -0
  126. package/dist/api/public/v1/_lib/artifacts.js +217 -0
  127. package/dist/api/public/v1/_lib/auth.d.ts +43 -0
  128. package/dist/api/public/v1/_lib/auth.d.ts.map +1 -0
  129. package/dist/api/public/v1/_lib/auth.js +108 -0
  130. package/dist/api/public/v1/_lib/errors.d.ts +17 -0
  131. package/dist/api/public/v1/_lib/errors.d.ts.map +1 -0
  132. package/dist/api/public/v1/_lib/errors.js +16 -0
  133. package/dist/api/public/v1/_lib/log.d.ts +29 -0
  134. package/dist/api/public/v1/_lib/log.d.ts.map +1 -0
  135. package/dist/api/public/v1/_lib/log.js +68 -0
  136. package/dist/api/public/v1/_lib/quota.d.ts +24 -0
  137. package/dist/api/public/v1/_lib/quota.d.ts.map +1 -0
  138. package/dist/api/public/v1/_lib/quota.js +118 -0
  139. package/dist/api/public/v1/_lib/router.d.ts +54 -0
  140. package/dist/api/public/v1/_lib/router.d.ts.map +1 -0
  141. package/dist/api/public/v1/_lib/router.js +119 -0
  142. package/dist/api/public/v1/connect.d.ts +20 -0
  143. package/dist/api/public/v1/connect.d.ts.map +1 -0
  144. package/dist/api/public/v1/connect.js +119 -0
  145. package/dist/api/public/v1/doc.d.ts +239 -0
  146. package/dist/api/public/v1/doc.d.ts.map +1 -0
  147. package/dist/api/public/v1/doc.js +253 -0
  148. package/dist/api/public/v1/history/[id].d.ts +92 -0
  149. package/dist/api/public/v1/history/[id].d.ts.map +1 -0
  150. package/dist/api/public/v1/history/[id].js +176 -0
  151. package/dist/api/public/v1/history.d.ts +30 -0
  152. package/dist/api/public/v1/history.d.ts.map +1 -0
  153. package/dist/api/public/v1/history.js +142 -0
  154. package/dist/api/public/v1/image-ai.d.ts +24 -0
  155. package/dist/api/public/v1/image-ai.d.ts.map +1 -0
  156. package/dist/api/public/v1/image-ai.js +233 -0
  157. package/dist/api/public/v1/prompts.d.ts +19 -0
  158. package/dist/api/public/v1/prompts.d.ts.map +1 -0
  159. package/dist/api/public/v1/prompts.js +107 -0
  160. package/dist/api/public/v1/provider.d.ts +16 -0
  161. package/dist/api/public/v1/provider.d.ts.map +1 -0
  162. package/dist/api/public/v1/provider.js +130 -0
  163. package/dist/api/public/v1/purchase.d.ts +11 -0
  164. package/dist/api/public/v1/purchase.d.ts.map +1 -0
  165. package/dist/api/public/v1/purchase.js +18 -0
  166. package/dist/api/public/v1/status.d.ts +35 -0
  167. package/dist/api/public/v1/status.d.ts.map +1 -0
  168. package/dist/api/public/v1/status.js +163 -0
  169. package/dist/api/public/v1/text-ai.d.ts +26 -0
  170. package/dist/api/public/v1/text-ai.d.ts.map +1 -0
  171. package/dist/api/public/v1/text-ai.js +239 -0
  172. package/dist/api/public/webhook.d.ts.map +1 -1
  173. package/dist/api/public/webhook.js +50 -39
  174. package/dist/api/track-usage.d.ts +12 -0
  175. package/dist/api/track-usage.d.ts.map +1 -0
  176. package/dist/api/track-usage.js +37 -0
  177. package/dist/components/Doc.d.ts.map +1 -1
  178. package/dist/components/Doc.js +1 -1
  179. package/dist/components/DocUsageCustom.js +6 -6
  180. package/dist/components/admin/UserTokenTab.d.ts.map +1 -1
  181. package/dist/components/admin/UserTokenTab.js +170 -23
  182. package/dist/components/auth/AuthDashboardAi.d.ts +2 -0
  183. package/dist/components/auth/AuthDashboardAi.d.ts.map +1 -0
  184. package/dist/components/auth/AuthDashboardAi.js +53 -0
  185. package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts +277 -0
  186. package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts.map +1 -0
  187. package/dist/docs/REFACTORING_BILLING_GUIDE.js +276 -0
  188. package/dist/index.d.ts +15 -1
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +18 -1
  191. package/dist/scripts/migrate-tokens-to-wallet.d.ts +13 -0
  192. package/dist/scripts/migrate-tokens-to-wallet.d.ts.map +1 -0
  193. package/dist/scripts/migrate-tokens-to-wallet.js +165 -0
  194. package/dist/server/__tests__/billing.test.d.ts +5 -0
  195. package/dist/server/__tests__/billing.test.d.ts.map +1 -0
  196. package/dist/server/__tests__/billing.test.js +223 -0
  197. package/dist/server/ai-client.d.ts +59 -0
  198. package/dist/server/ai-client.d.ts.map +1 -0
  199. package/dist/server/ai-client.js +111 -0
  200. package/dist/server/ai-generation-service.d.ts +66 -0
  201. package/dist/server/ai-generation-service.d.ts.map +1 -0
  202. package/dist/server/ai-generation-service.js +274 -0
  203. package/dist/server/billing.d.ts +200 -0
  204. package/dist/server/billing.d.ts.map +1 -0
  205. package/dist/server/billing.js +488 -0
  206. package/dist/server/gateway-service.d.ts +13 -0
  207. package/dist/server/gateway-service.d.ts.map +1 -0
  208. package/dist/server/gateway-service.js +161 -0
  209. package/dist/server/global-settings.d.ts +16 -0
  210. package/dist/server/global-settings.d.ts.map +1 -0
  211. package/dist/server/global-settings.js +42 -0
  212. package/dist/server/model-filter.d.ts +25 -0
  213. package/dist/server/model-filter.d.ts.map +1 -0
  214. package/dist/server/model-filter.js +240 -0
  215. package/dist/server/ocr.d.ts +39 -0
  216. package/dist/server/ocr.d.ts.map +1 -0
  217. package/dist/server/ocr.js +280 -0
  218. package/dist/server/openai-client.d.ts +19 -0
  219. package/dist/server/openai-client.d.ts.map +1 -0
  220. package/dist/server/openai-client.js +26 -0
  221. package/dist/server/pricing-config.d.ts +18 -0
  222. package/dist/server/pricing-config.d.ts.map +1 -0
  223. package/dist/server/pricing-config.js +94 -0
  224. package/dist/server/pricing-validator.d.ts +41 -0
  225. package/dist/server/pricing-validator.d.ts.map +1 -0
  226. package/dist/server/pricing-validator.js +113 -0
  227. package/dist/server/pricing.d.ts +121 -0
  228. package/dist/server/pricing.d.ts.map +1 -0
  229. package/dist/server/pricing.js +225 -0
  230. package/dist/server/quota.d.ts +66 -0
  231. package/dist/server/quota.d.ts.map +1 -0
  232. package/dist/server/quota.js +538 -0
  233. package/dist/server/wallet-repair.d.ts +32 -0
  234. package/dist/server/wallet-repair.d.ts.map +1 -0
  235. package/dist/server/wallet-repair.js +189 -0
  236. package/dist/server.d.ts +13 -1
  237. package/dist/server.d.ts.map +1 -1
  238. package/dist/server.js +87 -16
  239. package/dist/sitemap/handlers/prompts.d.ts +6 -0
  240. package/dist/sitemap/handlers/prompts.d.ts.map +1 -0
  241. package/dist/sitemap/handlers/prompts.js +72 -0
  242. package/dist/sitemap/handlers/users.d.ts +6 -0
  243. package/dist/sitemap/handlers/users.d.ts.map +1 -0
  244. package/dist/sitemap/handlers/users.js +80 -0
  245. package/dist/sitemap/manifest.d.ts +8 -0
  246. package/dist/sitemap/manifest.d.ts.map +1 -0
  247. package/dist/sitemap/manifest.js +27 -0
  248. package/dist/types/gateway.d.ts +40 -0
  249. package/dist/types/gateway.d.ts.map +1 -0
  250. package/dist/types/gateway.js +4 -0
  251. package/dist/types/quota.d.ts +74 -0
  252. package/dist/types/quota.d.ts.map +1 -0
  253. package/dist/types/quota.js +11 -0
  254. package/dist/utils/date.d.ts +7 -0
  255. package/dist/utils/date.d.ts.map +1 -0
  256. package/dist/utils/date.js +17 -0
  257. package/dist/web/admin/AdminTokenPacksPage.js +1 -1
  258. package/dist/web/admin/BillingAnalyticsPage.d.ts +2 -0
  259. package/dist/web/admin/BillingAnalyticsPage.d.ts.map +1 -0
  260. package/dist/web/admin/BillingAnalyticsPage.js +141 -0
  261. package/dist/web/admin/GlobalAISettingsPage.d.ts +2 -0
  262. package/dist/web/admin/GlobalAISettingsPage.d.ts.map +1 -0
  263. package/dist/web/admin/GlobalAISettingsPage.js +93 -0
  264. package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
  265. package/dist/web/admin/UserTokenPage.js +20 -7
  266. package/dist/web/auth/AISettingsPage.d.ts +2 -0
  267. package/dist/web/auth/AISettingsPage.d.ts.map +1 -0
  268. package/dist/web/auth/AISettingsPage.js +258 -0
  269. package/dist/web/auth/APIKeysPage.d.ts +2 -0
  270. package/dist/web/auth/APIKeysPage.d.ts.map +1 -0
  271. package/dist/web/auth/APIKeysPage.js +154 -0
  272. package/dist/web/auth/HistoryPage.d.ts +2 -0
  273. package/dist/web/auth/HistoryPage.d.ts.map +1 -0
  274. package/dist/web/auth/HistoryPage.js +279 -0
  275. package/dist/web/auth/PromptsPage.d.ts +5 -0
  276. package/dist/web/auth/PromptsPage.d.ts.map +1 -0
  277. package/dist/web/auth/PromptsPage.js +137 -0
  278. package/dist/web/auth/TokenPage.d.ts.map +1 -1
  279. package/dist/web/auth/TokenPage.js +88 -31
  280. package/dist/web/auth/UsageAndTokensPage.d.ts +2 -0
  281. package/dist/web/auth/UsageAndTokensPage.d.ts.map +1 -0
  282. package/dist/web/auth/UsageAndTokensPage.js +157 -0
  283. package/dist/web/auth/UsagePage.d.ts +2 -0
  284. package/dist/web/auth/UsagePage.d.ts.map +1 -0
  285. package/dist/web/auth/UsagePage.js +62 -0
  286. package/dist/web/auth/components/ApiKeyFilterSelect.d.ts +13 -0
  287. package/dist/web/auth/components/ApiKeyFilterSelect.d.ts.map +1 -0
  288. package/dist/web/auth/components/ApiKeyFilterSelect.js +16 -0
  289. package/dist/web/auth/components/ModelUsageTable.d.ts +19 -0
  290. package/dist/web/auth/components/ModelUsageTable.d.ts.map +1 -0
  291. package/dist/web/auth/components/ModelUsageTable.js +37 -0
  292. package/dist/web/auth/components/PurchaseButton.d.ts +7 -0
  293. package/dist/web/auth/components/PurchaseButton.d.ts.map +1 -0
  294. package/dist/web/auth/components/PurchaseButton.js +13 -0
  295. package/dist/web/auth/components/TokenHistoryCard.d.ts +20 -0
  296. package/dist/web/auth/components/TokenHistoryCard.d.ts.map +1 -0
  297. package/dist/web/auth/components/TokenHistoryCard.js +76 -0
  298. package/dist/web/auth/components/TokenKpiGrid.d.ts +24 -0
  299. package/dist/web/auth/components/TokenKpiGrid.d.ts.map +1 -0
  300. package/dist/web/auth/components/TokenKpiGrid.js +38 -0
  301. package/dist/web/auth/components/UsageByDayChart.d.ts +11 -0
  302. package/dist/web/auth/components/UsageByDayChart.d.ts.map +1 -0
  303. package/dist/web/auth/components/UsageByDayChart.js +32 -0
  304. package/dist/web/auth/components/UsageByModelBarChart.d.ts +12 -0
  305. package/dist/web/auth/components/UsageByModelBarChart.d.ts.map +1 -0
  306. package/dist/web/auth/components/UsageByModelBarChart.js +32 -0
  307. package/dist/web/auth/components/WalletStatusCard.d.ts +9 -0
  308. package/dist/web/auth/components/WalletStatusCard.d.ts.map +1 -0
  309. package/dist/web/auth/components/WalletStatusCard.js +50 -0
  310. package/dist/web/components/ImageGenerative.d.ts +3 -1
  311. package/dist/web/components/ImageGenerative.d.ts.map +1 -1
  312. package/dist/web/components/ImageGenerative.js +139 -52
  313. package/dist/web/components/TextareaGenerative.d.ts +3 -1
  314. package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
  315. package/dist/web/components/TextareaGenerative.js +10 -5
  316. package/dist/web/public/PromptDetailPage.d.ts +25 -0
  317. package/dist/web/public/PromptDetailPage.d.ts.map +1 -0
  318. package/dist/web/public/PromptDetailPage.js +71 -0
  319. package/dist/web/public/PromptDetailPageServer.d.ts +15 -0
  320. package/dist/web/public/PromptDetailPageServer.d.ts.map +1 -0
  321. package/dist/web/public/PromptDetailPageServer.js +68 -0
  322. package/dist/web/public/PublicPromptsPage.d.ts +5 -0
  323. package/dist/web/public/PublicPromptsPage.d.ts.map +1 -0
  324. package/dist/web/public/PublicPromptsPage.js +110 -0
  325. package/dist/web/public/PurchaseTokensPage.d.ts +2 -0
  326. package/dist/web/public/PurchaseTokensPage.d.ts.map +1 -0
  327. package/dist/web/public/PurchaseTokensPage.js +98 -0
  328. package/dist/web/public/UserAvatar.d.ts +13 -0
  329. package/dist/web/public/UserAvatar.d.ts.map +1 -0
  330. package/dist/web/public/UserAvatar.js +13 -0
  331. package/dist/web/public/UserDetailPageServer.d.ts +15 -0
  332. package/dist/web/public/UserDetailPageServer.d.ts.map +1 -0
  333. package/dist/web/public/UserDetailPageServer.js +31 -0
  334. package/dist/web/public/UserPromptsPage.d.ts +9 -0
  335. package/dist/web/public/UserPromptsPage.d.ts.map +1 -0
  336. package/dist/web/public/UserPromptsPage.js +112 -0
  337. package/dist/web/public/UserPromptsPageServer.d.ts +15 -0
  338. package/dist/web/public/UserPromptsPageServer.d.ts.map +1 -0
  339. package/dist/web/public/UserPromptsPageServer.js +31 -0
  340. package/package.json +18 -9
  341. package/supabase/migrations/20251125000000_ai_tokens.sql +7 -0
  342. package/supabase/migrations/20260123100002_user_token_quota_monthly copy.sql +173 -0
  343. package/supabase/migrations/20260128100003_update_and_add_table.sql +368 -0
  344. package/supabase/migrations/20260128120000_seed_providers_models.sql +78 -0
  345. package/supabase/migrations/20260128131405_add_api_key_id_to_ledgers.sql +41 -0
  346. package/supabase/migrations/20260128140000_ai_artifacts_storage.sql +99 -0
  347. package/supabase/migrations/20260128140002_ai_user_settings.sql +57 -0
  348. package/supabase/migrations/20260128150000_drop_ai_user_settings.sql +21 -0
  349. package/supabase/migrations/20260128160000_wallet_billing_system.sql +192 -0
  350. package/supabase/migrations/20260128160001_wallet_rpc_functions.sql +165 -0
  351. package/supabase/migrations/20260128170000_add_pack_coef_to_token_packs.sql +30 -0
  352. package/supabase/migrations/20260129120000_wallet_view_rpc.sql +41 -0
  353. package/supabase/migrations/20260129220003_update_pack_margins.sql +31 -0
  354. package/supabase/migrations/20260129330004_ai_user_prompts.sql +151 -0
  355. package/supabase/migrations/20260129330005_ai_prompts_ip_tracking.sql +92 -0
  356. package/supabase/migrations/20260129330006_ai_prompts_slug.sql +64 -0
  357. package/supabase/migrations/20260129330007_ai_prompts_view_slug.sql +26 -0
  358. package/supabase/migrations/20260129440000_ai_prompts_view_username.sql +33 -0
  359. package/supabase/migrations/20260129450000_ai_prompts_add_lang.sql +40 -0
  360. package/supabase/migrations/20260131000000_extract_model_prompt_in_ledger.sql +92 -0
  361. package/supabase/migrations/20260131140000_fix_duplicate_purchases.sql +64 -0
  362. package/supabase/migrations/20260201120000_module-ai_default_models.sql +63 -0
  363. package/supabase/migrations/20260201130000_module-ai_remove_provider_tables.sql +17 -0
  364. package/supabase/migrations-down/20251217120000_user_token_quota_monthly.sql +34 -0
  365. package/supabase/migrations-down/20260128131405_add_api_key_id_to_ledgers.sql +25 -0
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Types for Vercel AI Gateway /v1/models API
3
+ */
4
+ export {};
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Types for monthly token quota system
3
+ * Separate from purchased tokens (user_token_ledger)
4
+ */
5
+ export type PlanType = "free" | "pro" | "premium" | "enterprise";
6
+ export interface UserTokenQuotaMonthly {
7
+ owner_id: string;
8
+ plan: PlanType;
9
+ default_included_tokens: number;
10
+ custom_included_tokens: number | null;
11
+ effective_included_tokens: number;
12
+ used_included_tokens: number;
13
+ period_start: string | null;
14
+ period_end: string | null;
15
+ subscription_id: string | null;
16
+ source: "stripe" | "manual";
17
+ created_at: string;
18
+ updated_at: string;
19
+ }
20
+ export interface UserTokenQuotaLedger {
21
+ id: string;
22
+ owner_id: string;
23
+ tokens_used: number;
24
+ model: string | null;
25
+ prompt: string | null;
26
+ period_start: string;
27
+ period_end: string;
28
+ meta: Record<string, unknown>;
29
+ created_at: string;
30
+ }
31
+ export interface TokenQuotaStatus {
32
+ hasQuota: boolean;
33
+ plan: PlanType;
34
+ effectiveQuota: number;
35
+ usedQuota: number;
36
+ remainingQuota: number;
37
+ periodStart: string | null;
38
+ periodEnd: string | null;
39
+ isActive: boolean;
40
+ }
41
+ export interface TokenBalance {
42
+ quota: TokenQuotaStatus;
43
+ purchased: {
44
+ balance: number;
45
+ totalAdded: number;
46
+ totalUsed: number;
47
+ };
48
+ }
49
+ export interface DebitTokensRequest {
50
+ owner_id: string;
51
+ tokens: number;
52
+ model?: string;
53
+ prompt?: string;
54
+ meta?: Record<string, unknown>;
55
+ }
56
+ export interface DebitTokensResponse {
57
+ success: boolean;
58
+ debitedFromQuota: number;
59
+ debitedFromPurchased: number;
60
+ remainingQuota: number;
61
+ remainingPurchased: number;
62
+ error?: string;
63
+ }
64
+ export interface UpdateQuotaRequest {
65
+ owner_id: string;
66
+ plan: PlanType;
67
+ custom_included_tokens?: number | null;
68
+ period_start: string;
69
+ period_end: string;
70
+ subscription_id?: string | null;
71
+ reset_usage?: boolean;
72
+ }
73
+ export declare const PLAN_DEFAULT_QUOTAS: Record<PlanType, number>;
74
+ //# sourceMappingURL=quota.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../src/types/quota.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,YAAY,CAAC;AAEjE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,uBAAuB,EAAE,MAAM,CAAC;IAChC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,yBAAyB,EAAE,MAAM,CAAC;IAClC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAE3B,KAAK,EAAE,gBAAgB,CAAC;IAGxB,SAAS,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAGD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAKxD,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Types for monthly token quota system
3
+ * Separate from purchased tokens (user_token_ledger)
4
+ */
5
+ // Default quotas by plan
6
+ export const PLAN_DEFAULT_QUOTAS = {
7
+ free: 0,
8
+ pro: 100000,
9
+ premium: 500000,
10
+ enterprise: 2000000,
11
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Date formatting utilities
3
+ */
4
+ export declare function formatDateFr(date: string | Date): string;
5
+ export declare function formatDateTimeFr(date: string | Date): string;
6
+ export declare function timestampToDate(timestamp: number | undefined): Date | null;
7
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGxD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAG5D;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CAK1E"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Date formatting utilities
3
+ */
4
+ export function formatDateFr(date) {
5
+ const dateObj = typeof date === "string" ? new Date(date) : date;
6
+ return dateObj.toLocaleDateString("fr-FR");
7
+ }
8
+ export function formatDateTimeFr(date) {
9
+ const dateObj = typeof date === "string" ? new Date(date) : date;
10
+ return dateObj.toLocaleString("fr-FR");
11
+ }
12
+ export function timestampToDate(timestamp) {
13
+ if (!timestamp || typeof timestamp !== "number") {
14
+ return null;
15
+ }
16
+ return new Date(timestamp * 1000);
17
+ }
@@ -122,7 +122,7 @@ export function AdminTokenPacksPage() {
122
122
  if (loading) {
123
123
  return (_jsx("div", { className: "flex justify-center items-center min-h-96", children: _jsx(Spinner, { size: "lg" }) }));
124
124
  }
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") ||
125
+ return (_jsxs("div", { className: "container mx-auto p-2 md:p-6 max-w-7xl mt-16", 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
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
127
  ? t("admin.packs.status.active") || "Actif"
128
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
@@ -0,0 +1,2 @@
1
+ export declare function BillingAnalyticsPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=BillingAnalyticsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BillingAnalyticsPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/BillingAnalyticsPage.tsx"],"names":[],"mappings":"AAuGA,wBAAgB,oBAAoB,4CAonBnC"}
@@ -0,0 +1,141 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
4
+ import { useModuleTranslation } from "@lastbrain/core";
5
+ import { Card, CardBody, CardHeader, Spinner, Button, Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, Alert, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Select, SelectItem, } from "@lastbrain/ui";
6
+ import { DollarSign, TrendingUp, Users, Wallet, ArrowDownRight, Eye, } from "lucide-react";
7
+ export function BillingAnalyticsPage() {
8
+ const t = useModuleTranslation("ai");
9
+ const [loading, setLoading] = useState(true);
10
+ const [repairing, setRepairing] = useState(false);
11
+ const [data, setData] = useState(null);
12
+ const [sortBy, setSortBy] = useState("margin");
13
+ // Modal state
14
+ const [selectedUser, setSelectedUser] = useState(null);
15
+ const [selectedMonth, setSelectedMonth] = useState("");
16
+ const [modalOpen, setModalOpen] = useState(false);
17
+ const [monthlyDetails, setMonthlyDetails] = useState(null);
18
+ const [loadingDetails, setLoadingDetails] = useState(false);
19
+ useEffect(() => {
20
+ fetchAnalytics();
21
+ }, []);
22
+ const fetchAnalytics = async () => {
23
+ try {
24
+ setLoading(true);
25
+ const response = await fetch("/api/ai/admin/billing-analytics");
26
+ if (response.ok) {
27
+ const result = await response.json();
28
+ setData(result);
29
+ }
30
+ }
31
+ catch (error) {
32
+ console.error("Error fetching analytics:", error);
33
+ }
34
+ finally {
35
+ setLoading(false);
36
+ }
37
+ };
38
+ const fetchMonthlyDetails = async (userId, month) => {
39
+ try {
40
+ setLoadingDetails(true);
41
+ const response = await fetch(`/api/ai/admin/user-monthly-details?userId=${userId}&month=${month}`);
42
+ if (response.ok) {
43
+ const result = await response.json();
44
+ setMonthlyDetails(result);
45
+ }
46
+ }
47
+ catch (error) {
48
+ console.error("Error fetching monthly details:", error);
49
+ }
50
+ finally {
51
+ setLoadingDetails(false);
52
+ }
53
+ };
54
+ const openUserDetails = (user) => {
55
+ setSelectedUser(user);
56
+ const currentMonth = new Date().toISOString().substring(0, 7); // YYYY-MM
57
+ setSelectedMonth(currentMonth);
58
+ setModalOpen(true);
59
+ fetchMonthlyDetails(user.userId, currentMonth);
60
+ };
61
+ const handleMonthChange = (month) => {
62
+ setSelectedMonth(month);
63
+ if (selectedUser) {
64
+ fetchMonthlyDetails(selectedUser.userId, month);
65
+ }
66
+ };
67
+ // Generate last 12 months for selector
68
+ const getLastMonths = () => {
69
+ const months = [];
70
+ const now = new Date();
71
+ // Start from current month (i=0) and go back 11 months
72
+ for (let i = 0; i < 12; i++) {
73
+ const year = now.getFullYear();
74
+ const month = now.getMonth() - i;
75
+ const date = new Date(year, month, 1);
76
+ // Format as YYYY-MM without timezone conversion
77
+ const yearStr = date.getFullYear();
78
+ const monthStr = String(date.getMonth() + 1).padStart(2, "0");
79
+ const monthFormatted = `${yearStr}-${monthStr}`;
80
+ months.push(monthFormatted);
81
+ }
82
+ console.log("Available months:", months);
83
+ return months;
84
+ };
85
+ const repairWallets = async () => {
86
+ if (!confirm("Réparer tous les wallets incohérents ? (tokens > 0 mais wallet = 0 USD)")) {
87
+ return;
88
+ }
89
+ try {
90
+ setRepairing(true);
91
+ const response = await fetch("/api/ai/admin/wallet-repair?all=true", {
92
+ method: "POST",
93
+ });
94
+ if (response.ok) {
95
+ const result = await response.json();
96
+ alert(`✅ Réparation terminée :\n- ${result.repaired} wallets réparés\n- ${result.failed} échecs`);
97
+ // Refresh analytics
98
+ fetchAnalytics();
99
+ }
100
+ else {
101
+ const error = await response.json();
102
+ alert(`❌ Erreur : ${error.error}`);
103
+ }
104
+ }
105
+ catch (error) {
106
+ alert(`❌ Erreur : ${error.message}`);
107
+ }
108
+ finally {
109
+ setRepairing(false);
110
+ }
111
+ };
112
+ if (loading) {
113
+ return (_jsx("div", { className: "flex justify-center items-center h-96 mt-16", children: _jsx(Spinner, { size: "lg" }) }));
114
+ }
115
+ if (!data) {
116
+ return (_jsx("div", { className: "text-center p-8 mt-16", children: _jsx("p", { className: "text-gray-500", children: "Impossible de charger les analytics" }) }));
117
+ }
118
+ // Sort users
119
+ const sortedUsers = [...data.users].sort((a, b) => {
120
+ switch (sortBy) {
121
+ case "margin":
122
+ return b.realMarginUsd - a.realMarginUsd; // Marge réelle
123
+ case "revenue":
124
+ return b.totalPurchaseUsd - a.totalPurchaseUsd; // CA réel (achats)
125
+ case "cost":
126
+ return b.totalCharges - a.totalCharges; // Charges totales
127
+ default:
128
+ return 0;
129
+ }
130
+ });
131
+ return (_jsx("div", { className: "container mx-auto py-8 px-4 mt-16", children: _jsxs("div", { className: "max-w-7xl mx-auto", children: [_jsx("div", { className: "mb-8", children: _jsxs("div", { className: "flex justify-between items-center", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-2", children: "Analytics de Facturation" }), _jsx("p", { className: "text-gray-600", children: "Vue d'ensemble du CA, co\u00FBts fournisseur et marges exactes" })] }), _jsx(Button, { color: "warning", onPress: repairWallets, isLoading: repairing, isDisabled: repairing, children: "\uD83D\uDD27 R\u00E9parer Wallets" })] }) }), _jsxs("div", { className: "grid md:grid-cols-4 gap-4 mb-8", children: [_jsx(Card, { children: _jsx(CardBody, { className: "p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Users, { className: "text-blue-500", size: 32 }), _jsxs("div", { children: [_jsx("div", { className: "text-sm text-gray-600", children: "Utilisateurs actifs" }), _jsx("div", { className: "text-2xl font-bold", children: data.global.totalUsers })] })] }) }) }), _jsx(Card, { className: "border-2 border-green-500", children: _jsx(CardBody, { className: "p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(DollarSign, { className: "text-green-500", size: 32 }), _jsxs("div", { children: [_jsx("div", { className: "text-sm text-gray-600", children: "CA R\u00E9el (Achats)" }), _jsxs("div", { className: "text-2xl font-bold text-green-600", children: ["$", data.global.totalPurchaseUsd.toFixed(2)] })] })] }) }) }), _jsx(Card, { className: "border-2 border-orange-500", children: _jsx(CardBody, { className: "p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(ArrowDownRight, { className: "text-orange-500", size: 32 }), _jsxs("div", { children: [_jsx("div", { className: "text-sm text-gray-600", children: "Charges Totales" }), _jsxs("div", { className: "text-2xl font-bold text-orange-600", children: ["$", data.global.totalCharges.toFixed(2)] }), _jsxs("div", { className: "text-xs text-gray-500", children: ["Util: $", data.global.totalProviderCostUsd.toFixed(2), data.global.totalGiftBudgetUsd > 0 && (_jsxs(_Fragment, { children: [" ", "+ Budget Offert: $", data.global.totalGiftBudgetUsd.toFixed(2)] }))] })] })] }) }) }), _jsx(Card, { className: "border-2 border-purple-500", children: _jsx(CardBody, { className: "p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(TrendingUp, { className: "text-purple-500", size: 32 }), _jsxs("div", { children: [_jsx("div", { className: "text-sm text-gray-600", children: "Marge R\u00E9elle" }), _jsxs("div", { className: "text-2xl font-bold text-purple-600", children: ["$", data.global.realMarginUsd.toFixed(2)] }), _jsxs("div", { className: "text-xs text-gray-500", children: ["\u2248 ", data.global.avgRealMarginPercent.toFixed(1), "% moyen"] })] })] }) }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex justify-between items-center", children: [_jsx("h2", { className: "text-xl font-bold", children: "D\u00E9tail par utilisateur" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", color: sortBy === "margin" ? "primary" : "default", onPress: () => setSortBy("margin"), children: "Trier par Marge" }), _jsx(Button, { size: "sm", color: sortBy === "revenue" ? "primary" : "default", onPress: () => setSortBy("revenue"), children: "Trier par CA" }), _jsx(Button, { size: "sm", color: sortBy === "cost" ? "primary" : "default", onPress: () => setSortBy("cost"), children: "Trier par Co\u00FBt" })] })] }) }), _jsx(CardBody, { children: _jsx("div", { className: "overflow-x-auto", children: _jsxs(Table, { className: "w-full text-sm", children: [_jsxs(TableHeader, { className: "border-b", children: [_jsx(TableColumn, { className: "p-3", children: "Email" }), _jsx(TableColumn, { className: "p-3 text-right", children: "Achats (USD)" }), _jsx(TableColumn, { className: "p-3 text-right", children: "CA (USD)" }), _jsx(TableColumn, { className: "p-3 text-right", children: "Charges (USD)" }), _jsx(TableColumn, { className: "p-3 text-right", children: "Marge (USD)" }), _jsx(TableColumn, { className: "p-3 text-right", children: "Marge %" }), _jsx(TableColumn, { className: "p-3 text-right", children: "Wallet Budget" })] }), _jsx(TableBody, { children: sortedUsers.map((user) => (_jsxs(TableRow, { className: " ", children: [_jsx(TableCell, { className: "p-3 font-medium", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: user.email }), _jsx(Button, { size: "sm", isIconOnly: true, variant: "light", onPress: () => openUserDetails(user), children: _jsx(Eye, { size: 16 }) })] }) }), _jsxs(TableCell, { className: "p-3 text-right", children: ["$", user.totalPurchaseUsd.toFixed(2)] }), _jsxs(TableCell, { className: "p-3 text-right text-green-600 font-medium", children: ["$", user.totalPurchaseUsd.toFixed(2)] }), _jsxs(TableCell, { className: "p-3 text-right text-orange-600", children: ["$", user.totalCharges.toFixed(2)] }), _jsxs(TableCell, { className: "p-3 text-right text-purple-600 font-bold", children: ["$", user.realMarginUsd.toFixed(2)] }), _jsx(TableCell, { className: "p-3 text-right", children: _jsxs("span", { className: user.realMarginPercent > 30
132
+ ? "text-green-600 font-medium"
133
+ : user.realMarginPercent > 15
134
+ ? "text-blue-600"
135
+ : "text-orange-600", children: [user.realMarginPercent.toFixed(1), "%"] }) }), _jsxs(TableCell, { className: "p-3 text-right text-xs text-gray-500", children: ["$", user.walletProviderBudgetUsd.toFixed(4)] })] }, user.userId))) })] }) }) })] }), _jsx(Alert, { color: "primary", className: "mt-6 ", startContent: _jsx(Wallet, { size: 42 }), hideIcon: true, children: _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-medium mb-2", children: "Comment lire ces donn\u00E9es :" }), _jsxs("ul", { className: "space-y-1 list-disc list-inside", children: [_jsxs("li", { children: [_jsx("strong", { children: "CA (Chiffre d'affaires)" }), " : Montant total factur\u00E9 aux utilisateurs pour leur consommation r\u00E9elle"] }), _jsxs("li", { children: [_jsx("strong", { children: "Co\u00FBt Fournisseur" }), " : Co\u00FBt r\u00E9el pay\u00E9 aux fournisseurs d'IA (OpenAI, Anthropic, etc.)"] }), _jsxs("li", { children: [_jsx("strong", { children: "Marge" }), " : Diff\u00E9rence entre CA et Co\u00FBt (b\u00E9n\u00E9fice brut)"] }), _jsxs("li", { children: [_jsx("strong", { children: "Wallet Budget" }), " : Budget fournisseur restant dans le wallet de l'utilisateur (lissage automatique)"] })] })] }) }), _jsx(Modal, { isOpen: modalOpen, onClose: () => setModalOpen(false), size: "3xl", scrollBehavior: "inside", backdrop: "blur", children: _jsxs(ModalContent, { children: [_jsxs(ModalHeader, { className: "flex flex-col gap-1", children: [_jsxs("h3", { className: "text-xl font-bold", children: ["D\u00E9tails mensuels - ", selectedUser?.email] }), _jsxs("div", { className: "flex gap-2 items-center mt-2", children: [_jsx("span", { className: "text-sm text-gray-600", children: "Mois :" }), _jsx(Select, { size: "sm", selectedKeys: selectedMonth ? [selectedMonth] : [], onChange: (e) => handleMonthChange(e.target.value), className: "max-w-[200px]", children: getLastMonths().map((month) => (_jsx(SelectItem, { children: new Date(month + "-01").toLocaleDateString("fr-FR", {
136
+ year: "numeric",
137
+ month: "long",
138
+ }) }, month))) })] })] }), _jsx(ModalBody, { children: loadingDetails ? (_jsx("div", { className: "flex justify-center p-8", children: _jsx(Spinner, {}) })) : monthlyDetails ? (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "grid grid-cols-2 md:grid-cols-5 gap-3", children: [_jsx(Card, { children: _jsxs(CardBody, { className: "p-3", children: [_jsx("div", { className: "text-xs text-gray-600", children: "Achats" }), _jsx("div", { className: "text-lg font-bold", children: monthlyDetails.summary.totalPurchases })] }) }), _jsx(Card, { children: _jsxs(CardBody, { className: "p-3", children: [_jsx("div", { className: "text-xs text-gray-600", children: "Gifts" }), _jsx("div", { className: "text-lg font-bold text-blue-600", children: monthlyDetails.summary.totalGifts })] }) }), _jsx(Card, { children: _jsxs(CardBody, { className: "p-3", children: [_jsx("div", { className: "text-xs text-gray-600", children: "Consommations" }), _jsx("div", { className: "text-lg font-bold", children: monthlyDetails.summary.totalUsages })] }) }), _jsx(Card, { className: "border border-green-500", children: _jsxs(CardBody, { className: "p-3", children: [_jsx("div", { className: "text-xs text-gray-600", children: "CA (Achats)" }), _jsxs("div", { className: "text-lg font-bold text-green-600", children: ["$", monthlyDetails.summary.totalPurchaseUsd.toFixed(2)] })] }) }), _jsx(Card, { className: "border border-orange-500", children: _jsxs(CardBody, { className: "p-3", children: [_jsx("div", { className: "text-xs text-gray-600", children: "Charges" }), _jsxs("div", { className: "text-lg font-bold text-orange-600", children: ["$", monthlyDetails.summary.totalCharges.toFixed(2)] }), _jsxs("div", { className: "text-xs text-gray-500 mt-1", children: ["Util: $", monthlyDetails.summary.totalProviderCostUsd.toFixed(2), monthlyDetails.summary.totalGiftBudgetUsd > 0 && (_jsxs(_Fragment, { children: [" ", "+ Gifts: $", monthlyDetails.summary.totalGiftBudgetUsd.toFixed(2)] }))] })] }) })] }), _jsx(Card, { className: "border-2 border-purple-500", children: _jsx(CardBody, { className: "p-4", children: _jsxs("div", { className: "flex justify-between items-center", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm text-gray-600", children: "Marge R\u00E9elle" }), _jsxs("div", { className: "text-2xl font-bold text-purple-600", children: ["$", monthlyDetails.summary.realMarginUsd.toFixed(2)] }), _jsxs("div", { className: "text-xs text-gray-500 mt-1", children: ["CA $", monthlyDetails.summary.totalPurchaseUsd.toFixed(2), " ", "- Charges $", monthlyDetails.summary.totalCharges.toFixed(2)] })] }), _jsxs("div", { className: "text-right", children: [_jsx("div", { className: "text-sm text-gray-600", children: "Marge %" }), _jsxs("div", { className: "text-2xl font-bold", children: [monthlyDetails.summary.realMarginPercent.toFixed(1), "%"] })] })] }) }) }), _jsxs("div", { children: [_jsx("h4", { className: "font-bold mb-3", children: "Consommation par type" }), _jsxs(Table, { className: "text-sm", children: [_jsxs(TableHeader, { children: [_jsx(TableColumn, { children: "Type d'action" }), _jsx(TableColumn, { className: "text-right", children: "Appels" }), _jsx(TableColumn, { className: "text-right", children: "Co\u00FBt moyen" }), _jsx(TableColumn, { className: "text-right", children: "Co\u00FBt total" }), _jsx(TableColumn, { className: "text-right", children: "CA" }), _jsx(TableColumn, { className: "text-right", children: "Marge" })] }), _jsx(TableBody, { children: monthlyDetails.usageByAction.map((item, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: item.actionType }), _jsx(TableCell, { className: "text-right", children: item.count }), _jsxs(TableCell, { className: "text-right text-xs", children: ["$", item.avgCostPerCall.toFixed(6)] }), _jsxs(TableCell, { className: "text-right text-orange-600", children: ["$", item.providerCostUsd.toFixed(4)] }), _jsxs(TableCell, { className: "text-right text-green-600", children: ["$", item.sellUsd.toFixed(4)] }), _jsxs(TableCell, { className: "text-right text-purple-600 font-medium", children: ["$", item.marginUsd.toFixed(4)] })] }, idx))) })] })] }), monthlyDetails.recentEntries.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "font-bold mb-3", children: "Derni\u00E8res transactions (max 50)" }), _jsx("div", { className: "overflow-x-auto max-h-96 overflow-y-auto", children: _jsxs(Table, { className: "text-xs", children: [_jsxs(TableHeader, { children: [_jsx(TableColumn, { children: "Date" }), _jsx(TableColumn, { children: "Type" }), _jsx(TableColumn, { className: "text-right", children: "Co\u00FBt" }), _jsx(TableColumn, { className: "text-right", children: "CA" }), _jsx(TableColumn, { className: "text-right", children: "Marge" })] }), _jsx(TableBody, { children: monthlyDetails.recentEntries.map((entry, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: new Date(entry.created_at).toLocaleString("fr-FR") }), _jsx(TableCell, { children: _jsx("span", { className: `px-2 py-1 rounded text-xs ${entry.type === "purchase"
139
+ ? "bg-blue-100 text-blue-700"
140
+ : "bg-green-100 text-green-700"}`, children: entry.type }) }), _jsxs(TableCell, { className: "text-right", children: ["$", (entry.provider_cost_usd || 0).toFixed(6)] }), _jsxs(TableCell, { className: "text-right", children: ["$", (entry.sell_usd || 0).toFixed(6)] }), _jsxs(TableCell, { className: "text-right", children: ["$", (entry.margin_usd || 0).toFixed(6)] })] }, idx))) })] }) })] }))] })) : (_jsx("div", { className: "text-center p-8 text-gray-500", children: "Aucune donn\u00E9e disponible" })) }), _jsx(ModalFooter, { children: _jsx(Button, { color: "danger", variant: "light", onPress: () => setModalOpen(false), children: "Fermer" }) })] }) })] }) }));
141
+ }
@@ -0,0 +1,2 @@
1
+ export default function GlobalAISettingsPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=GlobalAISettingsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GlobalAISettingsPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/GlobalAISettingsPage.tsx"],"names":[],"mappings":"AAeA,MAAM,CAAC,OAAO,UAAU,oBAAoB,4CAiO3C"}
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
4
+ import { addToast, Button, Card, Input } from "@lastbrain/ui";
5
+ export default function GlobalAISettingsPage() {
6
+ const [settings, setSettings] = useState(null);
7
+ const [loading, setLoading] = useState(true);
8
+ const [saving, setSaving] = useState(false);
9
+ useEffect(() => {
10
+ fetchSettings();
11
+ }, []);
12
+ async function fetchSettings() {
13
+ try {
14
+ const res = await fetch("/api/ai/admin/global-ai-settings");
15
+ if (!res.ok) {
16
+ // Si 401 ou 403, rediriger
17
+ if (res.status === 401) {
18
+ window.location.href =
19
+ "/signin?redirect=" + encodeURIComponent(window.location.pathname);
20
+ return;
21
+ }
22
+ if (res.status === 403) {
23
+ window.location.href = "/";
24
+ return;
25
+ }
26
+ throw new Error("Failed to fetch settings");
27
+ }
28
+ const data = await res.json();
29
+ setSettings(data);
30
+ }
31
+ catch (err) {
32
+ addToast({
33
+ color: "danger",
34
+ description: "Erreur lors du chargement des paramètres",
35
+ });
36
+ }
37
+ finally {
38
+ setLoading(false);
39
+ }
40
+ }
41
+ async function handleSave(e) {
42
+ e.preventDefault();
43
+ if (!settings)
44
+ return;
45
+ setSaving(true);
46
+ try {
47
+ const res = await fetch("/api/ai/admin/global-ai-settings", {
48
+ method: "PUT",
49
+ headers: { "Content-Type": "application/json" },
50
+ body: JSON.stringify(settings),
51
+ });
52
+ if (!res.ok)
53
+ throw new Error("Failed to update settings");
54
+ const updated = await res.json();
55
+ setSettings(updated);
56
+ addToast({
57
+ color: "success",
58
+ description: "✅ Modèles par défaut mis à jour avec succès",
59
+ });
60
+ }
61
+ catch (err) {
62
+ addToast({
63
+ color: "danger",
64
+ description: "Erreur lors de la mise à jour",
65
+ });
66
+ }
67
+ finally {
68
+ setSaving(false);
69
+ }
70
+ }
71
+ if (loading) {
72
+ return (_jsxs("div", { className: "p-6 mt-16", children: [_jsx("h1", { className: "text-2xl font-bold mb-6", children: "Configuration IA Globale" }), _jsx("p", { className: "text-muted-foreground", children: "Chargement..." })] }));
73
+ }
74
+ if (!settings) {
75
+ return (_jsxs("div", { className: "p-6", children: [_jsx("h1", { className: "text-2xl font-bold mb-6", children: "Configuration IA Globale" }), _jsx("p", { className: "text-destructive", children: "Erreur de chargement des param\u00E8tres" })] }));
76
+ }
77
+ return (_jsxs("div", { className: "mx-auto p-6 max-w-4xl mt-16", children: [_jsxs("div", { className: "mb-6", children: [_jsx("h1", { className: "text-2xl font-bold", children: "Configuration IA Globale" }), _jsx("p", { className: "text-muted-foreground mt-2", children: "Modifie les mod\u00E8les par d\u00E9faut utilis\u00E9s pour tous les appels API (g\u00E9n\u00E9ration de recettes, images, etc.)" })] }), _jsxs("form", { onSubmit: handleSave, className: "space-y-6", children: [_jsxs(Card, { className: "p-6", children: [_jsx("h2", { className: "text-lg font-semibold mb-4", children: "\uD83D\uDCDD Mod\u00E8le Texte par D\u00E9faut" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { children: _jsx(Input, { id: "text_provider", label: "Provider", value: settings.default_text_provider, onChange: (e) => setSettings({
78
+ ...settings,
79
+ default_text_provider: e.target.value,
80
+ }), placeholder: "openai" }) }), _jsx("div", { children: _jsx(Input, { id: "text_model", label: "Model", value: settings.default_text_model, onChange: (e) => setSettings({
81
+ ...settings,
82
+ default_text_model: e.target.value,
83
+ }), placeholder: "gpt-4.1-mini" }) })] }), _jsx("p", { className: "text-xs text-muted-foreground mt-2", children: "Exemple : openai / gpt-4.1-mini" })] }), _jsxs(Card, { className: "p-6", children: [_jsx("h2", { className: "text-lg font-semibold mb-4", children: "\uD83D\uDDBC\uFE0F Mod\u00E8le Image par D\u00E9faut" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { children: _jsx(Input, { id: "image_provider", label: "Provider", value: settings.default_image_provider, onChange: (e) => setSettings({
84
+ ...settings,
85
+ default_image_provider: e.target.value,
86
+ }), placeholder: "google" }) }), _jsx("div", { children: _jsx(Input, { id: "image_model", label: "Model", value: settings.default_image_model, onChange: (e) => setSettings({
87
+ ...settings,
88
+ default_image_model: e.target.value,
89
+ }), placeholder: "gemini-2.5-flash-image" }) })] }), _jsx("p", { className: "text-xs text-muted-foreground mt-2", children: "Exemple : google / gemini-2.5-flash-image" })] }), _jsxs(Card, { className: "p-6", children: [_jsx("h2", { className: "text-lg font-semibold mb-4", children: "\u2699\uFE0F Limites Globales" }), _jsx("div", { children: _jsx(Input, { id: "max_tokens", label: "Max Tokens par Appel", value: settings.max_tokens_per_call.toString(), onChange: (e) => setSettings({
90
+ ...settings,
91
+ max_tokens_per_call: parseInt(e.target.value),
92
+ }) }) })] }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx(Button, { type: "submit", disabled: saving, children: saving ? "Enregistrement..." : "💾 Enregistrer" }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Derni\u00E8re mise \u00E0 jour :", " ", new Date(settings.updated_at).toLocaleString("fr-FR")] })] })] }), _jsxs("div", { className: "mt-8 p-4 bg-blue-50 dark:bg-blue-950 rounded-lg", children: [_jsx("h3", { className: "font-semibold mb-2", children: "\u2139\uFE0F Comment \u00E7a fonctionne ?" }), _jsxs("ul", { className: "text-sm space-y-1 text-muted-foreground", children: [_jsx("li", { children: "\u2022 Tous les futurs appels API (recettes, images, etc.) utiliseront automatiquement ces mod\u00E8les" }), _jsx("li", { children: "\u2022 Les utilisateurs peuvent override individuellement via user_ai_settings" }), _jsx("li", { children: "\u2022 Pas besoin de red\u00E9marrer l'app, les changements sont imm\u00E9diats" }), _jsxs("li", { children: ["\u2022 Mod\u00E8le actuel : Text = ", settings.default_text_provider, "/", settings.default_text_model, ", Image =", " ", settings.default_image_provider, "/", settings.default_image_model] })] })] })] }));
93
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"UserTokenPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/UserTokenPage.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,aAAa,4CAwc5B"}
1
+ {"version":3,"file":"UserTokenPage.d.ts","sourceRoot":"","sources":["../../../src/web/admin/UserTokenPage.tsx"],"names":[],"mappings":"AAmEA,wBAAgB,aAAa,4CA+hB5B"}
@@ -3,7 +3,7 @@ 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
+ import { logger, useModuleTranslation } from "@lastbrain/core";
7
7
  export function UserTokenPage() {
8
8
  const t = useModuleTranslation("ai");
9
9
  const [loading, setLoading] = useState(true);
@@ -23,13 +23,19 @@ export function UserTokenPage() {
23
23
  setLoading(true);
24
24
  // Récupérer toutes les données tokens de tous les utilisateurs
25
25
  const response = await fetch("/api/ai/admin/user-token");
26
+ console.log("🚀 ~ UserTokenPage ~ response:", response);
26
27
  if (!response.ok) {
27
28
  throw new Error(t("admin.tokens.error.loading") ||
28
29
  "Erreur lors du chargement des données");
29
30
  }
30
31
  const data = await response.json();
31
32
  // Traiter les données utilisateurs
32
- const usersData = data.users || [];
33
+ const usersData = (data.users || []).map((user) => ({
34
+ ...user,
35
+ includedTokens: user.includedTokens || 0,
36
+ usedIncludedTokens: user.usedIncludedTokens || 0,
37
+ remainingIncludedTokens: user.remainingIncludedTokens || 0,
38
+ }));
33
39
  setUsers(usersData);
34
40
  // Filtrer les transactions par mois
35
41
  const allTransactions = data.transactions || [];
@@ -53,7 +59,7 @@ export function UserTokenPage() {
53
59
  });
54
60
  }
55
61
  catch (error) {
56
- console.error(t("admin.tokens.error.loading") || "Erreur:", error);
62
+ logger.error(t("admin.tokens.error.loading") || "Erreur:", error);
57
63
  addToast({
58
64
  color: "danger",
59
65
  title: t("admin.tokens.error.loading") ||
@@ -82,6 +88,7 @@ export function UserTokenPage() {
82
88
  gift: t("tokens.type.gift") || "Cadeau",
83
89
  use: t("tokens.type.use") || "Utilisation",
84
90
  adjust: t("tokens.type.adjust") || "Ajustement",
91
+ quota_use: t("tokens.type.quota_use") || "Quota (abonnement)",
85
92
  };
86
93
  return labels[type] || type;
87
94
  };
@@ -91,22 +98,28 @@ export function UserTokenPage() {
91
98
  gift: "primary",
92
99
  use: "danger",
93
100
  adjust: "warning",
101
+ quota_use: "warning",
94
102
  };
95
103
  return colors[type] || "default";
96
104
  };
97
105
  // Filtrer les utilisateurs par recherche
98
106
  const filteredUsers = users.filter((user) => user.email.toLowerCase().includes(searchQuery.toLowerCase()) ||
99
107
  user.fullName?.toLowerCase().includes(searchQuery.toLowerCase()));
108
+ const totalIncludedTokens = users.reduce((sum, user) => sum + (user.includedTokens || 0), 0);
109
+ const totalIncludedUsed = users.reduce((sum, user) => sum + (user.usedIncludedTokens || 0), 0);
110
+ const totalIncludedRemaining = users.reduce((sum, user) => sum + (user.remainingIncludedTokens || 0), 0);
100
111
  if (loading) {
101
- return (_jsx("div", { className: "flex justify-center items-center min-h-96", children: _jsx(Spinner, { size: "lg" }) }));
112
+ return (_jsx("div", { className: "mt-16 flex justify-center items-center min-h-96", children: _jsx(Spinner, { size: "lg" }) }));
102
113
  }
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") ||
114
+ return (_jsxs("div", { className: "mt-16 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
115
  "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
116
  "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", {
106
117
  month: "long",
107
118
  year: "numeric",
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
119
+ }) }, month))) })] }) }) }), _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-5", children: [_jsxs("div", { className: "flex items-center justify-center gap-2 mb-2", children: [_jsx(Coins, { size: 22, className: "text-primary" }), _jsx("h3", { className: "text-sm font-medium text-gray-500", children: t("admin.tokens.included.total") || "Tokens inclus (plans)" })] }), _jsx("p", { className: "text-3xl font-bold text-primary", children: totalIncludedTokens.toLocaleString() })] }) }), _jsx(Card, { children: _jsxs(CardBody, { className: "text-center py-5", children: [_jsxs("div", { className: "flex items-center justify-center gap-2 mb-2", children: [_jsx(TrendingDown, { size: 22, className: "text-danger" }), _jsx("h3", { className: "text-sm font-medium text-gray-500", children: t("admin.tokens.included.used") || "Tokens inclus utilisés" })] }), _jsx("p", { className: "text-3xl font-bold text-danger", children: totalIncludedUsed.toLocaleString() })] }) }), _jsx(Card, { children: _jsxs(CardBody, { className: "text-center py-5", children: [_jsxs("div", { className: "flex items-center justify-center gap-2 mb-2", children: [_jsx(TrendingUp, { size: 22, className: "text-success" }), _jsx("h3", { className: "text-sm font-medium text-gray-500", children: t("admin.tokens.included.remaining") ||
120
+ "Tokens inclus restants" })] }), _jsx("p", { className: "text-3xl font-bold text-success", children: totalIncludedRemaining.toLocaleString() })] }) })] }), _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_included") || "TOKENS INCLUS" }), _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") ||
121
+ "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: _jsxs("div", { className: "text-sm text-gray-700", children: [_jsx("div", { className: "font-semibold text-primary", children: (user.includedTokens ?? 0).toLocaleString() }), _jsxs("div", { className: "text-xs text-gray-500", children: [t("admin.tokens.users.included_usage") || "Utilisé", " ", ": ", (user.usedIncludedTokens ?? 0).toLocaleString(), " \u2022", " ", t("admin.tokens.users.included_remaining") ||
122
+ "Reste", " ", ":", " ", (user.remainingIncludedTokens ?? 0).toLocaleString()] })] }) }), _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
110
123
  ? formatDate(user.lastActivity)
111
124
  : "-" }) })] }, user.userId))) })] })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: t("admin.tokens.transactions.title") ||
112
125
  "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") ||
@@ -0,0 +1,2 @@
1
+ export declare function AISettingsPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=AISettingsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AISettingsPage.d.ts","sourceRoot":"","sources":["../../../src/web/auth/AISettingsPage.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,cAAc,4CA4hB7B"}