css refactor
This commit is contained in:
@@ -23,60 +23,47 @@
|
||||
import { t } from "../lib/i18n";
|
||||
import TaskLogPanel from "./tasks/TaskLogPanel.svelte";
|
||||
|
||||
export let show = false;
|
||||
export let inline = false;
|
||||
export let taskId = null;
|
||||
export let taskStatus = null;
|
||||
export let realTimeLogs = [];
|
||||
let {
|
||||
show = $bindable(false),
|
||||
inline = false,
|
||||
taskId = null,
|
||||
taskStatus = null,
|
||||
realTimeLogs = [],
|
||||
} = $props();
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let logs = [];
|
||||
let loading = false;
|
||||
let error = "";
|
||||
let logs = $state([]);
|
||||
let loading = $state(false);
|
||||
let error = $state("");
|
||||
let interval;
|
||||
let autoScroll = true;
|
||||
let autoScroll = $state(true);
|
||||
|
||||
$: shouldShow = inline || show;
|
||||
let shouldShow = $derived(inline || show);
|
||||
|
||||
// [DEF:handleRealTimeLogs:Action]
|
||||
/** @PURPOSE Append real-time logs as they arrive from WebSocket, preventing duplicates */
|
||||
$: if (realTimeLogs && realTimeLogs.length > 0) {
|
||||
const lastLog = realTimeLogs[realTimeLogs.length - 1];
|
||||
const exists = logs.some(
|
||||
(l) =>
|
||||
l.timestamp === lastLog.timestamp &&
|
||||
l.message === lastLog.message,
|
||||
);
|
||||
if (!exists) {
|
||||
logs = [...logs, lastLog];
|
||||
console.log(
|
||||
`[TaskLogViewer][Action] Appended real-time log, total=${logs.length}`,
|
||||
$effect(() => {
|
||||
if (realTimeLogs && realTimeLogs.length > 0) {
|
||||
const lastLog = realTimeLogs[realTimeLogs.length - 1];
|
||||
const exists = logs.some(
|
||||
(l) =>
|
||||
l.timestamp === lastLog.timestamp &&
|
||||
l.message === lastLog.message,
|
||||
);
|
||||
if (!exists) {
|
||||
logs = [...logs, lastLog];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// [/DEF:handleRealTimeLogs:Action]
|
||||
|
||||
// [DEF:fetchLogs:Function]
|
||||
/**
|
||||
* @PURPOSE Fetches logs for the current task from API (polling fallback).
|
||||
* @PRE taskId must be set.
|
||||
* @POST logs array is updated with data from taskService.
|
||||
* @SIDE_EFFECT Updates logs, loading, and error state.
|
||||
*/
|
||||
async function fetchLogs() {
|
||||
if (!taskId) return;
|
||||
console.log(`[TaskLogViewer][Action] Fetching logs for task=${taskId}`);
|
||||
try {
|
||||
logs = await getTaskLogs(taskId);
|
||||
console.log(
|
||||
`[TaskLogViewer][Coherence:OK] Logs fetched count=${logs.length}`,
|
||||
);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
console.error(
|
||||
`[TaskLogViewer][Coherence:Failed] Error: ${e.message}`,
|
||||
);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
@@ -85,34 +72,31 @@
|
||||
|
||||
function handleFilterChange(event) {
|
||||
const { source, level } = event.detail;
|
||||
console.log(
|
||||
`[TaskLogViewer][Action] Filter changed: source=${source}, level=${level}`,
|
||||
);
|
||||
}
|
||||
|
||||
function handleRefresh() {
|
||||
console.log(`[TaskLogViewer][Action] Manual refresh`);
|
||||
fetchLogs();
|
||||
}
|
||||
|
||||
// React to changes in show/taskId/taskStatus
|
||||
$: if (shouldShow && taskId) {
|
||||
if (interval) clearInterval(interval);
|
||||
logs = [];
|
||||
loading = true;
|
||||
error = "";
|
||||
fetchLogs();
|
||||
$effect(() => {
|
||||
if (shouldShow && taskId) {
|
||||
if (interval) clearInterval(interval);
|
||||
logs = [];
|
||||
loading = true;
|
||||
error = "";
|
||||
fetchLogs();
|
||||
|
||||
if (
|
||||
taskStatus === "RUNNING" ||
|
||||
taskStatus === "AWAITING_INPUT" ||
|
||||
taskStatus === "AWAITING_MAPPING"
|
||||
) {
|
||||
interval = setInterval(fetchLogs, 5000);
|
||||
if (
|
||||
taskStatus === "RUNNING" ||
|
||||
taskStatus === "AWAITING_INPUT" ||
|
||||
taskStatus === "AWAITING_MAPPING"
|
||||
) {
|
||||
interval = setInterval(fetchLogs, 5000);
|
||||
}
|
||||
} else {
|
||||
if (interval) clearInterval(interval);
|
||||
}
|
||||
} else {
|
||||
if (interval) clearInterval(interval);
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (interval) clearInterval(interval);
|
||||
@@ -121,18 +105,25 @@
|
||||
|
||||
{#if shouldShow}
|
||||
{#if inline}
|
||||
<div class="log-viewer-inline">
|
||||
<div class="flex flex-col h-full w-full">
|
||||
{#if loading && logs.length === 0}
|
||||
<div class="loading-state">
|
||||
<div class="loading-spinner"></div>
|
||||
<div
|
||||
class="flex items-center justify-center gap-3 h-full text-terminal-text-subtle text-sm"
|
||||
>
|
||||
<div
|
||||
class="w-5 h-5 border-2 border-terminal-border border-t-primary rounded-full animate-spin"
|
||||
></div>
|
||||
<span>{$t.tasks?.loading || "Loading logs..."}</span>
|
||||
</div>
|
||||
{:else if error}
|
||||
<div class="error-state">
|
||||
<span class="error-icon">⚠</span>
|
||||
<div
|
||||
class="flex items-center justify-center gap-2 h-full text-log-error text-sm"
|
||||
>
|
||||
<span class="text-xl">⚠</span>
|
||||
<span>{error}</span>
|
||||
<button class="retry-btn" on:click={handleRefresh}
|
||||
>Retry</button
|
||||
<button
|
||||
class="bg-terminal-surface text-terminal-text-subtle border border-terminal-border rounded-md px-3 py-1 text-xs cursor-pointer transition-all hover:bg-terminal-border hover:text-terminal-text-bright"
|
||||
on:click={handleRefresh}>Retry</button
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
@@ -156,7 +147,7 @@
|
||||
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
|
||||
>
|
||||
<div
|
||||
class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
class="fixed inset-0 bg-gray-500/75 transition-opacity"
|
||||
aria-hidden="true"
|
||||
on:click={() => {
|
||||
show = false;
|
||||
@@ -210,67 +201,3 @@
|
||||
{/if}
|
||||
|
||||
<!-- [/DEF:TaskLogViewer:Component] -->
|
||||
|
||||
<style>
|
||||
.log-viewer-inline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
height: 100%;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
border: 2px solid #334155;
|
||||
border-top-color: #3b82f6;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.error-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
height: 100%;
|
||||
color: #f87171;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
background-color: #1e293b;
|
||||
color: #94a3b8;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background-color: #334155;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user