@hostlink/nuxt-light 1.37.1 → 1.39.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.
@@ -11,81 +11,247 @@ const { my } = await q({
11
11
  const obj = reactive({
12
12
  code: ""
13
13
  });
14
+ const currentStep = ref(1);
15
+ const secretVisible = ref(false);
16
+ const loading = ref(false);
14
17
  const save = async () => {
18
+ loading.value = true;
15
19
  try {
16
20
  await m("updateMy2FA", {
17
21
  code: obj.code,
18
22
  secret: my.my2FA.secret
19
23
  });
20
24
  $q.notify({
21
- message: "Your 2FA is updated",
22
- color: "positive"
25
+ message: "\u{1F389} Two-Factor Authentication has been successfully enabled!",
26
+ color: "positive",
27
+ timeout: 5e3,
28
+ actions: [
29
+ { label: "Dismiss", color: "white" }
30
+ ]
23
31
  });
32
+ await navigateTo("/User/setting");
24
33
  } catch (e) {
25
34
  $q.notify({
26
- message: e.message,
27
- color: "negative"
35
+ message: `\u274C Setup failed: ${e.message}`,
36
+ color: "negative",
37
+ timeout: 5e3,
38
+ actions: [
39
+ { label: "Retry", color: "white" }
40
+ ]
28
41
  });
42
+ } finally {
43
+ loading.value = false;
29
44
  }
30
45
  };
31
46
  const show = ref(true);
32
47
  if (my.twoFactorEnabled) {
33
48
  show.value = false;
49
+ currentStep.value = 3;
34
50
  }
35
- const onCopy = () => {
36
- navigator.clipboard.writeText(my.my2FA.secret).then(() => {
51
+ const onCopy = async () => {
52
+ try {
53
+ await navigator.clipboard.writeText(my.my2FA.secret);
37
54
  $q.notify({
38
- message: "Secret copied",
39
- color: "positive"
55
+ message: "\u2705 Secret key copied to clipboard",
56
+ color: "positive",
57
+ timeout: 2e3,
58
+ icon: "content_copy"
40
59
  });
41
- }, () => {
60
+ } catch (e) {
61
+ const textArea = document.createElement("textarea");
62
+ textArea.value = my.my2FA.secret;
63
+ document.body.appendChild(textArea);
64
+ textArea.select();
65
+ document.execCommand("copy");
66
+ document.body.removeChild(textArea);
42
67
  $q.notify({
43
- message: "Failed to copy",
44
- color: "negative"
68
+ message: "\u2705 Secret key copied to clipboard",
69
+ color: "positive",
70
+ timeout: 2e3
45
71
  });
46
- });
72
+ }
47
73
  };
48
74
  </script>
49
75
 
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
+
50
80
  <template>
51
- <l-form @submit="save" :bordered="false">
52
- <div>
53
- <p>
54
- {{ $t('Now download the app and scan the qrcode. Input the code to the following input and submit') }}
55
- </p>
56
- <p>
57
- For Android user, install
58
- <a type="primary" target="_blank"
59
- href="https://play.google.com/store/apps/details?id=com.azure.authenticator">Microsoft
60
- Authenticator</a>
81
+ <div class="two-factor-setup">
82
+ <!-- Header -->
83
+ <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:') }}</p>
89
+
90
+ <!-- App Selection Cards -->
91
+ <div class="row q-gutter-md q-mb-lg">
92
+ <div class="col-12 col-md-5">
93
+ <q-card flat bordered class="app-card">
94
+ <q-card-section class="q-pa-md">
95
+ <div class="text-h6 q-mb-sm">📱 Android</div>
96
+ <div class="q-gutter-sm">
97
+ <q-btn
98
+ outline
99
+ color="primary"
100
+ size="sm"
101
+ target="_blank"
102
+ href="https://play.google.com/store/apps/details?id=com.azure.authenticator"
103
+ label="Microsoft Authenticator"
104
+ class="full-width"
105
+ />
106
+ <q-btn
107
+ outline
108
+ color="primary"
109
+ size="sm"
110
+ target="_blank"
111
+ href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"
112
+ label="Google Authenticator"
113
+ class="full-width"
114
+ />
115
+ </div>
116
+ </q-card-section>
117
+ </q-card>
118
+ </div>
119
+
120
+ <div class="col-12 col-md-5">
121
+ <q-card flat bordered class="app-card">
122
+ <q-card-section class="q-pa-md">
123
+ <div class="text-h6 q-mb-sm">🍎 iOS</div>
124
+ <div class="q-gutter-sm">
125
+ <q-btn
126
+ outline
127
+ color="primary"
128
+ size="sm"
129
+ target="_blank"
130
+ href="https://apps.apple.com/us/app/microsoft-authenticator/id983156458"
131
+ label="Microsoft Authenticator"
132
+ class="full-width"
133
+ />
134
+ <q-btn
135
+ outline
136
+ color="primary"
137
+ size="sm"
138
+ target="_blank"
139
+ href="https://apps.apple.com/us/app/google-authenticator/id388497605"
140
+ label="Google Authenticator"
141
+ class="full-width"
142
+ />
143
+ </div>
144
+ </q-card-section>
145
+ </q-card>
146
+ </div>
147
+ </div>
61
148
 
62
- or
63
- <a type="primary" target="_blank"
64
- href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google
65
- Authenticator</a>
149
+ <q-btn
150
+ color="primary"
151
+ @click="currentStep = 2"
152
+ :label="$t('I have installed the app')"
153
+ icon-right="arrow_forward"
154
+ />
155
+ </div>
156
+ </q-step>
66
157
 
67
- </p>
158
+ <q-step :name="2" title="Scan QR Code" icon="qr_code" :done="currentStep > 2">
159
+ <div class="text-center q-mb-md">
160
+ <p class="text-body1 q-mb-md">{{ $t('Scan this QR code with your authenticator app:') }}</p>
161
+
162
+ <q-card flat bordered class="qr-card inline-block q-pa-lg">
163
+ <q-img :src="my.my2FA.image" width="200px" class="q-mb-md" />
164
+
165
+ <!-- Manual Entry Option -->
166
+ <q-expansion-item
167
+ icon="key"
168
+ :label="$t('Enter manually instead')"
169
+ class="text-left"
170
+ >
171
+ <div class="q-pa-md bg-grey-1 rounded-borders">
172
+ <div class="text-caption text-grey-7 q-mb-xs">{{ $t('Secret Key:') }}</div>
173
+ <div class="row items-center q-gutter-sm">
174
+ <div class="col">
175
+ <q-input
176
+ :model-value="secretVisible ? (my.my2FA?.secret || '') : '●'.repeat(my.my2FA?.secret?.length || 16)"
177
+ readonly
178
+ dense
179
+ outlined
180
+ class="secret-input"
181
+ />
182
+ </div>
183
+ <q-btn
184
+ flat
185
+ round
186
+ dense
187
+ :icon="secretVisible ? 'visibility_off' : 'visibility'"
188
+ @click="secretVisible = !secretVisible"
189
+ :title="secretVisible ? $t('Hide') : $t('Show')"
190
+ />
191
+ <q-btn
192
+ flat
193
+ round
194
+ dense
195
+ icon="content_copy"
196
+ @click="onCopy"
197
+ :title="$t('Copy to clipboard')"
198
+ />
199
+ </div>
200
+ </div>
201
+ </q-expansion-item>
202
+ </q-card>
68
203
 
69
- <p>
70
- For iOS user, install
71
- <a type="primary" target="_blank"
72
- href="https://apps.apple.com/us/app/microsoft-authenticator/id983156458">Microsoft
73
- Authenticator</a>
74
- or
75
- <a type="primary" target="_blank"
76
- href="https://apps.apple.com/us/app/google-authenticator/id388497605">Google
77
- Authenticator</a>
204
+ <div class="q-mt-md">
205
+ <q-btn
206
+ color="primary"
207
+ @click="currentStep = 3"
208
+ :label="$t('I have scanned the code')"
209
+ icon-right="arrow_forward"
210
+ />
211
+ </div>
212
+ </div>
213
+ </q-step>
78
214
 
79
- </p>
80
- </div>
81
- <q-img :src="my.my2FA.image" width="250px" />
82
- <p>
83
- Secret : <strong>{{ my.my2FA.secret }}</strong>
84
-
85
- <q-btn flat round dense icon="sym_o_content_copy" @click="onCopy" />
86
- </p>
215
+ <q-step :name="3" title="Verify Setup" icon="verified_user">
216
+ <div class="verify-section">
217
+ <p class="text-body1 q-mb-md">
218
+ {{ $t('Enter the 6-digit code from your authenticator app to complete setup:') }}
219
+ </p>
220
+
221
+ <div class="row justify-center q-mb-lg">
222
+ <div class="col-12 col-sm-8 col-md-6">
223
+ <q-input
224
+ v-model="obj.code"
225
+ :label="$t('Verification Code')"
226
+ hint="6-digit code from your authenticator app"
227
+ type="tel"
228
+ inputmode="numeric"
229
+ pattern="[0-9]*"
230
+ outlined
231
+ maxlength="6"
232
+ class="text-center code-input"
233
+ required
234
+ >
235
+ <template v-slot:prepend>
236
+ <q-icon name="security" />
237
+ </template>
238
+ </q-input>
239
+ </div>
240
+ </div>
87
241
 
88
- <l-input v-model="obj.code" label="Code"
89
- hint="Please scan the QR code with your authenticator app, and enter the code generated" required />
90
- </l-form>
242
+ <div class="text-center">
243
+ <q-btn
244
+ @click="save"
245
+ color="positive"
246
+ size="lg"
247
+ :label="$t('Complete Setup')"
248
+ icon="check_circle"
249
+ :loading="loading"
250
+ />
251
+ </div>
252
+ </div>
253
+ </q-step>
254
+ </q-stepper>
255
+ </div>
256
+ </div>
91
257
  </template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostlink/nuxt-light",
3
- "version": "1.37.1",
3
+ "version": "1.39.0",
4
4
  "description": "HostLink Nuxt Light Framework",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,31 +32,31 @@
32
32
  "dependencies": {
33
33
  "@azure/msal-browser": "^3.26.1",
34
34
  "@formkit/drag-and-drop": "^0.5.3",
35
- "@hostlink/light": "^2.8.0",
35
+ "@hostlink/light": "^2.9.0",
36
36
  "@nuxt/module-builder": "^1.0.1",
37
- "@quasar/extras": "^1.16.11",
37
+ "@quasar/extras": "^1.17.0",
38
38
  "@quasar/quasar-ui-qmarkdown": "^2.0.5",
39
- "axios": "^1.5.0",
39
+ "axios": "^1.12.2",
40
40
  "defu": "^6.1.4",
41
41
  "diff2html": "^3.4.47",
42
42
  "formkit-quasar": "^0.0.15",
43
43
  "json-to-graphql-query": "^2.2.5",
44
44
  "nuxt-quasar-ui": "^2.1.12",
45
- "quasar": "^2.17.0",
45
+ "quasar": "^2.18.5",
46
46
  "vue-i18n": "^9.2.2",
47
47
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@nuxt/devtools": "latest",
51
51
  "@nuxt/eslint-config": "^0.2.0",
52
- "@nuxt/kit": "^4.0.3",
53
- "@nuxt/schema": "^4.0.3",
52
+ "@nuxt/kit": "^4.1.2",
53
+ "@nuxt/schema": "^4.1.2",
54
54
  "@nuxt/test-utils": "^3.17.2",
55
55
  "@types/node": "^22.5.0",
56
56
  "changelogen": "^0.5.4",
57
57
  "eslint": "^8.46.0",
58
- "nuxt": "^4.0.3",
59
- "typescript": "^5.8.3",
58
+ "nuxt": "^4.1.2",
59
+ "typescript": "^5.9.2",
60
60
  "vue-tsc": "^2.2.8"
61
61
  }
62
62
  }