@hostlink/nuxt-light 1.18.2 → 1.19.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.
Files changed (32) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/L/ForgetPasswordDialog.vue +12 -14
  3. package/dist/runtime/components/L/System/Setting/authentication.vue +54 -0
  4. package/dist/runtime/components/L/System/Setting/modules.vue +1 -1
  5. package/dist/runtime/components/l-app-main.vue +10 -13
  6. package/dist/runtime/components/l-app.vue +31 -8
  7. package/dist/runtime/components/l-edit-btn.vue +1 -1
  8. package/dist/runtime/components/l-facebook-button.vue +28 -0
  9. package/dist/runtime/components/l-form-dialog.vue +1 -2
  10. package/dist/runtime/components/l-input.vue +14 -9
  11. package/dist/runtime/components/l-login.vue +66 -14
  12. package/dist/runtime/components/l-microsoft-button.vue +119 -0
  13. package/dist/runtime/components/l-page.vue +0 -1
  14. package/dist/runtime/components/l-tab.vue +4 -1
  15. package/dist/runtime/components/l-tabs.vue +2 -4
  16. package/dist/runtime/index.d.ts +1 -92
  17. package/dist/runtime/index.js +1 -197
  18. package/dist/runtime/lib/index.d.ts +1 -1
  19. package/dist/runtime/light.d.ts +1224 -0
  20. package/dist/runtime/light.js +242 -0
  21. package/dist/runtime/locales/zh-hk.json +29 -1
  22. package/dist/runtime/pages/System/database/table.vue +8 -10
  23. package/dist/runtime/pages/System/setting.vue +24 -2
  24. package/dist/runtime/pages/System/view_as.vue +3 -3
  25. package/dist/runtime/pages/User/_user_id/view.vue +6 -20
  26. package/dist/runtime/pages/User/setting/bio-auth.vue +34 -15
  27. package/dist/runtime/pages/User/setting/my_favorite.vue +2 -5
  28. package/dist/runtime/pages/User/setting/open_id.vue +169 -31
  29. package/dist/runtime/pages/User/setting/two-factor-auth.vue +2 -3
  30. package/dist/runtime/plugin.js +4 -1
  31. package/package.json +4 -2
  32. package/dist/runtime/components/L/ForgetPasswordResetDialog.vue +0 -34
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "1.18.2",
4
+ "version": "1.19.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
@@ -1,10 +1,11 @@
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
+ import { useI18n } from 'vue-i18n';
6
+ const { t } = useI18n();
5
7
  const $q = useQuasar()
6
8
 
7
-
8
9
  const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
9
10
 
10
11
  const username = ref('')
@@ -13,7 +14,7 @@ const onOKClick = async () => {
13
14
  if (username.value == '' || email.value == '') {
14
15
  $q.notify({
15
16
  type: 'negative',
16
- message: 'Username and Email is required'
17
+ message: t('Username and Email is required')
17
18
  })
18
19
  return;
19
20
  }
@@ -34,34 +35,31 @@ const onOKClick = async () => {
34
35
  } finally {
35
36
  $q.loading.hide();
36
37
  }
37
-
38
38
  }
39
-
40
39
  </script>
41
-
42
40
  <template>
43
41
  <q-dialog ref="dialogRef">
44
42
  <q-card class="q-dialog-plugin">
45
43
 
46
44
  <q-card-section class="q-dialog__title">
47
- Forget Password
45
+ {{ t('Forget password') }}
48
46
  </q-card-section>
49
47
 
50
48
  <q-card-section class="q-dialog__message">
51
- Please enter your username and email address, we will send you a code to reset your password
49
+ {{ t('Please enter your username and email address, we will send you a code to reset your password') }}
52
50
  </q-card-section>
53
51
 
54
52
  <q-card-section>
