Repair edge gateway manage page contract

This commit is contained in:
Jeppe Bundgaard
2026-04-22 17:00:38 +02:00
parent f6c8e8123a
commit c72da3879b
@@ -1,4 +1,6 @@
<script setup>
import { computed, ref, watch } from "vue";
const props = defineProps({
gateway: {
type: Object,
@@ -10,11 +12,11 @@ const props = defineProps({
},
metadataForm: {
type: Object,
required: true,
default: null,
},
transportMode: {
type: String,
default: "gateway",
default: null,
},
rotateConfirmation: {
type: String,
@@ -28,10 +30,30 @@ const props = defineProps({
type: Object,
default: null,
},
rotateBundle: {
type: Object,
default: null,
},
busy: {
type: Boolean,
default: false,
},
loadingMetadata: {
type: Boolean,
default: false,
},
loadingCutover: {
type: Boolean,
default: false,
},
loadingRotate: {
type: Boolean,
default: false,
},
loadingDelete: {
type: Boolean,
default: false,
},
});
const emit = defineEmits([
@@ -39,11 +61,117 @@ const emit = defineEmits([
"save-metadata",
"update-transport-mode",
"save-transport-mode",
"apply-cutover",
"update-rotate-confirmation",
"rotate-credentials",
"update-delete-confirmation",
"delete-gateway",
]);
const metadataLabel = ref("");
const metadataPrimary = ref(false);
const localTransportMode = ref("gateway");
const localRotateConfirmation = ref("");
const localDeleteConfirmation = ref("");
const gatewayLabel = computed(() => String(props.gateway?.label || props.gateway?.hostname || "").trim());
const effectiveCredentialBundle = computed(() => props.credentialBundle || props.rotateBundle || null);
const metadataBusy = computed(() => props.busy || props.loadingMetadata);
const cutoverBusy = computed(() => props.busy || props.loadingCutover);
const rotateBusy = computed(
() => props.busy || props.loadingRotate || localRotateConfirmation.value.trim().toUpperCase() !== "ROTATE"
);
const deleteBusy = computed(
() => props.busy || props.loadingDelete || localDeleteConfirmation.value.trim() !== gatewayLabel.value
);
watch(
() => [props.gateway?.id, props.gateway?.label, props.gateway?.is_primary, props.metadataForm],
() => {
metadataLabel.value = String(props.metadataForm?.label ?? props.gateway?.label ?? "");
metadataPrimary.value = Boolean(props.metadataForm?.is_primary ?? props.gateway?.is_primary);
},
{ immediate: true }
);
watch(
() => [props.gateway?.id, props.gateway?.department_transport_mode, props.transportMode],
() => {
localTransportMode.value = props.transportMode || props.gateway?.department_transport_mode || "gateway";
},
{ immediate: true }
);
watch(
() => [props.gateway?.id, props.rotateConfirmation],
() => {
localRotateConfirmation.value = props.rotateConfirmation || "";
},
{ immediate: true }
);
watch(
() => [props.gateway?.id, props.deleteConfirmation],
() => {
localDeleteConfirmation.value = props.deleteConfirmation || "";
},
{ immediate: true }
);
const updateMetadataField = (field, value) => {
if (field === "label") {
metadataLabel.value = value;
}
if (field === "is_primary") {
metadataPrimary.value = Boolean(value);
}
emit("update-metadata", field, value);
};
const saveMetadata = () => {
emit("save-metadata", {
label: metadataLabel.value.trim(),
is_primary: Boolean(metadataPrimary.value),
});
};
const updateTransportMode = (value) => {
localTransportMode.value = value || "gateway";
emit("update-transport-mode", localTransportMode.value);
};
const saveTransportMode = () => {
emit("apply-cutover", localTransportMode.value);
emit("save-transport-mode", localTransportMode.value);
};
const updateRotateConfirmation = (value) => {
localRotateConfirmation.value = value;
emit("update-rotate-confirmation", value);
};
const rotateCredentials = () => {
if (localRotateConfirmation.value.trim().toUpperCase() !== "ROTATE") {
return;
}
emit("rotate-credentials");
};
const updateDeleteConfirmation = (value) => {
localDeleteConfirmation.value = value;
emit("update-delete-confirmation", value);
};
const deleteGateway = () => {
if (localDeleteConfirmation.value.trim() !== gatewayLabel.value) {
return;
}
emit("delete-gateway");
};
</script>
<template>
@@ -55,21 +183,27 @@ const emit = defineEmits([
<input
class="input"
data-testid="gateway-metadata-label"
:value="metadataForm.label"
:value="metadataLabel"
placeholder="Gateway label"
@input="emit('update-metadata', 'label', $event.target.value)"
@input="updateMetadataField('label', $event.target.value)"
/>
<label class="edge-gateway-manage__checkbox">
<input
type="checkbox"
:checked="Boolean(metadataForm.is_primary)"
@change="emit('update-metadata', 'is_primary', $event.target.checked)"
:checked="metadataPrimary"
@change="updateMetadataField('is_primary', $event.target.checked)"
/>
Primary gateway for this department
</label>
</div>
<div class="edge-gateway-manage__actions">
<button class="button is-dark" type="button" data-testid="gateway-metadata-save" :disabled="busy" @click="emit('save-metadata')">
<button
class="button is-dark"
type="button"
data-testid="gateway-metadata-save"
:disabled="metadataBusy"
@click="saveMetadata"
>
Save metadata
</button>
</div>
@@ -80,14 +214,14 @@ const emit = defineEmits([
<h3>Preferred relay execution path</h3>
<div class="edge-gateway-manage__grid">
<div class="select is-fullwidth">
<select :value="transportMode" data-testid="gateway-transport-mode" @change="emit('update-transport-mode', $event.target.value)">
<select :value="localTransportMode" data-testid="gateway-transport-mode" @change="updateTransportMode($event.target.value)">
<option value="gateway">Gateway first</option>
<option value="cloud">Cloud only</option>
</select>
</div>
</div>
<div class="edge-gateway-manage__actions">
<button class="button is-dark" type="button" :disabled="busy" @click="emit('save-transport-mode')">
<button class="button is-dark" type="button" :disabled="cutoverBusy" @click="saveTransportMode">
Save cutover mode
</button>
</div>
@@ -100,22 +234,34 @@ const emit = defineEmits([
<input
class="input"
data-testid="gateway-rotate-confirmation"
:value="rotateConfirmation"
:value="localRotateConfirmation"
placeholder="Type ROTATE"
@input="emit('update-rotate-confirmation', $event.target.value)"
@input="updateRotateConfirmation($event.target.value)"
/>
<div class="edge-gateway-manage__actions">
<button class="button is-warning is-light" type="button" data-testid="gateway-rotate-credentials" :disabled="busy" @click="emit('rotate-credentials')">
<button
class="button is-warning is-light"
type="button"
data-testid="gateway-rotate-credentials"
:disabled="rotateBusy"
@click="rotateCredentials"
>
Rotate credentials
</button>
</div>
<div v-if="credentialBundle" class="edge-gateway-manage__bundle" data-testid="gateway-credential-bundle">
<p><strong>Token:</strong> {{ credentialBundle.agent_token || credentialBundle.token || "Unavailable" }}</p>
<p><strong>Stack service:</strong> {{ credentialBundle.stackServiceName || credentialBundle.stack_service_name || "Unavailable" }}</p>
<p><strong>Compose file:</strong> {{ credentialBundle.composeFileName || credentialBundle.compose_file_name || "Unavailable" }}</p>
<ul v-if="credentialBundle.restart_instructions?.length">
<li v-for="instruction in credentialBundle.restart_instructions" :key="instruction">{{ instruction }}</li>
<div v-if="effectiveCredentialBundle" class="edge-gateway-manage__bundle" data-testid="gateway-credential-bundle">
<p><strong>Token:</strong> {{ effectiveCredentialBundle.agent_token || effectiveCredentialBundle.token || "Unavailable" }}</p>
<p>
<strong>Stack service:</strong>
{{ effectiveCredentialBundle.stackServiceName || effectiveCredentialBundle.stack_service_name || "Unavailable" }}
</p>
<p>
<strong>Compose file:</strong>
{{ effectiveCredentialBundle.composeFileName || effectiveCredentialBundle.compose_file_name || "Unavailable" }}
</p>
<ul v-if="effectiveCredentialBundle.restart_instructions?.length">
<li v-for="instruction in effectiveCredentialBundle.restart_instructions" :key="instruction">{{ instruction }}</li>
</ul>
</div>
</article>
@@ -127,12 +273,18 @@ const emit = defineEmits([
<input
class="input"
data-testid="gateway-delete-confirmation"
:value="deleteConfirmation"
:placeholder="gateway.label || gateway.hostname || 'Gateway label'"
@input="emit('update-delete-confirmation', $event.target.value)"
:value="localDeleteConfirmation"
:placeholder="gatewayLabel || 'Gateway label'"
@input="updateDeleteConfirmation($event.target.value)"
/>
<div class="edge-gateway-manage__actions">
<button class="button is-danger" type="button" data-testid="gateway-delete" :disabled="busy" @click="emit('delete-gateway')">
<button
class="button is-danger"
type="button"
data-testid="gateway-delete"
:disabled="deleteBusy"
@click="deleteGateway"
>
Delete gateway
</button>
</div>