Files
InvoiceShelf/resources/scripts-v2/features/auth/views/ResetPasswordView.vue
Darko Gjorgjijoski 774b2614f0 Phase 4a: Feature modules — layouts, auth, admin, dashboard,
customers, items, invoices, estimates, shared document form

77 files, 14451 lines. Typed layouts (CompanyLayout, AuthLayout,
header, sidebar, company switcher), auth views (login, register,
forgot/reset password), admin feature (dashboard, companies, users,
settings with typed store), company features (dashboard with chart/
stats, customers CRUD, items CRUD, invoices CRUD with full store,
estimates CRUD with full store), and shared document form components
(items table, item row, totals, notes, tax popup, template select,
exchange rate converter, calculation composable).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 06:30:00 +02:00

166 lines
3.8 KiB
Vue

<template>
<form id="loginForm" @submit.prevent="onSubmit">
<BaseInputGroup
:error="errorEmail"
:label="$t('login.email')"
class="mb-4"
required
>
<BaseInput
v-model="formData.email"
:invalid="v$.email.$error"
focus
type="email"
name="email"
@input="v$.email.$touch()"
/>
</BaseInputGroup>
<BaseInputGroup
:error="errorPassword"
:label="$t('login.password')"
class="mb-4"
required
>
<BaseInput
v-model="formData.password"
:invalid="v$.password.$error"
type="password"
name="password"
@input="v$.password.$touch()"
/>
</BaseInputGroup>
<BaseInputGroup
:error="errorConfirmPassword"
:label="$t('login.retype_password')"
class="mb-4"
required
>
<BaseInput
v-model="formData.password_confirmation"
:invalid="v$.password_confirmation.$error"
type="password"
name="password_confirmation"
@input="v$.password_confirmation.$touch()"
/>
</BaseInputGroup>
<BaseButton :loading="isLoading" type="submit" variant="primary">
{{ $t('login.reset_password') }}
</BaseButton>
</form>
</template>
<script setup lang="ts">
import { ref, computed, reactive } from 'vue'
import useVuelidate from '@vuelidate/core'
import { required, email, minLength, sameAs } from '@vuelidate/validators'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useAuthStore } from '../../../stores/auth.store'
import { useNotificationStore } from '../../../stores/notification.store'
import { handleApiError } from '../../../utils/error-handling'
interface ResetPasswordForm {
email: string
password: string
password_confirmation: string
}
const notificationStore = useNotificationStore()
const authStore = useAuthStore()
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const formData = reactive<ResetPasswordForm>({
email: '',
password: '',
password_confirmation: '',
})
const isLoading = ref<boolean>(false)
const rules = computed(() => ({
email: { required, email },
password: {
required,
minLength: minLength(8),
},
password_confirmation: {
sameAsPassword: sameAs(formData.password),
},
}))
const v$ = useVuelidate(rules, formData)
const errorEmail = computed<string>(() => {
if (!v$.value.email.$error) {
return ''
}
if (v$.value.email.required.$invalid) {
return t('validation.required')
}
if (v$.value.email.email) {
return t('validation.email_incorrect')
}
return ''
})
const errorPassword = computed<string>(() => {
if (!v$.value.password.$error) {
return ''
}
if (v$.value.password.required.$invalid) {
return t('validation.required')
}
if (v$.value.password.minLength) {
return t('validation.password_min_length', {
count: v$.value.password.minLength.$params.min,
})
}
return ''
})
const errorConfirmPassword = computed<string>(() => {
if (!v$.value.password_confirmation.$error) {
return ''
}
if (v$.value.password_confirmation.sameAsPassword.$invalid) {
return t('validation.password_incorrect')
}
return ''
})
async function onSubmit(): Promise<void> {
v$.value.$touch()
if (v$.value.$invalid) {
return
}
try {
isLoading.value = true
await authStore.resetPassword({
email: formData.email,
password: formData.password,
password_confirmation: formData.password_confirmation,
token: route.params.token as string,
})
notificationStore.showNotification({
type: 'success',
message: t('login.password_reset_successfully'),
})
router.push('/login')
} catch (err: unknown) {
handleApiError(err)
} finally {
isLoading.value = false
}
}
</script>