itube-specs 0.0.439 → 0.0.440
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.
|
@@ -1,83 +1,90 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
2
|
+
<ClientOnly>
|
|
3
|
+
<div
|
|
4
|
+
class="s-auth-register"
|
|
5
|
+
:class="{'_loading': loading}"
|
|
6
|
+
>
|
|
7
|
+
<SIcon
|
|
8
|
+
class="s-auth-register__icon"
|
|
9
|
+
name="user-circle"
|
|
10
|
+
size="40"
|
|
11
|
+
/>
|
|
12
|
+
<p
|
|
13
|
+
v-if="additionalText"
|
|
14
|
+
class="s-auth-register__text"
|
|
15
|
+
>{{ t(additionalText) }}</p>
|
|
16
|
+
<SInput
|
|
17
|
+
v-model="form.username"
|
|
18
|
+
:label="t('auth.username')"
|
|
19
|
+
name="login-name"
|
|
20
|
+
:error="error.username"
|
|
21
|
+
@update:error="(val: boolean) => error.username = val"
|
|
22
|
+
/>
|
|
23
|
+
<SInput
|
|
24
|
+
v-model="form.email"
|
|
25
|
+
:label="t('email')"
|
|
26
|
+
autocomplete="email"
|
|
27
|
+
:error="error.email"
|
|
28
|
+
@update:error="(val: boolean) => error.email = val"
|
|
29
|
+
/>
|
|
30
|
+
<SInput
|
|
31
|
+
v-model="form.password_hash"
|
|
32
|
+
:label="t('auth.password')"
|
|
33
|
+
type="password"
|
|
34
|
+
:error="error.password_hash"
|
|
35
|
+
autocomplete="new-password"
|
|
36
|
+
@update:error="(val: boolean) => error.password_hash = val"
|
|
37
|
+
/>
|
|
38
|
+
|
|
39
|
+
<div class="s-auth-register__buttons">
|
|
40
|
+
<SButton
|
|
41
|
+
:disabled="loading"
|
|
42
|
+
wide
|
|
43
|
+
size="l"
|
|
44
|
+
@click="submit"
|
|
45
|
+
>{{ t('auth.sign_up') }}
|
|
46
|
+
</SButton>
|
|
47
|
+
<SButton
|
|
48
|
+
wide
|
|
49
|
+
size="l"
|
|
50
|
+
theme="secondary"
|
|
51
|
+
@click="onLoginClick"
|
|
52
|
+
>{{ t('auth.log_in') }}
|
|
53
|
+
</SButton>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<p class="s-auth-register__agreement">{{ t('auth.agree_begin') }}
|
|
57
|
+
<NuxtLink
|
|
58
|
+
class="s-auth-register__agreement-link"
|
|
59
|
+
to="/terms"
|
|
60
|
+
target="_blank"
|
|
61
|
+
>{{ t('auth.agree_terms')}}
|
|
62
|
+
</NuxtLink>{{ t('auth.agree_and')}}
|
|
63
|
+
<NuxtLink
|
|
64
|
+
class="s-auth-register__agreement-link"
|
|
65
|
+
to="/policy"
|
|
66
|
+
target="_blank"
|
|
67
|
+
>{{ t('auth.agree_policy') }}
|
|
68
|
+
</NuxtLink>
|
|
69
|
+
</p>
|
|
53
70
|
</div>
|
|
54
|
-
|
|
55
|
-
<p class="s-auth-register__agreement">{{ t('auth.agree_begin') }}
|
|
56
|
-
<NuxtLink
|
|
57
|
-
class="s-auth-register__agreement-link"
|
|
58
|
-
to="/terms"
|
|
59
|
-
target="_blank"
|
|
60
|
-
>{{ t('auth.agree_terms')}}
|
|
61
|
-
</NuxtLink>{{ t('auth.agree_and')}}<NuxtLink
|
|
62
|
-
class="s-auth-register__agreement-link"
|
|
63
|
-
to="/policy"
|
|
64
|
-
target="_blank"
|
|
65
|
-
>{{ t('auth.agree_policy') }}
|
|
66
|
-
</NuxtLink>
|
|
67
|
-
</p>
|
|
68
|
-
</div>
|
|
71
|
+
</ClientOnly>
|
|
69
72
|
</template>
|
|
70
73
|
|
|
71
74
|
<script setup lang="ts">
|
|
72
75
|
import type { IRegistrateForm } from '../../types';
|
|
73
76
|
import { validateEmail, validatePassword, validateUsername } from '../../runtime';
|
|
74
77
|
import { AuthorizationApiService } from '~/services/api/authorization.service';
|
|
78
|
+
import { useReCaptcha } from 'vue-recaptcha-v3';
|
|
75
79
|
|
|
76
80
|
defineProps<{
|
|
77
81
|
additionalText?: string;
|
|
78
82
|
}>()
|
|
79
83
|
|
|
80
|
-
const {t} = useI18n();
|
|
84
|
+
const { t } = useI18n();
|
|
85
|
+
|
|
86
|
+
// Правильное использование composable для Vue 3
|
|
87
|
+
const { executeRecaptcha, recaptchaLoaded } = useReCaptcha();
|
|
81
88
|
|
|
82
89
|
const form = ref({
|
|
83
90
|
username: '',
|
|
@@ -104,8 +111,6 @@ const loading = ref(false);
|
|
|
104
111
|
|
|
105
112
|
const { setErrorState, snackbarText, snackbarTheme, showErrorSnack, resetSnackbar } = useSnackbar();
|
|
106
113
|
|
|
107
|
-
const { executeRecaptcha } = useGoogleRecaptcha();
|
|
108
|
-
|
|
109
114
|
function validateForm(): boolean {
|
|
110
115
|
const { username, email, password_hash } = form.value;
|
|
111
116
|
|
|
@@ -115,19 +120,12 @@ function validateForm(): boolean {
|
|
|
115
120
|
password_hash: !validatePassword(password_hash),
|
|
116
121
|
};
|
|
117
122
|
|
|
118
|
-
if (!validateEmail(email))
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (!validatePassword(password_hash)) {
|
|
122
|
-
showErrorSnack('error_password');
|
|
123
|
-
}
|
|
124
|
-
if (!validateUsername(username)) {
|
|
125
|
-
showErrorSnack('error_username');
|
|
126
|
-
}
|
|
123
|
+
if (!validateEmail(email)) showErrorSnack('error_email');
|
|
124
|
+
if (!validatePassword(password_hash)) showErrorSnack('error_password');
|
|
125
|
+
if (!validateUsername(username)) showErrorSnack('error_username');
|
|
127
126
|
|
|
128
127
|
error.value = errors;
|
|
129
128
|
|
|
130
|
-
|
|
131
129
|
return !Object.values(errors).some(Boolean);
|
|
132
130
|
}
|
|
133
131
|
|
|
@@ -140,14 +138,21 @@ async function submit() {
|
|
|
140
138
|
|
|
141
139
|
try {
|
|
142
140
|
loading.value = true;
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
|
|
142
|
+
// Здесь происходит lazy-загрузка скрипта reCAPTCHA (только при submit!)
|
|
143
|
+
await recaptchaLoaded();
|
|
144
|
+
// имя действия для Google Analytics
|
|
145
|
+
form.value.token = await executeRecaptcha('register');
|
|
146
|
+
|
|
145
147
|
await useUser(AuthorizationApiService).register(form.value);
|
|
148
|
+
|
|
146
149
|
useAuthPopup().closeAuthPopup();
|
|
147
150
|
snackbarTheme.value = 'success';
|
|
148
151
|
snackbarText.value = 'Registration success';
|
|
149
152
|
} catch (err) {
|
|
150
153
|
setErrorState(err);
|
|
154
|
+
// Опционально: обработка ошибки reCAPTCHA (например, если загрузка не удалась)
|
|
155
|
+
console.error('reCAPTCHA error:', err);
|
|
151
156
|
} finally {
|
|
152
157
|
loading.value = false;
|
|
153
158
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "itube-specs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.440",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"types": "./types/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@vueform/slider": "2.1.10",
|
|
52
|
-
"@vueuse/core": "12.8.2"
|
|
52
|
+
"@vueuse/core": "12.8.2",
|
|
53
|
+
"vue-recaptcha-v3": "^2.0.1"
|
|
53
54
|
}
|
|
54
55
|
}
|