- Sistemato INVITI alla App

- Completamento Profilo
- Registrazione tramite Invito, senza richiedere conferma email.
This commit is contained in:
Surya Paolo
2025-11-18 23:56:08 +01:00
parent fc569192e7
commit 4985e7565d
98 changed files with 2209 additions and 3595720 deletions

View File

@@ -1,719 +1,24 @@
// ========================================
// 🎨 MODERN SIGNUP COMPONENT STYLES
// ========================================
// Variables
$primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
$success-gradient: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
$error-gradient: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
$card-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
$card-shadow-hover: 0 15px 50px rgba(0, 0, 0, 0.15);
$border-radius: 20px;
$transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
// ========================================
// CONTAINER & LAYOUT
// ========================================
.signup-container {
min-height: 100vh;
display: flex;
flex-direction: column;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 20px 15px 100px 15px; // Extra padding bottom for fixed buttons
position: relative;
@media (max-width: 768px) {
padding: 0px 10px 110px 10px;
}
}
.signup-content {
max-width: 500px;
width: 100%;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 0px;
}
// ========================================
// HEADER SECTION
// ========================================
.header-section {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
margin-bottom: 10px;
/* spazio tra logo e titolo */
animation: fadeInDown 0.6s ease-out;
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
.title-section {
flex: 1;
margin-top: 5px;
.signup-title {
font-size: 2rem;
font-weight: 700;
background: $primary-gradient;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0 0 5px 0;
@media (max-width: 768px) {
font-size: 1.5rem;
}
}
.signup-subtitle {
color: #64748b;
font-size: 13px;
margin: 0;
font-weight: 400;
@media (max-width: 768px) {
font-size: 0.75rem;
}
}
}
// ========================================
// SUCCESS & ERROR CARDS
// ========================================
.already-logged,
.error-card {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
}
.success-card,
.error-card {
background: white;
border-radius: $border-radius;
padding: 48px 32px;
box-shadow: $card-shadow;
text-align: center;
animation: scaleIn 0.5s ease-out;
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@media (max-width: 768px) {
padding: 32px 24px;
}
}
.success-message {
font-size: 24px;
font-weight: 600;
color: #1e293b;
margin-bottom: 32px;
@media (max-width: 768px) {
font-size: 20px;
margin-bottom: 24px;
}
}
.error-message {
font-size: 20px;
color: #1e293b;
margin-top: 16px;
@media (max-width: 768px) {
font-size: 18px;
}
}
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
flex-wrap: wrap;
.action-btn {
min-width: 160px;
height: 48px;
font-weight: 600;
text-transform: none;
letter-spacing: 0.3px;
@media (max-width: 768px) {
min-width: 140px;
height: 44px;
}
}
}
// ========================================
// FORM CONTAINER
// ========================================
.form-container {
background: white;
border-radius: $border-radius;
padding: 36px 24px 24px 24px;
box-shadow: $card-shadow;
transition: $transition;
animation: fadeInUp 0.6s ease-out;
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
padding: 24px 16px 16px 16px;
border-radius: 16px;
}
}
.registration-form {
position: relative;
}
// ========================================
// PROGRESS INDICATOR
// ========================================
.progress-indicator {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40px;
position: relative;
padding: 0 20px;
@media (max-width: 768px) {
margin-bottom: 32px;
padding: 0 10px;
}
}
.progress-line {
position: absolute;
top: 20px;
left: 50px;
right: 50px;
height: 4px;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 0;
border-radius: 2px;
@media (max-width: 768px) {
left: 35px;
right: 35px;
top: 18px;
}
}
.progress-step {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
z-index: 1;
position: relative;
.step-circle {
width: 42px;
height: 42px;
border-radius: 50%;
background: #e2e8f0;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 16px;
color: #94a3b8;
transition: $transition;
border: 3px solid white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
@media (max-width: 768px) {
width: 36px;
height: 36px;
font-size: 14px;
}
}
.step-label {
font-size: 13px;
font-weight: 600;
color: #94a3b8;
transition: $transition;
text-align: center;
@media (max-width: 768px) {
font-size: 11px;
}
}
&.active {
.step-circle {
background: $primary-gradient;
color: white;
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.step-label {
color: #667eea;
font-weight: 700;
}
}
&.completed {
.step-circle {
background: $success-gradient;
color: white;
}
.step-label {
color: #11998e;
}
}
}
// ========================================
// CAROUSEL & SLIDES
// ========================================
.modern-carousel {
margin-bottom: 0;
min-height: 400px;
@media (max-width: 768px) {
min-height: 200px;
}
:deep(.q-carousel__slide) {
padding: 0;
}
}
.carousel-slide {
display: flex;
align-items: flex-start;
justify-content: center;
padding: 0 !important;
}
.slide-content {
width: 100%;
max-width: 100%;
animation: slideIn 0.4s ease-out;
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
}
.slide-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 12px;
margin-left: 6px;
margin-bottom: 32px;
@media (max-width: 768px) {
margin-bottom: 5px;
}
.q-icon {
margin-bottom: 16px;
opacity: 0;
animation: iconPop 0.6s ease-out 0.2s forwards;
@keyframes iconPop {
from {
opacity: 0;
transform: scale(0) rotate(-180deg);
}
to {
opacity: 1;
transform: scale(1) rotate(0deg);
}
}
@media (max-width: 768px) {
font-size: px !important;
margin-bottom: 0px;
}
}
.slide-title {
font-size: 26px;
font-weight: 700;
color: #1e293b;
margin: 0;
@media (max-width: 768px) {
font-size: 22px;
}
}
.slide-description {
font-size: 15px;
color: #64748b;
margin: 0;
font-weight: 400;
@media (max-width: 768px) {
font-size: 14px;
}
}
}
// ========================================
// FORM FIELDS
// ========================================
.form-fields {
display: flex;
flex-direction: column;
gap: 20px;
@media (max-width: 768px) {
gap: 15px;
}
}
.name-fields {
display: flex;
flex-direction: column;
gap: 20px;
@media (max-width: 768px) {
gap: 0px;
}
}
.modern-input {
:deep(.q-field__control) {
height: 56px;
border-radius: 14px;
background: #f8fafc;
transition: $transition;
&:hover {
background: #f1f5f9;
}
@media (max-width: 768px) {
height: 52px;
border-radius: 12px;
}
}
:deep(.q-field__label) {
font-weight: 500;
font-size: 14px;
}
:deep(.q-field__control)::before {
border-color: #e2e8f0;
}
:deep(.q-field__control):hover::before {
border-color: #cbd5e1;
}
:deep(.q-field--focused .q-field__control)::before {
border-color: #667eea;
border-width: 2px;
}
:deep(.q-field--filled .q-field__control) {
background: #f8fafc;
}
:deep(.q-field__prepend) {
.q-icon {
font-size: 22px;
margin-right: 4px;
}
}
:deep(.q-field__append) {
.q-btn {
opacity: 0.6;
transition: $transition;
&:hover {
opacity: 1;
}
}
}
}
// ========================================
// PRIVACY SECTION
// ========================================
.privacy-section {
margin-top: 24px;
@media (max-width: 768px) {
margin-top: 20px;
}
.q-separator {
background: #e2e8f0;
margin: 24px 0;
@media (max-width: 768px) {
margin: 20px 0;
}
}
}
.privacy-link {
text-align: center;
margin-bottom: 16px;
.q-btn {
font-weight: 500;
text-transform: none;
font-size: 14px;
&:hover {
background: rgba(102, 126, 234, 0.08);
}
}
}
.privacy-checkbox {
:deep(.q-checkbox__label) {
font-size: 14px;
line-height: 1.3;
color: #475569;
font-weight: 500;
}
:deep(.q-checkbox__inner) {
width: 22px;
height: 22px;
border-radius: 6px;
}
@media (max-width: 768px) {
:deep(.q-checkbox__label) {
font-size: 13px;
}
}
}
// ========================================
// FIXED BOTTOM ACTIONS
// ========================================
.fixed-bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
padding: 16px 20px;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.08);
z-index: 1000;
backdrop-filter: blur(10px);
border-top: 1px solid #e2e8f0;
@media (max-width: 768px) {
padding: 14px 16px;
}
}
.action-row {
max-width: 500px;
margin: 0 auto;
display: flex;
gap: 12px;
justify-content: space-between;
align-items: center;
@media (max-width: 768px) {
gap: 10px;
}
}
.nav-btn {
font-weight: 600;
text-transform: none;
letter-spacing: 0.3px;
height: 50px;
border-radius: 14px;
transition: $transition;
font-size: 15px;
@media (max-width: 768px) {
height: 46px;
border-radius: 12px;
font-size: 14px;
}
&:hover:not([disabled]) {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
&:active:not([disabled]) {
transform: translateY(0);
}
&[disabled] {
opacity: 0.5;
}
}
.prev-btn {
flex: 0 0 auto;
min-width: 130px;
@media (max-width: 768px) {
min-width: 110px;
}
}
.next-btn {
flex: 1;
background: $primary-gradient;
&:hover:not([disabled]) {
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
}
.submit-btn {
flex: 1;
background: $success-gradient;
font-size: 16px;
font-weight: 700;
&:hover:not([disabled]) {
box-shadow: 0 6px 20px rgba(17, 153, 142, 0.4);
}
@media (max-width: 768px) {
font-size: 15px;
}
}
// ========================================
// TELEGRAM SETUP
// ========================================
.telegram-setup,
.telegram-registration {
text-align: center;
padding: 48px 24px;
background: white;
border-radius: $border-radius;
box-shadow: $card-shadow;
@media (max-width: 768px) {
padding: 32px 20px;
}
.telegram-message {
font-size: 18px;
color: #475569;
margin: 24px 0;
line-height: 1.3;
@media (max-width: 768px) {
font-size: 16px;
margin: 20px 0;
}
}
.telegram-btn {
min-width: 200px;
height: 54px;
font-size: 16px;
font-weight: 600;
text-transform: none;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
&:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
}
@media (max-width: 768px) {
min-width: 180px;
height: 50px;
font-size: 15px;
}
}
}
// ========================================
// UTILITIES
// ========================================
.signup {
width: 100%;
margin: 0 auto;
max-width: 500px;
max-width: 450px;
}
.wrapper {
display: flex;
align-items: center;
justify-content: center;
}
// Legacy classes (kept for compatibility)
.clCellCode,
.clCellCode {
border-radius: 32px;
border-right: #2d2260;
height: 50px;
font-size: 1rem;
padding: 8px;
}
.clCell {
border-radius: 32px;
border-right: #2d2260;
@@ -722,43 +27,20 @@ $transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
padding: 8px;
}
.vue-country-select {
.vue-country-select{
border-radius: 32px;
}
.myuserinvitante {
.myuserinvitante{
font-weight: bold;
color: #667eea;
color: red;
font-size: 1.5rem;
}
.cosa_chiedere {
font-weight: 600;
color: #667eea;
.cosa_chiedere{
font-weight: bold;
color: blue;
font-size: 1rem;
padding: 10px;
text-align: center;
}
// ========================================
// ANIMATIONS & TRANSITIONS
// ========================================
.q-carousel__slide {
animation: fadeSlide 0.3s ease-out;
@keyframes fadeSlide {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
}
// Smooth scroll behavior
* {
scroll-behavior: smooth;
}

View File

@@ -1,30 +1,42 @@
import { tools } from '@tools'
import { tools } from '@tools';
import type { ISignupOptions } from 'model'
import type { ISignupOptions } from 'model';
import { Logo } from '@src/components/logo'
import { Logo } from '@src/components/logo';
import { CTitleBanner } from '../CTitleBanner'
import { CCopyBtn } from '../CCopyBtn'
import { CRegistration } from '../CRegistration'
import { PagePolicy } from '../PagePolicy'
import { computed, defineComponent, onMounted, reactive, ref, watch } from 'vue'
import { CSignIn } from '@src/components/CSignIn'
import { useQuasar } from 'quasar'
import { useI18n } from 'vue-i18n'
import { DefaultProfile, useUserStore } from '@store/UserStore'
import useValidate from '@vuelidate/core'
import useVuelidate from '@vuelidate/core'
// import 'vue-country-code/dist/vue-country-code.css'
import { shared_consts } from '@src/common/shared_vuejs'
import { CTitleBanner } from '../CTitleBanner';
import { CCopyBtn } from '../CCopyBtn';
import { CRegistration } from '../CRegistration';
import { PagePolicy } from '../PagePolicy';
import { computed, defineComponent, onMounted, reactive, ref, watch } from 'vue';
import { CSignIn } from '@src/components/CSignIn';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { DefaultProfile, useUserStore } from '@store/UserStore';
import useValidate from '@vuelidate/core';
import useVuelidate from '@vuelidate/core';
import { minLength, required, sameAs } from '@vuelidate/validators'
import { shared_consts } from '@src/common/shared_vuejs';
import { complexity, complexityUser, registereduser, aportadorexist } from '../../validation'
import { minLength, required, sameAs } from '@vuelidate/validators';
import { useRoute, useRouter } from 'vue-router'
import { static_data } from '@src/db/static_data'
import { useGlobalStore } from '@store/globalStore'
// import { ValidationRuleset } from 'vuelidate'
import {
complexity,
complexityUser,
registereduser,
aportadorexist,
} from '../../validation';
// import 'vue3-tel-input/dist/vue3-tel-input.css'
import { useRoute, useRouter } from 'vue-router';
import { static_data } from '@src/db/static_data';
import { useGlobalStore } from '@store/globalStore';
import { useInvitaAmicoStore } from 'app/src/stores/useInvitaAmicoStore';
// import {Loading, QSpinnerFacebook, QSpinnerGears} from 'quasar'
export default defineComponent({
name: 'CSignUp',
@@ -80,59 +92,78 @@ export default defineComponent({
required: false,
default: false,
},
token: {
type: String,
required: false,
default: '',
},
},
setup(props, { emit }) {
const $q = useQuasar()
const { t } = useI18n()
const userStore = useUserStore()
const $route = useRoute()
const $router = useRouter()
const $q = useQuasar();
const { t } = useI18n();
const userStore = useUserStore();
const $route = useRoute();
const $router = useRouter();
const countryname = ref('')
const iamadult = ref(false)
const duplicate_email = ref(false)
const duplicate_username = ref(false)
const visureg = ref(false)
const showpolicy = ref(false)
const visubuttBOT = ref(false)
const isalreadyReg = ref(false)
const needTelegram = ref(false)
const slide = ref('1')
const inputAportador = ref(<any>null)
const inputEmail = ref(<any>null)
const inputUsername = ref(<any>null)
const inputName = ref(<any>null)
const inputSurname = ref(<any>null)
const inputPassword = ref(<any>null)
const inputPassword2 = ref(<any>null)
const countryname = ref('');
const iamadult = ref(false);
const duplicate_email = ref(false);
const duplicate_username = ref(false);
const visureg = ref(false);
const showpolicy = ref(false);
const visubuttBOT = ref(false);
const isalreadyReg = ref(false);
const needTelegram = ref(false);
const slide = ref('1');
const inputAportador = ref(<any>null);
const inputEmail = ref(<any>null);
const inputUsername = ref(<any>null);
const inputName = ref(<any>null);
const inputSurname = ref(<any>null);
const inputPassword = ref(<any>null);
const inputPassword2 = ref(<any>null);
const invitaStore = useInvitaAmicoStore();
const checkifDisabled = computed(() => {
let ret = true
let ret = true;
if (slide.value === '1') {
// Email + Aportador
ret = !signup.email || (tools.getAskToVerifyReg() && (!signup.aportador_solidario || inputAportador.value?.hasError)) || (inputEmail.value && inputEmail.value.hasError)
// Invitante + Email
ret =
!signup.email ||
(tools.getAskToVerifyReg() &&
(!signup.aportador_solidario || inputAportador.value.hasError)) ||
(inputEmail.value && inputEmail.value.hasError);
} else if (slide.value === '2') {
// Username + Nome/Cognome
ret = !signup.username || (inputUsername.value && inputUsername.value.hasError)
// Username
ret = !signup.username || (inputUsername.value && inputUsername.value.hasError);
if (tools.getConfSiteOptionEnabled(shared_consts.ConfSite.regNameSurnameMandatory)) {
ret = ret || (!signup.name || (inputName.value && inputName.value.hasError))
ret = ret || (!signup.surname || (inputSurname.value && inputSurname.value.hasError))
if (
tools.getConfSiteOptionEnabled(shared_consts.ConfSite.regNameSurnameMandatory)
) {
ret = ret || !signup.name || (inputName.value && inputName.value.hasError);
ret =
ret || !signup.surname || (inputSurname.value && inputSurname.value.hasError);
}
} else if (slide.value === '3') {
// Password + Ripeti Password
ret = !signup.password || (!inputPassword.value || (inputPassword.value && inputPassword.value.hasError)) || (!inputPassword2.value || (inputPassword2.value && inputPassword2.value.hasError))
// Password
ret =
!signup.password ||
!inputPassword.value ||
(inputPassword.value && inputPassword.value.hasError) ||
!inputPassword2.value ||
(inputPassword2.value && inputPassword2.value.hasError);
}
return ret
})
return ret;
});
const typePassword = ref('password')
const typePassword = ref('password');
const ap_iniziale = ref('')
const ap_iniziale = ref('');
const globalStore = useGlobalStore()
const site = computed(() => globalStore.site)
const globalStore = useGlobalStore();
const site = computed(() => globalStore.site);
const signup = reactive(<ISignupOptions>{
email: '',
@@ -141,10 +172,10 @@ export default defineComponent({
surname: '',
password: '',
repeatPassword: '',
terms: true, // ✅ GIÀ SPUNTATA DI DEFAULT
terms: true,
profile: DefaultProfile,
aportador_solidario: '',
})
});
const validations: any = computed(() => {
const valid: any = {
@@ -164,198 +195,278 @@ export default defineComponent({
registereduser,
},
name: {
required: (props.collettivo || tools.getConfSiteOptionEnabled(shared_consts.ConfSite.regNameSurnameMandatory)) ? true : false,
required:
props.collettivo ||
tools.getConfSiteOptionEnabled(shared_consts.ConfSite.regNameSurnameMandatory)
? true
: false,
},
surname: {
required: (tools.getConfSiteOptionEnabled(shared_consts.ConfSite.regNameSurnameMandatory)) ? true : false,
required: tools.getConfSiteOptionEnabled(
shared_consts.ConfSite.regNameSurnameMandatory
)
? true
: false,
},
terms: {
required,
},
aportador_solidario: {
aportadorexist,
required
}
}
required,
},
};
if (props.show_namesurname) {
valid.name = {}
valid.surname = {}
valid.name = {};
valid.surname = {};
}
return valid
})
return valid;
});
// @ts-ignore
const v$ = useVuelidate(validations, signup)
const v$ = useVuelidate(validations, signup);
const invited = ref($route.params.invited)
const usernameteleg = ref($route.params.usernameteleg)
const idteleg = ref($route.params.idteleg)
const invited = ref($route.params.invited);
const usernameteleg = ref($route.params.usernameteleg);
const idteleg = ref($route.params.idteleg);
watch(() => slide.value, (to: any, from: any) => {
if (slide.value === '3') {
v$.value.$touch()
watch(
() => slide.value,
(to: any, from: any) => {
if (slide.value === '3') {
v$.value.$touch();
}
}
})
);
watch(() => invited, (to: any, from: any) => {
if (props.showaportador) {
console.log('changeaportador', $route.params.invited)
if (!signup.aportador_solidario) {
if ($route.params.invited) {
// @ts-ignore
signup.aportador_solidario = $route.params.invited
watch(
() => invited,
(to: any, from: any) => {
if (props.showaportador) {
console.log('changeaportador', $route.params.invited);
if (!signup.aportador_solidario) {
if ($route.params.invited) {
// @ts-ignore
signup.aportador_solidario = $route.params.invited;
}
}
}
}
})
);
function allowSubmit() {
let error = v$.value.$error || v$.value.$invalid || globalStore.serverError
let error = v$.value.$error || v$.value.$invalid || globalStore.serverError;
if (props.showadultcheck)
error = error || !iamadult.value
if (props.showadultcheck) error = error || !iamadult.value;
if (props.showcell) {
if (signup.profile)
error = error || signup.profile.cell!.length <= 6
else
error = true
if (signup.profile) error = error || signup.profile.cell!.length <= 6;
else error = true;
}
if (tools.getAskToVerifyReg()) {
error = error || !signup.aportador_solidario
error = error || !signup.aportador_solidario;
}
return !error
return !error;
}
function env() {
return process.env
return process.env;
}
function changeemail() {
signup.email = tools.removespaces(signup.email!)
signup.email = signup.email.toLowerCase()
emit('update:value', signup.email)
signup.email = tools.removespaces(signup.email!);
signup.email = signup.email.toLowerCase();
emit('update:value', signup.email);
}
function changeusername(value: string) {
signup.username = tools.removespaces(signup.username)
emit('update:value', signup.username)
signup.username = tools.removespaces(signup.username);
emit('update:value', signup.username);
}
function submitOk() {
v$.value.$touch()
v$.value.$touch();
signup.email = tools.removespaces(signup.email!)
signup.email = signup.email.toLowerCase()
signup.username = tools.removespaces(signup.username)
signup.email = tools.removespaces(signup.email!);
signup.email = signup.email.toLowerCase();
signup.username = tools.removespaces(signup.username);
// remove @
signup.username = tools.removeAt(signup.username)
signup.username = tools.removeAt(signup.username);
duplicate_email.value = false
duplicate_username.value = false
duplicate_email.value = false;
duplicate_username.value = false;
if (!signup.terms) {
tools.showNotif($q, t('reg.err.terms'))
return
tools.showNotif($q, t('reg.err.terms'));
return;
}
/*if (v$.signup.$error) {
tools.showNotif($q, t('reg.err.errore_generico'))
return
} */
if (signup.name) {
signup.name = tools.CapitalizeAllWords(signup.name)
signup.surname = tools.CapitalizeAllWords(signup.surname)
signup.name = tools.CapitalizeAllWords(signup.name);
signup.surname = tools.CapitalizeAllWords(signup.surname);
}
$q.loading.show({ message: t('reg.incorso') })
$q.loading.show({ message: t('reg.incorso') });
console.log(signup)
return userStore.signup(tools.clone(signup))
console.log(signup);
return userStore
.signup(tools.clone(signup))
.then((ris: any) => {
if (tools.SignUpcheckErrors($q, $router, ris.code, ris.msg))
$q.loading.hide()
}).catch((error: string) => {
console.log('ERROR = ' + error)
$q.loading.hide()
if (tools.SignUpcheckErrors($q, $router, ris.code, ris.msg)) $q.loading.hide();
})
.catch((error: string) => {
console.log('ERROR = ' + error);
$q.loading.hide();
});
}
function intcode_change(coderec: any) {
// console.log('intcode', coderec)
if (signup.profile) {
signup.profile.intcode_cell = '+' + coderec.dialCode
signup.profile.iso2_cell = coderec.iso2
signup.profile.intcode_cell = '+' + coderec.dialCode;
signup.profile.iso2_cell = coderec.iso2;
}
}
function selectcountry({ name, iso2, dialCode }: { name: string, iso2: string, dialCode: string }) {
signup.profile.nationality = iso2
countryname.value = name
function selectcountry({
name,
iso2,
dialCode,
}: {
name: string;
iso2: string;
dialCode: string;
}) {
// console.log(name, iso2, dialCode)
signup.profile.nationality = iso2;
countryname.value = name;
}
async function created() {
needTelegram.value = props.need_Telegram
needTelegram.value = props.need_Telegram;
console.log('$route.params', $route.params)
console.log('$route.params', $route.params);
ap_iniziale.value = $route.params.invited ? $route.params.invited.toString() : ''
ap_iniziale.value = $route.params.invited ? $route.params.invited.toString() : '';
signup.aportador_solidario = $route.params.invited ? $route.params.invited.toString() : ''
signup.username = $route.params.usernameteleg ? $route.params.usernameteleg.toString() : ''
signup.regexpire = $route.params.regexpire ? $route.params.regexpire.toString() : props.regexpire
signup.aportador_solidario = $route.params.invited
? $route.params.invited.toString()
: '';
signup.username = $route.params.usernameteleg
? $route.params.usernameteleg.toString()
: '';
signup.regexpire = $route.params.regexpire
? $route.params.regexpire.toString()
: props.regexpire;
if (signup.username)
isalreadyReg.value = await tools.registeredusername(signup.username)
signup.profile.username_telegram = signup.username
isalreadyReg.value = await tools.registeredusername(signup.username);
signup.profile.username_telegram = signup.username;
if ($route.params.idteleg) {
signup.profile.teleg_id = $route.params.idteleg ? parseInt($route.params.idteleg.toString(), 10) : 0
signup.profile.teleg_id = $route.params.idteleg
? parseInt($route.params.idteleg.toString(), 10)
: 0;
}
if (props.collettivo) {
signup.username = props.username_default!
signup.name = props.name_default!
signup.username = props.username_default!;
signup.name = props.name_default!;
}
// console.log('1) aportador_solidario', signup.aportador_solidario)
if (!signup.aportador_solidario)
signup.aportador_solidario = tools.getCookie(tools.APORTADOR_SOLIDARIO, signup.aportador_solidario)
signup.aportador_solidario = tools.getCookie(
tools.APORTADOR_SOLIDARIO,
signup.aportador_solidario
);
if (!signup.aportador_solidario || signup.aportador_solidario === 'undefined') {
if (!tools.getAskToVerifyReg()) {
signup.aportador_solidario = tools.APORTADOR_NONE
signup.aportador_solidario = tools.APORTADOR_NONE;
}
}
// console.log('signup.aportador_solidario', signup.aportador_solidario)
// console.log('getasktoverify', tools.getAskToVerifyReg())
if (tools.getAskToVerifyReg()) {
if (!signup.username || !signup.profile.teleg_id) {
visubuttBOT.value = true
// tools.copyStringToClipboard($q, signup.aportador_solidario, true)
visubuttBOT.value = true;
// window.location.href = tools.getLinkBotTelegram()
}
}
}
function myRuleEmail(val: string) {
return new Promise((resolve, reject) => {
// call
// resolve(true)
// --> content is valid
// resolve(false)
// --> content is NOT valid, no error message
// resolve(error_message)
// --> content is NOT valid, we have error message
tools.registeredemail(val).then((emailOk) => {
let risp = !!emailOk || t('reg.err.duplicate_email')
let risp = !!emailOk || t('reg.err.duplicate_email');
if (emailOk) {
risp = tools.isEmail(val) || t('reg.err.invalid_email')
emailOk = emailOk && tools.isEmail(val)
risp = tools.isEmail(val) || t('reg.err.invalid_email');
emailOk = emailOk && tools.isEmail(val);
}
resolve(risp)
})
})
if (emailOk) {
// risp = !tools.isEmailNoMicroZozz(val) || t('reg.err.invalid_email_micro')
}
resolve(risp);
});
// calling reject(...) will also mark the input
// as having an error, but there will not be any
// error message displayed below the input
// (only in browser console)
});
}
function showPassword() {
typePassword.value = typePassword.value === 'password' ? 'text' : 'password'
//
typePassword.value = typePassword.value === 'password' ? 'text' : 'password';
}
function regEventEmail(invited: boolean) {
console.log('EVENT RECEIVED: regEventEmail', invited)
visubuttBOT.value = false
needTelegram.value = false
console.log('EVENT RECEIVED: regEventEmail', invited);
// reg
visubuttBOT.value = false;
needTelegram.value = false;
}
created()
onMounted(async () => {
const token = props.token;
if (token) {
// carica le info della registrazione
const risinvite = await invitaStore.ottieniInvitoByToken(token);
if (risinvite && risinvite.email) {
signup.email = risinvite.email;
if (risinvite.usernameInvitante)
signup.aportador_solidario = risinvite.usernameInvitante
slide.value = '2'
}
}
});
created();
return {
changeemail,
@@ -391,6 +502,6 @@ export default defineComponent({
inputPassword,
inputPassword2,
shared_consts,
}
};
},
})
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,311 +0,0 @@
# 🎨 Componente CSignUp - Versione Migliorata
## ✨ Modifiche Principali
### 📊 Struttura Carousel (Da 4 a 3 Pagine)
#### **Pagina 1 - Dati Iniziali**
- ✉️ Email (campo principale)
- 👤 Aportador/Invitante (se necessario)
- 🎯 Focus: Raccolta dati di contatto
#### **Pagina 2 - Account**
- 👤 Username
- 📛 Nome e Cognome (se richiesto)
- 🎯 Focus: Identità utente
#### **Pagina 3 - Sicurezza e Privacy**
- 🔒 Password
- 🔒 Conferma Password
- ✅ Privacy Policy (GIÀ SPUNTATA)
- 🔞 Verifica Maggiore Età (se richiesto)
- 🎯 Focus: Sicurezza e consensi
---
## 🎨 Miglioramenti Estetici
### Design Moderno e Innovativo
- **Gradients Accattivanti**: Utilizzo di gradienti moderni per un look premium
- **Card con Shadow**: Effetti ombra eleganti per dare profondità
- **Animazioni Fluide**: Transizioni smooth tra le pagine
- **Icone Prominenti**: Icone grandi e colorate per guidare l'utente
- **Progress Indicator**: Barra di progresso visiva con step animati
### Color Scheme
```scss
Primary Gradient: #667eea #764ba2 (Viola/Blu)
Success Gradient: #11998e #38ef7d (Verde)
Error Gradient: #ee0979 #ff6a00 (Rosso/Arancio)
Background: #f5f7fa #c3cfe2 (Grigio chiaro)
```
---
## 📱 Responsive Design
### Breakpoints Ottimizzati
- **Desktop** (>768px): Layout completo con spaziature generose
- **Mobile** (<768px): Layout ottimizzato con dimensioni ridotte
### Caratteristiche Responsive
- Input con altezza adattiva (56px → 52px su mobile)
- Font size scalabili
- Padding e margini ottimizzati per ogni device
- Bottoni che si adattano alla larghezza disponibile
---
## 📍 Bottoni Fissi in Basso
### Implementazione
```scss
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
```
### Caratteristiche
- **Sempre Visibili**: I bottoni restano fissi durante lo scroll
- **Design Elegante**: Shadow e backdrop blur per un effetto premium
- **Azioni Contestuali**:
- Pagina 1-2: "Indietro" + "Continua"
- Pagina 3: "Indietro" + "Registrati" (verde, più grande)
### Animazioni Bottoni
- Hover: Movimento verso l'alto con shadow aumentata
- Click: Feedback visivo immediato
- Disabled: Opacità ridotta
---
## ☑️ Privacy Già Spuntata
### Implementazione nel TypeScript
```typescript
const signup = reactive(<ISignupOptions>{
// ... altri campi
terms: true, // ✅ GIÀ SPUNTATA DI DEFAULT
})
```
### User Experience
1. L'utente arriva alla pagina 3
2. La checkbox privacy è già selezionata
3. Link "Leggi la Privacy Policy" disponibile
4. L'utente può deselezionare se vuole (ma non può procedere)
---
## 👆 UX Ottimizzata
### Principi di Design Applicati
#### 1. **Minimo Effort**
- Campi ridotti al necessario
- Privacy pre-accettata
- 3 pagine invece di 4 (-25% di navigazione)
#### 2. **No Scroll Required**
- Altezza carousel ottimizzata (400px desktop, 360px mobile)
- Contenuto sempre visibile in una schermata
- Bottoni fissi eliminano necessità di scroll
#### 3. **Progressive Disclosure**
- Informazioni presentate gradualmente
- Step chiari e ben definiti
- Progress indicator sempre visibile
#### 4. **Error Prevention**
- Validazione real-time
- Bottoni disabilitati quando i dati non sono validi
- Messaggi di errore chiari e inline
#### 5. **Keyboard Navigation**
- Enter per passare al campo successivo
- Enter nell'ultimo campo per avanzare pagina
- Tab navigation ottimizzata
---
## 🎯 Caratteristiche Tecniche
### Progress Indicator Dinamico
```vue
<div class="progress-indicator">
<div v-for="step in 3" :key="step"
:class="{ active: parseInt(slide) >= step,
completed: parseInt(slide) > step }">
<!-- Step circle con animazioni -->
</div>
<div class="progress-line"
:style="{ width: `${(parseInt(slide) - 1) * 50}%` }">
</div>
</div>
```
### Validazione Intelligente
- Campo Email: Validazione asincrona per email duplicate
- Campo Username: Check real-time su disponibilità
- Password: Controllo complessità con feedback visivo
- Conferma Password: Match immediato
### Animazioni CSS
- **fadeInDown**: Header (0.6s)
- **fadeInUp**: Form container (0.6s)
- **scaleIn**: Success/Error cards (0.5s)
- **slideIn**: Contenuto slide (0.4s)
- **iconPop**: Icone header (0.6s con delay)
---
## 📦 File Modificati
### 1. CSignUp.vue
- ✅ Ridotto carousel a 3 slide
- ✅ Nuovo layout con progress indicator
- ✅ Bottoni fissi in basso
- ✅ Privacy section nell'ultima slide
- ✅ Header con icone e titoli descrittivi
- ✅ Animazioni fluide tra le slide
### 2. CSignUp.ts
-`terms: true` di default
- ✅ Logica di validazione per 3 slide
- ✅ Gestione keyboard navigation ottimizzata
### 3. CSignUp.scss
- ✅ Design system completo con variabili
- ✅ Gradients e colori moderni
- ✅ Animazioni e transizioni
- ✅ Responsive breakpoints
- ✅ Fixed bottom buttons styling
- ✅ Progress indicator styling
---
## 🚀 Come Usare
### Installazione
1. Sostituisci i file esistenti con le nuove versioni
2. Non sono richieste dipendenze aggiuntive
3. Il componente è retrocompatibile con le props esistenti
### Props Disponibili
Tutte le props originali sono mantenute:
- `showadultcheck`: Mostra checkbox maggiorenne
- `showcell`: Mostra campo telefono
- `showaportador`: Mostra campo invitante
- `shownationality`: Mostra campo nazionalità
- `show_namesurname`: Mostra campi nome/cognome
- `need_Telegram`: Richiede registrazione Telegram
- `collettivo`: Modalità collettivo
---
## 📊 Metriche di Miglioramento
| Metrica | Prima | Dopo | Miglioramento |
|---------|-------|------|---------------|
| Pagine Carousel | 4 | 3 | -25% |
| Click per Completamento | 6-8 | 4-5 | ~40% |
| Tempo Medio Registrazione | ~120s | ~80s | ~33% |
| Privacy Pre-accettata | ❌ | ✅ | +100% |
| Mobile Friendly | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | +66% |
---
## 🎨 Design Tokens
### Spacing
```scss
Small: 8px, 12px
Medium: 16px, 20px, 24px
Large: 32px, 40px, 48px
```
### Typography
```scss
Title: 32px / 28px (mobile)
Subtitle: 16px / 14px (mobile)
Slide Title: 26px / 22px (mobile)
Body: 15px / 14px (mobile)
```
### Border Radius
```scss
Small: 12px
Medium: 14px
Large: 20px
Full: 50% (circles)
```
---
## 🔧 Personalizzazione
### Cambiare i Colori
Modifica le variabili in `CSignUp.scss`:
```scss
$primary-gradient: linear-gradient(135deg, #TUO_COLORE_1 0%, #TUO_COLORE_2 100%);
$success-gradient: linear-gradient(135deg, #TUO_COLORE_1 0%, #TUO_COLORE_2 100%);
```
### Cambiare le Animazioni
Modifica i timing delle animazioni:
```scss
$transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
```
### Cambiare l'Altezza del Carousel
```scss
.modern-carousel {
min-height: 400px; // Modifica qui
}
```
---
## 🐛 Note e Compatibilità
### Compatibilità
- ✅ Vue 3
- ✅ Quasar Framework
- ✅ TypeScript
- ✅ Vuelidate
- ✅ Mobile & Desktop
### Browser Support
- ✅ Chrome/Edge (ultime 2 versioni)
- ✅ Firefox (ultime 2 versioni)
- ✅ Safari (ultime 2 versioni)
- ✅ Mobile browsers
---
## 📝 Changelog
### Versione 2.0 (Attuale)
- ✨ Ridotto carousel da 4 a 3 pagine
- ✨ Design completamente rinnovato
- ✨ Progress indicator con animazioni
- ✨ Bottoni fissi in basso
- ✨ Privacy pre-accettata
- ✨ UX ottimizzata senza scroll
- ✨ Completamente responsive
- ✨ Nuove animazioni e transizioni
### Versione 1.0 (Precedente)
- Form di registrazione base
- 4 pagine carousel
- Design classico
---
## 📧 Supporto
Per domande o problemi, contatta il team di sviluppo.
**Made with ❤️ for better UX**