150 lines
4.0 KiB
Vue
150 lines
4.0 KiB
Vue
<template>
|
|
<div>
|
|
<v-row class="mb-4">
|
|
<v-col>
|
|
<v-btn
|
|
color="primary"
|
|
prepend-icon="mdi-refresh"
|
|
@click="loadLogs"
|
|
:loading="loading"
|
|
>
|
|
Refresh
|
|
</v-btn>
|
|
</v-col>
|
|
<v-col cols="auto">
|
|
<v-select
|
|
v-model="filterStatus"
|
|
:items="statusOptions"
|
|
label="Status"
|
|
density="compact"
|
|
variant="outlined"
|
|
clearable
|
|
style="min-width: 140px"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<v-card>
|
|
<v-card-title>Recent Download Logs</v-card-title>
|
|
<v-card-text>
|
|
<div v-if="loading" class="text-center py-8">
|
|
<v-progress-circular indeterminate />
|
|
</div>
|
|
|
|
<v-expansion-panels v-else-if="filteredLogs.length">
|
|
<v-expansion-panel
|
|
v-for="log in filteredLogs"
|
|
:key="log.id"
|
|
>
|
|
<v-expansion-panel-title>
|
|
<div class="d-flex align-center ga-2" style="width: 100%">
|
|
<v-chip
|
|
:color="getStatusColor(log.status)"
|
|
size="small"
|
|
variant="tonal"
|
|
>
|
|
{{ log.status }}
|
|
</v-chip>
|
|
<span class="text-truncate" style="max-width: 400px">
|
|
{{ log.url }}
|
|
</span>
|
|
<v-spacer />
|
|
<span class="text-caption text-grey">
|
|
{{ formatDate(log.created_at) }}
|
|
</span>
|
|
</div>
|
|
</v-expansion-panel-title>
|
|
<v-expansion-panel-text>
|
|
<div v-if="log.error_message" class="mb-4">
|
|
<div class="text-subtitle-2 text-error">Error</div>
|
|
<v-code class="d-block pa-2 bg-error-lighten-5">{{ log.error_message }}</v-code>
|
|
</div>
|
|
|
|
<div v-if="log.stdout" class="mb-4">
|
|
<div class="text-subtitle-2">Output</div>
|
|
<pre class="log-output bg-grey-lighten-4 pa-2 rounded">{{ log.stdout }}</pre>
|
|
</div>
|
|
|
|
<div v-if="log.stderr">
|
|
<div class="text-subtitle-2 text-warning">Errors/Warnings</div>
|
|
<pre class="log-output bg-warning-lighten-5 pa-2 rounded">{{ log.stderr }}</pre>
|
|
</div>
|
|
|
|
<div v-if="!log.stdout && !log.stderr && !log.error_message" class="text-grey">
|
|
No log output available
|
|
</div>
|
|
</v-expansion-panel-text>
|
|
</v-expansion-panel>
|
|
</v-expansion-panels>
|
|
|
|
<div v-else class="text-center text-grey py-8">
|
|
<v-icon size="48" class="mb-2">mdi-text-box-outline</v-icon>
|
|
<div>No logs available</div>
|
|
</div>
|
|
</v-card-text>
|
|
</v-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { settingsApi } from '../services/api'
|
|
|
|
const loading = ref(false)
|
|
const logs = ref([])
|
|
const filterStatus = ref(null)
|
|
|
|
const statusOptions = [
|
|
{ title: 'Completed', value: 'completed' },
|
|
{ title: 'Failed', value: 'failed' },
|
|
{ title: 'Running', value: 'running' },
|
|
]
|
|
|
|
const filteredLogs = computed(() => {
|
|
if (!filterStatus.value) return logs.value
|
|
return logs.value.filter(l => l.status === filterStatus.value)
|
|
})
|
|
|
|
onMounted(() => {
|
|
loadLogs()
|
|
})
|
|
|
|
async function loadLogs() {
|
|
loading.value = true
|
|
try {
|
|
const response = await settingsApi.getLogs({ limit: 100 })
|
|
logs.value = response.data.logs
|
|
} catch (error) {
|
|
console.error('Failed to load logs:', error)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function getStatusColor(status) {
|
|
const colors = {
|
|
completed: 'success',
|
|
failed: 'error',
|
|
running: 'info',
|
|
pending: 'warning',
|
|
}
|
|
return colors[status] || 'grey'
|
|
}
|
|
|
|
function formatDate(dateStr) {
|
|
if (!dateStr) return ''
|
|
return new Date(dateStr).toLocaleString()
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.log-output {
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
max-height: 400px;
|
|
overflow: auto;
|
|
}
|
|
</style>
|