@hostlink/nuxt-light 1.73.2 → 1.74.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.
Files changed (52) 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/Card.vue +3 -2
  7. package/dist/runtime/components/L/FacebookButton.d.vue.ts +19 -3
  8. package/dist/runtime/components/L/FacebookButton.vue +73 -12
  9. package/dist/runtime/components/L/FacebookButton.vue.d.ts +19 -3
  10. package/dist/runtime/components/L/FavMenu.d.vue.ts +2 -0
  11. package/dist/runtime/components/L/FavMenu.vue +10 -2
  12. package/dist/runtime/components/L/FavMenu.vue.d.ts +2 -0
  13. package/dist/runtime/components/L/Form.d.vue.ts +29 -56
  14. package/dist/runtime/components/L/Form.vue +70 -80
  15. package/dist/runtime/components/L/Form.vue.d.ts +29 -56
  16. package/dist/runtime/components/L/FormLayout.d.vue.ts +36 -0
  17. package/dist/runtime/components/L/FormLayout.vue +36 -0
  18. package/dist/runtime/components/L/FormLayout.vue.d.ts +36 -0
  19. package/dist/runtime/components/L/Login.vue +1 -1
  20. package/dist/runtime/components/L/Menu.d.vue.ts +2 -0
  21. package/dist/runtime/components/L/Menu.vue +4 -4
  22. package/dist/runtime/components/L/Menu.vue.d.ts +2 -0
  23. package/dist/runtime/composables/useLight.d.ts +0 -4
  24. package/dist/runtime/formkit/Form.d.vue.ts +2 -2
  25. package/dist/runtime/formkit/Form.vue +15 -18
  26. package/dist/runtime/formkit/Form.vue.d.ts +2 -2
  27. package/dist/runtime/formkit/index.js +2 -0
  28. package/dist/runtime/locales/en.json +8 -2
  29. package/dist/runtime/locales/zh-hk.json +8 -2
  30. package/dist/runtime/pages/Role/[name]/update-child.vue +4 -1
  31. package/dist/runtime/pages/System/database/table.vue +40 -2
  32. package/dist/runtime/pages/System/fs.vue +116 -15
  33. package/dist/runtime/pages/User/setting/index.vue +29 -12
  34. package/dist/runtime/pages/User/setting/information.vue +4 -28
  35. package/dist/runtime/pages/User/setting/open_id.vue +132 -91
  36. package/dist/runtime/pages/User/setting/two-factor-auth.vue +388 -143
  37. package/dist/runtime/pages/User/setting.vue +3 -4
  38. package/dist/runtime/types/form.d.ts +2 -0
  39. package/dist/runtime/types/form.js +0 -0
  40. package/dist/runtime/utils/filterMenuItems.d.ts +10 -0
  41. package/dist/runtime/utils/filterMenuItems.js +34 -0
  42. package/dist/runtime/utils/formLayout.d.ts +2 -0
  43. package/dist/runtime/utils/formLayout.js +7 -0
  44. package/dist/runtime/utils/socialSdk.d.ts +2 -0
  45. package/dist/runtime/utils/socialSdk.js +127 -0
  46. package/package.json +14 -14
  47. package/dist/runtime/pages/Role/add2.d.vue.ts +0 -3
  48. package/dist/runtime/pages/Role/add2.vue +0 -57
  49. package/dist/runtime/pages/Role/add2.vue.d.ts +0 -3
  50. package/dist/runtime/pages/System/test.d.vue.ts +0 -3
  51. package/dist/runtime/pages/System/test.vue +0 -13
  52. package/dist/runtime/pages/System/test.vue.d.ts +0 -3
@@ -1,192 +1,437 @@
1
1
  <script setup>
2
- import { ref, reactive } from "vue";
2
+ import { computed, reactive, ref } from "vue";
3
3
  import { useQuasar } from "quasar";
4
- import { q, m, navigateTo } from "#imports";
4
+ import { m, q } from "#imports";
5
5
  const $q = useQuasar();
