@fishawack/lab-velocity 1.2.2 → 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>
@@ -123,13 +123,16 @@ export function authRoutes(node, store, nested = 'auth') {
123
123
  export function configureRoutes(router) {
124
124
 
125
125
  router.beforeEach((to, from, next) => {
126
- const { authenticated, user, authBase, redirect } = store.state.auth;
126
+ const { authenticated, user, authBase, redirect, forcePasswordChange } = store.state.auth;
127
127
 
128
128
  const admin = to.path.includes("/admin");
129
129
 
130
130
  if (to.query.verified) {
131
131
  next({ name: `${authBase}.success-verify` });
132
132
  } else if (authenticated) {
133
+ if(forcePasswordChange) {
134
+ next(false);
135
+ }
133
136
  if (admin && !user?.admin) {
134
137
  next({ name: redirect });
135
138
  } else if (to.name === "login" || to.name === `${authBase}.login`) {
@@ -6,6 +6,7 @@ const store = {
6
6
  return {
7
7
  authBase : process.env.HYDRATE_ROUTE ?? 'auth',
8
8
  authenticated : false,
9
+ forcePasswordChange: false,
9
10
  intended: null,
10
11
  user: null,
11
12
  redirect: process.env.HYDRATE_REDIRECT ?? 'index'
@@ -30,6 +31,9 @@ const store = {
30
31
  setIntended(state, value) {
31
32
  state.intended = value;
32
33
  },
34
+ setForcePasswordChange(state, value) {
35
+ state.forcePasswordChange = value;
36
+ }
33
37
  },
34
38
 
35
39
  actions: {
@@ -42,6 +46,7 @@ const store = {
42
46
  })
43
47
  .then((res) => {
44
48
  commit("setUser", res.data.data);
49
+ commit("setForcePasswordChange",res.data.data?.force_password_change);
45
50
  return res.data.data;
46
51
  })
47
52
  .catch(errors);
@@ -50,6 +55,7 @@ const store = {
50
55
  logout({ commit }, { errors }) {
51
56
  commit("setAuth", false);
52
57
  commit("setUser", null);
58
+ commit("setForcePasswordChange", false);
53
59
 
54
60
  return axios.post("/logout");
55
61
  },
@@ -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>
@@ -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() {
@@ -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']} });
@@ -1,4 +1,5 @@
1
1
  @import "../components/button";
2
+ @import "./modal";
2
3
 
3
4
  // AuthModule
4
5
  .AuthModule {
@@ -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.2",
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
  },