@hostlink/nuxt-light 1.73.2 → 1.74.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 (50) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/L/App.vue +0 -19
  3. package/dist/runtime/components/L/AppMain.d.vue.ts +3 -3
  4. package/dist/runtime/components/L/AppMain.vue +43 -10
  5. package/dist/runtime/components/L/AppMain.vue.d.ts +3 -3
  6. package/dist/runtime/components/L/FacebookButton.d.vue.ts +19 -3
  7. package/dist/runtime/components/L/FacebookButton.vue +73 -12
  8. package/dist/runtime/components/L/FacebookButton.vue.d.ts +19 -3
  9. package/dist/runtime/components/L/FavMenu.d.vue.ts +2 -0
  10. package/dist/runtime/components/L/FavMenu.vue +10 -2
  11. package/dist/runtime/components/L/FavMenu.vue.d.ts +2 -0
  12. package/dist/runtime/components/L/Form.d.vue.ts +29 -56
  13. package/dist/runtime/components/L/Form.vue +70 -80
  14. package/dist/runtime/components/L/Form.vue.d.ts +29 -56
  15. package/dist/runtime/components/L/FormLayout.d.vue.ts +36 -0
  16. package/dist/runtime/components/L/FormLayout.vue +36 -0
  17. package/dist/runtime/components/L/FormLayout.vue.d.ts +36 -0
  18. package/dist/runtime/components/L/Login.vue +1 -1
  19. package/dist/runtime/components/L/Menu.d.vue.ts +2 -0
  20. package/dist/runtime/components/L/Menu.vue +4 -4
  21. package/dist/runtime/components/L/Menu.vue.d.ts +2 -0
  22. package/dist/runtime/composables/useLight.d.ts +0 -4
  23. package/dist/runtime/formkit/Form.d.vue.ts +2 -2
  24. package/dist/runtime/formkit/Form.vue +15 -18
  25. package/dist/runtime/formkit/Form.vue.d.ts +2 -2
  26. package/dist/runtime/formkit/index.js +2 -0
  27. package/dist/runtime/locales/en.json +8 -2
  28. package/dist/runtime/locales/zh-hk.json +8 -2
  29. package/dist/runtime/pages/Role/[name]/update-child.vue +4 -1
  30. package/dist/runtime/pages/System/fs.vue +116 -15
  31. package/dist/runtime/pages/User/setting/index.vue +29 -12
  32. package/dist/runtime/pages/User/setting/information.vue +4 -28
  33. package/dist/runtime/pages/User/setting/open_id.vue +132 -91
  34. package/dist/runtime/pages/User/setting/two-factor-auth.vue +388 -143
  35. package/dist/runtime/pages/User/setting.vue +3 -4
  36. package/dist/runtime/types/form.d.ts +2 -0
  37. package/dist/runtime/types/form.js +0 -0
  38. package/dist/runtime/utils/filterMenuItems.d.ts +10 -0
  39. package/dist/runtime/utils/filterMenuItems.js +34 -0
  40. package/dist/runtime/utils/formLayout.d.ts +2 -0
  41. package/dist/runtime/utils/formLayout.js +7 -0
  42. package/dist/runtime/utils/socialSdk.d.ts +2 -0
  43. package/dist/runtime/utils/socialSdk.js +127 -0
  44. package/package.json +14 -14
  45. package/dist/runtime/pages/Role/add2.d.vue.ts +0 -3
  46. package/dist/runtime/pages/Role/add2.vue +0 -57
  47. package/dist/runtime/pages/Role/add2.vue.d.ts +0 -3
  48. package/dist/runtime/pages/System/test.d.vue.ts +0 -3
  49. package/dist/runtime/pages/System/test.vue +0 -13
  50. package/dist/runtime/pages/System/test.vue.d.ts +0 -3
@@ -1,32 +1,49 @@
1
1
  <script setup>
