frontend/src/components/SecretsCreateGuided.vue
<!-- SecretsCreateGuided.vue -->
<script lang="ts">
import { defineComponent, ref, computed } from 'vue';
import { SecretCreateOptions } from '../types/custom'
export default defineComponent({
name: 'SecretsCreateGuided',
props: {
show: {
type: Boolean,
required: true
},
cluster: {
type: Object as PropType<KubernetesCluster | null>,
required: false,
default: null
}
},
emits: ['close', 'create-secret'],
setup(props, { emit }) {
const name = ref('');
const namespace = ref('default');
const isSubmitting = ref(false);
const error = ref('');
const secretData = ref([{ key: '', value: '', showValue: false }]);
const isFormValid = computed(() => {
if (!name.value.trim()) return false;
if (!namespace.value.trim()) return false;
// Check if at least one key-value pair is valid
return secretData.value.some(item =>
item.key.trim() !== '' && item.value.trim() !== ''
);
});
const addSecretData = () => {
secretData.value.push({ key: '', value: '', showValue: false });
};
const removeSecretData = (index: number) => {
if (secretData.value.length > 1) {
secretData.value.splice(index, 1);
}
};
const resetForm = () => {
name.value = '';
namespace.value = 'default';
secretData.value = [{ key: '', value: '' }];
error.value = '';
isSubmitting.value = false;
};
const closeModal = () => {
resetForm();
emit('close');
};
const toggleValueVisibility = (index: number) => {
secretData.value[index].showValue = !secretData.value[index].showValue;
};
const createSecret = () => {
if (!props.cluster) {
error.value = 'No cluster selected';
return;
}
if (!name.value.trim()) {
error.value = 'Name is required';
return;
}
if (!namespace.value.trim()) {
error.value = 'Namespace is required';
return;
}
// Check if at least one key-value pair is valid
const validData = secretData.value.filter(
item => item.key.trim() !== '' && item.value.trim() !== ''
);
if (validData.length === 0) {
error.value = 'At least one key-value pair is required';
return;
}
isSubmitting.value = true;
const stringData = {};
validData.forEach(item => {
stringData[item.key.trim()] = item.value;
});
const opts: SecretCreateOptions = {
context: props.cluster.contextName,
opts: {
apiVersion: 'v1',
kind: 'Secret',
metadata: {
name: name.value.trim(),
namespace: namespace.value.trim()
},
type: 'Opaque',
stringData: stringData
}
}
emit('create-secret', opts);
resetForm();
closeModal();
};
return {
name,
namespace,
secretData,
isSubmitting,
error,
isFormValid,
addSecretData,
removeSecretData,
closeModal,
createSecret,
toggleValueVisibility
};
}
});
</script>
<template>
<div v-if="show" class="modal-overlay" @click="closeModal">
<div class="modal-content" @click.stop>
<div class="modal-header">
<h3>Create Secret</h3>
<button class="close-button" @click="closeModal">×</button>
</div>
<div class="modal-body">
<div v-if="error" class="error-message">{{ error }}</div>
<div class="form-group">
<label for="name">Name:</label>
<input
id="name"
v-model="name"
type="text"
placeholder="test-secret-1"
:disabled="isSubmitting"
/>
</div>
<div class="form-group">
<label for="namespace">Namespace:</label>
<input
id="namespace"
v-model="namespace"
type="text"
placeholder="default"
:disabled="isSubmitting"
/>
</div>
<div class="form-section">
<h4>Secret Data</h4>
<div
v-for="(item, index) in secretData"
:key="index"
class="secret-data-row"
>
<div class="form-group key-field">
<label :for="`key-${index}`">Key:</label>
<input
:id="`key-${index}`"
v-model="item.key"
type="text"
placeholder="username"
:disabled="isSubmitting"
/>
</div>
<div class="form-group value-field">
<label :for="`value-${index}`">Value:</label>
<input
:id="`value-${index}`"
v-model="item.value"
type="password"
placeholder="••••••••"
:disabled="isSubmitting"
/>
</div>
<button
class="remove-button"
@click="removeSecretData(index)"
:disabled="isSubmitting || secretData.length <= 1"
>
×
</button>
</div>
<!-- Single add button outside the v-for loop -->
<button
class="add-button"
@click="addSecretData"
:disabled="isSubmitting"
>
+ Add Another Key-Value Pair
</button>
</div>
</div>
<div class="modal-footer">
<button
class="cancel-button"
@click="closeModal"
:disabled="isSubmitting"
>
Cancel
</button>
<button
class="create-button"
@click="createSecret"
:disabled="isSubmitting"
>
{{ isSubmitting ? 'Creating...' : 'Create' }}
</button>
</div>
</div>
</div>
</template>
<style src="@/assets/css/CreateResource.css" scoped></style>