128 lines
3.1 KiB
Vue
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>
|