55
- <q-input v-model="username" label="Username" stack-label
56
- :rules="[val => !!val || 'Username is required']" hide-bottom-space />
57
- <q-input v-model="email" label="Email" stack-label type="email" hide-bottom-space
58
- :rules="[val => !!val || 'Email is required']" />
53
+ <q-input v-model="username" :label="$t('Username')" stack-label
54
+ :rules="[val => !!val || $t('Username is required')]" hide-bottom-space />
55
+ <q-input v-model="email" :label="$t('Email')" stack-label type="email" hide-bottom-space
56
+ :rules="[val => !!val || $t('Email is required')]" />
59
57
  </q-card-section>
60
58
 
61
59
 
62
60
  <q-card-actions align="right">
63
- <q-btn label="Cancel" @click="onDialogCancel" flat color="primary" />
64
- <q-btn label="OK" @click="onOKClick" flat color="primary" />
61
+ <q-btn :label="$t('Cancel')" @click="onDialogCancel" flat color="primary" />
62
+ <q-btn :label="$t('OK')" @click="onOKClick" flat color="primary" />
65
63
  </q-card-actions>
66
64
  </q-card>
67
65
  </q-dialog>
@@ -0,0 +1,54 @@
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
+ }
12
+
13
+
14
+ withDefaults(defineProps<LSystemSettingAuthenticationProps>(), {
15
+ authentication_password_based: "1",
16
+ authentication_google_client_id: "",
17
+ authentication_facebook_app_id: "",
18
+ authentication_microsoft_client_id: ""
19
+ })
20
+
21
+ const emits = defineEmits(["submit"])
22
+
23
+ const onSubmit = async (d: LSystemSettingAuthenticationProps) => {
24
+
25
+ //at least one authentication method should be enabled
26
+ if (d.authentication_password_based === "0" && !d.authentication_google_client_id && !d.authentication_facebook_app_id && !d.authentication_microsoft_client_id) {
27
+
28
+ light.notify({ message: "At least one authentication method should be enabled", color: "negative" })
29
+ return
30
+
31
+ }
32
+
33
+
34
+
35
+ emits("submit", d)
36
+ }
37
+
38
+ </script>
39
+ <template>
40
+
41
+
42
+ <form-kit type="l-form" :bordered="false" @submit="onSubmit" :value="$props">
43
+
44
+ <form-kit label="Password based" type="l-checkbox" name="authentication_password_based" true-value="1"
45
+ false-value="0" />
46
+ <q-separator />
47
+
48
+ <form-kit label="Google Client ID" type="l-input" name="authentication_google_client_id" />
49
+
50
+ <form-kit label="Facebook App ID" type="l-input" name="authentication_facebook_app_id" />
51
+
52
+ <form-kit label="Microsoft Client ID" type="l-input" name="authentication_microsoft_client_id" />
53
+ </form-kit>
54
+ </template>
@@ -39,7 +39,7 @@ const onSubmit = async (d, form) => {
39
39
  file_manager: modelValue.file_manager,
40
40
  revision: modelValue.revision
41
41
  }" @submit="onSubmit">
42
- <q-field label="File manager" stack-label>
42
+ <q-field :label="$t('File Manager')" stack-label>
43
43
  <FormKit type="l-checkbox" label="Show" name="file_manager" true-value="1" false-value="0" />
44
44
  </q-field>
45
45
 
@@ -7,6 +7,8 @@ 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
+
10
12
  const emits = defineEmits(["logout"]);
11
13
  const $q = useQuasar();
12
14
  $q.loading.show()
