Files
keystone/resources/js/components/StatusIndicator.vue
Harry Bayliss 85c44296ac
Some checks failed
CI / Tests (push) Failing after 56s
CI / Lint (push) Failing after 1m35s
Restructure UX and seed a fully simulated organisation
Rework the dashboard, environment topology view, header navigation, and
status rendering, and standardise selects on a shadcn-vue component.

Replace the thin database seeder with a SimulatedEnvironmentSeeder that
builds a fully wired, mostly-running organisation (ACTIVE server fleet,
managed + GHCR registries, Gitea source provider, ClipBin app with
production/staging environments, services, slices, endpoints, managed
variables, build artifacts, and a completed/in-progress/failed operations
history) so the new UI renders against realistic data.
2026-06-08 22:09:57 +01:00

92 lines
2.3 KiB
Vue

<script setup lang="ts">
import { computed } from "vue";
const props = withDefaults(
defineProps<{
status?: string | null;
label?: string;
size?: "sm" | "md";
}>(),
{
status: "unknown",
size: "sm",
},
);
type Tone = "positive" | "negative" | "pending" | "neutral";
const tones: Record<string, Tone> = {
active: "positive",
running: "positive",
succeeded: "positive",
success: "positive",
completed: "positive",
ready: "positive",
verified: "positive",
enabled: "positive",
healthy: "positive",
stopped: "negative",
failed: "negative",
error: "negative",
errored: "negative",
unhealthy: "negative",
cancelled: "negative",
installing: "pending",
pending: "pending",
"in-progress": "pending",
building: "pending",
planning: "pending",
deploying: "pending",
provisioning: "pending",
queued: "pending",
};
const normalised = computed(() => (props.status ?? "unknown").toString().toLowerCase().trim());
const tone = computed<Tone>(() => tones[normalised.value] ?? "neutral");
const label = computed(
() =>
props.label ??
(props.status ?? "unknown").toString().replaceAll("-", " ").replaceAll("_", " "),
);
const dotClasses = computed(() => {
switch (tone.value) {
case "positive":
return "bg-green-500";
case "negative":
return "bg-red-500";
case "pending":
return "bg-amber-500 animate-pulse";
default:
return "bg-zinc-400 dark:bg-zinc-500";
}
});
const textClasses = computed(() => {
switch (tone.value) {
case "positive":
return "text-green-700 dark:text-green-400";
case "negative":
return "text-red-700 dark:text-red-400";
case "pending":
return "text-amber-700 dark:text-amber-400";
default:
return "text-muted-foreground";
}
});
</script>
<template>
<span class="inline-flex items-center gap-1.5">
<span
class="inline-block rounded-full"
:class="[dotClasses, size === 'md' ? 'size-2' : 'size-1.5']"
/>
<span class="capitalize" :class="[textClasses, size === 'md' ? 'text-sm' : 'text-xs']">{{
label
}}</span>
</span>
</template>