Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | 14x 48x 14x 1x 8x 1x 8x 14x 1x 1x 1x 1x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 9x 9x 9x 9x 9x 4x 5x 5x 5x 9x | <template>
<AuthLayout
:presentation-title="t('auth.presentationTitleLogin')"
:presentation-subtitle="t('auth.presentationSubtitleLogin')"
>
<span>{{ t('auth.newToFlows') }}</span>
<router-link to="/signup" class="btn btn-success shadow p-2 my-3 w-100">
{{ t('auth.tryItFree') }}
</router-link>
<form class="signin-form" @submit.prevent="handleLogin">
<hr class="w-100" />
<span class="mb-2">{{ t('auth.alreadyMember') }}</span>
<label for="email" class="form-label">{{ t('auth.email') }}</label>
<input
id="email"
v-model="email"
class="form-control"
type="email"
name="username"
autocomplete="username"
required
/>
<label for="password" class="form-label">{{ t('auth.password') }}</label>
<div class="password-wrapper">
<input
id="password"
v-model="password"
class="form-control pe-5"
:type="showPassword ? 'text' : 'password'"
name="password"
autocomplete="current-password"
required
/>
<button
type="button"
class="password-toggle"
:aria-label="showPassword ? t('auth.hidePassword') : t('auth.showPassword')"
@click="showPassword = !showPassword"
>
<FontAwesomeIcon :icon="showPassword ? 'eye-slash' : 'eye'" />
</button>
</div>
<router-link to="/forgot-password" class="forgot-password">{{
t('auth.forgotPassword')
}}</router-link>
<button type="submit" class="btn btn-secondary shadow p-2 my-3" :disabled="loading">
{{ t('auth.signIn') }}
</button>
<div v-if="error" class="alert alert-danger" role="alert">
{{ error }}
<div v-if="isNotConfirmedError" class="mt-2">
<router-link
:to="{ path: '/signup', query: { email, confirm: 'true' } }"
class="btn btn-sm btn-secondary w-100"
>
{{ t('auth.confirmYourAccount') }}
</router-link>
</div>
</div>
<div v-if="resetSuccess" class="alert alert-success" role="alert">
{{ t('auth.resetPasswordSuccess') }}
</div>
</form>
<div class="row vertical-center">
<div class="col-5"><hr /></div>
<div class="col-2 separator-text">{{ t('auth.or') }}</div>
<div class="col-5"><hr /></div>
</div>
<a
href="#"
class="btn btn-light signin-google shadow p-2 my-3 w-100"
@click.prevent="authStore.signInWithGoogle()"
>
<span class="icon">
<svg
width="18"
height="18"
viewBox="0 0 256 262"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
>
<path
d="M254.69 106.22H129.775v51.002h73.14c-3.666 15.153-8.197 20.653-12.692 25.788-5.01 5.702-9.894 10.653-15.43 14.717l-1.413 2.294 43.225 30.734 2.993-1.17c22.808-22.018 36.388-55.09 36.388-98.727 0-8.456.243-16.632-1.297-24.638"
fill="#4486F4"
/>
<path
d="M130.965 261.93c35.635 0 66.484-10.96 88.633-32.345l-44.805-31.858c-10.44 7.663-23.235 12.12-43.828 12.12-34.67 0-64.096-22.38-74.674-53.472l-2.52-1.466-39.713 32.06.18 3.354c21.65 42.487 65.774 71.605 116.728 71.605"
fill="#34A853"
/>
<path
d="M52.083 130.965c0-9.032 1.538-17.7 4.333-25.78l.273-2.36L15.08 71.47l-1.506 1.43C4.9 90.4 0 110.106 0 130.964c0 21.375 5.154 41.536 14.237 59.36l42.054-33.95c-2.713-7.976-4.207-16.516-4.207-25.41"
fill="#FBBC05"
/>
<path
d="M56.416 105.185c10.685-30.9 40.015-53.102 74.55-53.102 19.68 0 37.663 7.225 51.482 19.146l36.562-37.22C195.753 12.88 164.863 0 130.965 0 79.493 0 34.98 29.708 13.575 72.9l42.84 32.285"
fill="#EC4235"
/>
</svg>
</span>
<span class="text">{{ t('auth.signInWithGoogle') }}</span>
</a>
</AuthLayout>
</template>
<script setup lang="ts">
// @implements UC-AUTH-002.1
import { ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useUILanguage } from '@/composables/useUILanguage'
import AuthLayout from '@/components/AuthLayout.vue'
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const { t } = useUILanguage()
const email = ref('')
const password = ref('')
const showPassword = ref(false)
const loading = ref(false)
const error = ref('')
const isNotConfirmedError = ref(false)
const resetSuccess = ref(false)
onMounted(() => {
// Check if redirected from password reset
Iif (route.query.resetSuccess === 'true') {
resetSuccess.value = true
// Clear query param from URL
router.replace({ query: {} })
}
})
async function handleLogin() {
loading.value = true
error.value = ''
isNotConfirmedError.value = false
try {
await authStore.login(email.value, password.value)
router.push('/')
} catch (err: any) {
const isUserNotConfirmedError =
err?.name === 'UserNotConfirmedException' ||
err?.code === 'UserNotConfirmedException' ||
err?.message === 'User is not confirmed.'
isNotConfirmedError.value = isUserNotConfirmedError
error.value = isUserNotConfirmedError
? t('auth.userNotConfirmed')
: err.message || 'Login failed'
} finally {
loading.value = false
}
}
</script>
<style scoped>
.icon {
align-items: center;
justify-content: center;
margin-right: 8px;
}
.forgot-password {
font-size: small;
}
.separator-text {
text-align: center;
color: #666666;
font-size: small;
}
</style>
|