File size: 4,054 Bytes
cfb0fa4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | <script lang="ts">
import Modal from '$lib/components/common/Modal.svelte';
import { getContext } from 'svelte';
import { getModelHistory } from '$lib/apis/evaluations';
import ModelActivityChart from './ModelActivityChart.svelte';
import XMark from '$lib/components/icons/XMark.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
export let show = false;
export let model = null;
export let onClose: () => void = () => {};
const i18n = getContext('i18n');
type TimeRange = '30d' | '1y' | 'all';
const TIME_RANGES: { key: TimeRange; label: string; days: number }[] = [
{ key: '30d', label: '30D', days: 30 },
{ key: '1y', label: '1Y', days: 365 },
{ key: 'all', label: 'All', days: 0 } // 0 = all time, starts from first feedback
];
let selectedRange: TimeRange = '30d';
let history: Array<{ date: string; won: number; lost: number }> = [];
let loadingHistory = false;
const close = () => {
show = false;
onClose();
};
const loadHistory = async (days: number) => {
if (!model?.id) return;
loadingHistory = true;
try {
const result = await getModelHistory(localStorage.token, model.id, days);
history = result?.history ?? [];
} catch (err) {
console.error('Failed to load model history:', err);
history = [];
}
loadingHistory = false;
};
const selectRange = (range: TimeRange) => {
selectedRange = range;
const config = TIME_RANGES.find((r) => r.key === range);
if (config) {
loadHistory(config.days);
}
};
// Load history when model changes and modal is shown
$: if (show && model?.id) {
selectRange(selectedRange);
}
// Use top_tags from backend response (already computed)
$: topTags = model?.top_tags ?? [];
</script>
<Modal size="md" bind:show>
{#if model}
<div class="flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<Tooltip content={`${model.name} (${model.id})`} placement="top-start">
<div class="text-lg font-medium self-center line-clamp-1">
{model.name}
</div>
</Tooltip>
<button class="self-center" on:click={close} aria-label="Close">
<XMark className={'size-5'} />
</button>
</div>
<div class="px-5 pb-4 dark:text-gray-200">
<!-- Activity Chart -->
<div class="mb-4">
<div class="flex items-center justify-between mb-2">
<div class="text-xs text-gray-500 font-medium uppercase tracking-wide">
{$i18n.t('Activity')}
</div>
<div
class="inline-flex rounded-full bg-gray-100/80 p-0.5 dark:bg-gray-800/80 backdrop-blur-sm"
>
{#each TIME_RANGES as range}
<button
type="button"
class="rounded-full transition-all duration-200 px-2.5 py-0.5 text-xs font-medium {selectedRange ===
range.key
? 'bg-white text-gray-900 shadow-sm dark:bg-gray-700 dark:text-white'
: 'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'}"
on:click={() => selectRange(range.key)}
>
{range.label}
</button>
{/each}
</div>
</div>
<ModelActivityChart
{history}
loading={loadingHistory}
aggregateWeekly={selectedRange === '1y' || selectedRange === 'all'}
/>
</div>
<div class="mb-4">
<div class="text-xs text-gray-500 mb-2 font-medium uppercase tracking-wide">
{$i18n.t('Tags')}
</div>
{#if topTags.length}
<div class="flex flex-wrap gap-1 -mx-1">
{#each topTags as tagInfo}
<span class="px-2 py-0.5 rounded-full bg-gray-100 dark:bg-gray-850 text-xs">
{tagInfo.tag} <span class="text-gray-500 font-medium">{tagInfo.count}</span>
</span>
{/each}
</div>
{:else}
<span class="text-gray-500 text-sm">-</span>
{/if}
</div>
<div class="flex justify-end pt-2">
<button
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="button"
on:click={close}
>
{$i18n.t('Close')}
</button>
</div>
</div>
{/if}
</Modal>
|