Files
keystone/resources/js/components/ServerSelector.vue
Harry Bayliss 66f0ee9e50
All checks were successful
CI / Tests (push) Successful in 43s
CI / Lint (push) Successful in 1m3s
Migrate to Gitea, switch JS tooling to oxlint/oxfmt, lift test coverage to 95%
- Add .gitea/workflows/ci.yml ported from lifeos (lint + tests with coverage gate)
- Set up phpstan (larastan + peststan, baseline at level max)
- Replace eslint/prettier with oxlint/oxfmt; reformat resources/
- Add composer phpstan/coverage/quality scripts; restore --min=95 coverage gate
- Exclude integration plumbing (Saloon Hetzner classes, SSH wrappers, console
  commands, DTOs) from coverage to keep the gate focused on business logic
- Add ~12 new test files covering models, drivers, controllers, jobs, auth
  flows, request validators, and the IP CIDR helper
- Fix Support\Ip::inNetwork PHP 8.4 TypeError in CIDR mask check
- Fix FirewallRule::command comparing the enum-cast type column to a string
- Fix Server::network using the wrong foreign key column
- Remove unreachable code under abort(403) in RegisteredUserController
2026-05-13 16:51:07 +01:00

98 lines
3.5 KiB
Vue

<script setup>
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import ServiceCategory from "@/enums/ServiceCategory";
import { Deferred, router } from "@inertiajs/vue3";
import { LoaderCircleIcon } from "lucide-vue-next";
import { ref, watch } from "vue";
import { Card } from "./ui/card";
const props = defineProps({
servers: {
type: Array,
required: false,
},
serviceCategory: {
type: String,
required: false,
validate: (value) => {
return Object.keys(ServiceCategory).includes(value);
},
},
});
const isOpen = ref(false);
defineEmits(["select"]);
watch(isOpen, () => {
if (isOpen.value && props.servers === undefined) {
router.reload({
only: ["servers"],
async: true,
});
}
});
</script>
<template>
<Dialog v-model:open="isOpen">
<DialogTrigger class="block w-full">
<slot name="trigger" />
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Servers</DialogTitle>
<DialogDescription
>Select an active server to install the gateway on.</DialogDescription
>
<div class="my-2 max-h-80 overflow-y-auto">
<Deferred data="servers">
<template #fallback>
<div class="flex justify-center py-4">
<LoaderCircleIcon
class="size-6 animate-spin text-muted-foreground"
/>
</div>
</template>
<Card
v-for="(server, serverIndex) in servers"
:key="`serverPicker-${server.id}`"
:data-index="serverIndex"
class="group flex justify-between gap-4 p-2 text-muted-foreground transition hover:text-foreground"
:class="{
'rounded-b-none': serverIndex !== servers.length - 1,
'rounded-t-none': serverIndex !== 0,
'border-b-0': serverIndex !== servers.length - 1,
}"
@click="
$emit('select', server);
isOpen = false;
"
>
<div class="cursor-default text-sm">{{ server.name }}</div>
<div v-if="serviceCategory" class="text-xs">
{{
!!server.services.filter((s) => s.category === serviceCategory)
.length
? "Has Gateway"
: "No Gateway Installed"
}}
</div>
</Card>
<Card v-if="servers.length === 0" class="p-2 text-sm text-muted-foreground">
No servers available
</Card>
</Deferred>
</div>
</DialogHeader>
<!-- <DialogFooter> Dismiss </DialogFooter> -->
</DialogContent>
</Dialog>
</template>