259 lines
8.3 KiB
Vue
259 lines
8.3 KiB
Vue
<script setup lang="ts">
|
|
definePageMeta({
|
|
middleware: 'auth'
|
|
})
|
|
|
|
useHead({ title: 'Cambio Password - Visione' })
|
|
|
|
const { changePassword, user, syncUser } = useAuth()
|
|
const router = useRouter()
|
|
|
|
const formData = reactive({
|
|
current_password: '',
|
|
new_password: '',
|
|
confirm_password: ''
|
|
})
|
|
|
|
const isLoading = ref(false)
|
|
const errorMessage = ref('')
|
|
|
|
const showCurrent = ref(false)
|
|
const showNew = ref(false)
|
|
const showConfirm = ref(false)
|
|
|
|
const canSubmit = computed(() => {
|
|
return (
|
|
formData.current_password.length > 0 &&
|
|
formData.new_password.length >= 8 &&
|
|
formData.new_password === formData.confirm_password
|
|
)
|
|
})
|
|
|
|
const handleSubmit = async () => {
|
|
if (!canSubmit.value) return
|
|
|
|
isLoading.value = true
|
|
errorMessage.value = ''
|
|
|
|
try {
|
|
const res = await changePassword({
|
|
current_password: formData.current_password,
|
|
new_password: formData.new_password
|
|
})
|
|
|
|
if (res.ok) {
|
|
await syncUser()
|
|
await router.push('/profile')
|
|
}
|
|
} catch (error: any) {
|
|
errorMessage.value = error.data?.message || 'Errore. Verifica la password attuale.'
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="password-reset-page py-5">
|
|
<div class="container">
|
|
<div class="row justify-content-center">
|
|
<div class="col-12 col-xl-10">
|
|
<div class="row g-4 align-items-stretch">
|
|
<div class="col-12 col-lg-5">
|
|
<section class="password-reset-intro h-100">
|
|
<span class="password-reset-eyebrow">Accesso protetto</span>
|
|
<h1 class="h2 mb-3">Imposta nuova password</h1>
|
|
<p class="mb-4">
|
|
Aggiorna credenziali prima di continuare. Password nuova deve essere diversa da quella attuale.
|
|
</p>
|
|
<div v-if="user?.must_change_password" class="callout callout-highlight callout-warning mb-0">
|
|
<div class="callout-title d-flex align-items-center">
|
|
<BIIcon name="it-warning-circle" size="sm" color="warning" class="me-2" />
|
|
Azione richiesta
|
|
</div>
|
|
<p class="mb-0">Per motivi di sicurezza, imposta una nuova password per il tuo account.</p>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
<div class="col-12 col-lg-7">
|
|
<div class="card card-bg shadow-sm border-0 h-100">
|
|
<div class="card-body p-4 p-lg-5">
|
|
<h2 class="card-title h4 mb-4">Cambia password</h2>
|
|
|
|
<form @submit.prevent="handleSubmit" class="d-grid gap-4">
|
|
<div class="password-field">
|
|
<label for="current_password" class="form-label">Password attuale</label>
|
|
<div class="password-field-control">
|
|
<BIIcon name="it-lock" size="sm" class="password-field-icon" />
|
|
<input
|
|
id="current_password"
|
|
v-model="formData.current_password"
|
|
:type="showCurrent ? 'text' : 'password'"
|
|
class="form-control"
|
|
autocomplete="current-password"
|
|
>
|
|
<button
|
|
type="button"
|
|
class="btn btn-password-toggle"
|
|
:aria-label="showCurrent ? 'Nascondi password attuale' : 'Mostra password attuale'"
|
|
@click="showCurrent = !showCurrent"
|
|
>
|
|
<BIIcon :name="showCurrent ? 'it-password-visible' : 'it-password-invisible'" size="sm" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="password-field">
|
|
<label for="new_password" class="form-label">Nuova password</label>
|
|
<div class="password-field-control">
|
|
<BIIcon name="it-lock" size="sm" class="password-field-icon" />
|
|
<input
|
|
id="new_password"
|
|
v-model="formData.new_password"
|
|
:type="showNew ? 'text' : 'password'"
|
|
class="form-control"
|
|
autocomplete="new-password"
|
|
>
|
|
<button
|
|
type="button"
|
|
class="btn btn-password-toggle"
|
|
:aria-label="showNew ? 'Nascondi nuova password' : 'Mostra nuova password'"
|
|
@click="showNew = !showNew"
|
|
>
|
|
<BIIcon :name="showNew ? 'it-password-visible' : 'it-password-invisible'" size="sm" />
|
|
</button>
|
|
</div>
|
|
<div class="form-text">Minimo 8 caratteri.</div>
|
|
</div>
|
|
|
|
<div class="password-field">
|
|
<label for="confirm_password" class="form-label">Conferma nuova password</label>
|
|
<div class="password-field-control">
|
|
<BIIcon name="it-lock" size="sm" class="password-field-icon" />
|
|
<input
|
|
id="confirm_password"
|
|
v-model="formData.confirm_password"
|
|
:type="showConfirm ? 'text' : 'password'"
|
|
:class="{ 'is-invalid': formData.confirm_password && formData.confirm_password !== formData.new_password }"
|
|
class="form-control"
|
|
autocomplete="new-password"
|
|
>
|
|
<button
|
|
type="button"
|
|
class="btn btn-password-toggle"
|
|
:aria-label="showConfirm ? 'Nascondi conferma password' : 'Mostra conferma password'"
|
|
@click="showConfirm = !showConfirm"
|
|
>
|
|
<BIIcon :name="showConfirm ? 'it-password-visible' : 'it-password-invisible'" size="sm" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="errorMessage" class="alert alert-danger mb-0" role="alert">
|
|
{{ errorMessage }}
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
class="btn btn-primary w-100 py-2"
|
|
:disabled="!canSubmit || isLoading"
|
|
>
|
|
<span v-if="isLoading" class="spinner-border spinner-border-sm me-2"></span>
|
|
{{ isLoading ? 'Aggiornamento in corso' : 'Aggiorna password' }}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.password-reset-page {
|
|
background: linear-gradient(180deg, #f0f6fc 0%, #ffffff 65%);
|
|
}
|
|
|
|
.password-reset-intro {
|
|
background: #17324d;
|
|
border-radius: 8px;
|
|
color: #fff;
|
|
padding: 2rem;
|
|
}
|
|
|
|
.password-reset-intro :deep(h1),
|
|
.password-reset-intro :deep(p),
|
|
.password-reset-intro :deep(span) {
|
|
color: inherit;
|
|
}
|
|
|
|
.password-reset-eyebrow {
|
|
display: inline-block;
|
|
font-size: 0.8125rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.04em;
|
|
margin-bottom: 1rem;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.password-field {
|
|
display: grid;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.password-field-control {
|
|
align-items: center;
|
|
background: #fff;
|
|
border: 1px solid #c5d0db;
|
|
border-radius: 8px;
|
|
display: grid;
|
|
gap: 0.5rem;
|
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
padding-left: 0.875rem;
|
|
}
|
|
|
|
.password-field-control:focus-within {
|
|
border-color: #06c;
|
|
box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.16);
|
|
}
|
|
|
|
.password-field-icon {
|
|
color: #5c6f82;
|
|
}
|
|
|
|
.password-field-control .form-control {
|
|
background: transparent;
|
|
border: 0;
|
|
border-radius: 0;
|
|
box-shadow: none;
|
|
min-width: 0;
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
|
|
.btn-password-toggle {
|
|
align-items: center;
|
|
color: #06c;
|
|
display: inline-flex;
|
|
height: 100%;
|
|
justify-content: center;
|
|
min-height: 3rem;
|
|
padding: 0 0.875rem;
|
|
}
|
|
|
|
.btn-password-toggle:hover,
|
|
.btn-password-toggle:focus {
|
|
background: rgba(0, 102, 204, 0.08);
|
|
color: #06c;
|
|
}
|
|
|
|
@media (max-width: 991.98px) {
|
|
.password-reset-intro {
|
|
padding: 1.5rem;
|
|
}
|
|
}
|
|
</style>
|