mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-04-17 10:14:08 +00:00
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>
130 lines
3.1 KiB
Vue
130 lines
3.1 KiB
Vue
<template>
|
|
<form id="loginForm" class="mt-12 text-left" @submit.prevent="onSubmit">
|
|
<BaseInputGroup
|
|
:error="v$.email.$error && v$.email.$errors[0].$message"
|
|
:label="$t('login.email')"
|
|
class="mb-4"
|
|
required
|
|
>
|
|
<BaseInput
|
|
v-model="authStore.loginData.email"
|
|
:invalid="v$.email.$error"
|
|
focus
|
|
type="email"
|
|
name="email"
|
|
@input="v$.email.$touch()"
|
|
/>
|
|
</BaseInputGroup>
|
|
|
|
<BaseInputGroup
|
|
:error="v$.password.$error && v$.password.$errors[0].$message"
|
|
:label="$t('login.password')"
|
|
class="mb-4"
|
|
required
|
|
>
|
|
<BaseInput
|
|
v-model="authStore.loginData.password"
|
|
:invalid="v$.password.$error"
|
|
:type="inputType"
|
|
name="password"
|
|
@input="v$.password.$touch()"
|
|
>
|
|
<template #right>
|
|
<BaseIcon
|
|
:name="isShowPassword ? 'EyeIcon' : 'EyeSlashIcon'"
|
|
class="mr-1 text-muted cursor-pointer"
|
|
@click="isShowPassword = !isShowPassword"
|
|
/>
|
|
</template>
|
|
</BaseInput>
|
|
</BaseInputGroup>
|
|
|
|
<div class="mt-5 mb-8">
|
|
<div class="mb-4">
|
|
<router-link
|
|
to="forgot-password"
|
|
class="text-sm text-primary-400 hover:text-body"
|
|
>
|
|
{{ $t('login.forgot_password') }}
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
|
|
<BaseButton :loading="isLoading" type="submit">
|
|
{{ $t('login.login') }}
|
|
</BaseButton>
|
|
</form>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { required, email, helpers } from '@vuelidate/validators'
|
|
import { useVuelidate } from '@vuelidate/core'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useAuthStore } from '../../../stores/auth.store'
|
|
import { useNotificationStore } from '../../../stores/notification.store'
|
|
|
|
declare global {
|
|
interface Window {
|
|
demo_mode?: boolean
|
|
}
|
|
}
|
|
|
|
const notificationStore = useNotificationStore()
|
|
const authStore = useAuthStore()
|
|
const { t } = useI18n()
|
|
const router = useRouter()
|
|
const isLoading = ref<boolean>(false)
|
|
const isShowPassword = ref<boolean>(false)
|
|
|
|
const rules = {
|
|
email: {
|
|
required: helpers.withMessage(t('validation.required'), required),
|
|
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
|
},
|
|
password: {
|
|
required: helpers.withMessage(t('validation.required'), required),
|
|
},
|
|
}
|
|
|
|
const v$ = useVuelidate(
|
|
rules,
|
|
computed(() => authStore.loginData)
|
|
)
|
|
|
|
const inputType = computed<string>(() => {
|
|
return isShowPassword.value ? 'text' : 'password'
|
|
})
|
|
|
|
async function onSubmit(): Promise<void> {
|
|
v$.value.$touch()
|
|
|
|
if (v$.value.$invalid) {
|
|
return
|
|
}
|
|
|
|
isLoading.value = true
|
|
|
|
try {
|
|
await authStore.login(authStore.loginData)
|
|
|
|
router.push('/admin/dashboard')
|
|
|
|
notificationStore.showNotification({
|
|
type: 'success',
|
|
message: 'Logged in successfully.',
|
|
})
|
|
} catch {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (window.demo_mode) {
|
|
authStore.loginData.email = 'demo@invoiceshelf.com'
|
|
authStore.loginData.password = 'demo'
|
|
}
|
|
})
|
|
</script>
|