@hostlink/nuxt-light 1.18.3 → 1.19.1

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.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "1.18.3",
4
+ "version": "1.19.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import { ref } from 'vue'
3
3
  import { useQuasar, useDialogPluginComponent } from 'quasar'
4
- import { m, api } from '#imports';
4
+ import { api } from '#imports';
5
5
  import { useI18n } from 'vue-i18n';
6
6
  const { t } = useI18n();
7
7
  const $q = useQuasar()
@@ -0,0 +1,57 @@
1
+ <script setup lang="ts">
2
+ import { useLight, m } from "#imports"
3
+ const light = useLight()
4
+
5
+
6
+ export interface LSystemSettingAuthenticationProps {
7
+ authentication_password_based: string,
8
+ authentication_google_client_id: string,
9
+ authentication_facebook_app_id: string,
10
+ authentication_microsoft_client_id: string,
11
+ authentication_microsoft_tenant_id: string
12
+ }
13
+
14
+
15
+ withDefaults(defineProps<LSystemSettingAuthenticationProps>(), {
16
+ authentication_password_based: "1",
17
+ authentication_google_client_id: "",
18
+ authentication_facebook_app_id: "",
19
+ authentication_microsoft_client_id: "",
20
+ authentication_microsoft_tenant_id: "",
21
+ })
22
+
23
+ const emits = defineEmits(["submit"])
24
+
25
+ const onSubmit = async (d: LSystemSettingAuthenticationProps) => {
26
+
27
+ //at least one authentication method should be enabled
28
+ if (d.authentication_password_based === "0" && !d.authentication_google_client_id && !d.authentication_facebook_app_id && !d.authentication_microsoft_client_id) {
29
+
30
+ light.notify({ message: "At least one authentication method should be enabled", color: "negative" })
31
+ return
32
+
33
+ }
34
+
35
+
36
+
37
+ emits("submit", d)
38
+ }
39
+
40
+ </script>
41
+ <template>
42
+
43
+
44
+ <form-kit type="l-form" :bordered="false" @submit="onSubmit" :value="$props">
45
+
46
+ <form-kit label="Password based" type="l-checkbox" name="authentication_password_based" true-value="1"
47
+ false-value="0" />
48
+ <q-separator />
49
+
50
+ <form-kit label="Google Client ID" type="l-input" name="authentication_google_client_id" />
51
+
52
+ <form-kit label="Facebook App ID" type="l-input" name="authentication_facebook_app_id" />
53
+
54
+ <form-kit label="Microsoft Client ID" type="l-input" name="authentication_microsoft_client_id" />
55
+ <form-kit label="Microsoft Tenant ID" type="l-input" name="authentication_microsoft_tenant_id" />
56
+ </form-kit>
57
+ </template>
@@ -1,4 +1,4 @@
1
- <script setup>
1
+ <script setup lang="ts">
2
2
  import { useRoute, useRouter } from 'vue-router';
3
3
  import { useLight, q, m } from "#imports";
4
4
  import { useQuasar } from 'quasar';
@@ -7,15 +7,12 @@ import { ref, computed, reactive, provide, watch, toRaw } from 'vue';
7
7
  import { useRuntimeConfig } from 'nuxt/app';
8
8
  import { api } from '#imports';
9
9
 
10
- const { t } = useI18n();
11
-
12
10
  const emits = defineEmits(["logout"]);
13
11
  const $q = useQuasar();
14
12
  $q.loading.show()
15
13
 
16
14
  const config = useRuntimeConfig();
17
15
 
18
-
19
16
  const appVersion = config.public.appVersion ?? '0.0.1';
20
17
 
