@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.
- package/README.md +52 -1
- package/dist/ai.build.config.d.ts.map +1 -1
- package/dist/ai.build.config.js +508 -9
- package/dist/api/admin/ai-provider-models/[id].d.ts +18 -0
- package/dist/api/admin/ai-provider-models/[id].d.ts.map +1 -0
- package/dist/api/admin/ai-provider-models/[id].js +58 -0
- package/dist/api/admin/ai-provider-models.d.ts +20 -0
- package/dist/api/admin/ai-provider-models.d.ts.map +1 -0
- package/dist/api/admin/ai-provider-models.js +26 -0
- package/dist/api/admin/ai-providers/[key].d.ts +18 -0
- package/dist/api/admin/ai-providers/[key].d.ts.map +1 -0
- package/dist/api/admin/ai-providers/[key].js +55 -0
- package/dist/api/admin/ai-providers.d.ts +20 -0
- package/dist/api/admin/ai-providers.d.ts.map +1 -0
- package/dist/api/admin/ai-providers.js +26 -0
- package/dist/api/admin/billing-analytics.d.ts +43 -0
- package/dist/api/admin/billing-analytics.d.ts.map +1 -0
- package/dist/api/admin/billing-analytics.js +144 -0
- package/dist/api/admin/global-ai-settings.d.ts +14 -0
- package/dist/api/admin/global-ai-settings.d.ts.map +1 -0
- package/dist/api/admin/global-ai-settings.js +63 -0
- package/dist/api/admin/token-packs/[id].d.ts.map +1 -1
- package/dist/api/admin/token-packs/[id].js +3 -2
- package/dist/api/admin/token-packs.d.ts.map +1 -1
- package/dist/api/admin/token-packs.js +3 -2
- package/dist/api/admin/user-monthly-details.d.ts +49 -0
- package/dist/api/admin/user-monthly-details.d.ts.map +1 -0
- package/dist/api/admin/user-monthly-details.js +140 -0
- package/dist/api/admin/user-quota.d.ts +21 -0
- package/dist/api/admin/user-quota.d.ts.map +1 -0
- package/dist/api/admin/user-quota.js +59 -0
- package/dist/api/admin/user-token/[id].d.ts.map +1 -1
- package/dist/api/admin/user-token/[id].js +2 -1
- package/dist/api/admin/user-token.d.ts +5 -2
- package/dist/api/admin/user-token.d.ts.map +1 -1
- package/dist/api/admin/user-token.js +91 -17
- package/dist/api/admin/user-usage-by-model.d.ts +22 -0
- package/dist/api/admin/user-usage-by-model.d.ts.map +1 -0
- package/dist/api/admin/user-usage-by-model.js +78 -0
- package/dist/api/admin/user-wallet-analytics.d.ts +15 -0
- package/dist/api/admin/user-wallet-analytics.d.ts.map +1 -0
- package/dist/api/admin/user-wallet-analytics.js +67 -0
- package/dist/api/admin/wallet-repair/route.d.ts +30 -0
- package/dist/api/admin/wallet-repair/route.d.ts.map +1 -0
- package/dist/api/admin/wallet-repair/route.js +63 -0
- package/dist/api/auth/ai-model-settings.d.ts +21 -0
- package/dist/api/auth/ai-model-settings.d.ts.map +1 -0
- package/dist/api/auth/ai-model-settings.js +86 -0
- package/dist/api/auth/ai-settings.d.ts +17 -0
- package/dist/api/auth/ai-settings.d.ts.map +1 -0
- package/dist/api/auth/ai-settings.js +87 -0
- package/dist/api/auth/api-keys/[id].d.ts +17 -0
- package/dist/api/auth/api-keys/[id].d.ts.map +1 -0
- package/dist/api/auth/api-keys/[id].js +66 -0
- package/dist/api/auth/api-keys.d.ts +19 -0
- package/dist/api/auth/api-keys.d.ts.map +1 -0
- package/dist/api/auth/api-keys.js +94 -0
- package/dist/api/auth/create-checkout.d.ts +1 -1
- package/dist/api/auth/create-checkout.d.ts.map +1 -1
- package/dist/api/auth/create-checkout.js +8 -6
- package/dist/api/auth/generate-image.d.ts +2 -2
- package/dist/api/auth/generate-image.d.ts.map +1 -1
- package/dist/api/auth/generate-image.js +404 -104
- package/dist/api/auth/generate-text.d.ts +3 -2
- package/dist/api/auth/generate-text.d.ts.map +1 -1
- package/dist/api/auth/generate-text.js +130 -58
- package/dist/api/auth/process-ocr.d.ts +9 -0
- package/dist/api/auth/process-ocr.d.ts.map +1 -0
- package/dist/api/auth/process-ocr.js +43 -0
- package/dist/api/auth/prompts/stats.d.ts +14 -0
- package/dist/api/auth/prompts/stats.d.ts.map +1 -0
- package/dist/api/auth/prompts/stats.js +46 -0
- package/dist/api/auth/prompts.d.ts +26 -0
- package/dist/api/auth/prompts.d.ts.map +1 -0
- package/dist/api/auth/prompts.js +175 -0
- package/dist/api/auth/token-balance.d.ts +26 -0
- package/dist/api/auth/token-balance.d.ts.map +1 -0
- package/dist/api/auth/token-balance.js +47 -0
- package/dist/api/auth/token-checkout.d.ts.map +1 -1
- package/dist/api/auth/token-checkout.js +22 -4
- package/dist/api/auth/token-packs.d.ts.map +1 -1
- package/dist/api/auth/token-packs.js +2 -1
- package/dist/api/auth/usage-by-model.d.ts +25 -0
- package/dist/api/auth/usage-by-model.d.ts.map +1 -0
- package/dist/api/auth/usage-by-model.js +95 -0
- package/dist/api/auth/usage.d.ts +26 -0
- package/dist/api/auth/usage.d.ts.map +1 -0
- package/dist/api/auth/usage.js +127 -0
- package/dist/api/auth/user-tokens.d.ts.map +1 -1
- package/dist/api/auth/user-tokens.js +36 -2
- package/dist/api/auth/wallet/route.d.ts +17 -0
- package/dist/api/auth/wallet/route.d.ts.map +1 -0
- package/dist/api/auth/wallet/route.js +68 -0
- package/dist/api/auth/wallet.d.ts +16 -0
- package/dist/api/auth/wallet.d.ts.map +1 -0
- package/dist/api/auth/wallet.js +71 -0
- package/dist/api/public/gateway-models.d.ts +25 -0
- package/dist/api/public/gateway-models.d.ts.map +1 -0
- package/dist/api/public/gateway-models.js +49 -0
- package/dist/api/public/pricing-summary.d.ts +46 -0
- package/dist/api/public/pricing-summary.d.ts.map +1 -0
- package/dist/api/public/pricing-summary.js +70 -0
- package/dist/api/public/prompts/[id]/stats.d.ts +15 -0
- package/dist/api/public/prompts/[id]/stats.d.ts.map +1 -0
- package/dist/api/public/prompts/[id]/stats.js +37 -0
- package/dist/api/public/prompts/[id]/view.d.ts +15 -0
- package/dist/api/public/prompts/[id]/view.d.ts.map +1 -0
- package/dist/api/public/prompts/[id]/view.js +41 -0
- package/dist/api/public/prompts/slug/[slug].d.ts +15 -0
- package/dist/api/public/prompts/slug/[slug].d.ts.map +1 -0
- package/dist/api/public/prompts/slug/[slug].js +40 -0
- package/dist/api/public/prompts/user/[username].d.ts +17 -0
- package/dist/api/public/prompts/user/[username].d.ts.map +1 -0
- package/dist/api/public/prompts/user/[username].js +51 -0
- package/dist/api/public/prompts.d.ts +15 -0
- package/dist/api/public/prompts.d.ts.map +1 -0
- package/dist/api/public/prompts.js +45 -0
- package/dist/api/public/token-packs.d.ts +11 -0
- package/dist/api/public/token-packs.d.ts.map +1 -0
- package/dist/api/public/token-packs.js +25 -0
- package/dist/api/public/token-pricing.d.ts +44 -0
- package/dist/api/public/token-pricing.d.ts.map +1 -0
- package/dist/api/public/token-pricing.js +168 -0
- package/dist/api/public/v1/_lib/artifacts.d.ts +52 -0
- package/dist/api/public/v1/_lib/artifacts.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/artifacts.js +217 -0
- package/dist/api/public/v1/_lib/auth.d.ts +43 -0
- package/dist/api/public/v1/_lib/auth.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/auth.js +108 -0
- package/dist/api/public/v1/_lib/errors.d.ts +17 -0
- package/dist/api/public/v1/_lib/errors.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/errors.js +16 -0
- package/dist/api/public/v1/_lib/log.d.ts +29 -0
- package/dist/api/public/v1/_lib/log.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/log.js +68 -0
- package/dist/api/public/v1/_lib/quota.d.ts +24 -0
- package/dist/api/public/v1/_lib/quota.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/quota.js +118 -0
- package/dist/api/public/v1/_lib/router.d.ts +54 -0
- package/dist/api/public/v1/_lib/router.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/router.js +119 -0
- package/dist/api/public/v1/connect.d.ts +20 -0
- package/dist/api/public/v1/connect.d.ts.map +1 -0
- package/dist/api/public/v1/connect.js +119 -0
- package/dist/api/public/v1/doc.d.ts +239 -0
- package/dist/api/public/v1/doc.d.ts.map +1 -0
- package/dist/api/public/v1/doc.js +253 -0
- package/dist/api/public/v1/history/[id].d.ts +92 -0
- package/dist/api/public/v1/history/[id].d.ts.map +1 -0
- package/dist/api/public/v1/history/[id].js +176 -0
- package/dist/api/public/v1/history.d.ts +30 -0
- package/dist/api/public/v1/history.d.ts.map +1 -0
- package/dist/api/public/v1/history.js +142 -0
- package/dist/api/public/v1/image-ai.d.ts +24 -0
- package/dist/api/public/v1/image-ai.d.ts.map +1 -0
- package/dist/api/public/v1/image-ai.js +233 -0
- package/dist/api/public/v1/prompts.d.ts +19 -0
- package/dist/api/public/v1/prompts.d.ts.map +1 -0
- package/dist/api/public/v1/prompts.js +107 -0
- package/dist/api/public/v1/provider.d.ts +16 -0
- package/dist/api/public/v1/provider.d.ts.map +1 -0
- package/dist/api/public/v1/provider.js +130 -0
- package/dist/api/public/v1/purchase.d.ts +11 -0
- package/dist/api/public/v1/purchase.d.ts.map +1 -0
- package/dist/api/public/v1/purchase.js +18 -0
- package/dist/api/public/v1/status.d.ts +35 -0
- package/dist/api/public/v1/status.d.ts.map +1 -0
- package/dist/api/public/v1/status.js +163 -0
- package/dist/api/public/v1/text-ai.d.ts +26 -0
- package/dist/api/public/v1/text-ai.d.ts.map +1 -0
- package/dist/api/public/v1/text-ai.js +239 -0
- package/dist/api/public/webhook.d.ts.map +1 -1
- package/dist/api/public/webhook.js +50 -39
- package/dist/api/track-usage.d.ts +12 -0
- package/dist/api/track-usage.d.ts.map +1 -0
- package/dist/api/track-usage.js +37 -0
- package/dist/components/Doc.d.ts.map +1 -1
- package/dist/components/Doc.js +1 -1
- package/dist/components/DocUsageCustom.js +6 -6
- package/dist/components/admin/UserTokenTab.d.ts.map +1 -1
- package/dist/components/admin/UserTokenTab.js +170 -23
- package/dist/components/auth/AuthDashboardAi.d.ts +2 -0
- package/dist/components/auth/AuthDashboardAi.d.ts.map +1 -0
- package/dist/components/auth/AuthDashboardAi.js +53 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts +277 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts.map +1 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.js +276 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/scripts/migrate-tokens-to-wallet.d.ts +13 -0
- package/dist/scripts/migrate-tokens-to-wallet.d.ts.map +1 -0
- package/dist/scripts/migrate-tokens-to-wallet.js +165 -0
- package/dist/server/__tests__/billing.test.d.ts +5 -0
- package/dist/server/__tests__/billing.test.d.ts.map +1 -0
- package/dist/server/__tests__/billing.test.js +223 -0
- package/dist/server/ai-client.d.ts +59 -0
- package/dist/server/ai-client.d.ts.map +1 -0
- package/dist/server/ai-client.js +111 -0
- package/dist/server/ai-generation-service.d.ts +66 -0
- package/dist/server/ai-generation-service.d.ts.map +1 -0
- package/dist/server/ai-generation-service.js +274 -0
- package/dist/server/billing.d.ts +200 -0
- package/dist/server/billing.d.ts.map +1 -0
- package/dist/server/billing.js +488 -0
- package/dist/server/gateway-service.d.ts +13 -0
- package/dist/server/gateway-service.d.ts.map +1 -0
- package/dist/server/gateway-service.js +161 -0
- package/dist/server/global-settings.d.ts +16 -0
- package/dist/server/global-settings.d.ts.map +1 -0
- package/dist/server/global-settings.js +42 -0
- package/dist/server/model-filter.d.ts +25 -0
- package/dist/server/model-filter.d.ts.map +1 -0
- package/dist/server/model-filter.js +240 -0
- package/dist/server/ocr.d.ts +39 -0
- package/dist/server/ocr.d.ts.map +1 -0
- package/dist/server/ocr.js +280 -0
- package/dist/server/openai-client.d.ts +19 -0
- package/dist/server/openai-client.d.ts.map +1 -0
- package/dist/server/openai-client.js +26 -0
- package/dist/server/pricing-config.d.ts +18 -0
- package/dist/server/pricing-config.d.ts.map +1 -0
- package/dist/server/pricing-config.js +94 -0
- package/dist/server/pricing-validator.d.ts +41 -0
- package/dist/server/pricing-validator.d.ts.map +1 -0
- package/dist/server/pricing-validator.js +113 -0
- package/dist/server/pricing.d.ts +121 -0
- package/dist/server/pricing.d.ts.map +1 -0
- package/dist/server/pricing.js +225 -0
- package/dist/server/quota.d.ts +66 -0
- package/dist/server/quota.d.ts.map +1 -0
- package/dist/server/quota.js +538 -0
- package/dist/server/wallet-repair.d.ts +32 -0
- package/dist/server/wallet-repair.d.ts.map +1 -0
- package/dist/server/wallet-repair.js +189 -0
- package/dist/server.d.ts +13 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +87 -16
- package/dist/sitemap/handlers/prompts.d.ts +6 -0
- package/dist/sitemap/handlers/prompts.d.ts.map +1 -0
- package/dist/sitemap/handlers/prompts.js +72 -0
- package/dist/sitemap/handlers/users.d.ts +6 -0
- package/dist/sitemap/handlers/users.d.ts.map +1 -0
- package/dist/sitemap/handlers/users.js +80 -0
- package/dist/sitemap/manifest.d.ts +8 -0
- package/dist/sitemap/manifest.d.ts.map +1 -0
- package/dist/sitemap/manifest.js +27 -0
- package/dist/types/gateway.d.ts +40 -0
- package/dist/types/gateway.d.ts.map +1 -0
- package/dist/types/gateway.js +4 -0
- package/dist/types/quota.d.ts +74 -0
- package/dist/types/quota.d.ts.map +1 -0
- package/dist/types/quota.js +11 -0
- package/dist/utils/date.d.ts +7 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +17 -0
- package/dist/web/admin/AdminTokenPacksPage.js +1 -1
- package/dist/web/admin/BillingAnalyticsPage.d.ts +2 -0
- package/dist/web/admin/BillingAnalyticsPage.d.ts.map +1 -0
- package/dist/web/admin/BillingAnalyticsPage.js +141 -0
- package/dist/web/admin/GlobalAISettingsPage.d.ts +2 -0
- package/dist/web/admin/GlobalAISettingsPage.d.ts.map +1 -0
- package/dist/web/admin/GlobalAISettingsPage.js +93 -0
- package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
- package/dist/web/admin/UserTokenPage.js +20 -7
- package/dist/web/auth/AISettingsPage.d.ts +2 -0
- package/dist/web/auth/AISettingsPage.d.ts.map +1 -0
- package/dist/web/auth/AISettingsPage.js +258 -0
- package/dist/web/auth/APIKeysPage.d.ts +2 -0
- package/dist/web/auth/APIKeysPage.d.ts.map +1 -0
- package/dist/web/auth/APIKeysPage.js +154 -0
- package/dist/web/auth/HistoryPage.d.ts +2 -0
- package/dist/web/auth/HistoryPage.d.ts.map +1 -0
- package/dist/web/auth/HistoryPage.js +279 -0
- package/dist/web/auth/PromptsPage.d.ts +5 -0
- package/dist/web/auth/PromptsPage.d.ts.map +1 -0
- package/dist/web/auth/PromptsPage.js +137 -0
- package/dist/web/auth/TokenPage.d.ts.map +1 -1
- package/dist/web/auth/TokenPage.js +88 -31
- package/dist/web/auth/UsageAndTokensPage.d.ts +2 -0
- package/dist/web/auth/UsageAndTokensPage.d.ts.map +1 -0
- package/dist/web/auth/UsageAndTokensPage.js +157 -0
- package/dist/web/auth/UsagePage.d.ts +2 -0
- package/dist/web/auth/UsagePage.d.ts.map +1 -0
- package/dist/web/auth/UsagePage.js +62 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.d.ts +13 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.d.ts.map +1 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.js +16 -0
- package/dist/web/auth/components/ModelUsageTable.d.ts +19 -0
- package/dist/web/auth/components/ModelUsageTable.d.ts.map +1 -0
- package/dist/web/auth/components/ModelUsageTable.js +37 -0
- package/dist/web/auth/components/PurchaseButton.d.ts +7 -0
- package/dist/web/auth/components/PurchaseButton.d.ts.map +1 -0
- package/dist/web/auth/components/PurchaseButton.js +13 -0
- package/dist/web/auth/components/TokenHistoryCard.d.ts +20 -0
- package/dist/web/auth/components/TokenHistoryCard.d.ts.map +1 -0
- package/dist/web/auth/components/TokenHistoryCard.js +76 -0
- package/dist/web/auth/components/TokenKpiGrid.d.ts +24 -0
- package/dist/web/auth/components/TokenKpiGrid.d.ts.map +1 -0
- package/dist/web/auth/components/TokenKpiGrid.js +38 -0
- package/dist/web/auth/components/UsageByDayChart.d.ts +11 -0
- package/dist/web/auth/components/UsageByDayChart.d.ts.map +1 -0
- package/dist/web/auth/components/UsageByDayChart.js +32 -0
- package/dist/web/auth/components/UsageByModelBarChart.d.ts +12 -0
- package/dist/web/auth/components/UsageByModelBarChart.d.ts.map +1 -0
- package/dist/web/auth/components/UsageByModelBarChart.js +32 -0
- package/dist/web/auth/components/WalletStatusCard.d.ts +9 -0
- package/dist/web/auth/components/WalletStatusCard.d.ts.map +1 -0
- package/dist/web/auth/components/WalletStatusCard.js +50 -0
- package/dist/web/components/ImageGenerative.d.ts +3 -1
- package/dist/web/components/ImageGenerative.d.ts.map +1 -1
- package/dist/web/components/ImageGenerative.js +139 -52
- package/dist/web/components/TextareaGenerative.d.ts +3 -1
- package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
- package/dist/web/components/TextareaGenerative.js +10 -5
- package/dist/web/public/PromptDetailPage.d.ts +25 -0
- package/dist/web/public/PromptDetailPage.d.ts.map +1 -0
- package/dist/web/public/PromptDetailPage.js +71 -0
- package/dist/web/public/PromptDetailPageServer.d.ts +15 -0
- package/dist/web/public/PromptDetailPageServer.d.ts.map +1 -0
- package/dist/web/public/PromptDetailPageServer.js +68 -0
- package/dist/web/public/PublicPromptsPage.d.ts +5 -0
- package/dist/web/public/PublicPromptsPage.d.ts.map +1 -0
- package/dist/web/public/PublicPromptsPage.js +110 -0
- package/dist/web/public/PurchaseTokensPage.d.ts +2 -0
- package/dist/web/public/PurchaseTokensPage.d.ts.map +1 -0
- package/dist/web/public/PurchaseTokensPage.js +98 -0
- package/dist/web/public/UserAvatar.d.ts +13 -0
- package/dist/web/public/UserAvatar.d.ts.map +1 -0
- package/dist/web/public/UserAvatar.js +13 -0
- package/dist/web/public/UserDetailPageServer.d.ts +15 -0
- package/dist/web/public/UserDetailPageServer.d.ts.map +1 -0
- package/dist/web/public/UserDetailPageServer.js +31 -0
- package/dist/web/public/UserPromptsPage.d.ts +9 -0
- package/dist/web/public/UserPromptsPage.d.ts.map +1 -0
- package/dist/web/public/UserPromptsPage.js +112 -0
- package/dist/web/public/UserPromptsPageServer.d.ts +15 -0
- package/dist/web/public/UserPromptsPageServer.d.ts.map +1 -0
- package/dist/web/public/UserPromptsPageServer.js +31 -0
- package/package.json +18 -9
- package/supabase/migrations/20251125000000_ai_tokens.sql +7 -0
- package/supabase/migrations/20260123100002_user_token_quota_monthly copy.sql +173 -0
- package/supabase/migrations/20260128100003_update_and_add_table.sql +368 -0
- package/supabase/migrations/20260128120000_seed_providers_models.sql +78 -0
- package/supabase/migrations/20260128131405_add_api_key_id_to_ledgers.sql +41 -0
- package/supabase/migrations/20260128140000_ai_artifacts_storage.sql +99 -0
- package/supabase/migrations/20260128140002_ai_user_settings.sql +57 -0
- package/supabase/migrations/20260128150000_drop_ai_user_settings.sql +21 -0
- package/supabase/migrations/20260128160000_wallet_billing_system.sql +192 -0
- package/supabase/migrations/20260128160001_wallet_rpc_functions.sql +165 -0
- package/supabase/migrations/20260128170000_add_pack_coef_to_token_packs.sql +30 -0
- package/supabase/migrations/20260129120000_wallet_view_rpc.sql +41 -0
- package/supabase/migrations/20260129220003_update_pack_margins.sql +31 -0
- package/supabase/migrations/20260129330004_ai_user_prompts.sql +151 -0
- package/supabase/migrations/20260129330005_ai_prompts_ip_tracking.sql +92 -0
- package/supabase/migrations/20260129330006_ai_prompts_slug.sql +64 -0
- package/supabase/migrations/20260129330007_ai_prompts_view_slug.sql +26 -0
- package/supabase/migrations/20260129440000_ai_prompts_view_username.sql +33 -0
- package/supabase/migrations/20260129450000_ai_prompts_add_lang.sql +40 -0
- package/supabase/migrations/20260131000000_extract_model_prompt_in_ledger.sql +92 -0
- package/supabase/migrations/20260131140000_fix_duplicate_purchases.sql +64 -0
- package/supabase/migrations/20260201120000_module-ai_default_models.sql +63 -0
- package/supabase/migrations/20260201130000_module-ai_remove_provider_tables.sql +17 -0
- package/supabase/migrations-down/20251217120000_user_token_quota_monthly.sql +34 -0
- package/supabase/migrations-down/20260128131405_add_api_key_id_to_ledgers.sql +25 -0
|
@@ -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,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 @@
|
|
|
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 @@
|
|
|
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":"
|
|
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
|
-
|
|
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(
|
|
109
|
-
"
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"AISettingsPage.d.ts","sourceRoot":"","sources":["../../../src/web/auth/AISettingsPage.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,cAAc,4CA4hB7B"}
|