mirror of
https://github.com/OliveTin/OliveTin
synced 2025-12-13 09:35:37 +00:00
fix: all broken integration tests
This commit is contained in:
@@ -1,73 +1,155 @@
|
||||
<template>
|
||||
<div v-if="!dashboard" style = "text-align: center">
|
||||
<p>Loading... {{ title }}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<section v-if="!dashboard && !initError" style = "text-align: center; padding: 2em;">
|
||||
<HugeiconsIcon :icon="Loading03Icon" width="3em" height="3em" style="animation: spin 1s linear infinite;" />
|
||||
<p>Loading dashboard...</p>
|
||||
<p style="color: var(--fg2);">{{ loadingTime }}s</p>
|
||||
</section>
|
||||
<section v-if="initError" style="text-align: center; padding: 2em;" class = "bad">
|
||||
<h2 style="color: var(--error);">Initialization Failed</h2>
|
||||
<p>{{ initError }}</p>
|
||||
<p style="color: var(--fg2);">Please check your configuration and try again.</p>
|
||||
</section>
|
||||
<div v-else-if="dashboard">
|
||||
<section v-if="dashboard.contents.length == 0">
|
||||
<legend>{{ dashboard.title }}</legend>
|
||||
<p>This dashboard is empty.</p>
|
||||
<p style = "text-align: center" class = "padding">This dashboard is empty.</p>
|
||||
</section>
|
||||
|
||||
<section class="transparent" v-else>
|
||||
<div v-for="component in dashboard.contents" :key="component.title">
|
||||
<div v-if="component.type == 'fieldset'">
|
||||
<fieldset>
|
||||
<legend v-if = "dashboard.title != 'Default'">{{ component.title }}</legend>
|
||||
<fieldset>
|
||||
<legend v-if = "dashboard.title != 'Default'">{{ component.title }}</legend>
|
||||
|
||||
<template v-for="subcomponent in component.contents">
|
||||
<div v-if="subcomponent.type == 'display'" class="display">
|
||||
<div v-html="subcomponent.title" />
|
||||
</div>
|
||||
|
||||
<ActionButton v-else-if="subcomponent.type == 'link'" :actionData="subcomponent.action"
|
||||
:key="subcomponent.title" />
|
||||
|
||||
<div v-else-if="subcomponent.type == 'directory'">
|
||||
<router-link :to="{ name: 'Dashboard', params: { title: subcomponent.title } }"
|
||||
class="dashboard-link">
|
||||
<button>
|
||||
{{ subcomponent.title }}
|
||||
</button>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
OTHER: {{ subcomponent.type }}
|
||||
{{ subcomponent }}
|
||||
</div>
|
||||
</template>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<ActionButton v-else :actionData="action" v-for="action in component.contents" :key="action.title" />
|
||||
<template v-for="subcomponent in component.contents">
|
||||
<DashboardComponent :component="subcomponent" />
|
||||
</template>
|
||||
</fieldset>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ActionButton from './ActionButton.vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import DashboardComponent from './components/DashboardComponent.vue'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import { HugeiconsIcon } from '@hugeicons/vue'
|
||||
import { Loading03Icon } from '@hugeicons/core-free-icons'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
required: false
|
||||
}
|
||||
})
|
||||
|
||||
const dashboard = ref(null)
|
||||
const loadingTime = ref(0)
|
||||
const initError = ref(null)
|
||||
let loadingTimer = null
|
||||
let checkInitInterval = null
|
||||
|
||||
async function getDashboard() {
|
||||
const ret = await window.client.getDashboard({
|
||||
title: props.title,
|
||||
})
|
||||
let title = props.title
|
||||
|
||||
dashboard.value = ret.dashboard
|
||||
// If no specific title was provided or it's the placeholder 'default',
|
||||
// prefer the first configured root dashboard (e.g., "Test").
|
||||
if ((!title || title === 'default') && window.initResponse.rootDashboards && window.initResponse.rootDashboards.length > 0) {
|
||||
title = window.initResponse.rootDashboards[0]
|
||||
}
|
||||
|
||||
try {
|
||||
const ret = await window.client.getDashboard({
|
||||
title: title,
|
||||
})
|
||||
|
||||
if (!ret || !ret.dashboard) {
|
||||
throw new Error('No dashboard found')
|
||||
}
|
||||
|
||||
dashboard.value = ret.dashboard
|
||||
document.title = ret.dashboard.title + ' - OliveTin'
|
||||
|
||||
// Clear any previous init error since we successfully loaded
|
||||
initError.value = null
|
||||
|
||||
// Stop the loading timer once dashboard is loaded
|
||||
if (loadingTimer) {
|
||||
clearInterval(loadingTimer)
|
||||
loadingTimer = null
|
||||
}
|
||||
|
||||
// Set attribute to indicate dashboard is loaded successfully
|
||||
document.body.setAttribute('loaded-dashboard', title || 'default')
|
||||
} catch (e) {
|
||||
// On error, provide a safe fallback state
|
||||
console.error('Failed to load dashboard', e)
|
||||
dashboard.value = { title: title || 'Default', contents: [] }
|
||||
document.title = 'Error - OliveTin'
|
||||
|
||||
// Stop the loading timer on error
|
||||
if (loadingTimer) {
|
||||
clearInterval(loadingTimer)
|
||||
loadingTimer = null
|
||||
}
|
||||
|
||||
// Set attribute even on error so tests can proceed
|
||||
document.body.setAttribute('loaded-dashboard', title || 'error')
|
||||
}
|
||||
}
|
||||
|
||||
function waitForInitAndLoadDashboard() {
|
||||
// Start the loading timer
|
||||
loadingTime.value = 0
|
||||
loadingTimer = setInterval(() => {
|
||||
loadingTime.value++
|
||||
}, 1000)
|
||||
|
||||
// Check if init has completed successfully
|
||||
if (window.initCompleted && window.initResponse) {
|
||||
getDashboard()
|
||||
} else if (window.initError) {
|
||||
// Init failed, show error immediately
|
||||
initError.value = window.initErrorMessage || 'Initialization failed. Please check your configuration and try again.'
|
||||
// Stop the loading timer since we're showing an error
|
||||
if (loadingTimer) {
|
||||
clearInterval(loadingTimer)
|
||||
loadingTimer = null
|
||||
}
|
||||
} else {
|
||||
// Init hasn't completed yet, poll for completion
|
||||
checkInitInterval = setInterval(() => {
|
||||
if (window.initCompleted && window.initResponse) {
|
||||
clearInterval(checkInitInterval)
|
||||
checkInitInterval = null
|
||||
getDashboard()
|
||||
} else if (window.initError) {
|
||||
clearInterval(checkInitInterval)
|
||||
checkInitInterval = null
|
||||
initError.value = window.initErrorMessage || 'Initialization failed. Please check your configuration and try again.'
|
||||
// Stop the loading timer since we're showing an error
|
||||
if (loadingTimer) {
|
||||
clearInterval(loadingTimer)
|
||||
loadingTimer = null
|
||||
}
|
||||
}
|
||||
}, 100) // Check every 100ms
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDashboard()
|
||||
waitForInitAndLoadDashboard()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// Clean up the timers when component is unmounted
|
||||
if (loadingTimer) {
|
||||
clearInterval(loadingTimer)
|
||||
loadingTimer = null
|
||||
}
|
||||
if (checkInitInterval) {
|
||||
clearInterval(checkInitInterval)
|
||||
checkInitInterval = null
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -80,4 +162,13 @@ fieldset {
|
||||
justify-content: center;
|
||||
place-items: stretch;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user