21
18
  const tt = await q({
@@ -113,10 +110,9 @@ if (my && my.roles.indexOf('Administrators') != -1) {
113
110
  light.isAdmin = true;
114
111
  }
115
112
 
116
- const menuOverlayHeader = ref(false)
117
113
  const layoutView = computed(() => {
118
114
  let s = ''
119
- s += menuOverlayHeader.value ? 'l' : 'h'
115
+ s += style.menuOverlayHeader ? 'l' : 'h'
120
116
  s += 'Hh LpR lFr'
121
117
  return s
122
118
  })
@@ -124,7 +120,8 @@ const layoutView = computed(() => {
124
120
  const style = reactive({
125
121
  miniState: my.styles?.miniState || false,
126
122
  dense: my.styles?.dense || false,
127
- footer: my.styles?.footer || false
123
+ footer: my.styles?.footer || false,
124
+ menuOverlayHeader: my.styles?.menuOverlayHeader || false
128
125
  });
129
126
 
130
127
  const isMouseOnDrawer = ref(false)
@@ -176,6 +173,10 @@ watch(() => light.theme, async () => {
176
173
  $q.dark.set(light.isDarkMode());
177
174
  })
178
175
 
176
+ watch(() => style.menuOverlayHeader, async (value) => {
177
+ await light.setStyle("menuOverlayHeader", value)
178
+ })
179
+
179
180
  watch(() => style.miniState, async (value) => {
180
181
  await m("updateMyStyle", {
181
182
  name: "miniState",
@@ -1,7 +1,6 @@
1
1
  <script setup>
2
- import { provide, ref } from 'vue'
3
- import { useLight, watch } from "#imports";
4
-
2
+ import { ref } from 'vue'
3
+ import { reloadNuxtApp, useLight, watch } from "#imports";
5
4
  import { useQuasar } from 'quasar'
6
5
  import { q } from '#imports'
7
6
  import { useRoute } from "vue-router";
@@ -24,23 +23,49 @@ try {
24
23
  companyLogo: true,
25
24
  logged: true,
26
25
  twoFactorAuthentication: true,
26
+ forgetPasswordEnabled: true,
27
27
  googleClientId: true,
28
- forgetPasswordEnabled: true
28
+ microsoftClientId: true,
29
+ microsoftTenantId: true,
30
+ passwordBasedEnabled: true,
31
+ facebookAppId: true,
29
32
  }
30
33
  })).app;
31
34
  light.company = app.value.company;
32
35
  light.companyLogo = app.value.companyLogo;
33
36
  } catch (e) {
34
-
35
37
  quasar.dialog({
36
38
  title: 'Error',
37
- message: 'Error loading api data. Please reload the page.',
39
+ message: e.message,
38
40
  ok: 'Reload',
39
41
  }).onOk(() => {
40
42
  window.location.reload();
41
43
  });
42
44
  }
43
45
 
46
+ if (app.value.facebookAppId) {
47
+
48
+ //load facebook sdk
49
+
50
+ (function (d, s, id) {
51
+ var js, fjs = d.getElementsByTagName(s)[0];
52
+ if (d.getElementById(id)) { return; }
53
+ js = d.createElement(s); js.id = id;
54
+ js.src = "https://connect.facebook.net/en_US/sdk.js";
55
+ fjs.parentNode.insertBefore(js, fjs);
56
+ }(document, 'script', 'facebook-jssdk')
57
+ );
58
+
59
+ window.fbAsyncInit = function () {
60
+ FB.init({
61
+ appId: app.value.facebookAppId,
62
+ xfbml: true,
63
+ version: 'v21.0'
64
+ });
65
+ }
66
+ }
67
+
68
+
44
69
 
45
70
 
46
71
  </script>
@@ -48,12 +73,13 @@ try {
48
73
  <q-layout v-if="!app.logged">
49
74
  <q-page-container class="bg-grey-2" style="color:#1f1f1f">
50
75
  <q-page padding>
76
+ <div id="fb-root"></div>
51
77
  <l-login v-bind="app" @login="app.logged = true"></l-login>
52
78
  </q-page>
53
79
  </q-page-container>
54
80
  </q-layout>
55
81
 
56
- <l-app-main v-else @logout="app.logged = false">
82
+ <l-app-main v-else @logout="app.logged = false" v-bind="app">
57
83
  <template #header>
58
84
  <slot name="header"></slot>
59
85
  </template>
@@ -0,0 +1,28 @@
1
+ <script setup>
2
+
3
+ import { ref, onMounted } from 'vue';
4
+ const button = ref(null);
5
+ const emits = defineEmits(["login"]);
6
+
7
+ onMounted(() => {
8
+ if (window.FB) {
9
+ window.FB.XFBML.parse()
10
+ }
11
+
12
+ })
13
+
14
+ window.onFacebookLogin = () => {
15
+
16
+
17
+
18
+
19
+ window.FB.getAccessToken(accessToken => {
20
+ emits('login', accessToken)
21
+ })
22
+ }
23
+
24
+ </script>
25
+ <template>
26
+ <div class="fb-login-button" data-size="large" data-button-type="" data-layout="" data-auto-logout-link="false"
27
+ data-use-continue-as="false" ref="button" data-onlogin="onFacebookLogin()"></div>
28
+ </template>
@@ -1,20 +1,27 @@
1
- <script setup>
2
- import { ref, reactive, onMounted } from 'vue'
1
+ <script setup lang="ts">
2
+ import { ref, reactive, onMounted, resolveComponent } from 'vue'
3
3
  import { useQuasar } from 'quasar';
4
- import { api, useHead } from '#imports';
4
+ import { api, useHead, m } from '#imports';
5
5
  import { useI18n } from 'vue-i18n';
6
6
 
7
+
7
8
  const { t } = useI18n();
8
9
 
9
10
  const emits = defineEmits(["login"]);
10
11
 
11
- const props = defineProps({
12
- twoFactorAuthentication: Boolean,
13
- googleClientId: String,
14
- forgetPasswordEnabled: Boolean,
15
- company: String,
16
- companyLogo: String
17
- })
12
+ export interface LLoginProps {
13
+ company: string,
14
+ companyLogo: string,
15
+ twoFactorAuthentication: boolean,
16
+ forgetPasswordEnabled: boolean,
17
+ passwordBasedEnabled: boolean,
18
+ googleClientId?: string,
19
+ microsoftClientId?: string,
20
+ microsoftTenantId?: string,
21
+ facebookAppId?: string
22
+ }
23
+
24
+ const props = defineProps<LLoginProps>();
18
25
 
19
26
  useHead({
20
27
  title: props.company
@@ -27,11 +34,12 @@ const data = reactive({
27
34
 
28
35
  const $q = useQuasar()
29
36
 
30
- const loginWithCode = (username, password) => {
37
+ const loginWithCode = (username: string, password: string) => {
31
38
  $q.dialog({
32
39
  title: t("Enter your code"),
33
40
  message: t("Please enter your two factor authentication code (If you lost your authenticator, please contact your administrator)"),
34
41
  prompt: {
42
+ model: "",
35
43
  type: "text",
36
44
  required: true
37
45
  },
@@ -46,10 +54,15 @@ const loginWithCode = (username, password) => {
46
54
 
47
55
  }
48
56
 
57
+ const loading = ref(false);
58
+
49
59
  const submit = async () => {
50
60
  if (await form1.value.validate()) {
51
61
 
62
+
63
+
52
64
  try {
65
+ loading.value = true;
53
66
  await api.auth.login(data.username, data.password, data.code)
54
67
  //window.self.location.reload();
55
68
  //
@@ -68,13 +81,15 @@ const submit = async () => {
68
81
  color: "negative",
69
82
  icon: "sym_o_error",
70
83
  });
84
+ } finally {
85
+ loading.value = false;
71
86
 
72
87
 
73
88
  }
74
89
  }
75
90
  }
76
91
 
77
- const resetPassword = (username, code) => {
92
+ const resetPassword = (username: string, code: string) => {
78
93
  $q.dialog({
79
94
  title: t("Reset password"),
80
95
  message: t("Please enter your new password"),
@@ -116,6 +131,7 @@ const forgetPassword = async () => {
116
131
  title: t("Enter your code"),
117
132
  message: t("Please enter the code sent to your email, your code will expire in 10 minutes"),
118
133
  prompt: {
134
+ model: "",
119
135
  type: "text",
120
136
  required: true
121
137
  },
@@ -144,8 +160,12 @@ const bioLogin = async () => {
144
160
  try {
145
161
  await api.auth.WebAuthn.login(localStorage.getItem("username"));
146
162
  window.self.location.reload();
147
- } catch (e) {
148
- notify(e.message, "negative");
163
+ } catch (e: any) {
164
+ $q.notify({
165
+ message: e.message,
166
+ color: "negative",
167
+ icon: "sym_o_error",
168
+ });
149
169
  }
150
170
 
151
171
  }
@@ -153,12 +173,17 @@ const handleGoogleCredentialResponse = async (response) => {
153
173
  try {
154
174
  await api.auth.googleLogin(response.credential);
155
175
  window.self.location.reload();
156
- } catch (e) {
157
- notify(e.message, "negative");
176
+ } catch (e: any) {
177
+ $q.notify({
178
+ message: e.message,
179
+ color: "negative",
180
+ icon: "sym_o_error",
181
+ });
158
182
  }
159
183
  }
160
184
 
161
185
  onMounted(() => {
186
+
162
187
  if (props.googleClientId) {
163
188
  if (!window.google) {
164
189
  $q.notify({
@@ -191,6 +216,34 @@ onMounted(() => {
191
216
  );
192
217
  }
193
218
  })
219
+
220
+
221
+
222
+ const microsoftLogin = async (resp) => {
223
+ try {
224
+ await api.auth.microsoftLogin(resp.accessToken);
225
+ emits("login");
226
+ } catch (e) {
227
+ $q.notify({
228
+ message: e.message,
229
+ color: "negative",
230
+ });
231
+
232
+ }
233
+
234
+ }
235
+
236
+ const facebookLogin = (accessToken) => {
237
+ m("facebookLogin", { access_token: accessToken }).then(() => {
238
+ emits("login");
239
+ }).catch((e) => {
240
+ $q.notify({
241
+ message: e.message,
242
+ color: "negative",
243
+ });
244
+ });
245
+ }
246
+
194
247
  </script>
195
248
 
196
249
  <template>
@@ -204,7 +257,7 @@ onMounted(() => {
204
257
  <div class="text-h6">
205
258
  {{ company }}
206
259
  </div>
207
- <q-form ref="form1">
260
+ <q-form ref="form1" v-if="passwordBasedEnabled">
208
261
  <div class="q-gutter-sm">
209
262
  <l-input v-model.trim="data.username" label="Username" :rules="[v => !!v || $t('Username is required')]"
210
263
  clearable :outlined="false" stackLabel autocomplete="username">
@@ -229,15 +282,25 @@ onMounted(() => {
229
282
  </q-form>
230
283
  </q-card-section>
231
284
  <q-card-actions>
232
- <l-btn label="Login" outline rounded color="primary" icon="sym_o_login" @click="submit" />
285
+ <l-btn label="Login" outline rounded color="primary" icon="sym_o_login" @click="submit"
286
+ v-if="passwordBasedEnabled" />
233
287
  <l-btn v-if="hasBioLogin" outline rounded color="primary" icon="sym_o_fingerprint" @click="bioLogin" />
234
288
  <l-btn label="Forget password" outline rounded color="primary" icon="sym_o_lock_reset" @click="forgetPassword"
235
289
  v-if="forgetPasswordEnabled" />
236
290
  </q-card-actions>
237
- <q-card-actions v-if="props.googleClientId">
291
+ <q-card-actions v-if="googleClientId">
238
292
  <div>
239
293
  <div id="g_id_signin"></div>
240
294
  </div>
241
295
  </q-card-actions>
296
+
297
+ <q-card-actions v-if="microsoftClientId">
298
+ <l-microsoft-button :client-id="microsoftClientId" :tenant-id="microsoftTenantId"
299
+ @login="microsoftLogin"></l-microsoft-button>
300
+ </q-card-actions>
301
+
302
+ <q-card-actions v-if="facebookAppId">
303
+ <l-facebook-button @login="facebookLogin" />
304
+ </q-card-actions>
242
305
  </q-card>
243
306
  </template>
@@ -0,0 +1,120 @@
1
+ <script setup lang="ts">
2
+ import { PublicClientApplication } from '@azure/msal-browser';
3
+ import { useQuasar } from 'quasar';
4
+ const $q = useQuasar()
5
+
6
+ const props = defineProps({
7
+ clientId: {
8
+ type: String,
9
+ required: true
10
+ },
11
+ tenantId: String
12
+ })
13
+
14
+ const emits = defineEmits(["login"]);
15
+
16
+ const onClick = async () => {
17
+ const myMSALObject = new PublicClientApplication({
18
+ auth: {
19
+ clientId: props.clientId,
20
+ authority: "https://login.microsoftonline.com/" + props.tenantId,
21
+ redirectUri: window.location.origin,
22
+ },
23
+ cache: {
24
+ cacheLocation: "memoryStorage",
25
+ storeAuthStateInCookie: false,
26
+ }
27
+ });
28
+
29
+ try {
30
+ await myMSALObject.initialize();
31
+
32
+ const resp = await myMSALObject.loginPopup({
33
+ scopes: ["user.read"]
34
+ });
35
+
36
+ emits("login", resp);
37
+
38
+ } catch (e: any) {
39
+ $q.dialog({
40
+ title: "Error",
41
+ message: e.message,
42
+ color: "negative",
43
+ });
44
+ }
45
+
46
+
47
+ }
48
+
49
+ </script>
50
+
51
+ <template>
52
+ <a @click="onClick" class="cursor-pointer">
53
+ <svg xmlns="http://www.w3.org/2000/svg" width="215" height="41" viewBox="0 0 215 41">
54
+ <title>MS-SymbolLockup</title>
55
+ <rect width="215" height="41" fill="#fff" />
56
+ <path d="M214,1V40H1V1H214m1-1H0V41H215V0Z" fill="#8c8c8c" />
57
+ <path
58
+ d="M45.812,25.082V23.288a2.849,2.849,0,0,0,.576.4,4.5,4.5,0,0,0,.707.3,5.513,5.513,0,0,0,.747.187,3.965,3.965,0,0,0,.688.065,2.937,2.937,0,0,0,1.637-.365,1.2,1.2,0,0,0,.538-1.062,1.16,1.16,0,0,0-.179-.649,1.928,1.928,0,0,0-.5-.5,5.355,5.355,0,0,0-.757-.435q-.437-.209-.935-.436c-.356-.19-.687-.383-1-.578a4.358,4.358,0,0,1-.8-.648,2.728,2.728,0,0,1-.534-.8,2.6,2.6,0,0,1-.194-1.047,2.416,2.416,0,0,1,.333-1.285,2.811,2.811,0,0,1,.879-.9,4.026,4.026,0,0,1,1.242-.528,5.922,5.922,0,0,1,1.42-.172,5.715,5.715,0,0,1,2.4.374v1.721a3.832,3.832,0,0,0-2.3-.645,4.106,4.106,0,0,0-.773.074,2.348,2.348,0,0,0-.689.241,1.5,1.5,0,0,0-.494.433,1.054,1.054,0,0,0-.19.637,1.211,1.211,0,0,0,.146.608,1.551,1.551,0,0,0,.429.468,4.276,4.276,0,0,0,.688.414c.271.134.584.28.942.436q.547.285,1.036.6a4.881,4.881,0,0,1,.856.7,3.015,3.015,0,0,1,.586.846,2.464,2.464,0,0,1,.217,1.058,2.635,2.635,0,0,1-.322,1.348,2.608,2.608,0,0,1-.868.892,3.82,3.82,0,0,1-1.257.5,6.988,6.988,0,0,1-1.5.155c-.176,0-.392-.014-.649-.04s-.518-.067-.787-.117a7.772,7.772,0,0,1-.761-.187A2.4,2.4,0,0,1,45.812,25.082Z"
59
+ fill="#5e5e5e" />
60
+ <path
61
+ d="M55.129,16.426a1.02,1.02,0,0,1-.714-.272.89.89,0,0,1-.3-.688.916.916,0,0,1,.3-.7,1.008,1.008,0,0,1,.714-.278,1.039,1.039,0,0,1,.732.278.909.909,0,0,1,.3.7.9.9,0,0,1-.3.678A1.034,1.034,0,0,1,55.129,16.426Zm.842,9.074h-1.7V18h1.7Z"
62
+ fill="#5e5e5e" />
63
+ <path
64
+ d="M65.017,24.9q0,4.131-4.153,4.131a6.187,6.187,0,0,1-2.556-.491V26.986a4.726,4.726,0,0,0,2.337.7,2.342,2.342,0,0,0,2.672-2.628V24.24h-.029a2.947,2.947,0,0,1-4.742.436,4.041,4.041,0,0,1-.838-2.684,4.738,4.738,0,0,1,.9-3.04,3,3,0,0,1,2.476-1.128,2.384,2.384,0,0,1,2.2,1.216h.029V18h1.7Zm-1.684-2.835v-.973a1.91,1.91,0,0,0-.524-1.352A1.718,1.718,0,0,0,61.5,19.18a1.793,1.793,0,0,0-1.512.714,3.217,3.217,0,0,0-.546,2,2.774,2.774,0,0,0,.524,1.769,1.678,1.678,0,0,0,1.387.662,1.805,1.805,0,0,0,1.429-.632A2.391,2.391,0,0,0,63.333,22.064Z"
65
+ fill="#5e5e5e" />
66
+ <path
67
+ d="M73.908,25.5h-1.7V21.273q0-2.1-1.486-2.1a1.622,1.622,0,0,0-1.282.582,2.162,2.162,0,0,0-.5,1.469V25.5H67.229V18h1.707v1.245h.029A2.673,2.673,0,0,1,71.4,17.824a2.265,2.265,0,0,1,1.868.795,3.57,3.57,0,0,1,.644,2.3Z"
68
+ fill="#5e5e5e" />
69
+ <path
70
+ d="M80.962,16.426a1.02,1.02,0,0,1-.714-.272.89.89,0,0,1-.3-.688.916.916,0,0,1,.3-.7,1.008,1.008,0,0,1,.714-.278,1.039,1.039,0,0,1,.732.278.909.909,0,0,1,.3.7.9.9,0,0,1-.3.678A1.034,1.034,0,0,1,80.962,16.426ZM81.8,25.5H80.1V18h1.7Z"
71
+ fill="#5e5e5e" />
72
+ <path
73
+ d="M90.7,25.5H89V21.273q0-2.1-1.486-2.1a1.622,1.622,0,0,0-1.282.582,2.157,2.157,0,0,0-.506,1.469V25.5H84.023V18H85.73v1.245h.03a2.673,2.673,0,0,1,2.431-1.421,2.265,2.265,0,0,1,1.868.795,3.57,3.57,0,0,1,.644,2.3Z"
74
+ fill="#5e5e5e" />
75
+ <path
76
+ d="M106.984,18l-2.212,7.5h-1.78l-1.361-5.083a3.215,3.215,0,0,1-.1-.659H101.5a3.069,3.069,0,0,1-.131.644l-1.48,5.1H98.145L95.939,18H97.7l1.363,5.405a3.16,3.16,0,0,1,.087.645H99.2a3.384,3.384,0,0,1,.117-.659L100.832,18h1.6l1.347,5.428a3.732,3.732,0,0,1,.095.644h.052a3.387,3.387,0,0,1,.11-.644L105.365,18Z"
77
+ fill="#5e5e5e" />
78
+ <path
79
+ d="M109.1,16.426a1.018,1.018,0,0,1-.714-.272.886.886,0,0,1-.3-.688.912.912,0,0,1,.3-.7,1.006,1.006,0,0,1,.714-.278,1.039,1.039,0,0,1,.732.278.912.912,0,0,1,.3.7.9.9,0,0,1-.3.678A1.034,1.034,0,0,1,109.1,16.426Zm.841,9.074h-1.7V18h1.7Z"
80
+ fill="#5e5e5e" />
81
+ <path
82
+ d="M116.117,25.42a2.955,2.955,0,0,1-1.31.248q-2.182,0-2.183-2.094V19.333h-1.253V18h1.253V16.264l1.7-.483V18h1.794v1.333h-1.794v3.75a1.484,1.484,0,0,0,.241.952,1.006,1.006,0,0,0,.807.285,1.167,1.167,0,0,0,.746-.248Z"
83
+ fill="#5e5e5e" />
84
+ <path
85
+ d="M124.248,25.5h-1.7V21.4q0-2.226-1.487-2.226a1.556,1.556,0,0,0-1.26.644,2.568,2.568,0,0,0-.513,1.649V25.5h-1.707V14.4h1.707v4.849h.029a2.685,2.685,0,0,1,2.432-1.421q2.5,0,2.5,3.055Z"
86
+ fill="#5e5e5e" />
87
+ <path
88
+ d="M141.907,25.5h-1.728V18.7q0-.835.1-2.043h-.029a6.992,6.992,0,0,1-.285.988L136.831,25.5h-1.2l-3.143-7.793a7.236,7.236,0,0,1-.277-1.047h-.029q.057.63.058,2.058V25.5h-1.611V15h2.453l2.762,7a10.884,10.884,0,0,1,.41,1.2h.036c.181-.551.327-.962.44-1.23L139.541,15h2.366Z"
89
+ fill="#5e5e5e" />
90
+ <path
91
+ d="M145.158,16.426a1.022,1.022,0,0,1-.714-.272.89.89,0,0,1-.3-.688.916.916,0,0,1,.3-.7,1.009,1.009,0,0,1,.714-.278,1.043,1.043,0,0,1,.733.278.911.911,0,0,1,.3.7.9.9,0,0,1-.3.678A1.038,1.038,0,0,1,145.158,16.426ZM146,25.5h-1.7V18H146Z"
92
+ fill="#5e5e5e" />
93
+ <path
94
+ d="M153.589,25.156a4.2,4.2,0,0,1-2.131.52,3.606,3.606,0,0,1-2.695-1.044,3.691,3.691,0,0,1-1.026-2.706,4.07,4.07,0,0,1,1.1-2.978,3.944,3.944,0,0,1,2.948-1.124,4.3,4.3,0,0,1,1.81.36v1.582a2.743,2.743,0,0,0-1.67-.586,2.32,2.32,0,0,0-1.766.728,2.665,2.665,0,0,0-.688,1.908,2.536,2.536,0,0,0,.648,1.838,2.3,2.3,0,0,0,1.739.674,2.716,2.716,0,0,0,1.729-.652Z"
95
+ fill="#5e5e5e" />
96
+ <path
97
+ d="M159.625,19.619a1.4,1.4,0,0,0-.887-.242,1.513,1.513,0,0,0-1.259.682,3.04,3.04,0,0,0-.506,1.852V25.5h-1.7V18h1.7v1.545H157a2.606,2.606,0,0,1,.766-1.233,1.724,1.724,0,0,1,1.154-.444,1.432,1.432,0,0,1,.7.14Z"
98
+ fill="#5e5e5e" />
99
+ <path
100
+ d="M164.02,25.676a3.719,3.719,0,0,1-2.773-1.051,3.8,3.8,0,0,1-1.036-2.787,3.7,3.7,0,0,1,3.991-4.014,3.6,3.6,0,0,1,2.739,1.033,3.986,3.986,0,0,1,.982,2.864,3.932,3.932,0,0,1-1.059,2.875A3.8,3.8,0,0,1,164.02,25.676Zm.08-6.5a1.938,1.938,0,0,0-1.575.7,2.913,2.913,0,0,0-.579,1.919,2.744,2.744,0,0,0,.586,1.856,1.965,1.965,0,0,0,1.568.678,1.87,1.87,0,0,0,1.542-.666,2.956,2.956,0,0,0,.538-1.9,3,3,0,0,0-.538-1.911A1.858,1.858,0,0,0,164.1,19.18Z"
101
+ fill="#5e5e5e" />
102
+ <path
103
+ d="M169.182,25.266V23.691a3.392,3.392,0,0,0,2.1.725q1.539,0,1.538-.908a.714.714,0,0,0-.132-.436,1.241,1.241,0,0,0-.355-.318,2.784,2.784,0,0,0-.527-.25q-.3-.108-.677-.248a7.052,7.052,0,0,1-.832-.389,2.545,2.545,0,0,1-.615-.465,1.745,1.745,0,0,1-.37-.59,2.145,2.145,0,0,1-.125-.769,1.775,1.775,0,0,1,.256-.955,2.223,2.223,0,0,1,.69-.7,3.289,3.289,0,0,1,.98-.425,4.511,4.511,0,0,1,1.136-.143,5.181,5.181,0,0,1,1.86.315v1.487a3.136,3.136,0,0,0-1.816-.542,2.317,2.317,0,0,0-.582.066,1.472,1.472,0,0,0-.443.183.886.886,0,0,0-.286.282.669.669,0,0,0-.1.363.77.77,0,0,0,.1.41.93.93,0,0,0,.3.3,2.654,2.654,0,0,0,.483.234q.282.105.649.23a9.4,9.4,0,0,1,.867.4,2.886,2.886,0,0,1,.656.465,1.806,1.806,0,0,1,.417.6,2.034,2.034,0,0,1,.147.81,1.847,1.847,0,0,1-.264,1,2.205,2.205,0,0,1-.7.7,3.292,3.292,0,0,1-1.015.413,5.222,5.222,0,0,1-1.212.136A5.115,5.115,0,0,1,169.182,25.266Z"
104
+ fill="#5e5e5e" />
105
+ <path
106
+ d="M179.443,25.676a3.717,3.717,0,0,1-2.772-1.051,3.793,3.793,0,0,1-1.036-2.787,3.7,3.7,0,0,1,3.991-4.014,3.6,3.6,0,0,1,2.739,1.033,3.986,3.986,0,0,1,.982,2.864,3.932,3.932,0,0,1-1.059,2.875A3.8,3.8,0,0,1,179.443,25.676Zm.08-6.5a1.936,1.936,0,0,0-1.574.7,2.908,2.908,0,0,0-.579,1.919,2.739,2.739,0,0,0,.586,1.856,1.964,1.964,0,0,0,1.567.678,1.868,1.868,0,0,0,1.542-.666,2.95,2.95,0,0,0,.539-1.9,2.99,2.99,0,0,0-.539-1.911A1.857,1.857,0,0,0,179.523,19.18Z"
107
+ fill="#5e5e5e" />
108
+ <path
109
+ d="M189.067,15.781a1.533,1.533,0,0,0-.784-.2q-1.237,0-1.237,1.4V18h1.743v1.333h-1.736V25.5h-1.7V19.333h-1.282V18h1.282V16.784a2.362,2.362,0,0,1,.777-1.871,2.82,2.82,0,0,1,1.94-.684,2.879,2.879,0,0,1,1,.138Z"
110
+ fill="#5e5e5e" />
111
+ <path
112
+ d="M194.23,25.42a2.955,2.955,0,0,1-1.31.248q-2.182,0-2.183-2.094V19.333h-1.253V18h1.253V16.264l1.7-.483V18h1.793v1.333h-1.793v3.75a1.484,1.484,0,0,0,.241.952,1,1,0,0,0,.806.285,1.165,1.165,0,0,0,.746-.248Z"
113
+ fill="#5e5e5e" />
114
+ <rect x="13" y="11" width="9" height="9" fill="#f25022" />
115
+ <rect x="13" y="21" width="9" height="9" fill="#00a4ef" />
116
+ <rect x="23" y="11" width="9" height="9" fill="#7fba00" />
117
+ <rect x="23" y="21" width="9" height="9" fill="#ffb900" />
118
+ </svg>
119
+ </a>
120
+ </template>
@@ -50,7 +50,6 @@ const onDelete = async () => {
50
50
 
51
51
  }
52
52
 
53
- console.log(light)
54
53
  useHead({
55
54
  title: light.company + " - " + title,
56
55
  })
@@ -1,6 +1,8 @@
1
1
  <script setup>
2
2
  import { ref } from 'vue'
3
- import { q, m } from '#imports'
3
+ import { q, m, useLight } from '#imports';
4
+
5
+ const light = useLight()
4
6
 
5
7
  const { app } = await q({ app: { config: ["name", "value"] } })
6
8
  const obj = app.config.reduce((acc, cur) => {
@@ -11,6 +13,24 @@ const obj = app.config.reduce((acc, cur) => {
11
13
  obj.revision = obj.revision ? obj.revision.split(',') : []
12
14
 
13
15
  const tab = ref('general')
16
+
17
+ const onSubmit = async (d) => {
18
+ let data = [];
19
+ Object.keys(d).forEach((key) => {
20
+ data.push({
21
+ name: key,
22
+ value: d[key]
23
+ })
24
+ })
25
+ await m("updateAppConfigs", { data })
26
+
27
+ light.notify({ message: "Settings saved", color: "positive" })
28
+
29
+ //update the modelValue
30
+ Object.keys(d).forEach((key) => {
31
+ obj[key] = d[key]
32
+ })
33
+ }
14
34
  </script>
15
35
  <template>
16
36
  <l-page>
@@ -24,6 +44,7 @@ const tab = ref('general')
24
44
  <q-tab name="Modules" icon="sym_o_apps" :label="$t('Modules')" />
25
45
  <q-tab name="mail" icon="sym_o_email" :label="$t('Mail')" />
26
46
  <q-tab name="forget-password" icon="sym_o_lock" :label="$t('Forget password')" />
47
+ <q-tab name="authentication" icon="sym_o_passkey" :label="$t('Authentication')" />
27
48
  <q-tab name="developer" icon="sym_o_code" :label="$t('Developer')" />
28
49
  </q-tabs>
29
50
  </template>
@@ -34,6 +55,7 @@ const tab = ref('general')
34
55
  <l-system-setting-modules v-if="tab == 'Modules'" v-model="obj" />
35
56
  <l-system-setting-developer v-if="tab == 'developer'" v-model="obj" />
36
57
  <l-system-setting-forget-password v-if="tab == 'forget-password'" v-model="obj" />
58
+ <l-system-setting-authentication v-if="tab == 'authentication'" v-bind="obj" @submit="onSubmit" />
37
59
  </template>
38
60
  </q-splitter>
39
61
  </l-card>
@@ -1,13 +1,24 @@
1
1
  <script setup>
2
2
  import { reactive, onMounted, nextTick } from "vue"
3
3
  import { useQuasar } from "quasar";
4
- import { q, m, api, useLight } from '#imports'
4
+ import { q, m, useLight } from '#imports'
5
5
  import { useI18n } from 'vue-i18n'
6
6
 
7
7
  const light = useLight()
8
8
  const { t } = useI18n()
9
9
  const quasar = useQuasar();
10
- let { app, my } = await q({ app: ['googleClientId'], my: ["gmail"] })
10
+ let { app, my } = await q({
11
+ app: {
12
+ googleClientId: true,
13
+ microsoftClientId: true,
14
+ microsoftTenantId: true,
15
+ facebookAppId: true,
16
+ }, my: {
17
+ google: true,
18
+ microsoft: true,
19
+ facebook: true,
20
+ }
21
+ })
11
22
 
12
23
  const $q = useQuasar();
13
24
  my = reactive(my);
@@ -17,7 +28,7 @@ const handleGoogleCredentialResponse = async (response) => {
17
28
  await m("googleRegister", { credential: response.credential })
18
29
 
19
30
  const resp = await q("my", ["gmail"]);
20
- my.gmail = resp.gmail;
31
+ my.google = resp.google;
21
32
 
22
33
  light.notify({
23
34
  message: "Google account linked",
@@ -84,33 +95,147 @@ const onUnlink = async () => {
84
95
  my.gmail = null;
85
96
  window.location.reload();
86
97
  })
98
+ }
99
+
100
+ const ms = reactive({});
101
+
102
+ const onUnlinkMicrosoft = async () => {
103
+ //confirm
104
+ light.dialog({
105
+ title: "Unlink",
106
+ color: "negative",
107
+ message: "Are you sure you want to unlink your Microsoft account?",
108
+ ok: "Yes",
109
+ cancel: "No",
110
+ }).onOk(async () => {
111
+ await m("unlinkMicrosoft");
112
+ my.microsoft = null;
113
+ window.location.reload();
114
+ })
115
+ }
116
+
117
+ const onLinkMicrosoft = async (resp) => {
118
+ try {
119
+ await m("microsoftRegister", { account_id: resp.account.localAccountId });
120
+ light.notify({
121
+ message: "Microsoft account linked",
122
+ color: "positive",
123
+ });
87
124
 
125
+ window.location.reload();
126
+ } catch (e) {
127
+ light.notify({
128
+ message: e.message,
129
+ color: "negative",
130
+ });
131
+ }
132
+ }
133
+
134
+ const onLinkFacebook = (accessToken) => {
135
+ try {
136
+ m("facebookRegister", { access_token: accessToken })
137
+ light.notify({
138
+ message: "Facebook account linked",
139
+ color: "positive",
140
+ });
141
+ } catch (e) {
142
+ light.notify({
143
+ message: e.message,
144
+ color: "negative",
145
+ });
146
+ }
147
+
148
+ }
149
+
150
+ const onUnlinkFacebook = async () => {
151
+ //confirm
152
+ light.dialog({
153
+ title: "Unlink",
154
+ color: "negative",
155
+ message: "Are you sure you want to unlink your Facebook account?",
156
+ ok: "Yes",
157
+ cancel: "No",
158
+ }).onOk(async () => {
159
+ await m("unlinkFacebook");
160
+ my.facebook = null;
161
+ window.location.reload();
162
+ })
88
163
  }
89
164
 
90
165
  </script>
91
166
  <template>
92
- <q-card>
93
- <template v-if="app.googleClientId">
94
- <template v-if="my.gmail">
95
- <q-card-section>
167
+ <q-card :bordered="false">
168
+ <q-card-section>
169
+ <h2 class="text-h6">Google</h2>
170
+ <template v-if="app.googleClientId">
171
+ <template v-if="my.google">
172
+
96
173
  {{ $t('You have already linked your Google account.') }}<br />
97
- Your gmail id is {{ my.gmail }}
98
- </q-card-section>
174
+ Your gmail id is {{ my.google }}<br />
175
+ <l-btn label="Unlink" @click="onUnlink" icon="sym_o_link_off"></l-btn>
99
176
 
100
- <q-card-actions>
101
- <l-btn label="Unlink" @click="onUnlink" icon="sym_o_delete"></l-btn>
102
- </q-card-actions>
177
+ </template>
103
178
 
179
+ <template v-else>
180
+ {{ $t('Click the button below to link your Google account.') }}
181
+ <div id="g_id_signin"></div>
182
+ </template>
104
183
  </template>
105
184
 
106
- <q-card-section v-else>
107
- {{ $t('Click the button below to link your Google account.') }}
108
- <div id="g_id_signin"></div>
109
- </q-card-section>
110
- </template>
185
+ <template v-else>
186
+ Google login is not available. Please set authentication google client id in server settings.
187
+ </template>
188
+ </q-card-section>
189
+
111
190
 
112
- <q-card-section v-else>
113
- Google login is not available. Please set GOOGLE_CLIENT_ID in server settings.
191
+ <q-separator />
192
+
193
+ <q-card-section>
194
+ <h2 class="text-h6">Microsoft</h2>
195
+ <template v-if="app.microsoftClientId">
196
+ <template v-if="my.microsoft">
197
+ {{ $t('You have already linked your Microsoft account.') }}<br />
198
+ Your account id is {{ my.microsoft }}
199
+ <br />
200
+ <l-btn label="Unlink" @click="onUnlinkMicrosoft" icon="sym_o_link_off"></l-btn>
201
+ </template>
202
+ <template v-else>
203
+ <div>{{ $t('Click the button below to link your Microsoft account.') }}</div>
204
+ <div>
205
+ <l-microsoft-button :client-id="app.microsoftClientId" :tenant-id="app.microsoftTenantId"
206
+ @login="onLinkMicrosoft" />
207
+ </div>
208
+ </template>
209
+ </template>
210
+ <template v-else>
211
+ Micorsoft login is not available. Please set authentication microsft client id in server settings.
212
+ </template>
114
213
  </q-card-section>
214
+
215
+
216
+ <q-separator />
217
+ <q-card-section>
218
+ <h2 class="text-h6">Facebook</h2>
219
+ <template v-if="app.facebookAppId">
220
+ <template v-if="my.facebook">
221
+ {{ $t('You have already linked your Facebook account.') }}<br />
222
+ Your account id is {{ my.facebook }}
223
+ <l-btn label="Unlink" @click="onUnlinkFacebook" icon="sym_o_link_off"></l-btn>
224
+ </template>
225
+ <template v-else>
226
+ <div>{{ $t('Click the button below to link your Facebook account.') }}</div>
227
+ <l-facebook-button @login="onLinkFacebook" />
228
+ </template>
229
+ </template>
230
+ <template v-else>
231
+ Facebook login is not available. Please set authentication facebook app id in server settings.
232
+ </template>
233
+ </q-card-section>
234
+
235
+
236
+
237
+
238
+
239
+
115
240
  </q-card>
116
241
  </template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostlink/nuxt-light",
3
- "version": "1.18.3",
3
+ "version": "1.19.1",
4
4
  "description": "HostLink Nuxt Light Framework",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,8 +32,9 @@
32
32
  "test:watch": "vitest watch"
33
33
  },
34
34
  "dependencies": {
35
+ "@azure/msal-browser": "^3.26.1",
35
36
  "@formkit/drag-and-drop": "^0.1.6",
36
- "@hostlink/light": "^2.0.2",
37
+ "@hostlink/light": "^2.1.0",
37
38
  "@nuxt/kit": "^3.7.4",
38
39
  "@nuxt/module-builder": "^0.8.3",
39
40
  "@quasar/extras": "^1.16.11",
@@ -47,6 +48,7 @@
47
48
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"
48
49
  },
49
50
  "devDependencies": {
51
+ "@azure/identity": "^4.5.0",
50
52
  "@nuxt/devtools": "latest",
51
53
  "@nuxt/eslint-config": "^0.2.0",
52
54
  "@nuxt/schema": "^3.7.4",