@fishawack/lab-velocity 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,105 @@
1
+ <template>
2
+ <div :class="{'active': open}"
3
+ class="modal modal--auth">
4
+
5
+ <div class="modal__container"
6
+ v-on:click.self="close">
7
+
8
+ <div class="modal__box AuthModule__form" :class="boxCls">
9
+ <component v-if="compName"
10
+ :is="compName"
11
+ v-bind="compProps"
12
+ @close="close"
13
+ />
14
+ </div>
15
+ <button class="button button--modal" @click="open = false"></button>
16
+ </div>
17
+
18
+ </div>
19
+ </template>
20
+
21
+ <script>
22
+ "use strict";
23
+
24
+ export default {
25
+ name: "AuthModal",
26
+
27
+ data() {
28
+ return {
29
+ open: false,
30
+ compName: null,
31
+ compProps: null,
32
+ cb: null,
33
+ boxCls: null,
34
+ validComponents: {
35
+ "force-reset" : 'VForceReset',
36
+ "password-change" : 'VPasswordChange'
37
+ }
38
+ };
39
+ },
40
+
41
+ methods: {
42
+ reset(){
43
+ this.compName = null;
44
+ this.compProps = null;
45
+ this.boxCls = null;
46
+ this.cb = null;
47
+ },
48
+ close() {
49
+ if(this.compName === 'VForceReset') {
50
+ if(!this.$store.state.auth.forcePasswordChange) {
51
+ this.open = false;
52
+ }
53
+ } else {
54
+ this.open = false;
55
+ }
56
+ }
57
+ },
58
+
59
+ created() {
60
+ this.$emitter.on('AuthModal', (comp) => {
61
+ this.compName = this.validComponents[comp.type];
62
+ this.open = comp;
63
+
64
+ });
65
+
66
+ window.addEventListener('keydown', (e) => {
67
+ if (e.key === 'Escape') {
68
+ this.open = false;
69
+ }
70
+ });
71
+ },
72
+
73
+ watch: {
74
+ open(isOpen) {
75
+ this.$emitter.emit('toggleBackgroundScroll', isOpen);
76
+
77
+ if(this.cb){
78
+ this.cb();
79
+ }
80
+
81
+ if (!isOpen) {
82
+ this.reset();
83
+ }
84
+ },
85
+ '$route'(to,from) {
86
+ if(from.fullPath !== to.fullPath) {
87
+ this.open = false;
88
+ }
89
+ },
90
+ '$store.state.auth.forcePasswordChange': {
91
+ async handler(forcePasswordChange) {
92
+ if(forcePasswordChange) {
93
+ this.compName = this.validComponents["force-reset"];
94
+ this.open = comp;
95
+ }
96
+ }
97
+ }
98
+ },
99
+
100
+ components: {
101
+ VForceReset: require("../routes/force-reset.vue").default,
102
+ VPasswordChange: require("../routes/change-password.vue").default,
103
+ }
104
+ };
105
+ </script>
@@ -1,6 +1,6 @@
1
1
  import axios from "axios";
2
2
 
3
- export default function setAxiosDefaults() {
3
+ function setAxiosDefaults() {
4
4
  axios.defaults.baseURL = process.env.APP_URL;
5
5
  axios.defaults.withCredentials = true;
6
6
  axios.defaults.withXSRFToken = true;
@@ -22,4 +22,6 @@ axios.interceptors.response.use(null, (error) => {
22
22
  }
23
23
 
24
24
  return Promise.reject(error);
25
- });
25
+ });
26
+
27
+ setAxiosDefaults();
@@ -100,5 +100,60 @@ export function authRoutes(node, store, nested = 'auth') {
100
100
  footer: false,
101
101
  fullpageModal: true,
102
102
  },
