Files
frontend/app/pages/auth/change-password.vue

218 lines
7.0 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="py-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-lg-8 col-xl-6">
<div v-if="user?.must_change_password" class="callout callout-highlight callout-warning mb-4">
<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>
<div class="card card-bg border-0 shadow-sm">
<div class="card-body p-4 p-lg-5">
<h1 class="h3 mb-3">Cambia password</h1>
<p class="text-muted mb-4">
Aggiorna credenziali prima di continuare. Password nuova deve essere diversa da quella attuale.
</p>
<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>
</template>
<style scoped>
.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;
}
</style>