Implement validation
This commit is contained in:
@ -84,7 +84,11 @@ const sourceField = ref<HTMLSlInputElement>()
|
|||||||
const publicKeyField = ref<HTMLSlInputElement>()
|
const publicKeyField = ref<HTMLSlInputElement>()
|
||||||
const clientCreateForm = ref<HTMLElement>()
|
const clientCreateForm = ref<HTMLElement>()
|
||||||
|
|
||||||
const props = defineProps<{ client?: Client | null; errors: any[] | null }>()
|
interface Props {
|
||||||
|
client?: Client
|
||||||
|
errors?: any[]
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'submit', data: ClientCreate): void
|
(e: 'submit', data: ClientCreate): void
|
||||||
(e: 'cancel'): void
|
(e: 'cancel'): void
|
||||||
@ -198,61 +202,3 @@ async function submitForm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.client-form sl-input,
|
|
||||||
.client-form sl-select,
|
|
||||||
.client-form sl-checkbox {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: var(--sl-spacing-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* user invalid styles */
|
|
||||||
.client-form sl-input[data-user-invalid]::part(base),
|
|
||||||
.client-form sl-select[data-user-invalid]::part(combobox),
|
|
||||||
.client-form sl-checkbox[data-user-invalid]::part(control) {
|
|
||||||
border-color: var(--sl-color-danger-600);
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-form [data-user-invalid]::part(form-control-label),
|
|
||||||
.client-form [data-user-invalid]::part(form-control-help-text),
|
|
||||||
.client-form sl-checkbox[data-user-invalid]::part(label) {
|
|
||||||
color: var(--sl-color-danger-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-form sl-checkbox[data-user-invalid]::part(control) {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-form sl-input:focus-within[data-user-invalid]::part(base),
|
|
||||||
.client-form sl-select:focus-within[data-user-invalid]::part(combobox),
|
|
||||||
.client-form sl-checkbox:focus-within[data-user-invalid]::part(control) {
|
|
||||||
border-color: var(--sl-color-danger-600);
|
|
||||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-300);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* User valid styles */
|
|
||||||
.client-form sl-input[data-user-valid]::part(base),
|
|
||||||
.client-form sl-select[data-user-valid]::part(combobox),
|
|
||||||
.client-form sl-checkbox[data-user-valid]::part(control) {
|
|
||||||
border-color: var(--sl-color-success-600);
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-form [data-user-valid]::part(form-control-label),
|
|
||||||
.client-form [data-user-valid]::part(form-control-help-text),
|
|
||||||
.client-form sl-checkbox[data-user-valid]::part(label) {
|
|
||||||
color: var(--sl-color-success-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-form sl-checkbox[data-user-valid]::part(control) {
|
|
||||||
background-color: var(--sl-color-success-600);
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.client-form sl-input:focus-within[data-user-valid]::part(base),
|
|
||||||
.client-form sl-select:focus-within[data-user-valid]::part(combobox),
|
|
||||||
.client-form sl-checkbox:focus-within[data-user-valid]::part(control) {
|
|
||||||
border-color: var(--sl-color-success-600);
|
|
||||||
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-success-300);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -6,7 +6,8 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
help-text="Name of the secret"
|
help-text="Name of the secret"
|
||||||
:value="secretName"
|
:value="secretName"
|
||||||
@input="secretName = $event.target.value"
|
@sl-input="secretName = $event.target.value"
|
||||||
|
@input="emit('clearErrors')"
|
||||||
ref="nameField"
|
ref="nameField"
|
||||||
></sl-input>
|
></sl-input>
|
||||||
<template v-if="group">
|
<template v-if="group">
|
||||||
@ -59,15 +60,24 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SecretCreate } from '@/client'
|
import type { SecretCreate } from '@/client'
|
||||||
import { ref } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { generateRandomPassword } from '@/api/password'
|
import { generateRandomPassword } from '@/api/password'
|
||||||
import AddSecretsToClients from '@/components/secrets/AddSecretToClients.vue'
|
import AddSecretsToClients from '@/components/secrets/AddSecretToClients.vue'
|
||||||
import ClientSelectDropdown from '@/components/clients/ClientSelectDropdown.vue'
|
import ClientSelectDropdown from '@/components/clients/ClientSelectDropdown.vue'
|
||||||
import { setFieldValidation } from '@/api/validation'
|
import { setFieldValidation } from '@/api/validation'
|
||||||
|
|
||||||
const props = defineProps<{ group?: string }>()
|
interface Props {
|
||||||
|
group?: string
|
||||||
|
errors?: any[]
|
||||||
|
}
|
||||||
|
|
||||||
const emit = defineEmits<{ (e: 'submit', data: SecretCreate): void; (e: 'cancel'): void }>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'submit', data: SecretCreate): void
|
||||||
|
(e: 'cancel'): void
|
||||||
|
(e: 'clearErrors'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
const secretName = ref<string>()
|
const secretName = ref<string>()
|
||||||
const createSecretForm = ref<HTMLFormElement>()
|
const createSecretForm = ref<HTMLFormElement>()
|
||||||
@ -96,6 +106,10 @@ function cancelCreateSecret() {
|
|||||||
emit('cancel')
|
emit('cancel')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetValidation() {
|
||||||
|
setFieldValidation(nameField, '')
|
||||||
|
}
|
||||||
|
|
||||||
function submitCreateSecret() {
|
function submitCreateSecret() {
|
||||||
validateName()
|
validateName()
|
||||||
validateSecret()
|
validateSecret()
|
||||||
@ -117,4 +131,15 @@ function submitCreateSecret() {
|
|||||||
emit('submit', secretCreate)
|
emit('submit', secretCreate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.errors,
|
||||||
|
(errors) => {
|
||||||
|
resetValidation()
|
||||||
|
const nameErrors = errors.filter((e) => e.loc.includes('name'))
|
||||||
|
if (nameErrors.length > 0) {
|
||||||
|
setFieldValidation(nameField, nameErrors[0].msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -58,6 +58,8 @@
|
|||||||
:group="createSecretParent"
|
:group="createSecretParent"
|
||||||
@submit="createSecret"
|
@submit="createSecret"
|
||||||
@cancel="createSecretDrawer = false"
|
@cancel="createSecretDrawer = false"
|
||||||
|
:errors="createErrors"
|
||||||
|
@clearErrors="clearCreateErrors"
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</template>
|
</template>
|
||||||
@ -71,6 +73,8 @@ import { useAlertsStore } from '@/store/useAlertsStore'
|
|||||||
import { SshecretAdmin } from '@/client'
|
import { SshecretAdmin } from '@/client'
|
||||||
import { SshecretObjectType } from '@/api/types'
|
import { SshecretObjectType } from '@/api/types'
|
||||||
import { splitPath } from '@/api/paths'
|
import { splitPath } from '@/api/paths'
|
||||||
|
import { ValidationError } from '@/api/errors'
|
||||||
|
import { assertSdkResponseOk } from '@/api/AssertSdkResponseOk'
|
||||||
import SecretGroup from '@/components/secrets/SecretGroup.vue'
|
import SecretGroup from '@/components/secrets/SecretGroup.vue'
|
||||||
import SecretGroupTreeItem from '@/components/secrets/SecretGroupTreeItem.vue'
|
import SecretGroupTreeItem from '@/components/secrets/SecretGroupTreeItem.vue'
|
||||||
import SecretGroupTreeEntry from '@/components/secrets/SecretGroupTreeEntry.vue'
|
import SecretGroupTreeEntry from '@/components/secrets/SecretGroupTreeEntry.vue'
|
||||||
@ -101,6 +105,8 @@ const createDrawerKey = ref(0)
|
|||||||
const createGroupDrawer = ref<boolean>(false)
|
const createGroupDrawer = ref<boolean>(false)
|
||||||
const createSecretDrawer = ref<boolean>(false)
|
const createSecretDrawer = ref<boolean>(false)
|
||||||
|
|
||||||
|
const createErrors = ref([])
|
||||||
|
|
||||||
function cancelCreateGroup() {
|
function cancelCreateGroup() {
|
||||||
createGroupDrawer.value = false
|
createGroupDrawer.value = false
|
||||||
drawerKey.value += 1
|
drawerKey.value += 1
|
||||||
@ -172,7 +178,8 @@ async function createSecret(secretCreate: SecretCreate) {
|
|||||||
const response = await SshecretAdmin.addSecretApiV1SecretsPost({
|
const response = await SshecretAdmin.addSecretApiV1SecretsPost({
|
||||||
body: secretCreate,
|
body: secretCreate,
|
||||||
})
|
})
|
||||||
if (response.status == 200) {
|
try {
|
||||||
|
assertSdkResponseOk(response)
|
||||||
alerts.showAlert('Secret created', 'success')
|
alerts.showAlert('Secret created', 'success')
|
||||||
// We can close the drawer now.
|
// We can close the drawer now.
|
||||||
createSecretDrawer.value = false
|
createSecretDrawer.value = false
|
||||||
@ -185,10 +192,19 @@ async function createSecret(secretCreate: SecretCreate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
treeState.selectSecret(secretCreate.name)
|
treeState.selectSecret(secretCreate.name)
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof ValidationError) {
|
||||||
|
createErrors.value = err.errors
|
||||||
} else {
|
} else {
|
||||||
console.error(response)
|
const errorMessage = err.message ?? 'Unknown error'
|
||||||
alerts.showAlert('Secret creation failed', 'error')
|
alerts.showAlert(`Error from backend: ${errorMessage}`, 'error')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCreateErrors() {
|
||||||
|
// Clear any errors from the create form.
|
||||||
|
createErrors.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
Reference in New Issue
Block a user