chore: ai, public course and other ui improvements

This commit is contained in:
Rotimi Best
2026-05-09 06:05:03 +01:00
parent 5717dd8004
commit c31ad63955
318 changed files with 20668 additions and 4788 deletions
@@ -0,0 +1,40 @@
<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/state';
import BuyTokensCard from '$features/settings/components/ai-credits/buy-tokens-card.svelte';
import TeamLeaderboard from '$features/settings/components/ai-credits/team-leaderboard.svelte';
import UsageChart from '$features/settings/components/ai-credits/usage-chart.svelte';
import UsageSummaryCards from '$features/settings/components/ai-credits/usage-summary-cards.svelte';
import { aiCreditsApi } from '$features/settings/api/ai-credits.svelte';
import { snackbar } from '$features/ui/snackbar/store';
import { currentOrg } from '$lib/utils/store/org';
import { t } from '$lib/utils/functions/translations';
let lastFetchedOrgId = $state<string | null>(null);
$effect(() => {
const orgId = $currentOrg.id;
if (!orgId || orgId === lastFetchedOrgId) {
return;
}
lastFetchedOrgId = orgId;
void aiCreditsApi.fetchAll();
});
onMount(() => {
if (page.url.searchParams.get('tokens') === 'success') {
snackbar.success('snackbar.ai_credits.purchase_success');
void aiCreditsApi.fetchAll();
}
});
</script>
<div class="ui:mx-auto flex w-full max-w-5xl flex-col gap-8 px-2 pb-12">
<UsageSummaryCards usage={aiCreditsApi.usage} purchased={aiCreditsApi.purchased} />
<p class="ui:text-muted-foreground -mt-4 text-sm">{$t('settings.ai_credits.cost_note')}</p>
<UsageChart usage={aiCreditsApi.usage} />
<TeamLeaderboard leaderboard={aiCreditsApi.leaderboard} />
<BuyTokensCard onBeforeRedirect={() => aiCreditsApi.fetchAll()} />
</div>
@@ -3,53 +3,14 @@
import { snackbar } from '$features/ui/snackbar/store';
import { currentOrgPlan, isFreePlan, currentOrg } from '$lib/utils/store/org';
import HourglassIcon from '@lucide/svelte/icons/hourglass';
import SparklesIcon from '@lucide/svelte/icons/sparkles';
import { openUpgradeModal } from '$lib/utils/functions/org';
import ReceiptIcon from '@lucide/svelte/icons/receipt';
import { Button } from '@cio/ui/base/button';
import * as Item from '@cio/ui/base/item';
import { ExternalLinkIcon, HoverableItem } from '@cio/ui/custom/moving-icons';
const PUBLIC_API_URL = import.meta.env.VITE_API_URL || '';
let isRedirecting = $state(false);
interface AiUsageData {
used: number;
allowance: number;
creditBalance: number;
remaining: number;
}
let aiUsage: AiUsageData | null = $state(null);
let aiUsageLoaded = $state(false);
$effect(() => {
if (!aiUsageLoaded && $currentOrg.id) {
aiUsageLoaded = true;
fetchAiUsage();
}
});
async function fetchAiUsage() {
try {
const response = await fetch(`${PUBLIC_API_URL}/agent/usage`, {
headers: { 'Cio-org-id': $currentOrg.id || '' },
credentials: 'include'
});
if (response.ok) {
const result = await response.json();
if (result.success) {
aiUsage = result.data;
}
}
} catch {
// Silently fail
}
}
async function onOpenBilling() {
console.log({
plan: $currentOrgPlan
@@ -119,39 +80,4 @@
</Item.Actions>
</Item.Root>
{/if}
{#if aiUsage}
<div class="mt-6">
<p class="text-md mb-3 font-medium">{$t('settings.billing.ai_credits')}</p>
<Item.Root variant="outline">
<Item.Media variant="icon">
<SparklesIcon />
</Item.Media>
<Item.Content>
<Item.Title>
{aiUsage.used.toLocaleString()} / {aiUsage.allowance.toLocaleString()}
{$t('settings.billing.ai_tokens_used')}
</Item.Title>
<Item.Description>
{aiUsage.creditBalance.toLocaleString()}
{$t('settings.billing.ai_bonus_credits')}
</Item.Description>
{@const usagePercent =
aiUsage.allowance > 0 ? Math.min(100, Math.round((aiUsage.used / aiUsage.allowance) * 100)) : 0}
<div class="ui:bg-muted mt-2 h-1.5 w-full rounded-full">
<div
class="h-1.5 rounded-full transition-all {usagePercent > 90
? 'bg-red-500'
: usagePercent > 70
? 'bg-amber-500'
: 'ui:bg-primary'}"
style="width: {usagePercent}%"
></div>
</div>
</Item.Content>
</Item.Root>
</div>
{/if}
</div>
@@ -6,6 +6,7 @@ export { default as OrgPage } from './org.svelte';
export { default as LandingpagePage } from './landingpage.svelte';
export { default as LandingpageEditor } from './landingpage-editor.svelte';
export { default as BillingPage } from './billing.svelte';
export { default as AiCreditsPage } from './ai-credits.svelte';
export { default as IntegrationsPage } from './integrations.svelte';
export { default as AuthPage } from './auth.svelte';
// Keep old exports for backward compatibility