103
+ },
104
+ {
105
+ path: "/admin",
106
+ redirect: () => {
107
+ const { authenticated } = store.state.auth;
108
+
109
+ if (!authenticated) {
110
+ store.commit("setAuth", true);
111
+ window.location = `${process.env.APP_URL}/login`;
112
+ } else {
113
+ window.location = `${process.env.APP_URL}/`;
114
+ }
115
+ },
116
+ name: "admin",
117
+ meta: {
118
+ guest: true,
119
+ },
103
120
  }]
121
+ }
122
+
123
+ export function configureRoutes(router) {
124
+
125
+ router.beforeEach((to, from, next) => {
126
+ const { authenticated, user, authBase, redirect, forcePasswordChange } = store.state.auth;
127
+
128
+ const admin = to.path.includes("/admin");
129
+
130
+ if (to.query.verified) {
131
+ next({ name: `${authBase}.success-verify` });
132
+ } else if (authenticated) {
133
+ if(forcePasswordChange) {
134
+ next(false);
135
+ }
136
+ if (admin && !user?.admin) {
137
+ next({ name: redirect });
138
+ } else if (to.name === "login" || to.name === `${authBase}.login`) {
139
+ next({ name: redirect });
140
+ } else if (
141
+ !user?.email_verified_at &&
142
+ to.matched.some((d) => d.meta.guest) !== true
143
+ ) {
144
+ next({ name: `${authBase}.verify` });
145
+ } else {
146
+ next();
147
+ }
148
+ } else {
149
+ if (to.matched.some((d) => d.meta.guest) === true) {
150
+ next();
151
+ } else if (admin) {
152
+ next({ name: "login" });
153
+ } else {
154
+ next({ name: `${authBase}.login` });
155
+ }
156
+ }
157
+ });
158
+
104
159
  }
@@ -1,64 +1,64 @@
1
1
  "use strict";
2
2
  import axios from "axios";
3
- import { createStore } from "vuex";
4
- import VuexPersistedState from 'vuex-persistedstate';
5
3
 
