@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,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PublicPromptsPage - Browse and discover public AI prompts
|
|
3
|
+
*/
|
|
4
|
+
"use client";
|
|
5
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
6
|
+
import { useState, useEffect, useCallback } from "react";
|
|
7
|
+
import { Card, CardHeader, CardBody, Button, Input, Chip, Spinner, AppLink, } from "@lastbrain/ui";
|
|
8
|
+
import { Sparkles, MessageSquare, Image as ImageIcon, Copy, TrendingUp, Eye, MousePointerClick, ExternalLink, } from "lucide-react";
|
|
9
|
+
import { UserAvatar } from "./UserAvatar";
|
|
10
|
+
import { useAuth } from "@lastbrain/core";
|
|
11
|
+
export function PublicPromptsPage() {
|
|
12
|
+
const { user } = useAuth();
|
|
13
|
+
const [prompts, setPrompts] = useState([]);
|
|
14
|
+
const [loading, setLoading] = useState(true);
|
|
15
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
16
|
+
const [hasMore, setHasMore] = useState(true);
|
|
17
|
+
const [filter, setFilter] = useState("all");
|
|
18
|
+
const [search, setSearch] = useState("");
|
|
19
|
+
const [copiedId, setCopiedId] = useState(null);
|
|
20
|
+
const [page, setPage] = useState(0);
|
|
21
|
+
const ITEMS_PER_PAGE = 12;
|
|
22
|
+
const fetchPublicPrompts = useCallback(async (currentPage, reset) => {
|
|
23
|
+
try {
|
|
24
|
+
if (reset) {
|
|
25
|
+
setLoading(true);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
setLoadingMore(true);
|
|
29
|
+
}
|
|
30
|
+
const params = new URLSearchParams();
|
|
31
|
+
if (filter !== "all") {
|
|
32
|
+
params.append("type", filter);
|
|
33
|
+
}
|
|
34
|
+
if (search) {
|
|
35
|
+
params.append("search", search);
|
|
36
|
+
}
|
|
37
|
+
params.append("limit", String(ITEMS_PER_PAGE));
|
|
38
|
+
params.append("offset", String(currentPage * ITEMS_PER_PAGE));
|
|
39
|
+
const response = await fetch(`/api/ai/public/prompts?${params}`);
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
if (response.ok) {
|
|
42
|
+
const newPrompts = data.prompts || [];
|
|
43
|
+
setPrompts((prev) => (reset ? newPrompts : [...prev, ...newPrompts]));
|
|
44
|
+
setHasMore(newPrompts.length === ITEMS_PER_PAGE);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error("Failed to fetch public prompts:", error);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
setLoading(false);
|
|
52
|
+
setLoadingMore(false);
|
|
53
|
+
}
|
|
54
|
+
}, [filter, search]);
|
|
55
|
+
// Reset and fetch when filter changes
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
setPrompts([]);
|
|
58
|
+
setPage(0);
|
|
59
|
+
setHasMore(true);
|
|
60
|
+
fetchPublicPrompts(0, true);
|
|
61
|
+
}, [filter, fetchPublicPrompts]);
|
|
62
|
+
// Reset and fetch when search changes (with debounce)
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const timeout = setTimeout(() => {
|
|
65
|
+
setPrompts([]);
|
|
66
|
+
setPage(0);
|
|
67
|
+
setHasMore(true);
|
|
68
|
+
fetchPublicPrompts(0, true);
|
|
69
|
+
}, 300);
|
|
70
|
+
return () => clearTimeout(timeout);
|
|
71
|
+
}, [search, fetchPublicPrompts]);
|
|
72
|
+
// Infinite scroll observer
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
const observer = new IntersectionObserver((entries) => {
|
|
75
|
+
if (entries[0].isIntersecting && hasMore && !loading && !loadingMore) {
|
|
76
|
+
const nextPage = page + 1;
|
|
77
|
+
setPage(nextPage);
|
|
78
|
+
fetchPublicPrompts(nextPage, false);
|
|
79
|
+
}
|
|
80
|
+
}, { threshold: 0.1 });
|
|
81
|
+
const target = document.getElementById("load-more-trigger");
|
|
82
|
+
if (target) {
|
|
83
|
+
observer.observe(target);
|
|
84
|
+
}
|
|
85
|
+
return () => {
|
|
86
|
+
if (target) {
|
|
87
|
+
observer.unobserve(target);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}, [hasMore, loading, loadingMore, page, fetchPublicPrompts]);
|
|
91
|
+
const handleCopy = async (prompt) => {
|
|
92
|
+
try {
|
|
93
|
+
await navigator.clipboard.writeText(prompt.content);
|
|
94
|
+
setCopiedId(prompt.id);
|
|
95
|
+
setTimeout(() => setCopiedId(null), 2000);
|
|
96
|
+
// Increment picked stat with IP tracking
|
|
97
|
+
await fetch(`/api/ai/public/prompts/${prompt.id}/stats`, {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: { "Content-Type": "application/json" },
|
|
100
|
+
body: JSON.stringify({ stat_type: "picked" }),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error("Failed to copy:", error);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
return (_jsxs("div", { className: "container mx-auto px-4 md:px-0 mt-24 space-y-6", children: [_jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [_jsxs("h1", { className: "text-4xl font-bold flex items-center justify-center gap-3 mb-4", children: [_jsx(Sparkles, { className: "w-10 h-10 text-primary" }), "AI Prompts Library"] }), _jsx("p", { className: "text-lg text-default-600 mb-2", children: "Discover and use high-quality AI prompts created by our community" }), _jsx("p", { className: "text-sm text-default-500", children: "Free to use \u2022 Copy with one click \u2022 SEO-optimized for discovery" })] }), _jsxs("div", { className: "flex flex-col md:flex-row gap-4", children: [_jsx(Input, { placeholder: "Search prompts...", value: search, onValueChange: setSearch, className: "flex-1" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: filter === "all" ? "solid" : "bordered", onPress: () => setFilter("all"), children: "All" }), _jsx(Button, { variant: filter === "text" ? "solid" : "bordered", onPress: () => setFilter("text"), startContent: _jsx(MessageSquare, { className: "w-4 h-4" }), children: "Text" }), _jsx(Button, { variant: filter === "image" ? "solid" : "bordered", onPress: () => setFilter("image"), startContent: _jsx(ImageIcon, { className: "w-4 h-4" }), children: "Image" })] })] }), loading && prompts.length === 0 ? (_jsx("div", { className: "flex justify-center py-12", children: _jsx(Spinner, { size: "lg" }) })) : prompts.length === 0 ? (_jsx(Card, { children: _jsx(CardBody, { className: "text-center py-12", children: _jsx("p", { className: "text-default-500", children: search
|
|
108
|
+
? "No prompts match your search"
|
|
109
|
+
: "No public prompts available yet" }) }) })) : (_jsx(_Fragment, { children: _jsx("div", { className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3", children: prompts.map((prompt) => (_jsxs(Card, { className: "hover:shadow-lg transition-shadow", children: [_jsx(CardHeader, { className: "flex justify-between items-start", children: _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 mb-1", children: [prompt.type === "text" ? (_jsx(MessageSquare, { className: "w-4 h-4 text-primary" })) : (_jsx(ImageIcon, { className: "w-4 h-4 text-secondary" })), _jsx("h3", { className: "font-semibold text-lg", children: prompt.title })] }), prompt.username && (_jsx(UserAvatar, { username: prompt.username, avatarUrl: prompt.avatar_url, size: "sm" })), _jsx("div", { className: "flex gap-2 mt-2 flex-wrap", children: prompt.tags?.slice(0, 3).map((tag) => (_jsx(Chip, { size: "sm", variant: "flat", children: tag }, tag))) })] }) }), _jsxs(CardBody, { children: [_jsx("p", { className: "text-sm text-default-600 line-clamp-3 mb-4", children: prompt.content }), _jsxs("div", { className: "flex items-center gap-3 text-xs text-default-500 mb-4", children: [_jsxs("span", { className: `flex items-center gap-1 ${prompt.views > 0 ? "text-primary" : ""}`, children: [_jsx(Eye, { className: "w-3 h-3" }), prompt.views || 0] }), _jsxs("span", { className: `flex items-center gap-1 ${prompt.used_count > 0 ? "text-secondary" : ""}`, children: [_jsx(MousePointerClick, { className: "w-3 h-3" }), prompt.used_count || 0] }), _jsxs("span", { className: `flex items-center gap-1 ${prompt.picked_count > 0 ? "text-warning" : ""}`, children: [_jsx(TrendingUp, { className: "w-3 h-3" }), prompt.picked_count || 0] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "bordered", as: AppLink, href: `/prompts/${prompt.slug || prompt.id}`, startContent: _jsx(ExternalLink, { className: "w-4 h-4" }), children: "View" }), _jsx(Button, { size: "sm", className: "flex-1", color: copiedId === prompt.id ? "success" : "primary", onPress: () => handleCopy(prompt), startContent: _jsx(Copy, { className: "w-4 h-4" }), children: copiedId === prompt.id ? "Copied!" : "Copy" })] })] })] }, prompt.id))) }) })), _jsx("div", { className: "mt-12 border-t border-default-200 pt-8", children: _jsxs("div", { className: "text-center max-w-2xl mx-auto", children: [_jsx("h2", { className: "text-2xl font-bold mb-4", children: "Create Your Own Prompts" }), _jsx("p", { className: "text-default-600 mb-6", children: "Sign in to save your favorite prompts and share them with the community" }), _jsxs("div", { id: "load-more-trigger", className: "h-20 flex justify-center items-center", children: [loadingMore && _jsx(Spinner, { size: "md" }), !hasMore && prompts.length > 0 && (_jsx("p", { className: "text-sm text-default-600 my-4", children: "No more prompts to load" }))] }), user ? (_jsx(Button, { as: AppLink, color: "primary", href: "/auth/ai/prompts", children: "Get Started" })) : (_jsx(Button, { as: AppLink, color: "primary", href: "/signin", children: "Get Started" }))] }) })] }));
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PurchaseTokensPage.d.ts","sourceRoot":"","sources":["../../../src/web/public/PurchaseTokensPage.tsx"],"names":[],"mappings":"AA4BA,wBAAgB,kBAAkB,4CAkUjC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { useLocalizedRouter, useModuleTranslation } from "@lastbrain/core";
|
|
5
|
+
import { Button, Card, CardBody, CardHeader, Spinner, Chip, addToast, } from "@lastbrain/ui";
|
|
6
|
+
import { Coins, ArrowRight, Check, Crown } from "lucide-react";
|
|
7
|
+
import { useAuth } from "@lastbrain/core";
|
|
8
|
+
export function PurchaseTokensPage() {
|
|
9
|
+
const t = useModuleTranslation("ai");
|
|
10
|
+
const router = useLocalizedRouter();
|
|
11
|
+
const { user } = useAuth();
|
|
12
|
+
const [tokenPacks, setTokenPacks] = useState([]);
|
|
13
|
+
const [loading, setLoading] = useState(true);
|
|
14
|
+
const [checkoutLoading, setCheckoutLoading] = useState(null);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
fetchTokenPacks();
|
|
17
|
+
}, [user]);
|
|
18
|
+
const fetchTokenPacks = async () => {
|
|
19
|
+
try {
|
|
20
|
+
setLoading(true);
|
|
21
|
+
const response = await fetch("/api/public/token-packs");
|
|
22
|
+
if (response.ok) {
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
setTokenPacks(data.data || []);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error("Error loading token packs:", error);
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
setLoading(false);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const handleBuyTokens = async (packId) => {
|
|
35
|
+
if (!user) {
|
|
36
|
+
router.push("/signin?redirect=/purchase-tokens");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
setCheckoutLoading(packId);
|
|
41
|
+
const response = await fetch("/api/ai/auth/token-checkout", {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { "Content-Type": "application/json" },
|
|
44
|
+
body: JSON.stringify({ pack_id: packId }),
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const error = await response.json();
|
|
48
|
+
throw new Error(error.error || t("tokens.error.checkout") || "Erreur lors du checkout");
|
|
49
|
+
}
|
|
50
|
+
const data = await response.json();
|
|
51
|
+
if (data.checkout_url) {
|
|
52
|
+
window.location.href = data.checkout_url;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
addToast({
|
|
57
|
+
color: "danger",
|
|
58
|
+
title: error.message ||
|
|
59
|
+
t("tokens.error.checkout") ||
|
|
60
|
+
"Erreur lors du checkout",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
setCheckoutLoading(null);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const formatPrice = (cents, currency) => {
|
|
68
|
+
return new Intl.NumberFormat("fr-FR", {
|
|
69
|
+
style: "currency",
|
|
70
|
+
currency,
|
|
71
|
+
}).format(cents / 100);
|
|
72
|
+
};
|
|
73
|
+
const formatTokensShort = (tokens) => {
|
|
74
|
+
if (tokens >= 1000 && tokens % 1000 === 0) {
|
|
75
|
+
const thousands = tokens / 1000;
|
|
76
|
+
return `${thousands}K`;
|
|
77
|
+
}
|
|
78
|
+
return tokens.toLocaleString();
|
|
79
|
+
};
|
|
80
|
+
const getPackIcon = (index) => {
|
|
81
|
+
const icons = [Coins, Crown];
|
|
82
|
+
const Icon = icons[Math.min(index, icons.length - 1)];
|
|
83
|
+
return Icon;
|
|
84
|
+
};
|
|
85
|
+
return (_jsx("div", { className: "container mx-auto py-12 px-4 mt-16", children: _jsxs("div", { className: "max-w-6xl mx-auto", children: [_jsxs("div", { className: "text-center mb-12", children: [_jsx(Coins, { size: 64, className: "mx-auto mb-4 text-primary" }), _jsx("h1", { className: "text-4xl font-bold mb-4", children: "Cr\u00E9dits IA" }), _jsx("p", { className: "text-xl text-default-800", children: "Achetez des cr\u00E9dits pour utiliser nos mod\u00E8les d'IA (texte & image)" })] }), _jsxs("div", { className: "grid md:grid-cols-2 gap-6 mb-12", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-2xl font-bold", children: t("tokens.info.what.title") }) }), _jsxs(CardBody, { children: [_jsx("p", { className: "text-default-800 mb-4", children: t("tokens.info.what.description") }), _jsxs("ul", { className: "space-y-2", children: [_jsxs("li", { className: "flex items-start gap-2", children: [_jsx(Check, { size: 20, className: "text-green-500 mt-1" }), _jsx("span", { children: t("tokens.info.what.point1") })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx(Check, { size: 20, className: "text-green-500 mt-1" }), _jsx("span", { children: t("tokens.info.what.point2") })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx(Check, { size: 20, className: "text-green-500 mt-1" }), _jsx("span", { children: t("tokens.info.what.point3") })] })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-2xl font-bold", children: t("tokens.info.how.title") }) }), _jsx(CardBody, { children: _jsxs("ol", { className: "space-y-3", children: [_jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "flex-shrink-0 w-6 h-6 bg-primary text-white rounded-full flex items-center justify-center text-sm font-bold", children: "1" }), _jsx("span", { children: t("tokens.info.how.step1") })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "flex-shrink-0 w-6 h-6 bg-primary text-white rounded-full flex items-center justify-center text-sm font-bold", children: "2" }), _jsx("span", { children: t("tokens.info.how.step2") })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "flex-shrink-0 w-6 h-6 bg-primary text-white rounded-full flex items-center justify-center text-sm font-bold", children: "3" }), _jsx("span", { children: t("tokens.info.how.step3") })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "flex-shrink-0 w-6 h-6 bg-primary text-white rounded-full flex items-center justify-center text-sm font-bold", children: "4" }), _jsx("span", { children: t("tokens.info.how.step4") })] })] }) })] })] }), _jsxs("div", { className: "mb-12", children: [_jsx("h2", { className: "text-3xl font-bold text-center mb-8", children: t("tokens.packs.title") }), loading ? (_jsx("div", { className: "flex justify-center py-12", children: _jsx(Spinner, { size: "lg" }) })) : tokenPacks.length === 0 ? (_jsx("div", { className: "text-center py-12", children: _jsx("p", { className: "text-default-800", children: t("tokens.packs.noPacks") }) })) : (_jsx("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-6", children: tokenPacks
|
|
86
|
+
.sort((a, b) => a.sort_order - b.sort_order)
|
|
87
|
+
.map((pack, index) => {
|
|
88
|
+
const Icon = getPackIcon(index);
|
|
89
|
+
const isPopular = index === 1; // Middle pack is popular
|
|
90
|
+
return (_jsxs(Card, { className: `relative ${isPopular ? "border-2 border-primary shadow-lg scale-105" : ""}`, children: [isPopular && (_jsx("div", { className: "absolute top-4 right-4", children: _jsx(Chip, { color: "primary", variant: "solid", size: "sm", children: t("tokens.packs.popular") }) })), _jsxs(CardBody, { className: "p-8 text-center", children: [_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: `w-auto p-3 rounded-full mb-4 ${isPopular ? "bg-primary/10" : "bg-secondary-100/20"}`, children: _jsx(Icon, { size: 32, className: isPopular
|
|
91
|
+
? "text-primary"
|
|
92
|
+
: "text-secondary-800" }) }) }), _jsx("h3", { className: "text-2xl font-bold mb-2", children: pack.name }), pack.description && (_jsx("p", { className: "text-default-800 text-sm mb-4", children: pack.description })), _jsxs("div", { className: "mb-6", children: [_jsx("p", { className: "text-4xl font-bold text-primary mb-1", children: formatPrice(pack.price_cents, pack.currency) }), _jsx("p", { className: "text-sm text-default-700", children: "Cr\u00E9dit IA" })] }), _jsx(Button, { color: isPopular ? "primary" : "default", size: "lg", className: "w-full", onPress: () => handleBuyTokens(pack.id), isLoading: checkoutLoading === pack.id, isDisabled: !pack.stripe_price_id, endContent: _jsx(ArrowRight, { size: 20 }), children: !pack.stripe_price_id
|
|
93
|
+
? "Non disponible (Stripe non configuré)"
|
|
94
|
+
: user
|
|
95
|
+
? t("tokens.packs.buy")
|
|
96
|
+
: t("tokens.packs.loginToBuy") })] })] }, pack.id));
|
|
97
|
+
}) }))] })] }) }));
|
|
98
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserAvatar - Display user avatar and username
|
|
3
|
+
*/
|
|
4
|
+
interface UserAvatarProps {
|
|
5
|
+
username: string;
|
|
6
|
+
avatarUrl?: string;
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
showUsername?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function UserAvatar({ username, avatarUrl, size, showUsername, className, }: UserAvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=UserAvatar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserAvatar.d.ts","sourceRoot":"","sources":["../../../src/web/public/UserAvatar.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAOH,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,SAAS,EACT,IAAW,EACX,YAAmB,EACnB,SAAc,GACf,EAAE,eAAe,2CAyBjB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserAvatar - Display user avatar and username
|
|
3
|
+
*/
|
|
4
|
+
"use client";
|
|
5
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { AppLink, Avatar } from "@lastbrain/ui";
|
|
7
|
+
import { User2 } from "lucide-react";
|
|
8
|
+
export function UserAvatar({ username, avatarUrl, size = "sm", showUsername = true, className = "", }) {
|
|
9
|
+
return (_jsxs(AppLink, { href: `/user/${username}`, className: `flex items-center gap-2 hover:opacity-80 transition-opacity ${className}`, children: [_jsx(Avatar, { size: size, src: "/api/storage" + avatarUrl, name: username, showFallback: true, fallback: _jsx(User2, { className: "w-4 h-4" }), isBordered: true, classNames: {
|
|
10
|
+
base: "bg-primary/10",
|
|
11
|
+
fallback: "text-primary",
|
|
12
|
+
} }), showUsername && (_jsxs("span", { className: "text-sm text-primary hover:underline", children: ["@", username] }))] }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserDetailPageServer - Server component for user profile and prompts SEO
|
|
3
|
+
*/
|
|
4
|
+
import { Metadata } from "next";
|
|
5
|
+
interface UserDetailPageServerProps {
|
|
6
|
+
params: Promise<{
|
|
7
|
+
lang: string;
|
|
8
|
+
username: string;
|
|
9
|
+
}>;
|
|
10
|
+
localeConfig?: any;
|
|
11
|
+
}
|
|
12
|
+
export declare function generateMetadata({ params, localeConfig, }: UserDetailPageServerProps): Promise<Metadata>;
|
|
13
|
+
export default function UserDetailPageServer({ params, }: UserDetailPageServerProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
14
|
+
export { UserDetailPageServer };
|
|
15
|
+
//# sourceMappingURL=UserDetailPageServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserDetailPageServer.d.ts","sourceRoot":"","sources":["../../../src/web/public/UserDetailPageServer.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAKhC,UAAU,yBAAyB;IACjC,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CAAC,EACrC,MAAM,EACN,YAAY,GACb,EAAE,yBAAyB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAY/C;AAED,wBAA8B,oBAAoB,CAAC,EACjD,MAAM,GACP,EAAE,yBAAyB,oDAgB3B;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
3
|
+
import { UserPromptsPage } from "./UserPromptsPage";
|
|
4
|
+
import { notFound } from "next/navigation";
|
|
5
|
+
export async function generateMetadata({ params, localeConfig, }) {
|
|
6
|
+
const { username } = await params;
|
|
7
|
+
return {
|
|
8
|
+
title: `${username} - AI Prompts Library`,
|
|
9
|
+
description: `Browse all public AI prompts created by ${username}`,
|
|
10
|
+
openGraph: {
|
|
11
|
+
title: `${username} - AI Prompts`,
|
|
12
|
+
description: `Browse all public AI prompts created by ${username}`,
|
|
13
|
+
type: "profile",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export default async function UserDetailPageServer({ params, }) {
|
|
18
|
+
const { username } = await params;
|
|
19
|
+
const supabase = getSupabaseServiceClient();
|
|
20
|
+
// Verify username exists
|
|
21
|
+
const { data: profileData } = await supabase
|
|
22
|
+
.from("user_profil")
|
|
23
|
+
.select("username")
|
|
24
|
+
.ilike("username", username)
|
|
25
|
+
.single();
|
|
26
|
+
if (!profileData) {
|
|
27
|
+
notFound();
|
|
28
|
+
}
|
|
29
|
+
return _jsx(UserPromptsPage, { username: username });
|
|
30
|
+
}
|
|
31
|
+
export { UserDetailPageServer };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserPromptsPage - Display all public prompts from a specific user
|
|
3
|
+
*/
|
|
4
|
+
interface UserPromptsPageProps {
|
|
5
|
+
username: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function UserPromptsPage({ username }: UserPromptsPageProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=UserPromptsPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserPromptsPage.d.ts","sourceRoot":"","sources":["../../../src/web/public/UserPromptsPage.tsx"],"names":[],"mappings":"AAAA;;GAEG;AA4CH,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE,oBAAoB,2CAiQjE"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserPromptsPage - Display all public prompts from a specific user
|
|
3
|
+
*/
|
|
4
|
+
"use client";
|
|
5
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { useState, useEffect, useCallback } from "react";
|
|
7
|
+
import { Card, CardHeader, CardBody, Chip, Spinner, AppLink, Button, Avatar, Input, } from "@lastbrain/ui";
|
|
8
|
+
import { MessageSquare, Image as ImageIcon, Copy, Eye, MousePointerClick, TrendingUp, ExternalLink, User2, } from "lucide-react";
|
|
9
|
+
export function UserPromptsPage({ username }) {
|
|
10
|
+
const [prompts, setPrompts] = useState([]);
|
|
11
|
+
const [loading, setLoading] = useState(true);
|
|
12
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
13
|
+
const [hasMore, setHasMore] = useState(true);
|
|
14
|
+
const [search, setSearch] = useState("");
|
|
15
|
+
const [copiedId, setCopiedId] = useState(null);
|
|
16
|
+
const [userAvatar, setUserAvatar] = useState();
|
|
17
|
+
const [page, setPage] = useState(0);
|
|
18
|
+
const ITEMS_PER_PAGE = 12;
|
|
19
|
+
const fetchUserPrompts = useCallback(async (currentPage, reset) => {
|
|
20
|
+
try {
|
|
21
|
+
if (reset) {
|
|
22
|
+
setLoading(true);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
setLoadingMore(true);
|
|
26
|
+
}
|
|
27
|
+
const params = new URLSearchParams();
|
|
28
|
+
if (search) {
|
|
29
|
+
params.append("search", search);
|
|
30
|
+
}
|
|
31
|
+
params.append("limit", String(ITEMS_PER_PAGE));
|
|
32
|
+
params.append("offset", String(currentPage * ITEMS_PER_PAGE));
|
|
33
|
+
const response = await fetch(`/api/ai/public/prompts/user/${username}?${params}`);
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
if (response.ok) {
|
|
36
|
+
const newPrompts = data.prompts || [];
|
|
37
|
+
setPrompts((prev) => (reset ? newPrompts : [...prev, ...newPrompts]));
|
|
38
|
+
setHasMore(newPrompts.length === ITEMS_PER_PAGE);
|
|
39
|
+
// Get avatar from first prompt if available
|
|
40
|
+
if (reset && newPrompts.length > 0 && newPrompts[0].avatar_url) {
|
|
41
|
+
setUserAvatar(newPrompts[0].avatar_url);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error("Failed to fetch user prompts:", error);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
setLoading(false);
|
|
50
|
+
setLoadingMore(false);
|
|
51
|
+
}
|
|
52
|
+
}, [username, search]);
|
|
53
|
+
// Reset and fetch when username changes
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
setPrompts([]);
|
|
56
|
+
setPage(0);
|
|
57
|
+
setHasMore(true);
|
|
58
|
+
fetchUserPrompts(0, true);
|
|
59
|
+
}, [username, fetchUserPrompts]);
|
|
60
|
+
// Reset and fetch when search changes (with debounce)
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
const timeout = setTimeout(() => {
|
|
63
|
+
setPrompts([]);
|
|
64
|
+
setPage(0);
|
|
65
|
+
setHasMore(true);
|
|
66
|
+
fetchUserPrompts(0, true);
|
|
67
|
+
}, 300);
|
|
68
|
+
return () => clearTimeout(timeout);
|
|
69
|
+
}, [search, fetchUserPrompts]);
|
|
70
|
+
// Infinite scroll observer
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const observer = new IntersectionObserver((entries) => {
|
|
73
|
+
if (entries[0].isIntersecting && hasMore && !loading && !loadingMore) {
|
|
74
|
+
const nextPage = page + 1;
|
|
75
|
+
setPage(nextPage);
|
|
76
|
+
fetchUserPrompts(nextPage, false);
|
|
77
|
+
}
|
|
78
|
+
}, { threshold: 0.1 });
|
|
79
|
+
const target = document.getElementById("load-more-trigger-user");
|
|
80
|
+
if (target) {
|
|
81
|
+
observer.observe(target);
|
|
82
|
+
}
|
|
83
|
+
return () => {
|
|
84
|
+
if (target) {
|
|
85
|
+
observer.unobserve(target);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}, [hasMore, loading, loadingMore, page, fetchUserPrompts]);
|
|
89
|
+
const handleCopy = async (prompt) => {
|
|
90
|
+
try {
|
|
91
|
+
await navigator.clipboard.writeText(prompt.content);
|
|
92
|
+
setCopiedId(prompt.id);
|
|
93
|
+
setTimeout(() => setCopiedId(null), 2000);
|
|
94
|
+
// Increment picked stat
|
|
95
|
+
await fetch(`/api/ai/public/prompts/${prompt.id}/stats`, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers: { "Content-Type": "application/json" },
|
|
98
|
+
body: JSON.stringify({ stat_type: "picked" }),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error("Failed to copy:", error);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
if (loading && prompts.length === 0) {
|
|
106
|
+
return (_jsx("div", { className: "flex items-center justify-center min-h-screen", children: _jsx(Spinner, { size: "lg" }) }));
|
|
107
|
+
}
|
|
108
|
+
return (_jsxs("div", { className: "container mx-auto px-4 md:px-0 mt-24 space-y-6", children: [_jsxs("div", { className: "flex flex-col justify-center text-center max-w-3xl mx-auto", children: [_jsx(Avatar, { size: "lg", src: "/api/storage" + userAvatar, name: username, showFallback: true, isBordered: true, fallback: _jsx(User2, { className: "w-12 h-12" }), classNames: {
|
|
109
|
+
base: "bg-primary/10 mx-auto mb-4 w-24 h-24",
|
|
110
|
+
fallback: "text-primary",
|
|
111
|
+
} }), _jsxs("h1", { className: "text-4xl font-bold flex items-center justify-center gap-3 mb-4", children: [username, "'s Prompts"] }), _jsxs("p", { className: "text-lg text-default-600", children: [prompts.length, " public prompt", prompts.length !== 1 ? "s" : ""] })] }), _jsx("div", { className: "max-w-md mx-auto", children: _jsx(Input, { placeholder: "Search in prompts...", value: search, onValueChange: setSearch, className: "flex-1" }) }), prompts.length === 0 ? (_jsx(Card, { children: _jsx(CardBody, { children: _jsx("p", { className: "text-center text-default-500", children: "No public prompts yet" }) }) })) : (_jsx("div", { className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3", children: prompts.map((prompt) => (_jsxs(Card, { className: "hover:shadow-lg transition-shadow", children: [_jsx(CardHeader, { children: _jsxs("div", { className: "w-full", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [prompt.type === "text" ? (_jsx(MessageSquare, { className: "w-4 h-4 text-primary" })) : (_jsx(ImageIcon, { className: "w-4 h-4 text-secondary" })), _jsx("h3", { className: "font-semibold text-lg", children: prompt.title })] }), _jsx("div", { className: "flex gap-2 mt-2 flex-wrap", children: prompt.tags?.slice(0, 3).map((tag) => (_jsx(Chip, { size: "sm", variant: "flat", children: tag }, tag))) })] }) }), _jsxs(CardBody, { children: [_jsx("p", { className: "text-sm text-default-600 line-clamp-3 mb-4", children: prompt.content }), _jsxs("div", { className: "flex items-center gap-3 text-xs text-default-500 mb-4", children: [_jsxs("span", { className: `flex items-center gap-1 ${prompt.views > 0 ? "text-primary" : ""}`, children: [_jsx(Eye, { className: "w-3 h-3" }), prompt.views || 0] }), _jsxs("span", { className: `flex items-center gap-1 ${prompt.used_count > 0 ? "text-secondary" : ""}`, children: [_jsx(MousePointerClick, { className: "w-3 h-3" }), prompt.used_count || 0] }), _jsxs("span", { className: `flex items-center gap-1 ${prompt.picked_count > 0 ? "text-warning" : ""}`, children: [_jsx(TrendingUp, { className: "w-3 h-3" }), prompt.picked_count || 0] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "bordered", as: AppLink, href: `/prompts/${prompt.slug || prompt.id}`, startContent: _jsx(ExternalLink, { className: "w-4 h-4" }), children: "View" }), _jsx(Button, { size: "sm", className: "flex-1", color: copiedId === prompt.id ? "success" : "primary", onPress: () => handleCopy(prompt), startContent: _jsx(Copy, { className: "w-4 h-4" }), children: copiedId === prompt.id ? "Copied!" : "Copy" })] })] })] }, prompt.id))) })), prompts.length > 0 && (_jsxs("div", { id: "load-more-trigger-user", className: "h-20 flex justify-center items-center", children: [loadingMore && _jsx(Spinner, { size: "md" }), !hasMore && (_jsx("p", { className: "text-sm text-default-400", children: "No more prompts to load" }))] }))] }));
|
|
112
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserPromptsPageServer - Server component for user prompts SEO
|
|
3
|
+
*/
|
|
4
|
+
import { Metadata } from "next";
|
|
5
|
+
interface UserPromptsPageServerProps {
|
|
6
|
+
params: Promise<{
|
|
7
|
+
lang: string;
|
|
8
|
+
username: string;
|
|
9
|
+
}>;
|
|
10
|
+
localeConfig?: any;
|
|
11
|
+
}
|
|
12
|
+
export declare function generateMetadata({ params, localeConfig, }: UserPromptsPageServerProps): Promise<Metadata>;
|
|
13
|
+
export default function UserPromptsPageServer({ params, }: UserPromptsPageServerProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
14
|
+
export { UserPromptsPageServer };
|
|
15
|
+
//# sourceMappingURL=UserPromptsPageServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserPromptsPageServer.d.ts","sourceRoot":"","sources":["../../../src/web/public/UserPromptsPageServer.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAKhC,UAAU,0BAA0B;IAClC,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CAAC,EACrC,MAAM,EACN,YAAY,GACb,EAAE,0BAA0B,GAAG,OAAO,CAAC,QAAQ,CAAC,CAYhD;AAED,wBAA8B,qBAAqB,CAAC,EAClD,MAAM,GACP,EAAE,0BAA0B,oDAgB5B;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
3
|
+
import { UserPromptsPage } from "./UserPromptsPage";
|
|
4
|
+
import { notFound } from "next/navigation";
|
|
5
|
+
export async function generateMetadata({ params, localeConfig, }) {
|
|
6
|
+
const { username } = await params;
|
|
7
|
+
return {
|
|
8
|
+
title: `${username}'s AI Prompts - Prompt Library`,
|
|
9
|
+
description: `Browse all public AI prompts created by ${username}`,
|
|
10
|
+
openGraph: {
|
|
11
|
+
title: `${username}'s AI Prompts`,
|
|
12
|
+
description: `Browse all public AI prompts created by ${username}`,
|
|
13
|
+
type: "profile",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export default async function UserPromptsPageServer({ params, }) {
|
|
18
|
+
const { username } = await params;
|
|
19
|
+
const supabase = getSupabaseServiceClient();
|
|
20
|
+
// Verify username exists
|
|
21
|
+
const { data: profileData } = await supabase
|
|
22
|
+
.from("user_profil")
|
|
23
|
+
.select("username")
|
|
24
|
+
.ilike("username", username)
|
|
25
|
+
.single();
|
|
26
|
+
if (!profileData) {
|
|
27
|
+
notFound();
|
|
28
|
+
}
|
|
29
|
+
return _jsx(UserPromptsPage, { username: username });
|
|
30
|
+
}
|
|
31
|
+
export { UserPromptsPageServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lastbrain/module-ai",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.30",
|
|
4
4
|
"description": "Module de génération IA (texte et images) avec gestion de tokens pour LastBrain",
|
|
5
5
|
"private": false,
|
|
6
6
|
"release": {
|
|
@@ -13,10 +13,9 @@
|
|
|
13
13
|
"lastbrain",
|
|
14
14
|
"module",
|
|
15
15
|
"ai",
|
|
16
|
-
"
|
|
16
|
+
"vercel-ai",
|
|
17
|
+
"vercel-ai-gateway",
|
|
17
18
|
"generation",
|
|
18
|
-
"dalle",
|
|
19
|
-
"gpt",
|
|
20
19
|
"tokens"
|
|
21
20
|
],
|
|
22
21
|
"repository": {
|
|
@@ -32,23 +31,29 @@
|
|
|
32
31
|
"supabase"
|
|
33
32
|
],
|
|
34
33
|
"dependencies": {
|
|
34
|
+
"@ai-sdk/openai": "^3.0.21",
|
|
35
35
|
"@heroui/react": "^2.4.23",
|
|
36
36
|
"@heroui/system": "^2.4.23",
|
|
37
37
|
"@heroui/theme": "^2.4.23",
|
|
38
|
-
"@lastbrain-labs/module-core-payment-pro": "^2.0.
|
|
39
|
-
"@lastbrain/core": "^2.0.
|
|
40
|
-
"@lastbrain/ui": "^2.0.
|
|
38
|
+
"@lastbrain-labs/module-core-payment-pro": "^2.0.30",
|
|
39
|
+
"@lastbrain/core": "^2.0.31",
|
|
40
|
+
"@lastbrain/ui": "^2.0.31",
|
|
41
|
+
"ai": "^6.0.58",
|
|
42
|
+
"build": "^0.1.4",
|
|
43
|
+
"canvas-confetti": "^1.9.4",
|
|
41
44
|
"lucide-react": "^0.554.0",
|
|
42
|
-
"next": "
|
|
43
|
-
"
|
|
45
|
+
"next": "16.0.10",
|
|
46
|
+
"pnpm": "^10.28.2",
|
|
44
47
|
"react": "^19.2.1",
|
|
45
48
|
"react-dom": "^19.2.1",
|
|
49
|
+
"recharts": "^2.12.7",
|
|
46
50
|
"zod": "^3.23.8"
|
|
47
51
|
},
|
|
48
52
|
"peerDependencies": {
|
|
49
53
|
"next": ">=15.0.0"
|
|
50
54
|
},
|
|
51
55
|
"devDependencies": {
|
|
56
|
+
"@types/canvas-confetti": "^1.9.0",
|
|
52
57
|
"typescript": "^5.4.0"
|
|
53
58
|
},
|
|
54
59
|
"exports": {
|
|
@@ -67,6 +72,10 @@
|
|
|
67
72
|
"./api/*": {
|
|
68
73
|
"types": "./dist/api/*.d.ts",
|
|
69
74
|
"default": "./dist/api/*.js"
|
|
75
|
+
},
|
|
76
|
+
"./sitemap/*": {
|
|
77
|
+
"types": "./dist/sitemap/*.d.ts",
|
|
78
|
+
"default": "./dist/sitemap/*.js"
|
|
70
79
|
}
|
|
71
80
|
},
|
|
72
81
|
"sideEffects": false,
|
|
@@ -57,6 +57,13 @@ BEGIN
|
|
|
57
57
|
RETURN NEW;
|
|
58
58
|
END IF;
|
|
59
59
|
|
|
60
|
+
-- Si le payload contient un flag d'autorisation d'overdraft dans meta,
|
|
61
|
+
-- permettre l'insertion (ex: meta = '{"allowOverdraft": true}')
|
|
62
|
+
IF COALESCE((NEW.meta->>'allowOverdraft')::boolean, false)
|
|
63
|
+
OR COALESCE((NEW.meta->>'allow_overdraft')::boolean, false) THEN
|
|
64
|
+
RETURN NEW;
|
|
65
|
+
END IF;
|
|
66
|
+
|
|
60
67
|
-- Calculer le solde actuel
|
|
61
68
|
SELECT COALESCE(SUM(amount), 0)
|
|
62
69
|
INTO current_balance
|