@@ -59,9 +61,8 @@ const tt = await q({
59
61
  let app = tt.app
60
62
  let my = reactive(tt.my)
61
63
 
62
- const light = useLight({
63
- color: my.styles?.color || 'primary'
64
- });
64
+ const light = useLight();
65
+
65
66
  light.init(my.styles);
66
67
 
67
68
  //set permission
@@ -158,9 +159,6 @@ const onChangeLocale = async (locale) => {
158
159
  window.location.reload();
159
160
  }
160
161
 
161
- const errors = computed(() => {
162
- return light.getErrors();
163
- })
164
162
 
165
163
  const reloadMenu = async () => {
166
164
  //menus.value = app.menus;
@@ -231,23 +229,22 @@ const onToggleFav = async () => {
231
229
  return;
232
230
  }
233
231
 
234
- $q.dialog({
232
+ light.dialog({
235
233
  title: 'Add to favorite',
236
234
  message: 'Enter favorite label',
237
235
  prompt: {
238
236
  //get window title, remove - and replace with space
239
237
  model: route.name.replace(/-/g, ' '),
238
+ isValid: (val) => val !== '',
240
239
  },
241
240
  cancel: true,
242
241
  persistent: true,
243
242
  }).onOk(async (data) => {
244
- if (data === '') return;
245
243
  if (await m("addMyFavorite", {
246
244
  path: route.fullPath,
247
245
  label: data,
248
246
  })) {
249
247
  light.reloadMyFavorites();
250
-
251
248
  }
252
249
  })
253
250
  }
@@ -424,8 +421,8 @@ const onLogout = async () => {
424
421
 
425
422
  <q-page-container :class="containerClass" :style="containerStyle"> <!-- Error message -->
426
423
  <slot name="header"></slot>
427
- <div class="q-gutter-sm q-pa-sm" v-if="errors.length > 0">
428
- <q-banner dense inline-actions class="bg-grey-4" v-for=" error in errors " rounded>
424
+ <div class="q-gutter-sm q-pa-sm" v-if="$light.errors.length > 0">
425
+ <q-banner dense inline-actions class="bg-grey-4" v-for=" error in $light.errors " rounded>
429
426
  {{ error }}
430
427
  <template v-slot:action>
431
428
  <q-btn flat label="Detail" @click="$q.dialog({
@@ -433,7 +430,7 @@ const onLogout = async () => {
433
430
  message: error.stack,
434
431
  fullWidth: true,
435
432
  })"></q-btn>
436
- <q-btn flat icon="sym_o_close" round dense @click="light.removeError(error)" />
433
+ <q-btn flat icon="sym_o_close" round dense @click="$light.removeError(error)" />
437
434
  </template>
438
435
  </q-banner>
439
436
 
@@ -461,7 +458,7 @@ const onLogout = async () => {
461
458
  <q-item-section>
462
459
  {{ light.company }} {{ appVersion }} - Copyright {{ app.copyrightYear }} {{ app.copyrightName
463
460
  }}. Build
464
- {{ light.getVersion() }}
461
+ {{ light.version }}
465
462
  </q-item-section>
466
463
  </q-item>
467
464
  </q-footer>
@@ -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";
@@ -25,31 +24,55 @@ try {
25
24
  logged: true,
26
25
  twoFactorAuthentication: true,
27
26
  googleClientId: true,
28
- forgetPasswordEnabled: true
27
+ forgetPasswordEnabled: true,
28
+ microsoftClientId: true,
29
+ passwordBasedEnabled: true,
30
+ facebookAppId: true,
29
31
  }
30
32
  })).app;
31
33
  light.company = app.value.company;
32
34
  light.companyLogo = app.value.companyLogo;
33
35
  } catch (e) {
34
-
35
36
  quasar.dialog({
36
37
  title: 'Error',
37
- message: 'Error loading api data. Please reload the page.',
38
+ message: e.message,
38
39
  ok: 'Reload',
39
40
  }).onOk(() => {
40
41
  window.location.reload();
41
42
  });
42
43
  }
43
44
 
45
+ if (app.value.facebookAppId) {
46
+
47
+ //load facebook sdk
48
+
49
+ (function (d, s, id) {
50
+ var js, fjs = d.getElementsByTagName(s)[0];
51
+ if (d.getElementById(id)) { return; }
52
+ js = d.createElement(s); js.id = id;
53
+ js.src = "https://connect.facebook.net/en_US/sdk.js";
54
+ fjs.parentNode.insertBefore(js, fjs);
55
+ }(document, 'script', 'facebook-jssdk')
56
+ );
57
+
58
+ window.fbAsyncInit = function () {
59
+ FB.init({
60
+ appId: app.value.facebookAppId,
61
+ xfbml: true,
62
+ version: 'v21.0'
63
+ });
64
+ }
65
+ }
66
+
67
+
44
68
 
45
- const color = ref(light.color);
46
- provide('color', color)
47
69
 
48
70
  </script>
49
71
  <template>
50
72
  <q-layout v-if="!app.logged">
51
73
  <q-page-container class="bg-grey-2" style="color:#1f1f1f">
52
74
  <q-page padding>
75
+ <div id="fb-root"></div>
53
76
  <l-login v-bind="app" @login="app.logged = true"></l-login>
54
77
  </q-page>
55
78
  </q-page-container>
@@ -1,3 +1,3 @@
1
1
  <template>
2
- <q-btn flat round icon="sym_o_edit" to="edit" />
2
+ <q-btn flat round dense icon="sym_o_edit" to="edit" />
3
3
  </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>
@@ -82,8 +82,7 @@ const onCancel = async () => {
82
82
  <template>
83
83
  <q-dialog ref="dialogRef">
84
84
 
85
- <q-card :style="{ width }" :loading="loading">
86
-
85
+ <q-card :style="{ width }" :loading="loading" class="q-dialog-plugin">
87
86
  <q-toolbar>
88
87
  <q-toolbar-title v-if="title">
89
88
  {{ title }}
@@ -11,7 +11,7 @@ export interface LInputProps extends QInputProps {
11
11
  required?: boolean;
12
12
  }
13
13
 
14
- const i18n = useI18n();
14
+ const { t } = useI18n();
15
15
  const light = useLight();
16
16
 
17
17
  const props = withDefaults(defineProps<LInputProps>(), {
@@ -37,13 +37,13 @@ const new_rules = props.rules || [];
37
37
 
38
38
  //has required prop (in properties)
39
39
  if (props.required) {
40
- new_rules.push(val => !!val || i18n.t('input_required', [i18n.t(props.label ?? "")]));
40
+ new_rules.push(val => !!val || t('input_required', [t(props.label ?? "")]));
41
41
  }
42
42
 
43
43
  if (props.type == "email") {
44
44
  new_rules.push(val => {
45
45
  if (val && !val.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
46
- return i18n.t("Invalid email format");
46
+ return t("Invalid email format");
47
47
  }
48
48
  });
49
49
  }
@@ -51,7 +51,7 @@ if (props.type == "email") {
51
51
  if (new_rules.indexOf("containUpper") >= 0) {
52
52
  new_rules.push(val => {
53
53
  if (val && !val.match(/[A-Z]/)) {
54
- return i18n.t("Must contain at least one uppercase letter");
54
+ return t("Must contain at least one uppercase letter");
55
55
  }
56
56
  });
57
57
  }
@@ -59,7 +59,7 @@ if (new_rules.indexOf("containUpper") >= 0) {
59
59
  if (new_rules.indexOf("containLower") >= 0) {
60
60
  new_rules.push(val => {
61
61
  if (val && !val.match(/[a-z]/)) {
62
- return i18n.t("Must contain at least one lowercase letter");
62
+ return t("Must contain at least one lowercase letter");
63
63
  }
64
64
  });
65
65
  }
@@ -67,7 +67,7 @@ if (new_rules.indexOf("containLower") >= 0) {
67
67
  if (new_rules.indexOf("containNumber") >= 0) {
68
68
  new_rules.push(val => {
69
69
  if (val && !val.match(/[0-9]/)) {
70
- return i18n.t("Must contain at least one number");
70
+ return t("Must contain at least one number");
71
71
  }
72
72
  });
73
73
  }
@@ -75,7 +75,7 @@ if (new_rules.indexOf("containNumber") >= 0) {
75
75
  if (new_rules.indexOf("containSpecial") >= 0) {
76
76
  new_rules.push(val => {
77
77
  if (val && !val.match(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/)) {
78
- return i18n.t("Must contain at least one symbol");
78
+ return t("Must contain at least one symbol");
79
79
  }
80
80
  });
81
81
  }
@@ -90,7 +90,7 @@ if (minLength) {
90
90
  const min = parseInt(minLength.replace("minLength:", ""));
91
91
  new_rules.push(val => {
92
92
  if (val && val.length < min) {
93
- return i18n.t("input_min", [min]);
93
+ return t("input_min", [min]);
94
94
  }
95
95
  });
96
96
  }
@@ -139,7 +139,7 @@ const attrs = computed(() => {
139
139
  if (props.stackLabel === undefined) a.stackLabel = light.getStyle("inputStackLabel");
140
140
 
141
141
  if (props.label) {
142
- a.label = i18n.t(props.label);
142
+ a.label = t(props.label);
143
143
 
144
144
  if (props.required) {
145
145
  a.label = "* " + a.label;
@@ -147,6 +147,11 @@ const attrs = computed(() => {
147
147
  }
148
148
 
149
149
  if (props.color === undefined) a.color = light.color
150
+
151
+ if (props.hint) {
152
+ a.hint = t(props.hint);
153
+ }
154
+
150
155
  return a;
151
156
  })
152
157
 
@@ -1,16 +1,23 @@
1
1
  <script setup>
2
2
  import { ref, reactive, onMounted } from 'vue'
3
3
  import { useQuasar } from 'quasar';
4
- import { api, useHead } from '#imports';
4
+ import { api, useHead, m, useLight } from '#imports';
5
+ import { useI18n } from 'vue-i18n';
6
+
7
+
8
+ const { t } = useI18n();
5
9
 
6
10
  const emits = defineEmits(["login"]);
7
11
 
8
12
  const props = defineProps({
9
13
  twoFactorAuthentication: Boolean,
10
- googleClientId: String,
14
+ googleClientId: String | undefined,
11
15
  forgetPasswordEnabled: Boolean,
12
16
  company: String,
13
- companyLogo: String
17
+ companyLogo: String,
18
+ microsoftClientId: String | undefined,
19
+ passwordBasedEnabled: Boolean,
20
+ facebookAppId: String | undefined,
14
21
  })
15
22
 
16
23
  useHead({
@@ -26,8 +33,8 @@ const $q = useQuasar()
26
33
 
27
34
  const loginWithCode = (username, password) => {
28
35
  $q.dialog({
29
- title: "Enter your code",
30
- message: "Please enter your two factor authentication code (If you lost your authenticator, please contact your administrator)",
36
+ title: t("Enter your code"),
37
+ message: t("Please enter your two factor authentication code (If you lost your authenticator, please contact your administrator)"),
31
38
  prompt: {
32
39
  type: "text",
33
40
  required: true
@@ -43,10 +50,15 @@ const loginWithCode = (username, password) => {
43
50
 
44
51
  }
45
52
 
53
+ const loading = ref(false);
54
+
46
55
  const submit = async () => {
47
56
  if (await form1.value.validate()) {
48
57
 
58
+
59
+
49
60
  try {
61
+ loading.value = true;
50
62
  await api.auth.login(data.username, data.password, data.code)
51
63
  //window.self.location.reload();
52
64
  //
@@ -65,6 +77,8 @@ const submit = async () => {
65
77
  color: "negative",
66
78
  icon: "sym_o_error",
67
79
  });
80
+ } finally {
81
+ loading.value = false;
68
82
 
69
83
 
70
84
  }
@@ -73,8 +87,8 @@ const submit = async () => {
73
87
 
74
88
  const resetPassword = (username, code) => {
75
89
  $q.dialog({
76
- title: "Reset password",
77
- message: "Please enter your new password",
90
+ title: t("Reset password"),
91
+ message: t("Please enter your new password"),
78
92
  prompt: {
79
93
  model: "",
80
94
  type: "password",
@@ -88,7 +102,7 @@ const resetPassword = (username, code) => {
88
102
  try {
89
103
  await api.auth.resetPassword(username, password, code);
90
104
  $q.notify({
91
- message: "Your password has been reset successfully",
105
+ message: t("Your password has been reset successfully"),
92
106
  color: "positive",
93
107
  icon: "sym_o_check",
94
108
  });
@@ -110,8 +124,8 @@ const forgetPassword = async () => {
110
124
  component: resolveComponent("l-forget-password-dialog"),
111
125
  }).onOk(async (data) => {
112
126
  $q.dialog({
113
- title: "Enter your code",
114
- message: "Please enter the code sent to your email, your code will expire in 10 minutes",
127
+ title: t("Enter your code"),
128
+ message: t("Please enter the code sent to your email, your code will expire in 10 minutes"),
115
129
  prompt: {
116
130
  type: "text",
117
131
  required: true
@@ -123,7 +137,7 @@ const forgetPassword = async () => {
123
137
  resetPassword(data.username, code);
124
138
  } else {
125
139
  $q.notify({
126
- message: "Your code is invalid",
140
+ message: t("Your code is invalid or expired"),
127
141
  color: "negative",
128
142
  icon: "sym_o_error",
129
143
  });
@@ -156,6 +170,7 @@ const handleGoogleCredentialResponse = async (response) => {
156
170
  }
157
171
 
158
172
  onMounted(() => {
173
+
159
174
  if (props.googleClientId) {
160
175
  if (!window.google) {
161
176
  $q.notify({
@@ -188,6 +203,34 @@ onMounted(() => {
188
203
  );
189
204
  }
190
205
  })
206
+
207
+
208
+
209
+ const microsoftLogin = async (resp) => {
210
+ try {
211
+ await api.auth.microsoftLogin(resp.accessToken);
212
+ emits("login");
213
+ } catch (e) {
214
+ $q.notify({
215
+ message: e.message,
216
+ color: "negative",
217
+ });
218
+
219
+ }
220
+
221
+ }
222
+
223
+ const facebookLogin = (accessToken) => {
224
+ m("facebookLogin", { access_token: accessToken }).then(() => {
225
+ emits("login");
226
+ }).catch((e) => {
227
+ $q.notify({
228
+ message: e.message,
229
+ color: "negative",
230
+ });
231
+ });
232
+ }
233
+
191
234
  </script>
192
235
 
193
236
  <template>
@@ -201,7 +244,7 @@ onMounted(() => {
201
244
  <div class="text-h6">
202
245
  {{ company }}
203
246
  </div>
204
- <q-form ref="form1">
247
+ <q-form ref="form1" v-if="passwordBasedEnabled">
205
248
  <div class="q-gutter-sm">
206
249
  <l-input v-model.trim="data.username" label="Username" :rules="[v => !!v || $t('Username is required')]"
207
250
  clearable :outlined="false" stackLabel autocomplete="username">
@@ -226,15 +269,24 @@ onMounted(() => {
226
269
  </q-form>
227
270
  </q-card-section>
228
271
  <q-card-actions>
229
- <l-btn label="Login" outline rounded color="primary" icon="sym_o_login" @click="submit" />
272
+ <l-btn label="Login" outline rounded color="primary" icon="sym_o_login" @click="submit"
273
+ v-if="passwordBasedEnabled" />
230
274
  <l-btn v-if="hasBioLogin" outline rounded color="primary" icon="sym_o_fingerprint" @click="bioLogin" />
231
275
  <l-btn label="Forget password" outline rounded color="primary" icon="sym_o_lock_reset" @click="forgetPassword"
232
276
  v-if="forgetPasswordEnabled" />
233
277
  </q-card-actions>
234
- <q-card-actions v-if="props.googleClientId">
278
+ <q-card-actions v-if="googleClientId">
235
279
  <div>
236
280
  <div id="g_id_signin"></div>
237
281
  </div>
238
282
  </q-card-actions>
283
+
284
+ <q-card-actions v-if="microsoftClientId">
285
+ <l-microsoft-button :client-id="microsoftClientId" @login="microsoftLogin"></l-microsoft-button>
286
+ </q-card-actions>
287
+
288
+ <q-card-actions v-if="facebookAppId">
289
+ <l-facebook-button @login="facebookLogin" />
290
+ </q-card-actions>
239
291
  </q-card>
240
292
  </template>