6
- export function initAuthStore(store) {
4
+ const store = {
5
+ state() {
6
+ return {
7
+ authBase : process.env.HYDRATE_ROUTE ?? 'auth',
8
+ authenticated : false,
9
+ forcePasswordChange: false,
10
+ intended: null,
11
+ user: null,
12
+ redirect: process.env.HYDRATE_REDIRECT ?? 'index'
13
+ }
14
+ },
7
15
 
8
- store.registerModule('auth',{
9
- state() {
10
- return {
11
- authBase : process.env.HYDRATE_ROUTE ?? 'auth',
12
- authenticated : false,
13
- intended: null,
14
- user: null,
15
- redirect: process.env.HYDRATE_REDIRECT ?? 'index'
16
+ mutations: {
17
+ setAuth(state, value) {
18
+ state.authenticated = value;
19
+
20
+ if (!value) {
21
+ this.commit("setUser", null);
22
+ }
23
+ },
24
+ setUser(state, value) {
25
+ state.user = value;
26
+
27
+ if(window.dataLayer){
28
+ window.dataLayer.push({ event: "logic", user: value });
16
29
  }
17
30
  },
31
+ setIntended(state, value) {
32
+ state.intended = value;
33
+ },
34
+ setForcePasswordChange(state, value) {
35
+ state.forcePasswordChange = value;
36
+ }
37
+ },
18
38
 
19
- mutations: {
20
- setAuth(state, value) {
21
- state.authenticated = value;
22
-
23
- if (!value) {
24
- this.commit("setUser", null);
25
- }
26
- },
27
- setUser(state, value) {
28
- console.log("we are setting the user");
29
- console.log(value);
30
- state.user = value;
31
-
32
- if(window.dataLayer){
33
- window.dataLayer.push({ event: "logic", user: value });
34
- }
35
- },
36
- setIntended(state, value) {
37
- state.intended = value;
38
- },
39
+ actions: {
40
+ getUser({ commit }, { errors, query = "" }) {
41
+ return axios
42
+ .get(`/api/user/self${query}`, {
43
+ params: {
44
+ include: "company"
45
+ }
46
+ })
47
+ .then((res) => {
48
+ commit("setUser", res.data.data);
49
+ commit("setForcePasswordChange",res.data.data?.force_password_change);
50
+ return res.data.data;
51
+ })
52
+ .catch(errors);
39
53
  },
40
54
 
41
- actions: {
42
- getUser({ commit }, { errors, query = "" }) {
43
- return axios
44
- .get(`/api/user/self${query}`, {
45
- params: {
46
- include: "company"
47
- }
48
- })
49
- .then((res) => {
50
- commit("setUser", res.data.data);
51
- return res.data.data;
52
- })
53
- .catch(errors);
54
- },
55
-
56
- logout({ commit }, { errors }) {
57
- commit("setAuth", false);
58
- commit("setUser", null);
59
-
60
- return axios.post("/logout");
61
- },
55
+ logout({ commit }, { errors }) {
56
+ commit("setAuth", false);
57
+ commit("setUser", null);
58
+ commit("setForcePasswordChange", false);
59
+
60
+ return axios.post("/logout");
62
61
  },
63
- });
64
- }
62
+ },
63
+ };
64
+ export default store;
@@ -0,0 +1,84 @@
1
+ import axios from "axios";
2
+
3
+
4
+ axios.interceptors.request.use(
5
+ request => {
6
+ throw { isLocal: true, data: retrieveResponse(request) };
7
+ },
8
+ error => {
9
+ return Promise.resolve(error);
10
+ }
11
+ );
12
+
13
+ axios.interceptors.response.use(
14
+ response => {
15
+ return response;
16
+ },
17
+ error => {
18
+ return Promise.resolve(error);
19
+ }
20
+ );
21
+
22
+ function retrieveResponse(request) {
23
+ return fakes[request.url][request.method];
24
+ }
25
+
26
+ const fakes = {
27
+ "/api/user/self": {
28
+ "get" : {
29
+ "data":{
30
+ "id": 2,
31
+ "company_id": 1,
32
+ "created_at": "2025-04-14T15:11:11.000000Z",
33
+ "updated_at": "2025-04-16T16:45:55.000000Z",
34
+ "newsletter": null,
35
+ "admin": 1,
36
+ "name": "Jeremy Viner",
37
+ "email": "jeremy.viner@avalerehealth.com",
38
+ "email_verified_at": "2025-04-16T16:45:55.000000Z",
39
+ "brands": [],
40
+ "company": {
41
+ "id": 1,
42
+ "name": "Company 0",
43
+ "created_at": "2025-04-14T15:08:50.000000Z",
44
+ "updated_at": "2025-04-14T15:08:50.000000Z",
45
+ "domains": [
46
+ "okuneva.com",
47
+ "bogisich.info",
48
+ "avalerehealth.com"
49
+ ],
50
+ "brands": [
51
+ 1
52
+ ],
53
+ "events": [],
54
+ "therapy_areas": [],
55
+ "event_therapy_areas": [],
56
+ "event_therapy_area_media_types": [],
57
+ "users_count": null,
58
+ "sso_client_id": "5ea409fc-8dcf-423e-b7fa-867422859ea4",
59
+ "sso_client_secret": "aSS8Q~ss0r-uh.fPLHUXVXp2kIs5IfwTjzomrb_I",
60
+ "sso_tenant": "4a33c544-865e-44a4-836f-bc51800f6c5e",
61
+ "sso_type": "azure"
62
+ }
63
+ }
64
+ }
65
+ },
66
+ "/user/password": {
67
+ "put": {
68
+
69
+ }
70
+ },
71
+ "/logged-in": {
72
+ "get":{"logged-in":true},
73
+ },
74
+ "/logout": {
75
+ "post": {
76
+
77
+ }
78
+ },
79
+ "/login" : {
80
+ "post": {
81
+ "two_factor": false
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,139 @@
1
+ <template>
2
+ <div class="relative">
3
+ <section id="resetPasswordForm">
4
+ <h1 class="h2 h2--small" v-html="!form.successful ? 'Change password' : 'Success'" />
5
+ <form class="form" @submit.prevent="submit">
6
+ <div v-if="!form.successful">
7
+ <p class="AM-mt-2 AM-mb-0">
8
+ Please complete the fields below to change your password.
9
+ </p>
10
+ <el-input
11
+ v-model="form.password"
12
+ class="AM-mt-3"
13
+ label="New Password"
14
+ placeholder="Enter your new password"
15
+ name="password"
16
+ :error="form.errors"
17
+ type="password"
18
+ autocomplete="new-password"
19
+ required
20
+ />
21
+
22
+ <VPasswordValidation :password="form.password" @passwordValid="updatePasswordValidity" />
23
+ <div class="flex AM-mt-3">
24
+ <elButton
25
+ class=""
26
+ type="primary"
27
+ :disabled="form.processing || !isPasswordValid"
28
+ :loading="form.processing"
29
+ @click="onSubmit"
30
+ >
31
+ <span v-text="'Change password'" />
32
+ </elButton>
33
+
34
+ <elButton
35
+ class=""
36
+ type="secondary"
37
+ @click="handleButton"
38
+ >
39
+ <span v-text="'Cancel'" />
40
+ </elButton>
41
+ </div>
42
+ </div>
43
+ <div v-else>
44
+ <strong class="">Email: {{ $store.state.auth?.user?.email }}</strong>
45
+ <p v-text="`Your password has been updated.`" />
46
+ <elButton
47
+ class=""
48
+ type="secondary"
49
+ @click="handleButton"
50
+ >
51
+ <span v-text="'Continue'" />
52
+ </elButton>
53
+ </div>
54
+ </form>
55
+ </section>
56
+ </div>
57
+ </template>
58
+
59
+ <script>
60
+ import Form from "form-backend-validation";
61
+
62
+ export default {
63
+ data() {
64
+ return {
65
+ form: new Form(
66
+ {
67
+ email: this.$store.state.auth.user?.email,
68
+ password: '',
69
+ },
70
+ { resetOnSuccess: false }
71
+ ),
72
+ isPasswordValid: false,
73
+ };
74
+ },
75
+
76
+ mounted() {
77
+
78
+ this.$store.dispatch("getUser", {
79
+ errors: this.$root.errors,
80
+ });
81
+ },
82
+
83
+ methods: {
84
+ async onSubmit() {
85
+ this.loading = true;
86
+
87
+ try {
88
+ await this.form.put('/user/password');
89
+ await this.login();
90
+ } catch (e) {
91
+ this.$root.errors(e);
92
+ } finally {
93
+ this.loading = false;
94
+ }
95
+ },
96
+ async login() {
97
+ this.loading = true;
98
+
99
+ try {
100
+ const res = await this.form.post("/login");
101
+
102
+ if(res['logged-in']){
103
+ try{
104
+ await this.$store.dispatch("logout", {
105
+ // errors: this.$root.errors,
106
+ });
107
+ } catch(e){}
108
+
109
+ await this.form.post("/login");
110
+ }
111
+
112
+ } catch (e) {
113
+ this.$root.errors(e);
114
+ } finally {
115
+ this.loading = false;
116
+ }
117
+ },
118
+
119
+ updatePasswordValidity(isValid) {
120
+ this.isPasswordValid = isValid;
121
+ },
122
+ handleButton() {
123
+ this.$emit('close');
124
+ }
125
+ },
126
+
127
+ metaInfo() {
128
+ return {
129
+ title: "Reset Password",
130
+ };
131
+ },
132
+
133
+ components: {
134
+ VPasswordValidation: require("./../components/VPasswordValidation.vue").default,
135
+ elInput: require('../../form/basic.vue').default,
136
+ elButton: require('../../basic/Button.vue').default,
137
+ },
138
+ };
139
+ </script>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="relative">
3
3
  <section id="resetPasswordForm">
4
- <h1 class="h2">Welcome</h1>
4
+ <h1 class="h2 h2--small" v-html="!form.successful ? 'Welcome' : 'Success'" />
5
5
  <form class="form" @submit.prevent="submit">
6
6
  <div v-if="!form.successful">
7
7
  <p class="AM-mt-2 AM-mb-0 AM-color-highlight">
@@ -35,17 +35,14 @@
35
35
  </elButton>
36
36
  </div>
37
37
  <div v-else>
38
- <h4 class="mt" v-text="`Success!`" />
39
- <strong class="">Email: {{ user?.email }}</strong>
38
+ <strong class="">Email: {{ $store.state.auth?.user?.email }}</strong>
40
39
  <p v-text="`Your password has been updated.`" />
41
- <p class="disclaimer AM-mb-2.5">
42
- <router-link
43
- class="color-1 underline"
44
- :to="{ name: `${$store.state.auth.authBase}.login` }"
45
- >
46
- Continue
47
- </router-link>
48
- </p>
40
+ <elButton
41
+ type="primary"
42
+ @click="handleButton"
43
+ >
44
+ Continue
45
+ </elButton>
49
46
  </div>
50
47
  </form>
51
48
  </section>
@@ -61,7 +58,7 @@ export default {
61
58
  return {
62
59
  form: new Form(
63
60
  {
64
- email: this.$store.state.user?.email,
61
+ email: this.$store.state.auth.user?.email,
65
62
  password: '',
66
63
  },
67
64
  { resetOnSuccess: false }
@@ -105,7 +102,6 @@ export default {
105
102
 
106
103
  await this.form.post("/login");
107
104
  }
108
- this.$router.push({name: `${this.$store.state.auth.authBase}.callback`,query: {authenticated: true}});
109
105
 
110
106
  } catch (e) {
111
107
  this.$root.errors(e);
@@ -117,6 +113,9 @@ export default {
117
113
  updatePasswordValidity(isValid) {
118
114
  this.isPasswordValid = isValid;
119
115
  },
116
+ handleButton() {
117
+ this.$emit('close');
118
+ }
120
119
  },
121
120
 
122
121
  metaInfo() {
@@ -117,8 +117,8 @@ export default {
117
117
  window.dataLayer.push({ event: "login", user });
118
118
  }
119
119
 
120
- if (this.$store.state.intended) {
121
- this.$router.push(this.$store.state.intended);
120
+ if (this.$store.state.auth.intended) {
121
+ this.$router.push(this.$store.state.auth.intended);
122
122
  } else {
123
123
  this.$router.push({ name: "members" });
124
124
  // Problem here
@@ -29,11 +29,10 @@ export default {
29
29
  window.dataLayer.push({ event: "login", user });
30
30
  }
31
31
 
32
- if (this.$store.state.intended) {
33
- this.$router.push(this.$store.state.intended);
32
+ if (this.$store.state.auth.intended) {
33
+ this.$router.push(this.$store.state.auth.intended);
34
34
  } else {
35
- this.$router.push({ name: "members" });
36
- // Problem Here
35
+ this.$router.push({ name: this.$store.state.auth.redirect });
37
36
  }
38
37
 
39
38
  this.$store.commit("setIntended", null);
@@ -28,7 +28,7 @@
28
28
 
29
29
  <el-button
30
30
  type="primary"
31
- :disabled="loading || (form.email?.length < 5 || form.password?.length < 8 )"
31
+ :disabled="loading || (!isValidEmail(form.email))"
32
32
  @click="onSubmit"
33
33
  >
34
34
  Continue
@@ -112,7 +112,12 @@ export default {
112
112
  vue.setCountdown();
113
113
  },1000);
114
114
  }
115
+ },
116
+ isValidEmail(email) {
117
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
118
+ return emailRegex.test(email);
115
119
  }
120
+
116
121
  },
117
122
 
118
123
  mounted() {
@@ -103,7 +103,6 @@ export default {
103
103
  await this.form.post("/register");
104
104
 
105
105
  }
106
- console.log(res);
107
106
  if(res['redirect']) {
108
107
  // Redirect here
109
108
  this.$router.push({ name: `${this.$store.state.auth.authBase}.account-exists`, query: {company:res['company']} });
@@ -129,8 +128,8 @@ export default {
129
128
  window.dataLayer.push({ event: "login", user });
130
129
  }
131
130
 
132
- if (this.$store.state.intended) {
133
- this.$router.push(this.$store.state.intended);
131
+ if (this.$store.state.auth.intended) {
132
+ this.$router.push(this.$store.state.auth.intended);
134
133
  } else {
135
134
  this.$router.push({ name: `${this.$store.state.auth.authBase}.verify` });
136
135
  // Problem Here
@@ -1,4 +1,5 @@
1
1
  @import "../components/button";
2
+ @import "./modal";
2
3
 
3
4
  // AuthModule
4
5
  .AuthModule {
@@ -6,7 +7,7 @@
6
7
  background-color: black;
7
8
  height: 100vh;
8
9
  width: 100%;
9
- background: url('../media/content/images/hero-login-large.jpg') center top no-repeat;
10
+ background: url('../media/content/images/hero-auth-background-desktop.jpg') center top no-repeat;
10
11
  background-size: cover;
11
12
  z-index: 10;
12
13
 
@@ -14,6 +15,12 @@
14
15
  width: 13.3rem;
15
16
  z-index: 99;
16
17
  }
18
+ @include breakpoint (max-width $tabletMax) {
19
+ background: url('../media/content/images/hero-auth-background-tablet.jpg') center top no-repeat;
20
+ }
21
+ @include breakpoint (max-width $mobileMax) {
22
+ background: url('../media/content/images/hero-auth-background-mobile.jpg') center top no-repeat;
23
+ }
17
24
  }
18
25
 
19
26
  .AuthModule__form {
@@ -0,0 +1,21 @@
1
+ @import "@fishawack/lab-ui/_modal.scss";
2
+
3
+ .modal--auth {
4
+ background-color: transparent;
5
+
6
+ transition: all 500ms ease-in-out;
7
+
8
+ &::before{
9
+ content: '';
10
+ pointer-events: none;
11
+ z-index: 0 !important;
12
+ position: fixed;
13
+ width: 100%;
14
+ height: 100%;
15
+ background: #00000066;
16
+ backdrop-filter: blur(1.5rem);
17
+ top: 0;
18
+ left: 0;
19
+ right: 0;
20
+ }
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishawack/lab-velocity",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Avalere Health branded style system",
5
5
  "scripts": {
6
6
  "setup": "npm ci || npm i && npm run content",
@@ -38,7 +38,9 @@
38
38
  "vue-loader": "^17.2.2",
39
39
  "vue-router": "^4.4.0",
40
40
  "vuex": "^4.1.0",
41
- "vuex-persistedstate": "^4.1.0"
41
+ "vuex-persistedstate": "^4.1.0",
42
+ "form-backend-validation": "github:mikemellor11/form-backend-validation#master",
43
+ "mitt": "^3.0.1"
42
44
  },
43
45
  "dependencies": {
44
46
  "@tiptap/extension-link": "^2.11.2",
@@ -53,7 +55,6 @@
53
55
  "@tiptap/starter-kit": "^2.11.2",
54
56
  "@tiptap/vue-3": "^2.11.2",
55
57
  "element-plus": "^2.7.8",
56
- "form-backend-validation": "github:mikemellor11/form-backend-validation#master",
57
58
  "quill": "^1.3.7",
58
59
  "sanitize-html": "^2.13.1"
59
60
  },