ancora su Typescript...
suddiviso i ts dai html e scss
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
"vue-property-decorator": "^7.2.0",
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-svgicon": "^3.1.0",
|
||||
"vuelidate": "^0.7.4",
|
||||
"vuex": "^3.0.1",
|
||||
"vuex-class": "^0.3.1",
|
||||
"vuex-module-decorators": "^0.4.3"
|
||||
|
||||
62
src/common/axios.ts
Normal file
62
src/common/axios.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
default as Axios,
|
||||
AxiosError,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse
|
||||
} from "axios";
|
||||
import { default as VueRouter } from "vue-router";
|
||||
import { TokenHelper } from "./token-helper";
|
||||
|
||||
let initialized: boolean = false;
|
||||
|
||||
interface IRequestConfig extends AxiosRequestConfig {
|
||||
ignore: number[];
|
||||
}
|
||||
|
||||
function handle(status: number, exclude: number[]) {
|
||||
if (exclude.length === 0) return true;
|
||||
else return exclude.find(o => o === status) === undefined;
|
||||
}
|
||||
|
||||
export function UseAxios(router: VueRouter) {
|
||||
if (!initialized) {
|
||||
Axios.interceptors.request.use((config: IRequestConfig) => {
|
||||
if (!config.headers["Authorization"]) {
|
||||
// append authorization header
|
||||
let bearerToken = TokenHelper.getBearerToken();
|
||||
|
||||
if (bearerToken.Authorization)
|
||||
Object.assign(config.headers, bearerToken);
|
||||
}
|
||||
|
||||
if (!config.maxRedirects || config.maxRedirects === 5)
|
||||
// ensure axios does not follow redirects, so custom response interceptor below can push to app login page
|
||||
config.maxRedirects = 0;
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
Axios.interceptors.response.use(undefined, (config: AxiosError) => {
|
||||
let response: AxiosResponse = config.response;
|
||||
let exclude = (<IRequestConfig>config.config).ignore || [];
|
||||
|
||||
if (response.status === 401 && handle(response.status, exclude)) {
|
||||
let location: string =
|
||||
response.headers["location"] || response.headers["Location"];
|
||||
|
||||
if (location) {
|
||||
let redirectTo = "/" + location;
|
||||
window.setTimeout(() => router.replace(redirectTo), 200);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status === 403 && handle(response.status, exclude)) {
|
||||
window.setTimeout(() => router.replace("/forbidden"), 200);
|
||||
}
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
50
src/common/debounce.ts
Normal file
50
src/common/debounce.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Returns a function, that, as long as it continues to be invoked, will not
|
||||
* be triggered. The function will be called after it stops being called for
|
||||
* N milliseconds. If `immediate` is passed, trigger the function on the
|
||||
* leading edge, instead of the trailing. The function also has a property 'clear'
|
||||
* that is a function which will clear the timer to prevent previously scheduled executions.
|
||||
*
|
||||
* @source underscore.js
|
||||
* @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
|
||||
* @param {Function} function to wrap
|
||||
* @param {Number} timeout in ms (`100`)
|
||||
* @param {Boolean} whether to execute at the beginning (`false`)
|
||||
* @api public
|
||||
*/
|
||||
|
||||
export function Debounce(func: Function, wait?: number, immediate?: boolean) {
|
||||
let timeout, args, context, timestamp, result
|
||||
if (null == wait) wait = 100
|
||||
|
||||
function later() {
|
||||
let last = Date.now() - timestamp
|
||||
|
||||
if (last < wait && last > 0) {
|
||||
timeout = setTimeout(later, wait - last)
|
||||
} else {
|
||||
timeout = null
|
||||
if (!immediate) {
|
||||
result = func.apply(context, args)
|
||||
context = args = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let debounced = function () {
|
||||
|
||||
context = this
|
||||
args = arguments
|
||||
timestamp = Date.now()
|
||||
let callNow = immediate && !timeout
|
||||
if (!timeout) timeout = setTimeout(later, wait)
|
||||
if (callNow) {
|
||||
result = func.apply(context, args)
|
||||
context = args = null
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return debounced
|
||||
}
|
||||
3
src/common/index.ts
Normal file
3
src/common/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './pattern'
|
||||
export * from './axios'
|
||||
export * from './debounce'
|
||||
19
src/common/pattern.ts
Normal file
19
src/common/pattern.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
export class Patterns {
|
||||
|
||||
/**
|
||||
* Alphanumeric, spaces and dashes allowed. Min 2 characters length.
|
||||
*/
|
||||
public static DisplayName: RegExp = /^[0-9a-zA-Z\s\-]{2,}/i;
|
||||
|
||||
/**
|
||||
* Same pattern used by JQuery userName validation
|
||||
*/
|
||||
public static Email: RegExp = /^((“[\w-\s]+”)|([\w-]+(?:\.[\w-]+)*)|(“[\w-\s]+”)([\w-]+(?:\.[\w-]+)*))(@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}[0-9];{1,2})\]?$)/i;
|
||||
|
||||
/**
|
||||
* 6 to 20 characters string with at least one digit, one upper case letter, one lower case letter and one special symbol
|
||||
*/
|
||||
public static Password: RegExp = /^((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})/i;
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default {
|
||||
msg: {
|
||||
hello: 'Buongiorno',
|
||||
myAppName: 'FreePlanet',
|
||||
myDescriz: 'La prima App Libera e per Tutti'
|
||||
}
|
||||
}
|
||||
8004
src/jquery.d.ts
vendored
Normal file
8004
src/jquery.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
src/jwt-decode.d.ts
vendored
Normal file
1
src/jwt-decode.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export function jwtDecode(token: string, options?: { header: boolean }): any
|
||||
8
src/model/glob.ts
Normal file
8
src/model/glob.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export interface IGlobState {
|
||||
conta: number
|
||||
isLoginPage: boolean
|
||||
layoutNeeded: boolean
|
||||
mobileMode: boolean
|
||||
menuCollapse: boolean
|
||||
posts: string[]
|
||||
}
|
||||
4
src/model/index.ts
Normal file
4
src/model/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './user'
|
||||
export * from './glob'
|
||||
export * from './signup-option'
|
||||
export * from './key-value'
|
||||
1
src/model/key-value.ts
Normal file
1
src/model/key-value.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type KeyValue = { key: string, value: string };
|
||||
8
src/model/signup-option.ts
Normal file
8
src/model/signup-option.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
export interface ISignupOptions {
|
||||
email?: string
|
||||
username: string
|
||||
password?: string
|
||||
lang?: string
|
||||
repeatPassword?: string
|
||||
}
|
||||
28
src/model/user.ts
Normal file
28
src/model/user.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { IToken } from '@/types'
|
||||
|
||||
export const DefaultUser = <IUserState>{
|
||||
email: '',
|
||||
username: '',
|
||||
idapp: process.env.APP_ID,
|
||||
password: '',
|
||||
lang: 'it'
|
||||
}
|
||||
|
||||
export interface IUserState {
|
||||
_id?: string
|
||||
email?: string
|
||||
username: string
|
||||
idapp?: any
|
||||
password?: string
|
||||
lang?: string
|
||||
repeatPassword?: string
|
||||
|
||||
idToken?: string
|
||||
userId?: number
|
||||
|
||||
tokens?: IToken[]
|
||||
|
||||
verifiedEmail?: boolean
|
||||
|
||||
tokenforgot?: string
|
||||
}
|
||||
47
src/mount.ts
Normal file
47
src/mount.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import './components/loader/loader.scss';
|
||||
|
||||
function show(el: HTMLElement) {
|
||||
el.style.display = 'block';
|
||||
el.style.visibility = 'visible';
|
||||
}
|
||||
|
||||
function mountApp(e: Event) {
|
||||
|
||||
document.getElementsByClassName('loader-wrapper')[0].remove();
|
||||
|
||||
show(document.getElementById('app'));
|
||||
|
||||
window['EntryPoint'].run(function (vue) {
|
||||
vue.$mount('#app');
|
||||
});
|
||||
};
|
||||
|
||||
(() => {
|
||||
|
||||
let elapsed: number = 0;
|
||||
let handle: number = null;
|
||||
let interval: number = 100;
|
||||
|
||||
show(<HTMLElement>document.getElementsByClassName('loader-wrapper')[0]);
|
||||
show(<HTMLElement>document.getElementsByClassName('loader-status')[0]);
|
||||
|
||||
var el: HTMLElement = <HTMLElement>document.getElementsByClassName('loader-progress')[0];
|
||||
|
||||
let updateProgress = () => {
|
||||
elapsed = elapsed + interval;
|
||||
let data = (elapsed * 0.001).toString().split('.')
|
||||
let progress = data[0].toString() + '.' + (data.length > 1 ? data[1].substring(0, 1) : 0);
|
||||
el.innerText = progress + ' ms';
|
||||
}
|
||||
|
||||
handle = window.setInterval(updateProgress, interval);
|
||||
|
||||
document.addEventListener('DOMContentLoaded', (e) => {
|
||||
updateProgress();
|
||||
window.clearInterval(handle);
|
||||
mountApp(e);
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { RouteConfig } from 'vue-router'
|
||||
const routes: RouteConfig[] = [
|
||||
{ path: '/', component: () => import('@/pages/Index.vue'), meta: { name: 'Home' } },
|
||||
{ path: '/test', component: () => import('@/views/login/test.vue'), meta: { name: 'Test' } },
|
||||
{ path: '/signup', component: () => import('@/views/login/signup.vue'), meta: { name: 'Registration' } },
|
||||
{ path: '/signup', component: () => import('@/views/login/signup/signup.vue'), meta: { name: 'Registration' } },
|
||||
/*
|
||||
{ path: '/signin', component: () => import('@/views/login/signin.vue'), meta: { name: 'Login' } },
|
||||
{ path: '/vreg', component: () => import('@/views/login/vreg.vue'), meta: { name: 'Verify Reg' } },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
import { IUserState, IGlobState } from '@/types'
|
||||
import { IUserState, IGlobState } from '@/model'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ const bcrypt = require('bcryptjs')
|
||||
import * as types from '@/store/mutation-types'
|
||||
import { serv_constants } from '@/store/modules/serv_constants'
|
||||
|
||||
import { IUserState, ILinkReg, IResult, IIdToken } from '@/types'
|
||||
import { IUserState } from '@/model'
|
||||
import { ILinkReg, IResult, IIdToken } from '@/types'
|
||||
|
||||
|
||||
export const ErroriMongoDb = {
|
||||
|
||||
27
src/types/index.d.ts
vendored
27
src/types/index.d.ts
vendored
@@ -3,33 +3,6 @@ export interface IToken {
|
||||
token: string
|
||||
}
|
||||
|
||||
export interface IUserState {
|
||||
_id?: string
|
||||
email?: string
|
||||
username: string
|
||||
idapp?: any
|
||||
password?: string
|
||||
lang?: string
|
||||
repeatPassword?: string
|
||||
|
||||
idToken?: string
|
||||
userId?: number
|
||||
|
||||
tokens?: IToken[]
|
||||
|
||||
verifiedEmail?: boolean
|
||||
|
||||
tokenforgot?: string
|
||||
}
|
||||
|
||||
export interface IGlobState {
|
||||
conta: number
|
||||
isLoginPage: boolean
|
||||
layoutNeeded: boolean
|
||||
mobileMode: boolean
|
||||
menuCollapse: boolean
|
||||
posts: string[]
|
||||
}
|
||||
|
||||
export interface ILinkReg {
|
||||
idLink: string
|
||||
|
||||
5
src/validation/complexity.ts
Normal file
5
src/validation/complexity.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Patterns } from '@/common'
|
||||
|
||||
export function complexity(password: string) {
|
||||
return Patterns.Password.test(password)
|
||||
}
|
||||
23
src/validation/duplicate.ts
Normal file
23
src/validation/duplicate.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
type t = string | number;
|
||||
type fn = () => t[];
|
||||
|
||||
export function duplicate(matches: t[] | fn, ignoreCase: boolean = false) {
|
||||
if (Array.isArray(matches)) return factory(matches, ignoreCase);
|
||||
|
||||
return value => {
|
||||
let cb = factory(matches(), ignoreCase);
|
||||
return cb(value);
|
||||
};
|
||||
}
|
||||
|
||||
function factory(values: t[], ignoreCase: boolean) {
|
||||
return value => {
|
||||
if (value === undefined || value === null || values.length === 0)
|
||||
return true;
|
||||
else{
|
||||
let flags = ignoreCase ? "i" : "";
|
||||
let exp = new RegExp(`^(${value})$`, flags);
|
||||
return values.find(o => exp.test(o.toString())) === undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
3
src/validation/index.ts
Normal file
3
src/validation/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { duplicate } from './duplicate'
|
||||
export { registered } from './registered'
|
||||
export { complexity } from './complexity'
|
||||
22
src/validation/registered.ts
Normal file
22
src/validation/registered.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { default as Axios, AxiosResponse } from 'axios';
|
||||
import { IPayload } from '../model';
|
||||
import { GlobalConfig, PayloadMessageTypes } from '../common';
|
||||
|
||||
const AUTH_URL = GlobalConfig.uri.auth;
|
||||
const VALIDATE_USER_URL = AUTH_URL + 'validateuser';
|
||||
|
||||
export function registered(userName: string) {
|
||||
|
||||
let config = {
|
||||
params: { userName: userName }
|
||||
};
|
||||
|
||||
let onSuccess = (res: AxiosResponse) => {
|
||||
let payload: IPayload<boolean> = res.data;
|
||||
return payload.message.messageTypeId !== PayloadMessageTypes.failure;
|
||||
}
|
||||
|
||||
return Axios.get(VALIDATE_USER_URL, config)
|
||||
.then(onSuccess);
|
||||
|
||||
};
|
||||
@@ -1,270 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<q-page padding class="signup">
|
||||
<div class="text-center">
|
||||
<p>
|
||||
<!--<img src="../../../assets/quasar-logo-full.svg">-->
|
||||
<img :src="`../../../assets/`+`${env('LOGO_REG')}`">
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!--Prova URL : {{env('PROVA_PAOLO')}}-->
|
||||
|
||||
<q-field
|
||||
:error="$v.user.email.$error"
|
||||
:error-label="`${errorMsg('email', $v.user.email)}`"
|
||||
>
|
||||
<q-input
|
||||
v-model="user.email"
|
||||
v-validate="'required|email|truthy'"
|
||||
:value="user.email"
|
||||
@change="val => { user.email = val }"
|
||||
:before="[{icon: 'mail', handler () {}}]"
|
||||
@blur="$v.user.email.$touch"
|
||||
:error="$v.user.email.$error"
|
||||
:float-label="$t('reg.email')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<q-field
|
||||
:error="$v.user.username.$error"
|
||||
:error-label="`${errorMsg('username', $v.user.username)}`"
|
||||
>
|
||||
<q-input
|
||||
:value="user.username"
|
||||
@change="val => { user.username = val }"
|
||||
:before="[{icon: 'person', handler () {}}]"
|
||||
@blur="$v.user.username.$touch"
|
||||
:error="$v.user.username.$error"
|
||||
:float-label="$t('reg.username')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<q-field
|
||||
:error="$v.user.password.$error"
|
||||
:error-label="`${errorMsg('password', $v.user.password)}`"
|
||||
>
|
||||
<q-input
|
||||
v-model="user.password"
|
||||
:before="[{icon: 'vpn_key', handler () {}}]"
|
||||
@blur="$v.user.password.$touch"
|
||||
:error="$v.user.password.$error"
|
||||
:float-label="$t('reg.password')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<q-field
|
||||
:error="$v.user.repeatPassword.$error"
|
||||
:error-label="`${errorMsg('repeatpassword', $v.user.repeatPassword)}`"
|
||||
>
|
||||
<q-input
|
||||
v-model="user.repeatPassword"
|
||||
:before="[{icon: 'vpn_key', handler () {}}]"
|
||||
@blur="$v.user.repeatPassword.$touch"
|
||||
:error="$v.user.repeatPassword.$error"
|
||||
:float-label="$t('reg.repeatPassword')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<q-field
|
||||
:error="$v.user.terms.$error"
|
||||
:error-label="`${errorMsg('terms', $v.user.terms)}`"
|
||||
>
|
||||
|
||||
<q-checkbox
|
||||
v-model="user.terms"
|
||||
:before="[{icon: 'vpn_key', handler () {}}]"
|
||||
color="secondary"
|
||||
@blur="$v.user.terms.$touch"
|
||||
:error="$v.user.terms.$error"
|
||||
:float-label="$t('reg.terms')"
|
||||
:label="$t('reg.terms')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<br>
|
||||
|
||||
<div align="center">
|
||||
<q-btn rounded size="lg" color="primary" @click="submit" :disable="$v.$error">{{$t('reg.submit')}}
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="ts">
|
||||
import { Component, Vue, Prop } from 'vue-property-decorator'
|
||||
import { UserModule } from '../../store/modules/user'
|
||||
//import { IUserState } from "../../types";
|
||||
import {ErroriMongoDb} from '../../store/modules/user'
|
||||
|
||||
/*
|
||||
import VeeValidate from 'vee-validate'
|
||||
Vue.use(VeeValidate)
|
||||
import { Validator } from 'vee-validate'
|
||||
|
||||
Validator.extend('truthy', {
|
||||
getMessage: field => 'The ' + field + ' value is not truthy.',
|
||||
validate: value => !!value
|
||||
})
|
||||
*/
|
||||
|
||||
//import {Loading, QSpinnerFacebook, QSpinnerGears} from 'quasar'
|
||||
|
||||
@Component({})
|
||||
export default class Signup extends Vue {
|
||||
duplicate_email = false
|
||||
duplicate_username = false
|
||||
user = {
|
||||
email: process.env.TEST_EMAIL,
|
||||
username: process.env.TEST_USERNAME,
|
||||
password: process.env.TEST_PASSWORD,
|
||||
repeatPassword: process.env.TEST_PASSWORD,
|
||||
dateOfBirth: '',
|
||||
terms: true,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
...mapGetters("user", [
|
||||
'getUsername',
|
||||
'getPassword',
|
||||
'getEmail',
|
||||
'getDateOfBirth',
|
||||
]),
|
||||
...mapGetters("user", [
|
||||
'getUserServer',
|
||||
'getServerCode',
|
||||
]),
|
||||
*/
|
||||
|
||||
/*
|
||||
validations: {
|
||||
isAsync: true,
|
||||
form: {
|
||||
email: {
|
||||
required, email,
|
||||
isUnique: value => {
|
||||
if (value === '') return true;
|
||||
return axios.get(process.env.MONGODB_HOST + '/email/' + value)
|
||||
.then(res => {
|
||||
return (res.status !== 200)
|
||||
}).catch((e) => {
|
||||
return true;
|
||||
})
|
||||
}
|
||||
},
|
||||
password: {required, minLength: minLength(8), maxLength: maxLength(20)},
|
||||
username: {
|
||||
required, minLength: minLength(6), maxLength: maxLength(20),
|
||||
isUnique: value => {
|
||||
if (value === '') return true;
|
||||
return axios.get(process.env.MONGODB_HOST + '/users/' + value)
|
||||
.then(res => {
|
||||
return (res.status !== 200)
|
||||
}).catch((e) => {
|
||||
return true;
|
||||
})
|
||||
}
|
||||
},
|
||||
repeatPassword: {
|
||||
sameAsPassword: sameAs('password')
|
||||
},
|
||||
terms: {required},
|
||||
|
||||
}
|
||||
}, */
|
||||
env() {
|
||||
return env
|
||||
}
|
||||
|
||||
showNotif(msg) {
|
||||
this.$q.notify(msg)
|
||||
}
|
||||
|
||||
errorMsg(cosa, item) {
|
||||
try {
|
||||
if (!item.$error) return ''
|
||||
if (item.$params.email && !item.email) return this.$t('reg.err.email')
|
||||
|
||||
if (cosa === 'repeatpassword') {
|
||||
if (!item.sameAsPassword) {
|
||||
return this.$t('reg.err.sameaspassword')
|
||||
}
|
||||
}
|
||||
|
||||
if (cosa === 'email') {
|
||||
//console.log("EMAIL " + item.isUnique);
|
||||
//console.log(item);
|
||||
if (!item.isUnique) return this.$t('reg.err.duplicate_email')
|
||||
} else if (cosa === 'username') {
|
||||
//console.log(item);
|
||||
if (!item.isUnique) return this.$t('reg.err.duplicate_username')
|
||||
}
|
||||
|
||||
if (!item.required) return this.$t('reg.err.required')
|
||||
if (!item.minLength) return this.$t('reg.err.atleast') + ` ${item.$params.minLength.min} ` + this.$t('reg.err.char')
|
||||
if (!item.maxLength) return this.$t('reg.err.notmore') + ` ${item.$params.maxLength.max} ` + this.$t('reg.err.char')
|
||||
return ''
|
||||
} catch (error) {
|
||||
//console.log("ERR : " + error);
|
||||
}
|
||||
}
|
||||
|
||||
checkErrors(riscode) {
|
||||
//console.log("RIS = " + riscode);
|
||||
if (riscode === ErroriMongoDb.DUPLICATE_EMAIL_ID) {
|
||||
this.showNotif(this.$t('reg.err.duplicate_email'))
|
||||
} else if (riscode === ErroriMongoDb.DUPLICATE_USERNAME_ID) {
|
||||
this.showNotif(this.$t('reg.err.duplicate_username'))
|
||||
} else if (riscode === ErroriMongoDb.OK) {
|
||||
this.$router.push('/')
|
||||
} else {
|
||||
this.showNotif("Errore num " + riscode)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
submit() {
|
||||
this.$v.user.$touch()
|
||||
|
||||
this.duplicate_email = false
|
||||
this.duplicate_username = false
|
||||
|
||||
if (!this.user.terms) {
|
||||
this.showNotif(this.$t('reg.err.terms'))
|
||||
return
|
||||
}
|
||||
|
||||
if (this.$v.user.$error) {
|
||||
this.showNotif(this.$t('reg.err.errore_generico'))
|
||||
return
|
||||
}
|
||||
|
||||
this.$q.loading.show({ message: this.$t('reg.incorso') })
|
||||
|
||||
console.log(this.user)
|
||||
UserModule.signup(this.user)
|
||||
.then((riscode) => {
|
||||
this.checkErrors(riscode)
|
||||
this.$q.loading.hide()
|
||||
}).catch(error => {
|
||||
console.log("ERROR = " + error)
|
||||
this.$q.loading.hide()
|
||||
})
|
||||
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.signup {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
max-width: 450px;
|
||||
}
|
||||
</style>
|
||||
@@ -62,10 +62,10 @@
|
||||
} from 'vuelidate/lib/validators'
|
||||
|
||||
import {mapGetters, mapActions} from 'vuex'
|
||||
import * as types from '../../store/mutation-types'
|
||||
import * as types from '../../../store/mutation-types'
|
||||
|
||||
//import {ErroriMongoDb} from '../../store/modules/user'
|
||||
import {serv_constants} from "../../store/modules/serv_constants";
|
||||
import {serv_constants} from "../../../store/modules/serv_constants";
|
||||
import axios from 'axios';
|
||||
|
||||
import {Loading, QSpinnerFacebook, QSpinnerGears} from 'quasar'
|
||||
28
src/views/login/signup/signup-validate.ts
Normal file
28
src/views/login/signup/signup-validate.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { ISignupOptions } from '@/model'
|
||||
import { required, minLength, email, sameAs } from 'vuelidate/lib/validators'
|
||||
import { ValidationRuleset } from 'vuelidate'
|
||||
import { complexity, registered } from '@/validation'
|
||||
|
||||
export type TSignup = { signup: ISignupOptions, validationGroup: string[] }
|
||||
|
||||
export const validations: ValidationRuleset<TSignup> = {
|
||||
signup: {
|
||||
confirmPassword: {
|
||||
required,
|
||||
sameAsPassword: sameAs('password')
|
||||
},
|
||||
displayName: {
|
||||
required,
|
||||
minLength: minLength(2)
|
||||
},
|
||||
password: {
|
||||
required,
|
||||
complexity
|
||||
},
|
||||
userName: {
|
||||
required,
|
||||
email,
|
||||
registered
|
||||
}
|
||||
}
|
||||
}
|
||||
100
src/views/login/signup/signup.html
Normal file
100
src/views/login/signup/signup.html
Normal file
@@ -0,0 +1,100 @@
|
||||
<div>
|
||||
<q-page padding class="signup">
|
||||
<div class="text-center">
|
||||
<p>
|
||||
<!--<img src="../../../assets/quasar-logo-full.svg">-->
|
||||
<img :src="`../../../assets/`+`${env('LOGO_REG')}`">
|
||||
</p>
|
||||
</div>
|
||||
|
||||
PROVAA
|
||||
|
||||
<!--Prova URL : {{env('PROVA_PAOLO')}}-->
|
||||
<!--
|
||||
|
||||
<q-field
|
||||
:error="v.user.email.error"
|
||||
error-label="{{ errors.first('email') }}"
|
||||
>
|
||||
<q-input
|
||||
v-model="user.email"
|
||||
v-validate="'required|email|truthy'"
|
||||
:value="user.email"
|
||||
@change="val => { user.email = val }"
|
||||
:before="[{icon: 'mail', handler () {}}]"
|
||||
@blur="v.user.email.touch"
|
||||
:error="v.user.email.error"
|
||||
:float-label="$t('reg.email')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<q-field
|
||||
:error="v.user.username.$error"
|
||||
:error-label="`${errorMsg('username', v.user.username)}`"
|
||||
>
|
||||
<q-input
|
||||
:value="user.username"
|
||||
@change="val => { user.username = val }"
|
||||
:before="[{icon: 'person', handler () {}}]"
|
||||
@blur="v.user.username.$touch"
|
||||
:error="v.user.username.$error"
|
||||
:float-label="$t('reg.username')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<q-field
|
||||
:error="v.user.password.$error"
|
||||
:error-label="`${errorMsg('password', v.user.password)}`"
|
||||
>
|
||||
<q-input
|
||||
v-model="user.password"
|
||||
:before="[{icon: 'vpn_key', handler () {}}]"
|
||||
@blur="v.user.password.$touch"
|
||||
:error="v.user.password.$error"
|
||||
:float-label="$t('reg.password')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<q-field
|
||||
:error="v.user.repeatPassword.$error"
|
||||
:error-label="`${errorMsg('repeatpassword', v.user.repeatPassword)}`"
|
||||
>
|
||||
<q-input
|
||||
v-model="user.repeatPassword"
|
||||
:before="[{icon: 'vpn_key', handler () {}}]"
|
||||
@blur="v.user.repeatPassword.$touch"
|
||||
:error="v.user.repeatPassword.$error"
|
||||
:float-label="$t('reg.repeatPassword')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
<q-field
|
||||
:error="v.user.terms.$error"
|
||||
:error-label="`${errorMsg('terms', v.user.terms)}`"
|
||||
>
|
||||
|
||||
<q-checkbox
|
||||
v-model="user.terms"
|
||||
:before="[{icon: 'vpn_key', handler () {}}]"
|
||||
color="secondary"
|
||||
@blur="v.user.terms.$touch"
|
||||
:error="v.user.terms.$error"
|
||||
:float-label="$t('reg.terms')"
|
||||
:label="$t('reg.terms')"
|
||||
/>
|
||||
</q-field>
|
||||
|
||||
-->
|
||||
|
||||
<br>
|
||||
|
||||
<!--
|
||||
<div align="center">
|
||||
<q-btn rounded size="lg" color="primary" @click="submit" :disable="">{{$t('reg.submit')}}
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
-->
|
||||
|
||||
</q-page>
|
||||
</div>
|
||||
5
src/views/login/signup/signup.scss
Normal file
5
src/views/login/signup/signup.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
.signup {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
max-width: 450px;
|
||||
}
|
||||
177
src/views/login/signup/signup.ts
Normal file
177
src/views/login/signup/signup.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import { Component, Vue, Prop } from 'vue-property-decorator'
|
||||
import { UserModule } from '@/store/modules/user'
|
||||
import { ErroriMongoDb } from '@/store/modules/user'
|
||||
|
||||
import { validationMixin } from 'vuelidate'
|
||||
import { required, minValue } from 'vuelidate/lib/validators'
|
||||
import { ISignupOptions, IUserState } from '@/model'
|
||||
import { validations, TSignup } from './signup-validate'
|
||||
// import './signup.scss'
|
||||
|
||||
// import {Loading, QSpinnerFacebook, QSpinnerGears} from 'quasar'
|
||||
|
||||
@Component({
|
||||
mixins: [validationMixin],
|
||||
name: 'Signup',
|
||||
// template: require('./signup.html'),
|
||||
validations: validations
|
||||
})
|
||||
|
||||
export default class Signup extends Vue {
|
||||
myProperty: string = ''
|
||||
|
||||
duplicate_email: boolean = false
|
||||
duplicate_username: boolean = false
|
||||
user: ISignupOptions = {
|
||||
email: process.env.TEST_EMAIL,
|
||||
username: process.env.TEST_USERNAME || '',
|
||||
password: process.env.TEST_PASSWORD,
|
||||
repeatPassword: process.env.TEST_PASSWORD
|
||||
}
|
||||
terms = true
|
||||
|
||||
$v: any
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
created() {
|
||||
|
||||
}
|
||||
|
||||
mounted() {
|
||||
if (this.$v) {
|
||||
this.$v.$touch()
|
||||
}
|
||||
}
|
||||
|
||||
get allowSubmit() {
|
||||
|
||||
let error = this.$v.signup.$error || this.$v.signup.$invalid
|
||||
return !error
|
||||
}
|
||||
|
||||
/*
|
||||
validations: {
|
||||
isAsync: true,
|
||||
form: {
|
||||
email: {
|
||||
required, email,
|
||||
isUnique: value => {
|
||||
if (value === '') return true;
|
||||
return axios.get(process.env.MONGODB_HOST + '/email/' + value)
|
||||
.then(res => {
|
||||
return (res.status !== 200)
|
||||
}).catch((e) => {
|
||||
return true;
|
||||
})
|
||||
}
|
||||
},
|
||||
password: {required, minLength: minLength(8), maxLength: maxLength(20)},
|
||||
username: {
|
||||
required, minLength: minLength(6), maxLength: maxLength(20),
|
||||
isUnique: value => {
|
||||
if (value === '') return true;
|
||||
return axios.get(process.env.MONGODB_HOST + '/users/' + value)
|
||||
.then(res => {
|
||||
return (res.status !== 200)
|
||||
}).catch((e) => {
|
||||
return true;
|
||||
})
|
||||
}
|
||||
},
|
||||
repeatPassword: {
|
||||
sameAsPassword: sameAs('password')
|
||||
},
|
||||
terms: {required},
|
||||
|
||||
}
|
||||
}, */
|
||||
env() {
|
||||
return process.env
|
||||
}
|
||||
|
||||
showNotif(msg: any) {
|
||||
this.$q.notify(msg)
|
||||
}
|
||||
|
||||
/*
|
||||
errorMsg(cosa: string, item: string) {
|
||||
try {
|
||||
if (!item.$error) return ''
|
||||
if (item.$params.email && !item.email) return this.$t('reg.err.email')
|
||||
|
||||
if (cosa === 'repeatpassword') {
|
||||
if (!item.sameAsPassword) {
|
||||
return this.$t('reg.err.sameaspassword')
|
||||
}
|
||||
}
|
||||
|
||||
if (cosa === 'email') {
|
||||
//console.log("EMAIL " + item.isUnique);
|
||||
//console.log(item);
|
||||
if (!item.isUnique) return this.$t('reg.err.duplicate_email')
|
||||
} else if (cosa === 'username') {
|
||||
//console.log(item);
|
||||
if (!item.isUnique) return this.$t('reg.err.duplicate_username')
|
||||
}
|
||||
|
||||
if (!item.required) return this.$t('reg.err.required')
|
||||
if (!item.minLength) return this.$t('reg.err.atleast') + ` ${item.$params.minLength.min} ` + this.$t('reg.err.char')
|
||||
if (!item.maxLength) return this.$t('reg.err.notmore') + ` ${item.$params.maxLength.max} ` + this.$t('reg.err.char')
|
||||
return ''
|
||||
} catch (error) {
|
||||
//console.log("ERR : " + error);
|
||||
}
|
||||
}
|
||||
|
||||
checkErrors(riscode) {
|
||||
//console.log("RIS = " + riscode);
|
||||
if (riscode === ErroriMongoDb.DUPLICATE_EMAIL_ID) {
|
||||
this.showNotif(this.$t('reg.err.duplicate_email'))
|
||||
} else if (riscode === ErroriMongoDb.DUPLICATE_USERNAME_ID) {
|
||||
this.showNotif(this.$t('reg.err.duplicate_username'))
|
||||
} else if (riscode === ErroriMongoDb.OK) {
|
||||
this.$router.push('/')
|
||||
} else {
|
||||
this.showNotif("Errore num " + riscode)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
submit() {
|
||||
this.$v.user.$touch()
|
||||
|
||||
this.duplicate_email = false
|
||||
this.duplicate_username = false
|
||||
|
||||
if (!this.user.terms) {
|
||||
this.showNotif(this.$t('reg.err.terms'))
|
||||
return
|
||||
}
|
||||
|
||||
if (this.v.user.$error) {
|
||||
this.showNotif(this.$t('reg.err.errore_generico'))
|
||||
return
|
||||
}
|
||||
|
||||
this.$q.loading.show({ message: this.$t('reg.incorso') })
|
||||
|
||||
console.log(this.user)
|
||||
UserModule.signup(this.user)
|
||||
.then((riscode) => {
|
||||
this.checkErrors(riscode)
|
||||
this.$q.loading.hide()
|
||||
}).catch(error => {
|
||||
console.log("ERROR = " + error)
|
||||
this.$q.loading.hide()
|
||||
})
|
||||
|
||||
|
||||
// ...
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
8
src/views/login/signup/signup.vue
Normal file
8
src/views/login/signup/signup.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template src="./signup.html">
|
||||
</template>
|
||||
|
||||
<script lang='ts' src="signup.ts" >
|
||||
</script>
|
||||
|
||||
<style src="signup.scss" lang="scss">
|
||||
</style>
|
||||
255
src/vuelidate.d.ts
vendored
Normal file
255
src/vuelidate.d.ts
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
declare module 'vuelidate' {
|
||||
|
||||
import { default as _Vue } from 'vue'
|
||||
|
||||
/**
|
||||
* @module augmentation to ComponentOptions defined by Vue.js
|
||||
*/
|
||||
module 'vue/types/options' {
|
||||
interface ComponentOptions<V extends _Vue> {
|
||||
validations?: ValidationRuleset<{}>
|
||||
}
|
||||
}
|
||||
|
||||
module 'vue/types/vue' {
|
||||
interface Vue {
|
||||
$v: Vuelidate<any>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instance of validator class at runtime
|
||||
*/
|
||||
interface IValidator {
|
||||
/**
|
||||
* Indicates the state of validation for given model. becomes true when any of it's child validators specified in options returns a falsy value. In case of validation groups, all grouped validators are considered.
|
||||
*/
|
||||
readonly $invalid: boolean
|
||||
/**
|
||||
* A flag representing if the field under validation was touched by the user at least once. Usually it is used to decide if the message is supposed to be displayed to the end user. Flag is managed manually. You have to use $touch and $reset methods to manipulate it. The $dirty flag is considered true if given model was $touched or all of it's children are $dirty.
|
||||
*/
|
||||
$dirty: boolean
|
||||
/**
|
||||
* Convenience flag to easily decide if a message should be displayed. It is a shorthand to $invalid && $dirty.
|
||||
*/
|
||||
readonly $error: boolean
|
||||
/**
|
||||
* Indicates if any child async validator is currently pending. Always false if all validators are synchronous.
|
||||
*/
|
||||
$pending: boolean
|
||||
|
||||
/**
|
||||
* Sets the $dirty flag of the model and all its children to true recursively.
|
||||
*/
|
||||
$touch(): void
|
||||
|
||||
/**
|
||||
* Sets the $dirty flag of the model and all its children to false recursively.
|
||||
*/
|
||||
$reset(): void
|
||||
}
|
||||
|
||||
/**
|
||||
* Builtin validators
|
||||
*/
|
||||
interface IDefaultValidators {
|
||||
/**
|
||||
* Accepts only alphabet characters.
|
||||
*/
|
||||
alpha?: boolean
|
||||
/**
|
||||
* Accepts only alphanumerics.
|
||||
*/
|
||||
alphaNum?: boolean
|
||||
/**
|
||||
* Checks if a number is in specified bounds. Min and max are both inclusive.
|
||||
*/
|
||||
between?: boolean
|
||||
/**
|
||||
* Accepts valid email addresses. Keep in mind you still have to carefully verify it on your server, as it is impossible to tell if the address is real without sending verification email.
|
||||
*/
|
||||
email?: boolean
|
||||
/**
|
||||
* Requires the input to have a maximum specified length, inclusive. Works with arrays.
|
||||
*/
|
||||
maxLength?: boolean
|
||||
/**
|
||||
* Requires the input to have a minimum specified length, inclusive. Works with arrays.
|
||||
*/
|
||||
minLength?: boolean
|
||||
/**
|
||||
* Requires non-empty data. Checks for empty arrays and strings containing only whitespaces.
|
||||
*/
|
||||
required?: boolean
|
||||
/**
|
||||
* Checks for equality with a given property. Locator might be either a sibling property name or a function, that will get your component as this and nested model which sibling properties under second parameter.
|
||||
*/
|
||||
sameAs?: boolean
|
||||
/**
|
||||
* Passes when at least one of provided validators passes.
|
||||
*/
|
||||
or?: boolean
|
||||
/**
|
||||
* Passes when all of provided validators passes.
|
||||
*/
|
||||
and?: boolean
|
||||
}
|
||||
|
||||
type EachByKey<T> = {
|
||||
[K in keyof T]: Validator<T[K]>
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds all validation models of collection validator. Always preserves the keys of original model, so it can be safely referenced in the v-for loop iterating over your data using the same index.
|
||||
*/
|
||||
type Each<T> =
|
||||
& {[key: number]: EachByKey<T>}
|
||||
& {$trackBy: string | Function}
|
||||
& IValidator
|
||||
|
||||
global {
|
||||
interface Array<T> {
|
||||
/**
|
||||
* Holds all validation models of collection validator. Always preserves the keys of original model, so it can be safely referenced in the v-for loop iterating over your data using the same index.
|
||||
*/
|
||||
$each: Each<T> & Vuelidate<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instance of validator class at runtime
|
||||
*/
|
||||
type Validator<T> = IValidator & IDefaultValidators & Each<T>
|
||||
|
||||
interface IPredicate {
|
||||
(value: any, parentVm?: IValidationRule): boolean | Promise<boolean>
|
||||
}
|
||||
|
||||
interface IPredicateGenerator {
|
||||
(...args: any[]): IPredicate
|
||||
}
|
||||
|
||||
interface IValidationRule {
|
||||
[key: string]: ValidationPredicate | IValidationRule | IValidationRule[]
|
||||
}
|
||||
|
||||
export type ValidationPredicate = IPredicateGenerator | IPredicate
|
||||
|
||||
/**
|
||||
* Represents mixin data exposed by Vuelidate instance
|
||||
*/
|
||||
export type Vuelidate<T> = {
|
||||
[K in keyof T]?: Vuelidate<T[K]> & Validator<T[K]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents component options used by Vuelidate
|
||||
*/
|
||||
export type ValidationRuleset<T> = {
|
||||
[K in keyof T]?: ValidationPredicate | IValidationRule | IValidationRule[] | string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents Vuelidate mixin data extending a Vue component instance. Have your Vue component options implement this
|
||||
* @param {Type} T - The interface or type being used to store model data requiring validation
|
||||
*
|
||||
* @example
|
||||
* export class Foo implements IVuelidate<IBar> {
|
||||
* data() {
|
||||
* return { bar: { length: 0 } };
|
||||
* }
|
||||
* validations: {
|
||||
* bar: {
|
||||
* length: {
|
||||
* between: between(1,5)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* $v: Vuelidate<IBar>;
|
||||
* }
|
||||
*/
|
||||
export interface IVuelidate<T> {
|
||||
$v: Vuelidate<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin object for supplying directly to components
|
||||
*/
|
||||
export const validationMixin: {
|
||||
beforeCreate(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vuelidate function that creates a validator directly, given a model, and a set of rules
|
||||
*/
|
||||
export const validateModel: {
|
||||
<T>(model: T, validations: ValidationRuleset<T>): IVuelidate<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vue plugin object
|
||||
*/
|
||||
export function Validation(Vue: typeof _Vue): void
|
||||
|
||||
export default Validation
|
||||
}
|
||||
|
||||
|
||||
declare module 'vuelidate/lib/validators' {
|
||||
|
||||
import { ValidationPredicate } from 'vuelidate'
|
||||
|
||||
/**
|
||||
* Accepts only alphabet characters.
|
||||
*/
|
||||
function alpha(value: any): boolean
|
||||
|
||||
/**
|
||||
* Accepts only alphanumerics.
|
||||
*/
|
||||
function alphaNum(value: any): boolean
|
||||
|
||||
/**
|
||||
* Checks if a number is in specified bounds. Min and max are both inclusive.
|
||||
*/
|
||||
function between(min: number, max: number): (value: any) => boolean
|
||||
|
||||
/**
|
||||
* Accepts valid email addresses. Keep in mind you still have to carefully verify it on your server, as it is impossible to tell if the address is real without sending verification email.
|
||||
*/
|
||||
function email(value: any): boolean
|
||||
|
||||
/**
|
||||
* Requires the input to have a maximum specified length, inclusive. Works with arrays.
|
||||
*/
|
||||
function maxLength(max: number): (value: any) => boolean
|
||||
|
||||
/**
|
||||
* Requires the input to have a minimum specified length, inclusive. Works with arrays.
|
||||
*/
|
||||
function minLength(min: number): (value: any) => boolean
|
||||
|
||||
/**
|
||||
* Requires non-empty data. Checks for empty arrays and strings containing only whitespaces.
|
||||
*/
|
||||
function required(value: any): boolean
|
||||
|
||||
/**
|
||||
* Checks for equality with a given property. Locator might be either a sibling property name or a function, that will get your component as this and nested model which sibling properties under second parameter.
|
||||
*/
|
||||
function sameAs(locator: string): (value: any, vm?: any) => boolean
|
||||
|
||||
|
||||
function minValue(min: number): (value: any) => boolean
|
||||
function maxValue(min: number): (value: any) => boolean
|
||||
|
||||
/**
|
||||
* Passes when at least one of provided validators passes.
|
||||
*/
|
||||
function or(...validators: ValidationPredicate[]): () => boolean
|
||||
|
||||
/**
|
||||
* Passes when all of provided validators passes.
|
||||
*/
|
||||
function and(...validators: ValidationPredicate[]): () => boolean
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"listEmittedFiles": false,
|
||||
"module": "es2015",
|
||||
"moduleResolution": "Node",
|
||||
"target": "es2015",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
"dom",
|
||||
"es6"
|
||||
],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
|
||||
Reference in New Issue
Block a user