refractor(auth): removed password from everywhere
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
ChangePasswordRequest,
|
|
||||||
CurrentUserResponse, EmailRequest, NotificationListResponse,
|
CurrentUserResponse, EmailRequest, NotificationListResponse,
|
||||||
OkResponse,
|
OkResponse,
|
||||||
OtpRequestResponse, PasswordLoginRequest, RegisterCitizenRequest,
|
OtpRequestResponse, RegisterCitizenRequest,
|
||||||
RegisterCitizenResponse, RewardsResponse,
|
RegisterCitizenResponse, RewardsResponse,
|
||||||
TokenResponse, VerifyOtpRequest, UpdateProfileRequest
|
TokenResponse, VerifyOtpRequest, UpdateProfileRequest
|
||||||
} from "~/types/api";
|
} from "~/types/api";
|
||||||
@@ -44,9 +43,7 @@ export const useAuth = () => {
|
|||||||
user.value = res.user
|
user.value = res.user
|
||||||
const path = currentPath ?? useNuxtApp().$router.currentRoute.value.path
|
const path = currentPath ?? useNuxtApp().$router.currentRoute.value.path
|
||||||
|
|
||||||
if (res.user.must_change_password && path !== '/auth/change-password') {
|
if (redirectOnSuccess && path.startsWith('/auth')) {
|
||||||
await nuxtApp.runWithContext(() => navigateTo('/auth/change-password'))
|
|
||||||
} else if (redirectOnSuccess && path.startsWith('/auth')) {
|
|
||||||
await nuxtApp.runWithContext(() => navigateTo('/dashboard'))
|
await nuxtApp.runWithContext(() => navigateTo('/dashboard'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,12 +82,6 @@ export const useAuth = () => {
|
|||||||
const verifyOtp = (body: VerifyOtpRequest) =>
|
const verifyOtp = (body: VerifyOtpRequest) =>
|
||||||
useApiFetch<TokenResponse>('/api/v1/auth/otp/verify', { method: 'POST', body })
|
useApiFetch<TokenResponse>('/api/v1/auth/otp/verify', { method: 'POST', body })
|
||||||
|
|
||||||
const loginWithPassword = (body: PasswordLoginRequest) =>
|
|
||||||
useApiFetch<TokenResponse>('/api/v1/auth/login/password', { method: 'POST', body })
|
|
||||||
|
|
||||||
const changePassword = (body: ChangePasswordRequest) =>
|
|
||||||
useApiFetch<OkResponse>('/api/v1/auth/change-password', { method: 'POST', body })
|
|
||||||
|
|
||||||
const updateProfile = (body: UpdateProfileRequest) =>
|
const updateProfile = (body: UpdateProfileRequest) =>
|
||||||
useApiFetch<CurrentUserResponse>('/api/v1/me', { method: 'PATCH', body })
|
useApiFetch<CurrentUserResponse>('/api/v1/me', { method: 'PATCH', body })
|
||||||
|
|
||||||
@@ -119,8 +110,6 @@ export const useAuth = () => {
|
|||||||
register,
|
register,
|
||||||
requestOtp,
|
requestOtp,
|
||||||
verifyOtp,
|
verifyOtp,
|
||||||
loginWithPassword,
|
|
||||||
changePassword,
|
|
||||||
updateProfile,
|
updateProfile,
|
||||||
fetchCurrentUser,
|
fetchCurrentUser,
|
||||||
fetchRewards,
|
fetchRewards,
|
||||||
|
|||||||
@@ -1,217 +0,0 @@
|
|||||||
<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>
|
|
||||||
+22
-80
@@ -7,20 +7,15 @@ definePageMeta({
|
|||||||
|
|
||||||
useHead({ title: 'Accedi - Visione' })
|
useHead({ title: 'Accedi - Visione' })
|
||||||
|
|
||||||
const { requestOtp, verifyOtp, loginWithPassword, handleLoginSuccess } = useAuth()
|
const { requestOtp, verifyOtp, handleLoginSuccess } = useAuth()
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
// UI State
|
// UI State
|
||||||
const loginMethod = ref<'otp' | 'password'>('otp')
|
|
||||||
const otpStep = ref<1 | 2>(1)
|
const otpStep = ref<1 | 2>(1)
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const errorMessage = ref('')
|
const errorMessage = ref('')
|
||||||
|
|
||||||
|
|
||||||
const email = ref('')
|
const email = ref('')
|
||||||
const password = ref('')
|
|
||||||
const otpCode = ref('')
|
const otpCode = ref('')
|
||||||
const showPassword = ref(false)
|
|
||||||
|
|
||||||
const handleRequestOtp = async () => {
|
const handleRequestOtp = async () => {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
@@ -47,19 +42,6 @@ const handleVerifyOtp = async () => {
|
|||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePasswordLogin = async () => {
|
|
||||||
isLoading.value = true
|
|
||||||
errorMessage.value = ''
|
|
||||||
try {
|
|
||||||
const res = await loginWithPassword({ email: email.value, password: password.value })
|
|
||||||
await handleLoginSuccess(res.token)
|
|
||||||
} catch (err: any) {
|
|
||||||
errorMessage.value = 'Credenziali non valide.'
|
|
||||||
} finally {
|
|
||||||
isLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -94,73 +76,33 @@ const handlePasswordLogin = async () => {
|
|||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<form v-if="otpStep === 1" @submit.prevent="handleRequestOtp">
|
||||||
<div v-if="loginMethod === 'otp'">
|
|
||||||
<form v-if="otpStep === 1" @submit.prevent="handleRequestOtp">
|
|
||||||
<div class="form-group mb-4">
|
|
||||||
<input type="email" class="form-control" id="email" v-model="email" required>
|
|
||||||
<label for="email" :class="{ 'active': email.length > 0 }">Indirizzo Email</label>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary w-100" :disabled="isLoading">
|
|
||||||
<span v-if="isLoading" class="spinner-border spinner-border-sm me-2" role="status"></span>
|
|
||||||
Ricevi codice OTP
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<form v-else @submit.prevent="handleVerifyOtp">
|
|
||||||
<div class="alert alert-success mb-4 small">
|
|
||||||
Abbiamo inviato un codice OTP a <strong>{{ email }}</strong>.
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-4">
|
|
||||||
<input type="text" class="form-control text-center text-tracking-widest fs-4" id="otp" v-model="otpCode" required maxlength="6" autocomplete="one-time-code">
|
|
||||||
<label for="otp" :class="{ 'active': otpCode.length > 0 }">Codice a 6 cifre</label>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary w-100 mb-3" :disabled="isLoading">
|
|
||||||
<span v-if="isLoading" class="spinner-border spinner-border-sm me-2" role="status"></span>
|
|
||||||
Verifica e Accedi
|
|
||||||
</button>
|
|
||||||
<div class="text-center">
|
|
||||||
<a href="#" class="small text-decoration-none" @click.prevent="otpStep = 1">Modifica indirizzo email</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<form v-else @submit.prevent="handlePasswordLogin">
|
|
||||||
<div class="form-group mb-4">
|
<div class="form-group mb-4">
|
||||||
<input type="email" class="form-control" id="emailPwd" v-model="email" required>
|
<input type="email" class="form-control" id="email" v-model="email" required>
|
||||||
<label for="emailPwd" :class="{ 'active': email.length > 0 }">Indirizzo Email</label>
|
<label for="email" :class="{ 'active': email.length > 0 }">Indirizzo Email</label>
|
||||||
</div>
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary w-100" :disabled="isLoading">
|
||||||
|
|
||||||
<div class="form-group mb-4 position-relative">
|
|
||||||
<input :type="showPassword ? 'text' : 'password'" class="form-control pe-5" id="password" v-model="password" required>
|
|
||||||
<label for="password" :class="{ 'active': password.length > 0 }">Password</label>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn bg-transparent border-0 shadow-none position-absolute p-2"
|
|
||||||
style="right: 0; top: 2px; z-index: 10;"
|
|
||||||
@click="showPassword = !showPassword"
|
|
||||||
:aria-pressed="showPassword"
|
|
||||||
aria-label="Mostra/Nascondi password"
|
|
||||||
>
|
|
||||||
<BIIcon :name="showPassword ? 'it-password-visible' : 'it-password-invisible'" color="primary" size="sm" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary w-100 mb-3" :disabled="isLoading">
|
|
||||||
<span v-if="isLoading" class="spinner-border spinner-border-sm me-2" role="status"></span>
|
<span v-if="isLoading" class="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||||
Accedi
|
Ricevi codice OTP
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<form v-else @submit.prevent="handleVerifyOtp">
|
||||||
<div class="text-center mt-4">
|
<div class="alert alert-success mb-4 small">
|
||||||
<a href="#" class="small text-decoration-none" @click.prevent="loginMethod = loginMethod === 'otp' ? 'password' : 'otp'; errorMessage = ''">
|
Abbiamo inviato un codice OTP a <strong>{{ email }}</strong>.
|
||||||
{{ loginMethod === 'otp' ? 'Vuoi usare la password? Clicca qui' : 'Usa il login sicuro senza password (OTP)' }}
|
</div>
|
||||||
</a>
|
<div class="form-group mb-4">
|
||||||
</div>
|
<input type="text" class="form-control text-center text-tracking-widest fs-4" id="otp" v-model="otpCode" required maxlength="6" autocomplete="one-time-code">
|
||||||
|
<label for="otp" :class="{ 'active': otpCode.length > 0 }">Codice a 6 cifre</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary w-100 mb-3" :disabled="isLoading">
|
||||||
|
<span v-if="isLoading" class="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||||
|
Verifica e Accedi
|
||||||
|
</button>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="#" class="small text-decoration-none" @click.prevent="otpStep = 1">Modifica indirizzo email</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,14 +14,12 @@ const router = useRouter()
|
|||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const errorMessage = ref('')
|
const errorMessage = ref('')
|
||||||
const successMessage = ref(false)
|
const successMessage = ref(false)
|
||||||
const showPassword = ref(false)
|
|
||||||
|
|
||||||
const form = ref({
|
const form = ref({
|
||||||
first_name: '',
|
first_name: '',
|
||||||
last_name: '',
|
last_name: '',
|
||||||
fiscal_code: '',
|
fiscal_code: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
|
||||||
phone: '',
|
phone: '',
|
||||||
privacy: false
|
privacy: false
|
||||||
})
|
})
|
||||||
@@ -56,7 +54,6 @@ const handleRegister = async () => {
|
|||||||
last_name: form.value.last_name,
|
last_name: form.value.last_name,
|
||||||
fiscal_code: fiscalCode,
|
fiscal_code: fiscalCode,
|
||||||
email: form.value.email,
|
email: form.value.email,
|
||||||
password: form.value.password,
|
|
||||||
phone: form.value.phone || undefined
|
phone: form.value.phone || undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -89,7 +86,7 @@ const handleRegister = async () => {
|
|||||||
<div class="card-body p-4 p-md-5">
|
<div class="card-body p-4 p-md-5">
|
||||||
|
|
||||||
<div v-if="successMessage" class="alert alert-success" role="alert">
|
<div v-if="successMessage" class="alert alert-success" role="alert">
|
||||||
Registrazione completata con successo! Reindirizzamento al login...
|
Registrazione completata con successo! Reindirizzamento all'accesso con OTP...
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="errorMessage" class="alert alert-danger mb-4" role="alert">
|
<div v-if="errorMessage" class="alert alert-danger mb-4" role="alert">
|
||||||
@@ -122,33 +119,15 @@ const handleRegister = async () => {
|
|||||||
<small class="form-text text-muted">Sarà utilizzato come identificativo univoco nel sistema PA.</small>
|
<small class="form-text text-muted">Sarà utilizzato come identificativo univoco nel sistema PA.</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="h5 text-primary font-weight-bold mt-5 mb-4 border-bottom pb-2">Credenziali e Contatti</h2>
|
<h2 class="h5 text-primary font-weight-bold mt-5 mb-4 border-bottom pb-2">Contatti</h2>
|
||||||
|
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12 col-md-6 mb-4 mb-md-0">
|
<div class="col-12">
|
||||||
<div class="form-group">
|
<div class="form-group position-relative">
|
||||||
<input type="email" class="form-control" id="regEmail" v-model="form.email" required>
|
<input type="email" class="form-control" id="regEmail" v-model="form.email" required>
|
||||||
<label for="regEmail" :class="{ 'active': form.email.length > 0 }">Indirizzo Email</label>
|
<label for="regEmail" :class="{ 'active': form.email.length > 0 }">Indirizzo Email</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6">
|
|
||||||
<!-- INPUT PASSWORD FIXATO -->
|
|
||||||
<div class="form-group position-relative">
|
|
||||||
<input :type="showPassword ? 'text' : 'password'" class="form-control pe-5" id="regPwd" v-model="form.password" required minlength="10">
|
|
||||||
<label for="regPwd" :class="{ 'active': form.password.length > 0 }">Password (min. 10 crt)</label>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn bg-transparent border-0 shadow-none position-absolute p-2"
|
|
||||||
style="right: 0; top: 2px; z-index: 10;"
|
|
||||||
@click="showPassword = !showPassword"
|
|
||||||
:aria-pressed="showPassword"
|
|
||||||
aria-label="Mostra/Nascondi password"
|
|
||||||
>
|
|
||||||
<BIIcon :name="showPassword ? 'it-password-visible' : 'it-password-invisible'" color="primary" size="sm" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group mb-5">
|
<div class="form-group mb-5">
|
||||||
@@ -169,6 +148,9 @@ const handleRegister = async () => {
|
|||||||
<span v-if="isLoading" class="spinner-border spinner-border-sm me-2" role="status"></span>
|
<span v-if="isLoading" class="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||||
Registra Profilo
|
Registra Profilo
|
||||||
</button>
|
</button>
|
||||||
|
<p class="small text-muted mt-3 mb-0">
|
||||||
|
Dopo la registrazione potrai accedere usando OTP via email o CIE, quando disponibile.
|
||||||
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -185,8 +167,4 @@ const handleRegister = async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* Previene che il testo finisca sotto l'icona */
|
|
||||||
.pe-5 {
|
|
||||||
padding-right: 3rem !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+1
-1
@@ -112,7 +112,7 @@ useHead({
|
|||||||
</li>
|
</li>
|
||||||
<li class="mt-3">
|
<li class="mt-3">
|
||||||
<BIIcon name="it-lock" color="primary" class="me-2" />
|
<BIIcon name="it-lock" color="primary" class="me-2" />
|
||||||
<strong>Sicurezza:</strong> Login Passwordless (OTP nativo) e sistema di autorizzazione Role-Based basato su token JWT.
|
<strong>Sicurezza:</strong> Accesso con OTP e sistema di autorizzazione Role-Based basato su token JWT.
|
||||||
</li>
|
</li>
|
||||||
<li class="mt-3">
|
<li class="mt-3">
|
||||||
<BIIcon name="it-piu" color="primary" class="me-2" />
|
<BIIcon name="it-piu" color="primary" class="me-2" />
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ useHead({
|
|||||||
<h2 class="h3 font-weight-bold mb-3">3. Finalità del Trattamento</h2>
|
<h2 class="h3 font-weight-bold mb-3">3. Finalità del Trattamento</h2>
|
||||||
<p class="text-muted mb-4">
|
<p class="text-muted mb-4">
|
||||||
I dati forniti verranno trattati esclusivamente per le seguenti finalità:
|
I dati forniti verranno trattati esclusivamente per le seguenti finalità:
|
||||||
<br>- Consentire la registrazione e l'autenticazione sicura (incluso login Passwordless tramite OTP).
|
<br>- Consentire la registrazione e l'autenticazione sicura tramite OTP.
|
||||||
<br>- Gestire la presa in carico e la risoluzione delle segnalazioni urbane da parte delle squadre operative.
|
<br>- Gestire la presa in carico e la risoluzione delle segnalazioni urbane da parte delle squadre operative.
|
||||||
<br>- Fornire comunicazioni di servizio relative allo stato di avanzamento delle segnalazioni.
|
<br>- Fornire comunicazioni di servizio relative allo stato di avanzamento delle segnalazioni.
|
||||||
<br>- Attribuire punteggi virtuali all'utente nell'ambito del sistema di <em>Gamification</em> volto a incentivare la cittadinanza attiva.
|
<br>- Attribuire punteggi virtuali all'utente nell'ambito del sistema di <em>Gamification</em> volto a incentivare la cittadinanza attiva.
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ const formatDate = (dateString: string) =>
|
|||||||
<div class="d-grid gap-2">
|
<div class="d-grid gap-2">
|
||||||
<NuxtLink to="/reports/new" class="btn btn-primary">Nuova segnalazione</NuxtLink>
|
<NuxtLink to="/reports/new" class="btn btn-primary">Nuova segnalazione</NuxtLink>
|
||||||
<NuxtLink to="/notifications" class="btn btn-outline-primary">Notifiche</NuxtLink>
|
<NuxtLink to="/notifications" class="btn btn-outline-primary">Notifiche</NuxtLink>
|
||||||
<NuxtLink to="/auth/change-password" class="btn btn-outline-secondary">Cambia password</NuxtLink>
|
|
||||||
<button type="button" class="btn btn-outline-danger" @click="logout">
|
<button type="button" class="btn btn-outline-danger" @click="logout">
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Vendored
-13
@@ -29,7 +29,6 @@ export interface CurrentUser {
|
|||||||
email: string;
|
email: string;
|
||||||
fiscal_code: string;
|
fiscal_code: string;
|
||||||
points: number;
|
points: number;
|
||||||
must_change_password: boolean;
|
|
||||||
role: string;
|
role: string;
|
||||||
roles: string[];
|
roles: string[];
|
||||||
permissions: string[];
|
permissions: string[];
|
||||||
@@ -45,7 +44,6 @@ export interface AdminUser {
|
|||||||
phone?: string;
|
phone?: string;
|
||||||
banned?: boolean;
|
banned?: boolean;
|
||||||
points?: number;
|
points?: number;
|
||||||
must_change_password?: boolean;
|
|
||||||
roles?: string; // Comma-separated
|
roles?: string; // Comma-separated
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,23 +189,12 @@ export interface RegisterCitizenRequest {
|
|||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
|
||||||
fiscal_code: string;
|
fiscal_code: string;
|
||||||
phone?: string;
|
phone?: string;
|
||||||
birth_date?: string;
|
birth_date?: string;
|
||||||
birth_place?: string;
|
birth_place?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PasswordLoginRequest {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChangePasswordRequest {
|
|
||||||
current_password: string;
|
|
||||||
new_password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpdateProfileRequest {
|
export interface UpdateProfileRequest {
|
||||||
first_name?: string;
|
first_name?: string;
|
||||||
last_name?: string;
|
last_name?: string;
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 90 KiB |
Reference in New Issue
Block a user