6
- const { my } = await q({
6
+ const initial = await q({
7
7
  my: {
8
- my2FA: true
8
+ twoFactorEnabled: true
9
+ },
10
+ app: {
11
+ twoFactorAuthentication: true
9
12
  }
10
13
  });
11
- const obj = reactive({
12
- code: ""
13
- });
14
+ const twoFactorEnabled = ref(Boolean(initial.my?.twoFactorEnabled));
15
+ const systemRequiresTwoFactor = Boolean(initial.app?.twoFactorAuthentication);
16
+ const setup = ref(null);
14
17
  const currentStep = ref(1);
15
18
  const secretVisible = ref(false);
16
- const loading = ref(false);
19
+ const setupLoading = ref(false);
20
+ const saveLoading = ref(false);
21
+ const disableLoading = ref(false);
22
+ const disableDialog = ref(false);
23
+ const setupForm = reactive({
24
+ code: ""
25
+ });
26
+ const disableForm = reactive({
27
+ password: "",
28
+ code: ""
29
+ });
30
+ const validSetupCode = computed(() => /^\d{6}$/.test(setupForm.code));
31
+ const validDisableForm = computed(() => disableForm.password.length > 0 && /^\d{6}$/.test(disableForm.code));
32
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
33
+ const resetSetup = () => {
34
+ setup.value = null;
35
+ setupForm.code = "";
36
+ currentStep.value = 1;
37
+ secretVisible.value = false;
38
+ };
39
+ const startSetup = async () => {
40
+ setupLoading.value = true;
41
+ try {
42
+ const result = await q({
43
+ my: {
44
+ my2FA: true
45
+ }
46
+ });
47
+ setup.value = result.my.my2FA;
48
+ currentStep.value = 1;
49
+ } catch (error) {
50
+ $q.notify({
51
+ message: `Setup failed: ${errorMessage(error)}`,
52
+ color: "negative"
53
+ });
54
+ } finally {
55
+ setupLoading.value = false;
56
+ }
57
+ };
17
58
  const save = async () => {
18
- loading.value = true;
59
+ if (!setup.value || !validSetupCode.value) {
60
+ return;
61
+ }
62
+ saveLoading.value = true;
19
63
  try {
20
64
  await m("updateMy2FA", {
21
- code: obj.code,
22
- secret: my.my2FA.secret
65
+ code: setupForm.code,
66
+ secret: setup.value.secret
23
67
  });
68
+ twoFactorEnabled.value = true;
69
+ resetSetup();
24
70
  $q.notify({
25
- message: "\u{1F389} Two-Factor Authentication has been successfully enabled!",
71
+ message: "Two-Factor Authentication has been successfully enabled!",
26
72
  color: "positive",
27
- timeout: 5e3,
28
- actions: [
29
- { label: "Dismiss", color: "white" }
30
- ]
73
+ timeout: 5e3
31
74
  });
32
- await navigateTo("/User/setting");
33
- } catch (e) {
75
+ } catch (error) {
34
76
  $q.notify({
35
- message: `\u274C Setup failed: ${e.message}`,
77
+ message: `Setup failed: ${errorMessage(error)}`,
36
78
  color: "negative",
37
- timeout: 5e3,
38
- actions: [
39
- { label: "Retry", color: "white" }
40
- ]
79
+ timeout: 5e3
41
80
  });
42
81
  } finally {
43
- loading.value = false;
82
+ saveLoading.value = false;
44
83
  }
45
84
  };
46
- const show = ref(true);
47
- if (my.twoFactorEnabled) {
48
- show.value = false;
49
- currentStep.value = 3;
50
- }
51
- const onCopy = async () => {
85
+ const openDisableDialog = () => {
86
+ disableForm.password = "";
87
+ disableForm.code = "";
88
+ disableDialog.value = true;
89
+ };
90
+ const disableTwoFactor = async () => {
91
+ if (!validDisableForm.value) {
92
+ return;
93
+ }
94
+ disableLoading.value = true;
52
95
  try {
53
- await navigator.clipboard.writeText(my.my2FA.secret);
96
+ await m("disableMy2FA", {
97
+ password: disableForm.password,
98
+ code: disableForm.code
99
+ });
100
+ twoFactorEnabled.value = false;
101
+ disableDialog.value = false;
102
+ disableForm.password = "";
103
+ disableForm.code = "";
104
+ resetSetup();
54
105
  $q.notify({
55
- message: "\u2705 Secret key copied to clipboard",
106
+ message: "Two-Factor Authentication has been disabled.",
56
107
  color: "positive",
57
- timeout: 2e3,
58
- icon: "content_copy"
108
+ timeout: 5e3
59
109
  });
60
- } catch (e) {
110
+ } catch (error) {
111
+ $q.notify({
112
+ message: `Unable to disable Two-Factor Authentication: ${errorMessage(error)}`,
113
+ color: "negative",
114
+ timeout: 5e3
115
+ });
116
+ } finally {
117
+ disableLoading.value = false;
118
+ }
119
+ };
120
+ const onCopy = async () => {
121
+ if (!setup.value?.secret) {
122
+ return;
123
+ }
124
+ try {
125
+ await navigator.clipboard.writeText(setup.value.secret);
126
+ } catch {
61
127
  const textArea = document.createElement("textarea");
62
- textArea.value = my.my2FA.secret;
128
+ textArea.value = setup.value.secret;
63
129
  document.body.appendChild(textArea);
64
130
  textArea.select();
65
131
  document.execCommand("copy");
66
132
  document.body.removeChild(textArea);
67
- $q.notify({
68
- message: "\u2705 Secret key copied to clipboard",
69
- color: "positive",
70
- timeout: 2e3
71
- });
72
133
  }
134
+ $q.notify({
135
+ message: "Secret key copied to clipboard",
136
+ color: "positive",
137
+ timeout: 2e3,
138
+ icon: "content_copy"
139
+ });
73
140
  };
74
141
  </script>
75
142
 
76
- <style scoped>
77
- .two-factor-setup{margin:0 auto;max-width:800px}.setup-header{text-align:center}.app-card{transition:all .3s ease}.app-card:hover{box-shadow:0 4px 12px rgba(0,0,0,.1);transform:translateY(-2px)}.qr-card{background:linear-gradient(135deg,#f5f7fa,#c3cfe2);border:2px dashed #1976d2}.secret-input{font-family:Courier New,monospace;font-size:14px}.code-input .q-field__control input{font-family:Courier New,monospace;font-size:24px;font-weight:700;letter-spacing:8px;text-align:center}.verify-section{text-align:center}@media (max-width:600px){.app-card{margin-bottom:1rem}.qr-card img{width:180px!important}.code-input .q-field__control input{font-size:20px;letter-spacing:4px}}.q-btn--loading{pointer-events:none}.q-stepper--vertical .q-stepper__step--done .q-stepper__label{opacity:.7}.q-btn,.q-card{transition:all .2s ease}.q-btn:hover{transform:translateY(-1px)}
78
- </style>
79
-
80
143
  <template>
81
144
  <div class="two-factor-setup">
82
- <!-- Header -->
83
145
  <div class="setup-header q-mb-lg">
84
- <h5 class="q-my-md">{{ $t('Setup Two-Factor Authentication') }}</h5>
85
- <q-stepper v-model="currentStep" vertical color="primary" animated flat>
86
- <q-step :name="1" title="Download Authenticator App" icon="download" :done="currentStep > 1">
87
- <div class="q-mb-md">
88
- <p class="text-body1 q-mb-md">{{ $t('Choose and download an authenticator app for your device:')
89
- }}</p>
90
-
91
- <!-- App Selection Cards -->
92
- <div class="row q-gutter-md q-mb-lg">
93
- <div class="col-12 col-md-5">
94
- <q-card flat bordered class="app-card">
95
- <q-card-section class="q-pa-md">
96
- <div class="text-h6 q-mb-sm">📱 Android</div>
97
- <div class="q-gutter-sm">
98
- <q-btn outline color="primary" size="sm" target="_blank"
99
- href="https://play.google.com/store/apps/details?id=com.azure.authenticator"
100
- label="Microsoft Authenticator" class="full-width" />
101
- <q-btn outline color="primary" size="sm" target="_blank"
102
- href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"
103
- label="Google Authenticator" class="full-width" />
104
- </div>
105
- </q-card-section>
106
- </q-card>
107
- </div>
146
+ <h5 class="q-my-md">{{ $t("Two-Factor Authentication") }}</h5>
147
+ </div>
108
148
 
109
- <div class="col-12 col-md-5">
110
- <q-card flat bordered class="app-card">
111
- <q-card-section class="q-pa-md">
112
- <div class="text-h6 q-mb-sm">🍎 iOS</div>
113
- <div class="q-gutter-sm">
114
- <q-btn outline color="primary" size="sm" target="_blank"
115
- href="https://apps.apple.com/us/app/microsoft-authenticator/id983156458"
116
- label="Microsoft Authenticator" class="full-width" />
117
- <q-btn outline color="primary" size="sm" target="_blank"
118
- href="https://apps.apple.com/us/app/google-authenticator/id388497605"
119
- label="Google Authenticator" class="full-width" />
120
- </div>
121
- </q-card-section>
122
- </q-card>
123
- </div>
124
- </div>
149
+ <q-card v-if="twoFactorEnabled" flat bordered>
150
+ <q-card-section class="text-center q-pa-xl">
151
+ <q-icon name="verified_user" color="positive" size="64px" />
152
+ <div class="text-h6 q-mt-md">
153
+ {{ $t("Two-Factor Authentication is enabled") }}
154
+ </div>
155
+ <p class="text-grey-7 q-mt-sm">
156
+ {{ $t("Your account is protected by an authenticator app.") }}
157
+ </p>
125
158
 
126
- <q-btn color="primary" @click="currentStep = 2" :label="$t('I have installed the app')"
127
- icon-right="arrow_forward" />
128
- </div>
129
- </q-step>
130
-
131
- <q-step :name="2" title="Scan QR Code" icon="qr_code" :done="currentStep > 2">
132
- <div class="text-center q-mb-md">
133
- <p class="text-body1 q-mb-md">{{ $t('Scan this QR code with your authenticator app:') }}</p>
134
-
135
- <q-card flat bordered class="qr-card inline-block q-pa-lg">
136
- <q-img :src="my.my2FA.image" width="200px" class="q-mb-md" />
137
-
138
- <!-- Manual Entry Option -->
139
- <q-expansion-item icon="key" :label="$t('Enter manually instead')" class="text-left">
140
- <div class="q-pa-md bg-grey-1 rounded-borders">
141
- <div class="text-caption text-grey-7 q-mb-xs">{{ $t('Secret Key:') }}</div>
142
- <div class="row items-center q-gutter-sm">
143
- <div class="col">
144
- <q-input
145
- :model-value="secretVisible ? (my.my2FA?.secret || '') : '●'.repeat(my.my2FA?.secret?.length || 16)"
146
- readonly dense outlined class="secret-input" />
147
- </div>
148
- <q-btn flat round dense :icon="secretVisible ? 'visibility_off' : 'visibility'"
149
- @click="secretVisible = !secretVisible"
150
- :title="secretVisible ? $t('Hide') : $t('Show')" />
151
- <q-btn flat round dense icon="content_copy" @click="onCopy"
152
- :title="$t('Copy to clipboard')" />
153
- </div>
159
+ <q-banner v-if="systemRequiresTwoFactor" rounded class="bg-blue-1 text-primary q-mt-lg">
160
+ <template #avatar>
161
+ <q-icon name="policy" />
162
+ </template>
163
+ {{ $t("Two-Factor Authentication is required by your administrator and cannot be disabled.") }}
164
+ </q-banner>
165
+
166
+ <q-btn
167
+ v-else
168
+ outline
169
+ color="negative"
170
+ icon="remove_moderator"
171
+ :label="$t('Disable Two-Factor Authentication')"
172
+ class="q-mt-lg"
173
+ @click="openDisableDialog"
174
+ />
175
+ </q-card-section>
176
+ </q-card>
177
+
178
+ <q-card v-else-if="!setup" flat bordered>
179
+ <q-card-section class="text-center q-pa-xl">
180
+ <q-icon name="security" color="grey-6" size="64px" />
181
+ <div class="text-h6 q-mt-md">
182
+ {{ $t("Two-Factor Authentication is not enabled") }}
183
+ </div>
184
+ <p class="text-grey-7 q-mt-sm">
185
+ {{ $t("Add an extra layer of security to your account with an authenticator app.") }}
186
+ </p>
187
+ <q-btn
188
+ color="primary"
189
+ icon="add_moderator"
190
+ :label="$t('Set up Two-Factor Authentication')"
191
+ :loading="setupLoading"
192
+ class="q-mt-md"
193
+ @click="startSetup"
194
+ />
195
+ </q-card-section>
196
+ </q-card>
197
+
198
+ <q-stepper v-else v-model="currentStep" vertical color="primary" animated flat bordered>
199
+ <q-step
200
+ :name="1"
201
+ :title="$t('Download Authenticator App')"
202
+ icon="download"
203
+ :done="currentStep > 1"
204
+ >
205
+ <p class="text-body1 q-mb-md">
206
+ {{ $t("Choose and download an authenticator app for your device:") }}
207
+ </p>
208
+
209
+ <div class="row q-col-gutter-md q-mb-lg">
210
+ <div class="col-12 col-md-6">
211
+ <q-card flat bordered class="app-card">
212
+ <q-card-section>
213
+ <div class="text-h6 q-mb-sm">📱 Android</div>
214
+ <div class="q-gutter-sm">
215
+ <q-btn
216
+ outline
217
+ color="primary"
218
+ size="sm"
219
+ target="_blank"
220
+ href="https://play.google.com/store/apps/details?id=com.azure.authenticator"
221
+ label="Microsoft Authenticator"
222
+ class="full-width"
223
+ />
224
+ <q-btn
225
+ outline
226
+ color="primary"
227
+ size="sm"
228
+ target="_blank"
229
+ href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"
230
+ label="Google Authenticator"
231
+ class="full-width"
232
+ />
154
233
  </div>
155
- </q-expansion-item>
234
+ </q-card-section>
156
235
  </q-card>
236
+ </div>
157
237
 
158
- <div class="q-mt-md">
159
- <q-btn color="primary" @click="currentStep = 3" :label="$t('I have scanned the code')"
160
- icon-right="arrow_forward" />
161
- </div>
238
+ <div class="col-12 col-md-6">
239
+ <q-card flat bordered class="app-card">
240
+ <q-card-section>
241
+ <div class="text-h6 q-mb-sm">🍎 iOS</div>
242
+ <div class="q-gutter-sm">
243
+ <q-btn
244
+ outline
245
+ color="primary"
246
+ size="sm"
247
+ target="_blank"
248
+ href="https://apps.apple.com/us/app/microsoft-authenticator/id983156458"
249
+ label="Microsoft Authenticator"
250
+ class="full-width"
251
+ />
252
+ <q-btn
253
+ outline
254
+ color="primary"
255
+ size="sm"
256
+ target="_blank"
257
+ href="https://apps.apple.com/us/app/google-authenticator/id388497605"
258
+ label="Google Authenticator"
259
+ class="full-width"
260
+ />
261
+ </div>
262
+ </q-card-section>
263
+ </q-card>
162
264
  </div>
163
- </q-step>
164
-
165
- <q-step :name="3" title="Verify Setup" icon="verified_user">
166
- <div class="verify-section">
167
- <p class="text-body1 q-mb-md">
168
- {{ $t('Enter the 6-digit code from your authenticator app to complete setup:') }}
169
- </p>
170
-
171
- <div class="row justify-center q-mb-lg">
172
- <div class="col-12 col-sm-8 col-md-6">
173
- <q-input v-model="obj.code" :label="$t('Verification Code')"
174
- hint="6-digit code from your authenticator app" type="tel" inputmode="numeric"
175
- pattern="[0-9]*" outlined maxlength="6" class="text-center code-input" required>
176
- <template v-slot:prepend>
177
- <q-icon name="security" />
178
- </template>
179
- </q-input>
265
+ </div>
266
+
267
+ <div class="row q-gutter-sm">
268
+ <q-btn
269
+ color="primary"
270
+ :label="$t('I have installed the app')"
271
+ icon-right="arrow_forward"
272
+ @click="currentStep = 2"
273
+ />
274
+ <q-btn flat :label="$t('Cancel')" @click="resetSetup" />
275
+ </div>
276
+ </q-step>
277
+
278
+ <q-step :name="2" :title="$t('Scan QR Code')" icon="qr_code" :done="currentStep > 2">
279
+ <div class="text-center q-mb-md">
280
+ <p class="text-body1 q-mb-md">
281
+ {{ $t("Scan this QR code with your authenticator app:") }}
282
+ </p>
283
+
284
+ <q-card flat bordered class="qr-card inline-block q-pa-lg">
285
+ <q-img :src="setup.image" width="200px" class="q-mb-md" />
286
+
287
+ <q-expansion-item
288
+ icon="key"
289
+ :label="$t('Enter manually instead')"
290
+ class="text-left"
291
+ >
292
+ <div class="q-pa-md bg-grey-1 rounded-borders">
293
+ <div class="text-caption text-grey-7 q-mb-xs">
294
+ {{ $t("Secret Key:") }}
295
+ </div>
296
+ <div class="row items-center q-gutter-sm">
297
+ <div class="col">
298
+ <q-input
299
+ :model-value="secretVisible ? setup.secret : '●'.repeat(setup.secret.length)"
300
+ readonly
301
+ dense
302
+ outlined
303
+ class="secret-input"
304
+ />
305
+ </div>
306
+ <q-btn
307
+ flat
308
+ round
309
+ dense
310
+ :icon="secretVisible ? 'visibility_off' : 'visibility'"
311
+ :title="secretVisible ? $t('Hide') : $t('Show')"
312
+ @click="secretVisible = !secretVisible"
313
+ />
314
+ <q-btn
315
+ flat
316
+ round
317
+ dense
318
+ icon="content_copy"
319
+ :title="$t('Copy to clipboard')"
320
+ @click="onCopy"
321
+ />
322
+ </div>
180
323
  </div>
181
- </div>
324
+ </q-expansion-item>
325
+ </q-card>
182
326
 
183
- <div class="text-center">
184
- <q-btn @click="save" color="positive" size="lg" :label="$t('Complete Setup')"
185
- icon="check_circle" :loading="loading" />
327
+ <div class="q-mt-md q-gutter-sm">
328
+ <q-btn flat icon="arrow_back" :label="$t('Back')" @click="currentStep = 1" />
329
+ <q-btn
330
+ color="primary"
331
+ :label="$t('I have scanned the code')"
332
+ icon-right="arrow_forward"
333
+ @click="currentStep = 3"
334
+ />
335
+ <q-btn flat :label="$t('Cancel')" @click="resetSetup" />
336
+ </div>
337
+ </div>
338
+ </q-step>
339
+
340
+ <q-step :name="3" :title="$t('Verify Setup')" icon="verified_user">
341
+ <div class="verify-section">
342
+ <p class="text-body1 q-mb-md">
343
+ {{ $t("Enter the 6-digit code from your authenticator app to complete setup:") }}
344
+ </p>
345
+
346
+ <div class="row justify-center q-mb-lg">
347
+ <div class="col-12 col-sm-8 col-md-6">
348
+ <q-input
349
+ v-model.trim="setupForm.code"
350
+ :label="$t('Verification Code')"
351
+ :hint="$t('6-digit code from your authenticator app')"
352
+ type="tel"
353
+ inputmode="numeric"
354
+ pattern="[0-9]*"
355
+ outlined
356
+ maxlength="6"
357
+ class="text-center code-input"
358
+ autocomplete="one-time-code"
359
+ >
360
+ <template #prepend>
361
+ <q-icon name="security" />
362
+ </template>
363
+ </q-input>
186
364
  </div>
187
365
  </div>
188
- </q-step>
189
- </q-stepper>
190
- </div>
366
+
367
+ <div class="text-center q-gutter-sm">
368
+ <q-btn flat icon="arrow_back" :label="$t('Back')" @click="currentStep = 2" />
369
+ <q-btn
370
+ color="positive"
371
+ size="lg"
372
+ :label="$t('Complete Setup')"
373
+ icon="check_circle"
374
+ :loading="saveLoading"
375
+ :disable="!validSetupCode"
376
+ @click="save"
377
+ />
378
+ <q-btn flat :label="$t('Cancel')" @click="resetSetup" />
379
+ </div>
380
+ </div>
381
+ </q-step>
382
+ </q-stepper>
383
+
384
+ <q-dialog v-model="disableDialog" persistent>
385
+ <q-card style="width: 480px; max-width: 90vw">
386
+ <q-form @submit.prevent="disableTwoFactor">
387
+ <q-card-section>
388
+ <div class="text-h6">{{ $t("Disable Two-Factor Authentication") }}</div>
389
+ </q-card-section>
390
+
391
+ <q-card-section class="q-pt-none">
392
+ <q-banner rounded class="bg-orange-1 text-dark q-mb-lg">
393
+ <template #avatar>
394
+ <q-icon name="warning" color="warning" />
395
+ </template>
396
+ {{ $t("This will reduce the security of your account.") }}
397
+ </q-banner>
398
+
399
+ <q-input
400
+ v-model="disableForm.password"
401
+ type="password"
402
+ :label="$t('Current password')"
403
+ autocomplete="current-password"
404
+ outlined
405
+ class="q-mb-md"
406
+ />
407
+ <q-input
408
+ v-model.trim="disableForm.code"
409
+ type="tel"
410
+ inputmode="numeric"
411
+ pattern="[0-9]*"
412
+ maxlength="6"
413
+ :label="$t('Current 2FA code')"
414
+ autocomplete="one-time-code"
415
+ outlined
416
+ />
417
+ </q-card-section>
418
+
419
+ <q-card-actions align="right">
420
+ <q-btn v-close-popup flat :label="$t('Cancel')" :disable="disableLoading" />
421
+ <q-btn
422
+ type="submit"
423
+ color="negative"
424
+ :label="$t('Disable')"
425
+ :loading="disableLoading"
426
+ :disable="!validDisableForm"
427
+ />
428
+ </q-card-actions>
429
+ </q-form>
430
+ </q-card>
431
+ </q-dialog>
191
432
  </div>
192
433
  </template>
434
+
435
+ <style scoped>
436
+ .two-factor-setup{margin:0 auto;max-width:800px}.setup-header,.verify-section{text-align:center}.app-card{height:100%;transition:all .2s ease}.app-card:hover{box-shadow:0 4px 12px rgba(0,0,0,.1);transform:translateY(-2px)}.qr-card{background:linear-gradient(135deg,#f5f7fa,#c3cfe2);border:2px dashed #1976d2}.secret-input{font-family:Courier New,monospace;font-size:14px}.code-input :deep(input){font-family:Courier New,monospace;font-size:24px;font-weight:700;letter-spacing:8px;text-align:center}@media (max-width:600px){.qr-card img{width:180px!important}.code-input :deep(input){font-size:20px;letter-spacing:4px}}
437
+ </style>
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import { ref } from "vue";
3
3
  import { useRoute } from "vue-router";
4
- const tab = ref("general");
4
+ const tab = ref("profile");
5
5
  const route = useRoute();
6
6
  </script>
7
7
 
@@ -11,9 +11,8 @@ const route = useRoute();
11
11
  <q-splitter unit="px" :model-value="120">
12
12
  <template #before>
13
13
  <q-tabs v-model="tab" vertical active-color="primary">
14
- <q-route-tab name="general" icon="sym_o_info" :label="$t('General')" to="/User/setting" exact />
15
- <q-route-tab name="information" icon="sym_o_info" :label="$t('Information')"
16
- to="/User/setting/information" exact />
14
+ <q-route-tab name="profile" icon="sym_o_person" :label="$t('Profile')" to="/User/setting"
15
+ exact />
17
16
  <q-route-tab name="password" icon="sym_o_password" :label="$t('Password')"
18
17
  to="/User/setting/password" exact />
19
18
  <q-route-tab name="style" icon="sym_o_style" :label="$t('Style')" to="/User/setting/style"
@@ -0,0 +1,2 @@
1
+ export type LFormLayout = "grid" | "stack";
2
+ export type LFormGutter = "none" | "xs" | "sm" | "md" | "lg" | "xl";
File without changes
@@ -0,0 +1,10 @@
1
+ export interface SearchableMenuItem {
2
+ label?: string;
3
+ to?: string;
4
+ path?: string;
5
+ children?: SearchableMenuItem[];
6
+ [key: string]: unknown;
7
+ }
8
+ type TranslateLabel = (label: string) => string;
9
+ export declare function filterMenuItems<T extends SearchableMenuItem>(items: readonly T[] | null | undefined, query: string | null | undefined, translateLabel?: TranslateLabel): T[];
10
+ export {};