feat: complete AI analytics experience

This commit is contained in:
2026-08-22 15:47:26 +08:00
parent 2bea50a291
commit 43f65a8259
35 changed files with 3497 additions and 574 deletions

View File

@@ -0,0 +1,57 @@
<script lang="ts">
import type { components } from '$lib/api/schema';
import { formatDateOnly } from '$lib/format';
type UpcomingEvent = components['schemas']['GrowthUpcomingEvent'];
type EventRecommendation = components['schemas']['GrowthEventRecommendation'];
let {
events = [],
recommendations = [],
now = new Date(),
}: {
events?: UpcomingEvent[];
recommendations?: EventRecommendation[];
now?: Date;
} = $props();
const visibleEvents = $derived.by(() => {
const byEvidenceId = new Map(events.map((event) => [event.evidenceId, event]));
const ranked = recommendations
.map((recommendation) => ({
...byEvidenceId.get(recommendation.evidenceId),
relevanceReason: recommendation.relevanceReason,
}))
.filter(
(event): event is UpcomingEvent & { relevanceReason: string } =>
Boolean(event.evidenceId) && new Date(event.date) >= now,
);
if (ranked.length) return ranked.slice(0, 3);
return events
.filter((event) => new Date(event.date) >= now)
.toSorted((a, b) => Date.parse(a.date) - Date.parse(b.date))
.slice(0, 3)
.map((event) => ({
...event,
relevanceReason: 'این مناسبت در تقویم بررسی‌شده به‌عنوان رویدادی مرتبط با کسب‌وکار ثبت شده است.',
}));
});
</script>
{#if visibleEvents.length}
<div class="grid gap-3 sm:grid-cols-3" role="list" aria-label="رویدادهای پیش رو">
{#each visibleEvents as event (event.evidenceId)}
<article class="bg-muted/35 rounded-lg border p-4 text-right" role="listitem">
<p class="text-muted-foreground text-xs">رویداد پیش رو</p>
<h3 class="mt-2 text-sm font-bold">{event.eventTitleFa}</h3>
<p class="text-muted-foreground mt-2 text-xs">{formatDateOnly(event.date)}</p>
<p class="text-muted-foreground mt-3 text-xs leading-5">{event.relevanceReason}</p>
</article>
{/each}
</div>
{:else}
<div class="py-10 text-center" role="status">
<p class="font-bold">رویداد مرتبطی در تقویم بررسی‌شده پیدا نشد</p>
<p class="text-muted-foreground mt-1 text-sm">با به‌روزرسانی تقویم، این بخش دوباره بررسی می‌شود.</p>
</div>
{/if}