2
- import { q, m, notify } from "#imports";
2
+ import { reset } from "@formkit/core";
3
+ import { m, notify, q } from "#imports";
3
4
  const { my } = await q({
4
5
  my: {
5
6
  username: true,
6
7
  email: true,
7
8
  first_name: true,
8
- last_name: true
9
+ last_name: true,
10
+ phone: true,
11
+ addr1: true,
12
+ addr2: true,
13
+ addr3: true
9
14
  }
10
15
  });
11
16
  const onSubmit = async (data, form) => {
12
- return await m("updateMy", {
17
+ await m("updateMy", {
13
18
  data: {
14
19
  email: data.email,
15
20
  first_name: data.first_name,
16
- last_name: data.last_name
21
+ last_name: data.last_name,
22
+ phone: data.phone,
23
+ addr1: data.addr1,
24
+ addr2: data.addr2,
25
+ addr3: data.addr3
17
26
  }
18
- }).then(() => {
19
- notify("Your profile has been updated");
20
- reset(form, data);
21
27
  });
28
+ notify("Your profile has been updated");
29
+ reset(form, data);
22
30
  };
23
31
  </script>
24
32
 
25
33
  <template>
26
- <FormKit type="l-form" :value="my" :bordered="false" @submit="onSubmit">
27
- <FormKit type="l-input" label="Username" name="username" readonly></FormKit>
28
- <FormKit type="l-input" label="Email" name="email" validation="required|email"></FormKit>
29
- <FormKit type="l-input" label="First name" name="first_name" validation="required"></FormKit>
30
- <FormKit type="l-input" label="Last name" name="last_name"></FormKit>
34
+ <FormKit type="l-form" layout="stack" :value="my" :bordered="false" @submit="onSubmit">
35
+ <div class="text-h6">{{ $t("Personal information") }}</div>
36
+ <FormKit type="l-input" label="Username" name="username" readonly />
37
+ <FormKit type="l-input" label="Email" name="email" validation="required|email" />
38
+ <FormKit type="l-input" label="First name" name="first_name" validation="required" />
39
+ <FormKit type="l-input" label="Last name" name="last_name" />
40
+
41
+ <q-separator spaced />
42
+
43
+ <div class="text-h6">{{ $t("Contact information") }}</div>
44
+ <FormKit type="l-input" label="Phone" name="phone" />
45
+ <FormKit type="l-input" label="Address1" name="addr1" />
46
+ <FormKit type="l-input" label="Address2" name="addr2" />
47
+ <FormKit type="l-input" label="Address3" name="addr3" />
31
48
  </FormKit>
32
49
  </template>
@@ -1,34 +1,10 @@
1
1
  <script setup>
2
- import { reset } from "@formkit/core";
3
- import { q, m, notify } from "#imports";
4
- const { my } = await q({
5
- my: {
6
- phone: true,
7
- addr1: true,
8
- addr2: true,
9
- addr3: true
10
- }
2
+ import { navigateTo } from "#imports";
3
+ await navigateTo("/User/setting", {
4
+ replace: true
11
5
  });
12
- const onSubmit = (data, form) => {
13
- return m("updateMy", {
14
- data: {
15
- phone: data.phone,
16
- addr1: data.addr1,
17
- addr2: data.addr2,
18
- addr3: data.addr3
19
- }
20
- }).then(() => {
21
- notify("Your information has been updated");
22
- reset(form, data);
23
- });
24
- };
25
6
  </script>
26
7
 
27
8
  <template>
28
- <FormKit type="l-form" :value="my" :bordered="false" @submit="onSubmit">
29
- <FormKit type="l-input" label="Phone" name="phone"></FormKit>
30
- <FormKit type="l-input" label="Address1" name="addr1"></FormKit>
31
- <FormKit type="l-input" label="Address2" name="addr2"></FormKit>
32
- <FormKit type="l-input" label="Address3" name="addr3"></FormKit>
33
- </FormKit>
9
+ <div />
34
10
  </template>
@@ -1,13 +1,12 @@
1
1
  <script setup>
2
- import { reactive, onMounted, nextTick } from "vue";
3
- import { useQuasar } from "quasar";
2
+ import { nextTick, onMounted, reactive, ref } from "vue";
4
3
  import { q, useLight } from "#imports";
5
4
  import { getApiClient } from "@hostlink/light";
6
- import { fabGoogle, fabFacebook, fabMicrosoft } from "@quasar/extras/fontawesome-v6";
5
+ import { fabGoogle, fabFacebook, fabMicrosoft } from "@quasar/extras/fontawesome-v7";
6
+ import { loadGoogleIdentityServices } from "../../../utils/socialSdk";
7
7
  const api = getApiClient();
8
8
  const light = useLight();
9
- const quasar = useQuasar();
10
- let { app, my } = await q({
9
+ const response = await q({
11
10
  app: {
12
11
  googleClientId: true,
13
12
  microsoftClientId: true,
@@ -20,123 +19,152 @@ let { app, my } = await q({
20
19
  facebook: true
21
20
  }
22
21
  });
23
- const $q = useQuasar();
24
- my = reactive(my);
25
- const handleGoogleCredentialResponse = async (response) => {
22
+ const app = response.app;
23
+ const my = reactive(response.my);
24
+ const providerLoading = reactive({
25
+ google: false,
26
+ microsoft: false,
27
+ facebook: false
28
+ });
29
+ const googleSdkLoading = ref(false);
30
+ const googleSdkError = ref("");
31
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
32
+ const notifyError = (error) => {
33
+ light.notify({
34
+ message: errorMessage(error),
35
+ color: "negative",
36
+ icon: "sym_o_error"
37
+ });
38
+ };
39
+ const refreshAccounts = async () => {
40
+ const result = await q({
41
+ my: {
42
+ google: true,
43
+ microsoft: true,
44
+ facebook: true
45
+ }
46
+ });
47
+ Object.assign(my, result.my);
48
+ };
49
+ const handleGoogleCredentialResponse = async (credentialResponse) => {
50
+ if (!credentialResponse.credential || providerLoading.google) {
51
+ return;
52
+ }
53
+ providerLoading.google = true;
26
54
  try {
27
- await api.auth.google.register(response.credential);
28
- const resp = await q({
29
- my: { google: true }
30
- });
31
- my.google = resp.my.google;
55
+ await api.auth.google.register(credentialResponse.credential);
56
+ await refreshAccounts();
32
57
  light.notify({
33
58
  message: "Google account linked",
34
59
  color: "positive"
35
60
  });
36
- } catch (e) {
37
- console.log(e);
61
+ } catch (error) {
62
+ notifyError(error);
63
+ } finally {
64
+ providerLoading.google = false;
38
65
  }
39
66
  };
40
- onMounted(() => {
41
- if (app.googleClientId) {
42
- if (!window.google) {
43
- quasar.notify({
44
- message: "Google login is not available",
45
- //set script https://accounts.google.com/gsi/client in nuuxt.config.js
46
- color: "negative",
47
- icon: "sym_o_error",
48
- position: "top",
49
- timeout: 2e3
50
- });
67
+ const initializeGoogle = async () => {
68
+ if (!app.googleClientId || my.google) {
69
+ return;
70
+ }
71
+ googleSdkLoading.value = true;
72
+ googleSdkError.value = "";
73
+ try {
74
+ const google = await loadGoogleIdentityServices();
75
+ googleSdkLoading.value = false;
76
+ await nextTick();
77
+ const target = document.getElementById("g_id_signin");
78
+ if (!target || my.google) {
51
79
  return;
52
80
  }
53
- }
54
- nextTick(() => {
81
+ target.replaceChildren();
55
82
  google.accounts.id.initialize({
56
83
  client_id: app.googleClientId,
57
84
  callback: handleGoogleCredentialResponse
58
85
  });
59
- google.accounts.id.renderButton(
60
- document.getElementById("g_id_signin"),
61
- {
62
- type: "profile",
63
- shape: "pill",
64
- theme: "outline",
65
- text: "signin_with",
66
- size: "large",
67
- logo_alignment: "left"
68
- }
69
- );
70
- });
71
- });
72
- const onUnlink = async () => {
73
- light.dialog({
74
- title: "Unlink",
75
- color: "negative",
76
- message: "Are you sure you want to unlink your Google account?",
77
- ok: "Yes",
78
- cancel: "No"
79
- }).onOk(async () => {
80
- await api.auth.google.unlink();
81
- my.gmail = null;
82
- window.location.reload();
83
- });
86
+ google.accounts.id.renderButton(target, {
87
+ type: "profile",
88
+ shape: "pill",
89
+ theme: "outline",
90
+ text: "signin_with",
91
+ size: "large",
92
+ logo_alignment: "left"
93
+ });
94
+ } catch (error) {
95
+ googleSdkError.value = errorMessage(error);
96
+ notifyError(error);
97
+ } finally {
98
+ googleSdkLoading.value = false;
99
+ }
84
100
  };
85
- const onUnlinkMicrosoft = async () => {
101
+ const unlinkProvider = (provider, label, unlink) => {
86
102
  light.dialog({
87
103
  title: "Unlink",
88
104
  color: "negative",
89
- message: "Are you sure you want to unlink your Microsoft account?",
105
+ message: `Are you sure you want to unlink your ${label} account?`,
90
106
  ok: "Yes",
91
107
  cancel: "No"
92
108
  }).onOk(async () => {
93
- await api.auth.microsoft.unlink();
94
- my.microsoft = null;
95
- window.location.reload();
109
+ providerLoading[provider] = true;
110
+ try {
111
+ await unlink();
112
+ await refreshAccounts();
113
+ light.notify({
114
+ message: `${label} account unlinked`,
115
+ color: "positive"
116
+ });
117
+ if (provider === "google") {
118
+ await initializeGoogle();
119
+ }
120
+ } catch (error) {
121
+ notifyError(error);
122
+ } finally {
123
+ providerLoading[provider] = false;
124
+ }
96
125
  });
97
126
  };
98
- const onLinkMicrosoft = async (resp) => {
127
+ const onUnlinkGoogle = () => unlinkProvider("google", "Google", api.auth.google.unlink);
128
+ const onUnlinkMicrosoft = () => unlinkProvider("microsoft", "Microsoft", api.auth.microsoft.unlink);
129
+ const onUnlinkFacebook = () => unlinkProvider("facebook", "Facebook", api.auth.facebook.unlink);
130
+ const onLinkMicrosoft = async (microsoftResponse) => {
131
+ const accountId = microsoftResponse.account?.localAccountId;
132
+ if (!accountId || providerLoading.microsoft) {
133
+ return;
134
+ }
135
+ providerLoading.microsoft = true;
99
136
  try {
100
- await api.auth.microsoft.register(resp.account.localAccountId);
137
+ await api.auth.microsoft.register(accountId);
138
+ await refreshAccounts();
101
139
  light.notify({
102
140
  message: "Microsoft account linked",
103
141
  color: "positive"
104
142
  });
105
- window.location.reload();
106
- } catch (e) {
107
- light.notify({
108
- message: e.message,
109
- color: "negative"
110
- });
143
+ } catch (error) {
144
+ notifyError(error);
145
+ } finally {
146
+ providerLoading.microsoft = false;
111
147
  }
112
148
  };
113
149
  const onLinkFacebook = async (accessToken) => {
150
+ if (providerLoading.facebook) {
151
+ return;
152
+ }
153
+ providerLoading.facebook = true;
114
154
  try {
115
155
  await api.auth.facebook.register(accessToken);
156
+ await refreshAccounts();
116
157
  light.notify({
117
158
  message: "Facebook account linked",
118
159
  color: "positive"
119
160
  });
120
- } catch (e) {
121
- light.notify({
122
- message: e.message,
123
- color: "negative"
124
- });
161
+ } catch (error) {
162
+ notifyError(error);
163
+ } finally {
164
+ providerLoading.facebook = false;
125
165
  }
126
166
  };
127
- const onUnlinkFacebook = async () => {
128
- light.dialog({
129
- title: "Unlink",
130
- color: "negative",
131
- message: "Are you sure you want to unlink your Facebook account?",
132
- ok: "Yes",
133
- cancel: "No"
134
- }).onOk(async () => {
135
- await api.auth.facebook.unlink();
136
- my.facebook = null;
137
- window.location.reload();
138
- });
139
- };
167
+ onMounted(initializeGoogle);
140
168
  const text1 = "Your social accounts are securely linked using industry-standard OAuth protocols. You can unlink them at any time.";
141
169
  </script>
142
170
 
@@ -174,14 +202,21 @@ const text1 = "Your social accounts are securely linked using industry-standard
174
202
  <div class="text-caption text-grey-6">{{ $t('Connected Account') }}</div>
175
203
  <div class="text-body2 text-weight-medium q-mt-xs">{{ my.google }}</div>
176
204
  </div>
177
- <q-btn class="full-width" color="negative" outline @click="onUnlink" icon="sym_o_link_off"
178
- :label="$t('Unlink Account')" />
205
+ <q-btn class="full-width" color="negative" outline @click="onUnlinkGoogle"
206
+ icon="sym_o_link_off" :label="$t('Unlink Account')"
207
+ :loading="providerLoading.google" />
179
208
  </template>
180
209
  <template v-else>
181
210
  <div class="text-center q-mb-md text-grey-6">
182
211
  {{ $t('Click to link your Google account for quick sign-in') }}
183
212
  </div>
184
- <div id="g_id_signin" class="flex justify-center"></div>
213
+ <div v-if="googleSdkLoading" class="flex justify-center q-pa-sm">
214
+ <q-spinner color="primary" size="32px" />
215
+ </div>
216
+ <q-banner v-else-if="googleSdkError" class="bg-red-1 text-negative">
217
+ {{ $t('Google login is not available') }}
218
+ </q-banner>
219
+ <div v-else id="g_id_signin" class="flex justify-center"></div>
185
220
  </template>
186
221
  </q-card-section>
187
222
  <q-card-section v-else>
@@ -219,13 +254,17 @@ const text1 = "Your social accounts are securely linked using industry-standard
219
254
  <div class="text-body2 text-weight-medium q-mt-xs">{{ my.microsoft }}</div>
220
255
  </div>
221
256
  <q-btn class="full-width" color="negative" outline @click="onUnlinkMicrosoft"
222
- icon="sym_o_link_off" :label="$t('Unlink Account')" />
257
+ icon="sym_o_link_off" :label="$t('Unlink Account')"
258
+ :loading="providerLoading.microsoft" />
223
259
  </template>
224
260
  <template v-else>
225
261
  <div class="text-center q-mb-md text-grey-6">
226
262
  {{ $t('Click to link your Microsoft account for quick sign-in') }}
227
263
  </div>
228
- <div class="flex justify-center">
264
+ <div v-if="providerLoading.microsoft" class="flex justify-center q-pa-sm">
265
+ <q-spinner color="primary" size="32px" />
266
+ </div>
267
+ <div v-else class="flex justify-center">
229
268
  <l-microsoft-button :client-id="app.microsoftClientId"
230
269
  :tenant-id="app.microsoftTenantId" @login="onLinkMicrosoft" />
231
270
  </div>
@@ -266,14 +305,16 @@ const text1 = "Your social accounts are securely linked using industry-standard
266
305
  <div class="text-body2 text-weight-medium q-mt-xs">{{ my.facebook }}</div>
267
306
  </div>
268
307
  <q-btn class="full-width" color="negative" outline @click="onUnlinkFacebook"
269
- icon="sym_o_link_off" :label="$t('Unlink Account')" />
308
+ icon="sym_o_link_off" :label="$t('Unlink Account')"
309
+ :loading="providerLoading.facebook" />
270
310
  </template>
271
311
  <template v-else>
272
312
  <div class="text-center q-mb-md text-grey-6">
273
313
  {{ $t('Click to link your Facebook account for quick sign-in') }}
274
314
  </div>
275
315
  <div class="flex justify-center">
276
- <l-facebook-button @login="onLinkFacebook" />
316
+ <l-facebook-button :app-id="app.facebookAppId"
317
+ :disable="providerLoading.facebook" @login="onLinkFacebook" />
277
318
  </div>
278
319
  </template>
279
320
  </q-card-section>