website/app/components/ServiceCard.vue

140 lines
No EOL
2.8 KiB
Vue

<template>
<div class="service-card">
<div class="card-header">
<div class="service-info">
<h3 class="service-name">{{ name }}</h3>
<span class="info-tooltip" title="Click for historical uptime metrics">?</span>
</div>
<div class="current-status-dot" :class="status.toLowerCase()"></div>
</div>
<div class="uptime-graph">
<div
v-for="(day, index) in uptimeHistory"
:key="index"
class="bar"
:class="day.status"
:title="formatTooltip(day)"
></div>
</div>
<div class="card-footer">
<span class="time-ago">90 days ago</span>
<span class="uptime-percentage">{{ uptimePercentage }}% uptime</span>
<span class="time-today">Today</span>
</div>
<div class="status-label">
{{ status }}
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
name: { type: String, required: true },
status: { type: String, default: 'Normal' },
uptimePercentage: { type: Number, default: 100.00 },
uptimeHistory: { type: Array, required: true }
})
const formatTooltip = (day) => {
return `${day.date}: ${day.status.toUpperCase()} (${day.incident || 'No incidents'})`
}
</script>
<style scoped>
.service-card {
background-color: #161b22;
border: 1px solid #30363d;
border-radius: 6px;
padding: 20px;
color: #c9d1d9;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.service-info {
display: flex;
align-items: center;
gap: 6px;
}
.service-name {
font-size: 1.05rem;
font-weight: 600;
margin: 0;
color: #f0f6fc;
}
.info-tooltip {
font-size: 0.75rem;
color: #8b949e;
background-color: #21262d;
width: 14px;
height: 14px;
border-radius: 50%;
display: inline-flex;
justify-content: center;
align-items: center;
cursor: help;
}
.current-status-dot {
width: 14px;
height: 14px;
border-radius: 50%;
}
.current-status-dot.normal { background-color: #238636; }
.current-status-dot.degraded { background-color: #d29922; }
.current-status-dot.outage { background-color: #f85149; }
.uptime-graph {
display: flex;
gap: 2px;
height: 34px;
margin-bottom: 8px;
}
.bar {
flex: 1;
background-color: #238636;
border-radius: 1px;
transition: opacity 0.2s;
}
.bar:hover {
opacity: 0.7;
transform: scaleY(1.1);
}
.bar.normal { background-color: #238636; }
.bar.degraded { background-color: #d29922; }
.bar.outage { background-color: #f85149; }
.card-footer {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: #8b949e;
border-bottom: 1px solid #21262d;
padding-bottom: 8px;
margin-bottom: 8px;
}
.uptime-percentage {
color: #8b949e;
}
.status-label {
font-size: 0.85rem;
color: #8b949e;
}
</style>