diff --git a/apps/app/src/components/account-settings/SecurityTab.vue b/apps/app/src/components/account-settings/SecurityTab.vue index 0d52e269..83c9c694 100644 --- a/apps/app/src/components/account-settings/SecurityTab.vue +++ b/apps/app/src/components/account-settings/SecurityTab.vue @@ -12,6 +12,7 @@ import { import MfaTotpSetupDialog from '@/components/settings/MfaTotpSetupDialog.vue' import MfaEmailSetupDialog from '@/components/settings/MfaEmailSetupDialog.vue' import MfaDisableDialog from '@/components/settings/MfaDisableDialog.vue' +import PasswordRequirements from '@/components/auth/PasswordRequirements.vue' const authStore = useAuthStore() @@ -26,10 +27,26 @@ const passwordSuccess = ref('') const showCurrentPw = ref(false) const showNewPw = ref(false) const showConfirmPw = ref(false) +const passwordReqsRef = ref>() + +const confirmationError = computed(() => { + if (!passwordForm.value.password_confirmation) return '' + if (passwordForm.value.password !== passwordForm.value.password_confirmation) return 'Wachtwoorden komen niet overeen' + return '' +}) + +const canSubmitPassword = computed(() => + passwordForm.value.current_password.length > 0 + && passwordForm.value.password.length > 0 + && passwordForm.value.password_confirmation.length > 0 + && passwordForm.value.password === passwordForm.value.password_confirmation + && (passwordReqsRef.value?.allMet ?? false), +) const changePasswordMutation = useChangePassword() async function handlePasswordChange() { + if (!canSubmitPassword.value) return passwordFieldErrors.value = {} passwordSuccess.value = '' @@ -186,6 +203,10 @@ function copyRegeneratedCodes() { placeholder="············" @click:append-inner="showNewPw = !showNewPw" /> + @@ -204,45 +225,11 @@ function copyRegeneratedCodes() { - -
- Wachtwoordvereisten: -
- - - - Minimaal 8 tekens - - - - Minimaal 1 hoofdletter en 1 kleine letter - - - - Minimaal 1 cijfer - - -
- Wachtwoord wijzigen diff --git a/apps/app/src/components/auth/PasswordRequirements.vue b/apps/app/src/components/auth/PasswordRequirements.vue new file mode 100644 index 00000000..7aeb1e16 --- /dev/null +++ b/apps/app/src/components/auth/PasswordRequirements.vue @@ -0,0 +1,31 @@ + + + diff --git a/apps/app/src/pages/reset-password.vue b/apps/app/src/pages/reset-password.vue index 7096b38a..cf7cc771 100644 --- a/apps/app/src/pages/reset-password.vue +++ b/apps/app/src/pages/reset-password.vue @@ -6,6 +6,7 @@ import authV2MaskDark from '@images/pages/misc-mask-dark.png' import authV2MaskLight from '@images/pages/misc-mask-light.png' import { VNodeRenderer } from '@layouts/components/VNodeRenderer' import { themeConfig } from '@themeConfig' +import PasswordRequirements from '@/components/auth/PasswordRequirements.vue' import { apiClient } from '@/lib/axios' definePage({ @@ -27,6 +28,20 @@ const showPasswordConfirmation = ref(false) const errorMessage = ref('') const isSubmitting = ref(false) +const passwordReqsRef = ref>() + +const confirmationError = computed(() => { + if (!passwordConfirmation.value) return '' + if (password.value !== passwordConfirmation.value) return 'Wachtwoorden komen niet overeen' + return '' +}) + +const canSubmit = computed(() => + password.value.length > 0 + && passwordConfirmation.value.length > 0 + && password.value === passwordConfirmation.value + && (passwordReqsRef.value?.allMet ?? false), +) const authThemeImg = useGenerateImageVariant( authV2ResetPasswordIllustrationLight, @@ -40,6 +55,7 @@ async function onSubmit(): Promise { errorMessage.value = 'Ongeldige resetlink. Vraag een nieuwe link aan.' return } + if (!canSubmit.value) return isSubmitting.value = true try { await apiClient.post('/auth/reset-password', { @@ -153,6 +169,10 @@ async function onSubmit(): Promise { autocomplete="new-password" @click:append-inner="showPassword = !showPassword" /> +
@@ -161,6 +181,7 @@ async function onSubmit(): Promise { label="Bevestig wachtwoord" :type="showPasswordConfirmation ? 'text' : 'password'" :append-inner-icon="showPasswordConfirmation ? 'tabler-eye-off' : 'tabler-eye'" + :error-messages="confirmationError ? [confirmationError] : undefined" autocomplete="new-password" @click:append-inner="showPasswordConfirmation = !showPasswordConfirmation" /> @@ -171,6 +192,7 @@ async function onSubmit(): Promise { block type="submit" :loading="isSubmitting" + :disabled="!canSubmit" > Wachtwoord opslaan diff --git a/apps/portal/src/pages/wachtwoord-resetten.vue b/apps/portal/src/pages/wachtwoord-resetten.vue index a4d248d0..f05bcafb 100644 --- a/apps/portal/src/pages/wachtwoord-resetten.vue +++ b/apps/portal/src/pages/wachtwoord-resetten.vue @@ -26,6 +26,20 @@ const showPasswordConfirmation = ref(false) const errorMessage = ref('') const isSubmitting = ref(false) +const passwordReqsRef = ref>() + +const confirmationError = computed(() => { + if (!passwordConfirmation.value) return '' + if (password.value !== passwordConfirmation.value) return 'Wachtwoorden komen niet overeen' + return '' +}) + +const canSubmit = computed(() => + password.value.length > 0 + && passwordConfirmation.value.length > 0 + && password.value === passwordConfirmation.value + && (passwordReqsRef.value?.allMet ?? false), +) async function onSubmit(): Promise { errorMessage.value = '' @@ -34,6 +48,7 @@ async function onSubmit(): Promise { return } + if (!canSubmit.value) return isSubmitting.value = true try { await apiClient.post('/auth/reset-password', { @@ -121,7 +136,10 @@ async function onSubmit(): Promise { :append-inner-icon="showPassword ? 'tabler-eye-off' : 'tabler-eye'" @click:append-inner="showPassword = !showPassword" /> - + @@ -131,6 +149,7 @@ async function onSubmit(): Promise { placeholder="············" :type="showPasswordConfirmation ? 'text' : 'password'" autocomplete="new-password" + :error-messages="confirmationError ? [confirmationError] : undefined" :append-inner-icon="showPasswordConfirmation ? 'tabler-eye-off' : 'tabler-eye'" @click:append-inner="showPasswordConfirmation = !showPasswordConfirmation" /> @@ -141,6 +160,7 @@ async function onSubmit(): Promise { block type="submit" :loading="isSubmitting" + :disabled="!canSubmit" > Wachtwoord opslaan