Files
Supervisor/components/StatusSite.vue
2026-03-09 10:50:41 +01:00

128 lines
3.1 KiB
Vue

<template>
<div class="bg-m-secondary w-[250px] h-[292px] rounded-md mx-4 shadow-md/50 shadow-black">
<p class="font-bold text-3xl text-m-tertiary my-1 mx-3">
Status
</p>
<template v-if="loading">
<div
v-for="n in 3"
:key="`skeleton-${n}`"
class="relative w-[200px] h-[68px] rounded-md mx-[25px] mb-3"
>
<ButtonSkeleton custom-class="h-full w-full" />
<div class="absolute inset-0 p-2">
<TextSkeleton custom-class="h-5 w-24 mb-2" />
<div class="flex items-center gap-2">
<CircleSkeleton custom-class="h-6 w-6" />
<TextSkeleton custom-class="h-5 w-20" />
</div>
</div>
</div>
</template>
<div
v-else
class="bg-m-tertiary w-[200px] h-[68px] rounded-md shadow-md/50 shadow-m-black mx-[25px] mb-3"
v-for="row in rows"
:key="`${row.label}-${row.url}`"
>
<p class="font-bold text-xl text-m-text mt-2 mx-2 mb-1">
{{ row.label }}
</p>
<div class="mx-2 flex items-center">
<span
class="inline-block h-[24px] w-[24px] rounded-full mr-2"
:class="statusClass(row.status)"
/>
<span class="font-semibold text-lg">
{{ statusLabel(row.status) }}
</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import ButtonSkeleton from "~/components/skeleton/ButtonSkeleton.vue"
import CircleSkeleton from "~/components/skeleton/CircleSkeleton.vue"
import TextSkeleton from "~/components/skeleton/TextSkeleton.vue"
import {onBeforeUnmount, onMounted, ref} from "vue"
interface StatusRow {
label: string
url: string
ok: boolean
status: number
checkedAt: string
error?: string
}
interface StatusResponse {
results: StatusRow[]
}
const props = withDefaults(
defineProps<{
endpoint?: string
refreshMs?: number
}>(),
{
endpoint: "/api/version-status",
refreshMs: 30000
}
)
const rows = ref<StatusRow[]>([])
const loading = ref(true)
const initialized = ref(false)
let timer: ReturnType<typeof setInterval> | null = null
const statusClass = (status: number) => {
if (status === 200) return "bg-m-success"
if (status === 0) return "bg-m-error"
return "bg-m-error"
}
const statusLabel = (status: number) => {
if (status === 200) return "HTTP 200"
if (status === 0) return "Injoignable"
return `KO (HTTP ${status})`
}
const checkStatus = async () => {
if (!initialized.value) {
loading.value = true
}
try {
const data = await $fetch<StatusResponse>(props.endpoint)
rows.value = data.results
} catch (error) {
rows.value = [
{
label: "Erreur",
url: props.endpoint,
ok: false,
status: 0,
checkedAt: new Date().toISOString(),
error: error instanceof Error ? error.message : String(error)
}
]
} finally {
initialized.value = true
loading.value = false
}
}
onMounted(() => {
checkStatus()
timer = setInterval(checkStatus, props.refreshMs)
})
onBeforeUnmount(() => {
if (timer) {
clearInterval(timer)
timer = null
}
})
</script>