Split the large dashboard detail page into smaller, focused components: DashboardHeader, DashboardGitManager, DashboardLinkedResources, and DashboardTaskHistory to improve maintainability.
105 lines
4.2 KiB
Svelte
105 lines
4.2 KiB
Svelte
<!-- [DEF:DashboardTaskHistory:Component] -->
|
|
<script>
|
|
/**
|
|
* @COMPLEXITY: 2
|
|
* @PURPOSE: Block for recent tasks table.
|
|
* @LAYER: UI
|
|
*/
|
|
import { t } from "$lib/i18n";
|
|
|
|
let {
|
|
isTaskHistoryLoading,
|
|
taskHistoryError,
|
|
taskHistory,
|
|
loadTaskHistory,
|
|
getValidationStatus,
|
|
toTaskTypeLabel,
|
|
getTaskStatusClasses,
|
|
getValidationStatusClasses,
|
|
formatDate,
|
|
openLlmReport
|
|
} = $props();
|
|
</script>
|
|
|
|
<div class="rounded-xl border border-slate-200 bg-white p-4">
|
|
<div class="mb-3 flex items-center justify-between">
|
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-slate-500">
|
|
{$t.tasks?.recent || "Recent tasks"}
|
|
</h2>
|
|
<button
|
|
class="rounded-md border border-slate-300 px-2 py-1 text-xs text-slate-700 hover:bg-slate-50"
|
|
onclick={loadTaskHistory}
|
|
disabled={isTaskHistoryLoading}
|
|
>
|
|
{$t.common?.refresh || "Refresh"}
|
|
</button>
|
|
</div>
|
|
{#if isTaskHistoryLoading}
|
|
<div class="space-y-2">
|
|
{#each Array(4) as _}
|
|
<div class="h-10 animate-pulse rounded bg-slate-100"></div>
|
|
{/each}
|
|
</div>
|
|
{:else if taskHistoryError}
|
|
<div class="rounded-lg border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700">
|
|
{taskHistoryError}
|
|
</div>
|
|
{:else if taskHistory.length === 0}
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-6 text-center text-sm text-slate-500">
|
|
{$t.tasks?.select_task || "No backup/LLM tasks yet"}
|
|
</div>
|
|
{:else}
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
|
<thead class="bg-slate-50">
|
|
<tr>
|
|
<th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.common?.type || "Type"}</th>
|
|
<th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.common?.status || "Status"}</th>
|
|
<th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.tasks?.result || "Check"}</th>
|
|
<th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.common?.started || "Started"}</th>
|
|
<th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.common?.finished || "Finished"}</th>
|
|
<th class="px-3 py-2 text-left font-semibold text-slate-600">{$t.common?.actions || "Actions"}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-100">
|
|
{#each taskHistory as task}
|
|
{@const validation = getValidationStatus(task)}
|
|
<tr>
|
|
<td class="px-3 py-2 text-slate-800">{toTaskTypeLabel(task.plugin_id)}</td>
|
|
<td class="px-3 py-2">
|
|
<span class={`rounded-full px-2 py-1 text-xs font-semibold uppercase ${getTaskStatusClasses(task.status)}`}>
|
|
{task.status}
|
|
</span>
|
|
</td>
|
|
<td class="px-3 py-2">
|
|
<span class={`inline-flex items-center gap-1 rounded-full border px-2 py-1 text-xs font-semibold uppercase ${getValidationStatusClasses(validation.level)}`}>
|
|
{#if validation.icon}
|
|
<span class="inline-flex min-w-[18px] items-center justify-center rounded-full bg-white/70 px-1 text-[10px] font-bold">
|
|
{validation.icon}
|
|
</span>
|
|
{/if}
|
|
{validation.label}
|
|
</span>
|
|
</td>
|
|
<td class="px-3 py-2 text-slate-700">{formatDate(task.started_at)}</td>
|
|
<td class="px-3 py-2 text-slate-700">{formatDate(task.finished_at)}</td>
|
|
<td class="px-3 py-2">
|
|
<div class="flex flex-wrap items-center gap-1">
|
|
{#if task.plugin_id === "llm_dashboard_validation"}
|
|
<button
|
|
class="inline-flex items-center gap-1 rounded-md border border-indigo-300 bg-indigo-50 px-2 py-1 text-xs text-indigo-700 hover:bg-indigo-100"
|
|
onclick={() => openLlmReport(task.id)}
|
|
>
|
|
{$t.tasks?.open_llm_report || "LLM report"}
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
<!-- [/DEF:DashboardTaskHistory:Component] --> |