inference-playground / src /lib /components /billing /BillingManagementModal.svelte
enzostvs's picture
enzostvs HF Staff
fake loading
17c8ce5
<script lang="ts">
import * as Dialog from '$lib/components/ui/dialog/index.js';
import * as Select from '$lib/components/ui/select/index.js';
import { authState } from '$lib/state/auth.svelte';
import { Save } from '@lucide/svelte';
import Button from '../ui/button/button.svelte';
import Spinner from '../loading/Spinner.svelte';
let { open = $bindable(false) }: { open: boolean } = $props();
const planColors = {
team: 'bg-purple-500/10 text-purple-600',
enterprise: 'bg-green-500/10 text-green-600'
};
let billingOption = $derived(authState.user?.billingOption || 'personal');
let selectedOrg = $derived(authState.user?.orgs?.find((org) => org.name === billingOption));
let loading = $state.raw<boolean>(false);
async function saveSettings() {
if (loading) return;
loading = true;
await new Promise((resolve) => setTimeout(resolve, 600));
loading = false;
if (billingOption === 'personal') {
localStorage.removeItem('hf-playground-billing-option');
} else {
localStorage.setItem('hf-playground-billing-option', selectedOrg?.name ?? '');
}
authState.user!.billingOption = billingOption;
open = false;
}
function resetSettings() {
billingOption = authState.user?.billingOption || 'personal';
}
</script>
<Dialog.Root
bind:open
onOpenChange={(value) => {
if (!value) {
resetSettings();
}
}}
>
<Dialog.Content class="max-w-sm! p-0!">
<Dialog.Header class="mb-0 rounded-none border-b p-5">
<Dialog.Title>Billing Settings</Dialog.Title>
<Dialog.Description>Manage your billing information.</Dialog.Description>
</Dialog.Header>
<Dialog.Description class="mt-0 px-5 pb-5">
<div>
<p class="mb-1.5 font-medium text-primary">
Billing option <span class="font-normal text-muted-foreground"
>(personal or organization)</span
>
</p>
<Select.Root type="single" bind:value={billingOption}>
<Select.Trigger
class="flex w-full items-center justify-start gap-2 py-5!"
disabled={loading}
>
{#if billingOption === 'personal'}
<img
src={authState.user?.avatarUrl}
alt={authState.user?.name}
class="size-5 rounded"
/>
{authState.user?.name}
<span class="text-xs text-muted-foreground">(Personal Account)</span>
{:else}
<img
src={selectedOrg?.avatarUrl}
alt={selectedOrg?.fullname || selectedOrg?.name}
class="size-5 rounded"
/>
{selectedOrg?.fullname || selectedOrg?.name}
{#if selectedOrg?.plan}
<span
class="rounded px-1 py-0.5 text-[10px] font-semibold {planColors[
selectedOrg?.plan as keyof typeof planColors
]} uppercase"
>
{selectedOrg?.plan}
</span>
{/if}
{/if}
</Select.Trigger>
<Select.Content class="space-y-3!">
<Select.Item value="personal" class="p-2.5!">
<div class="flex items-center gap-2">
<img
src={authState.user?.avatarUrl}
alt={authState.user?.name}
class="size-5 rounded"
/>
{authState.user?.name}
<span class="text-xs text-muted-foreground">(Personal Account)</span>
</div>
</Select.Item>
{#each authState.user?.orgs as org}
<Select.Item value={org.name} class="p-2.5!">
<div class="flex items-center gap-2 text-sm">
<img src={org.avatarUrl} alt={org.fullname || org.name} class="size-5 rounded" />
{org.fullname || org.name}
{#if org.plan}
<span
class="rounded px-1 py-0.5 text-[10px] font-semibold {planColors[
org.plan as keyof typeof planColors
]} uppercase"
>
{org.plan}
</span>
{/if}
</div>
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<div class="mt-5 flex items-center justify-end gap-3">
<Button variant="outline" onclick={resetSettings}>Reset</Button>
<Button class="flex-1" onclick={saveSettings}>
{#if loading}
<Spinner className="text-lg" />
Saving...
{:else}
<Save />
Save settings
{/if}
</Button>
</div>
</Dialog.Description>
</Dialog.Content>
</Dialog.Root>