@bagelink/auth 1.12.3 → 1.12.5
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.
- package/dist/index.cjs +2 -1183
- package/dist/index.d.ts +0 -9
- package/dist/index.mjs +3 -1184
- package/dist/routes.d.ts +0 -36
- package/package.json +1 -1
- package/src/index.ts +0 -13
- package/src/routes.ts +0 -96
- package/dist/Callback-BHqVaZZm.cjs +0 -4
- package/dist/Callback-C-XghN_z.js +0 -4
- package/dist/ForgotPasswordPage-BV9tyhHl.cjs +0 -4
- package/dist/ForgotPasswordPage-DvttMGb0.js +0 -4
- package/dist/LoginPage-hv1wc54S.cjs +0 -4
- package/dist/LoginPage-klj1NV4J.js +0 -4
- package/dist/ResetPasswordPage-COPrJmW8.cjs +0 -4
- package/dist/ResetPasswordPage-nvQ4uupb.js +0 -4
- package/dist/SignupPage-m36w9PLJ.cjs +0 -4
- package/dist/SignupPage-oUFYApYW.js +0 -4
- package/dist/components/auth/ForgotPasswordForm.vue.d.ts +0 -23
- package/dist/components/auth/LoginForm.vue.d.ts +0 -58
- package/dist/components/auth/ResetPasswordForm.vue.d.ts +0 -28
- package/dist/components/auth/SignupForm.vue.d.ts +0 -34
- package/dist/components/index.d.ts +0 -4
- package/dist/pages/Callback.vue.d.ts +0 -2
- package/dist/pages/ForgotPasswordPage.vue.d.ts +0 -13
- package/dist/pages/LoginPage.vue.d.ts +0 -38
- package/dist/pages/ResetPasswordPage.vue.d.ts +0 -13
- package/dist/pages/SignupPage.vue.d.ts +0 -16
- package/src/components/auth/ForgotPasswordForm.vue +0 -97
- package/src/components/auth/LoginForm.vue +0 -258
- package/src/components/auth/ResetPasswordForm.vue +0 -156
- package/src/components/auth/SignupForm.vue +0 -231
- package/src/components/index.ts +0 -5
- package/src/pages/Callback.vue +0 -196
- package/src/pages/ForgotPasswordPage.vue +0 -42
- package/src/pages/LoginPage.vue +0 -68
- package/src/pages/ResetPasswordPage.vue +0 -47
- package/src/pages/SignupPage.vue +0 -46
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { useAuth } from '@bagelink/auth'
|
|
3
|
-
import { Btn, PasswordInput, TextInput } from '@bagelink/vue'
|
|
4
|
-
import { computed, ref } from 'vue'
|
|
5
|
-
|
|
6
|
-
export interface LoginTexts {
|
|
7
|
-
title?: string
|
|
8
|
-
emailLabel?: string
|
|
9
|
-
passwordLabel?: string
|
|
10
|
-
forgotPassword?: string
|
|
11
|
-
loginButton?: string
|
|
12
|
-
signupButton?: string
|
|
13
|
-
orText?: string
|
|
14
|
-
githubButton?: string
|
|
15
|
-
googleButton?: string
|
|
16
|
-
microsoftButton?: string
|
|
17
|
-
appleButton?: string
|
|
18
|
-
oktaButton?: string
|
|
19
|
-
facebookButton?: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface Props {
|
|
23
|
-
hideRegularLogin?: boolean
|
|
24
|
-
showForgotPassword?: boolean
|
|
25
|
-
showSignupButton?: boolean
|
|
26
|
-
github?: boolean
|
|
27
|
-
google?: boolean
|
|
28
|
-
microsoft?: boolean
|
|
29
|
-
apple?: boolean
|
|
30
|
-
okta?: boolean
|
|
31
|
-
facebook?: boolean
|
|
32
|
-
ssoOutline?: boolean
|
|
33
|
-
ssoShowValue?: boolean
|
|
34
|
-
ssoSize?: 'xs' | 's' | 'm' | 'l' | 'xl' | 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large'
|
|
35
|
-
ssoAlign?: boolean
|
|
36
|
-
ssoBrandBackground?: boolean
|
|
37
|
-
useHebrewDefaults?: boolean
|
|
38
|
-
errorState?: 'normal' | 'email-required' | 'email-invalid' | 'password-required' | 'invalid-credentials' | 'server-error'
|
|
39
|
-
texts?: Partial<LoginTexts>
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
43
|
-
hideRegularLogin: false,
|
|
44
|
-
showForgotPassword: true,
|
|
45
|
-
showSignupButton: true,
|
|
46
|
-
github: false,
|
|
47
|
-
google: false,
|
|
48
|
-
microsoft: false,
|
|
49
|
-
apple: false,
|
|
50
|
-
okta: false,
|
|
51
|
-
facebook: false,
|
|
52
|
-
ssoOutline: true,
|
|
53
|
-
ssoShowValue: true,
|
|
54
|
-
ssoSize: undefined,
|
|
55
|
-
ssoAlign: false,
|
|
56
|
-
ssoBrandBackground: true,
|
|
57
|
-
useHebrewDefaults: false,
|
|
58
|
-
errorState: 'normal',
|
|
59
|
-
texts: () => ({})
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
defineEmits<{
|
|
63
|
-
switchForm: [form: string]
|
|
64
|
-
}>()
|
|
65
|
-
|
|
66
|
-
const { login, sso } = useAuth()
|
|
67
|
-
const form = ref({
|
|
68
|
-
email: '',
|
|
69
|
-
password: '',
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const internalError = ref<string>('')
|
|
73
|
-
|
|
74
|
-
// Text configuration with conditional defaults
|
|
75
|
-
const texts = computed(() => {
|
|
76
|
-
const hebrewDefaults = {
|
|
77
|
-
title: 'התחברות',
|
|
78
|
-
emailLabel: 'אימייל',
|
|
79
|
-
passwordLabel: 'סיסמה',
|
|
80
|
-
forgotPassword: 'שכחתם סיסמה?',
|
|
81
|
-
loginButton: 'התחברות',
|
|
82
|
-
signupButton: 'יצירת חשבון',
|
|
83
|
-
orText: 'או',
|
|
84
|
-
githubButton: 'התחברות עם GitHub',
|
|
85
|
-
googleButton: 'התחברות עם Google',
|
|
86
|
-
microsoftButton: 'התחברות עם Microsoft',
|
|
87
|
-
appleButton: 'התחברות עם Apple',
|
|
88
|
-
oktaButton: 'התחברות עם Okta',
|
|
89
|
-
facebookButton: 'התחברות עם Facebook'
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const englishDefaults = {
|
|
93
|
-
title: 'Login',
|
|
94
|
-
emailLabel: 'Email',
|
|
95
|
-
passwordLabel: 'Password',
|
|
96
|
-
forgotPassword: 'Forgot Password',
|
|
97
|
-
loginButton: 'Login',
|
|
98
|
-
signupButton: 'Create Account',
|
|
99
|
-
orText: 'or',
|
|
100
|
-
githubButton: 'Continue with GitHub',
|
|
101
|
-
googleButton: 'Continue with Google',
|
|
102
|
-
microsoftButton: 'Continue with Microsoft',
|
|
103
|
-
appleButton: 'Continue with Apple',
|
|
104
|
-
oktaButton: 'Continue with Okta',
|
|
105
|
-
facebookButton: 'Continue with Facebook'
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const defaults = props.useHebrewDefaults ? hebrewDefaults : englishDefaults
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
title: props.texts?.title ?? defaults.title,
|
|
112
|
-
emailLabel: props.texts?.emailLabel ?? defaults.emailLabel,
|
|
113
|
-
passwordLabel: props.texts?.passwordLabel ?? defaults.passwordLabel,
|
|
114
|
-
forgotPassword: props.texts?.forgotPassword ?? defaults.forgotPassword,
|
|
115
|
-
loginButton: props.texts?.loginButton ?? defaults.loginButton,
|
|
116
|
-
signupButton: props.texts?.signupButton ?? defaults.signupButton,
|
|
117
|
-
orText: props.texts?.orText ?? defaults.orText,
|
|
118
|
-
githubButton: props.texts?.githubButton ?? defaults.githubButton,
|
|
119
|
-
googleButton: props.texts?.googleButton ?? defaults.googleButton,
|
|
120
|
-
microsoftButton: props.texts?.microsoftButton ?? defaults.microsoftButton,
|
|
121
|
-
appleButton: props.texts?.appleButton ?? defaults.appleButton,
|
|
122
|
-
oktaButton: props.texts?.oktaButton ?? defaults.oktaButton,
|
|
123
|
-
facebookButton: props.texts?.facebookButton ?? defaults.facebookButton
|
|
124
|
-
}
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
const showAnySso = computed(() => props.github || props.google || props.microsoft
|
|
128
|
-
|| props.apple || props.okta || props.facebook
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
const textDirection = computed(() => ({ 'auth-rtl': props.useHebrewDefaults }))
|
|
132
|
-
|
|
133
|
-
const error = computed(() => getDevError() || internalError.value)
|
|
134
|
-
|
|
135
|
-
function getDevError() {
|
|
136
|
-
switch (props.errorState) {
|
|
137
|
-
case 'email-required': return props.useHebrewDefaults ? 'איימיל נדרש' : 'Email is required'
|
|
138
|
-
case 'email-invalid': return props.useHebrewDefaults ? 'יש להזין כתובת איימיל תקינה' : 'Please enter a valid email address'
|
|
139
|
-
case 'password-required': return props.useHebrewDefaults ? 'סיסמה נדרשת' : 'Password is required'
|
|
140
|
-
case 'invalid-credentials': return props.useHebrewDefaults ? 'איימיל או סיסמה לא נכונים' : 'Invalid email or password'
|
|
141
|
-
case 'server-error': return props.useHebrewDefaults ? 'ההתחברות נכשלה. אנא נסה שוב.' : 'Login failed. Please try again.'
|
|
142
|
-
default: return null
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function handleLogin() {
|
|
147
|
-
// Clear any previous internal errors
|
|
148
|
-
internalError.value = ''
|
|
149
|
-
|
|
150
|
-
// Development error states don't need actual form submission
|
|
151
|
-
if (props.errorState !== 'normal') {
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const { message } = await login(form.value)
|
|
156
|
-
internalError.value = message as string
|
|
157
|
-
}
|
|
158
|
-
</script>
|
|
159
|
-
|
|
160
|
-
<template>
|
|
161
|
-
<form :class="textDirection" @submit.prevent="handleLogin">
|
|
162
|
-
<h1 class="txt20 bold txt-center mb-1">
|
|
163
|
-
{{ texts.title }}
|
|
164
|
-
</h1>
|
|
165
|
-
|
|
166
|
-
<!-- Regular Login Form - Hidden when hideRegularLogin is true -->
|
|
167
|
-
<template v-if="!props.hideRegularLogin">
|
|
168
|
-
<TextInput v-model="form.email" type="email" :label="texts.emailLabel" autocomplete="email" />
|
|
169
|
-
<PasswordInput v-model="form.password" :label="texts.passwordLabel" />
|
|
170
|
-
|
|
171
|
-
<Btn
|
|
172
|
-
v-if="props.showForgotPassword" thin flat class="txt-12 mt-075 underline block"
|
|
173
|
-
:value="texts.forgotPassword" @click="$emit('switchForm', 'forgot-password')"
|
|
174
|
-
/>
|
|
175
|
-
<Btn type="submit" class="w-100 mt-1" :value="texts.loginButton" />
|
|
176
|
-
<Btn
|
|
177
|
-
v-if="props.showSignupButton" outline color="primary" class="w-100 mt-05" :value="texts.signupButton"
|
|
178
|
-
@click="$emit('switchForm', 'signup')"
|
|
179
|
-
/>
|
|
180
|
-
</template>
|
|
181
|
-
|
|
182
|
-
<!-- SSO Section -->
|
|
183
|
-
<div v-if="showAnySso">
|
|
184
|
-
<div v-if="!props.hideRegularLogin" class="flex gap-1 opacity-6">
|
|
185
|
-
<div class="line" />
|
|
186
|
-
<p class="txt-center my-05">
|
|
187
|
-
{{ texts.orText }}
|
|
188
|
-
</p>
|
|
189
|
-
<div class="line" />
|
|
190
|
-
</div>
|
|
191
|
-
|
|
192
|
-
<div
|
|
193
|
-
class="gap-05"
|
|
194
|
-
:class="{ 'grid grid-wrap-1': props.ssoShowValue, 'flex justify-content-center gap-05 flex-wrap': !props.ssoShowValue }"
|
|
195
|
-
>
|
|
196
|
-
<Btn
|
|
197
|
-
v-if="props.github" v-tooltip="!props.ssoShowValue ? texts.githubButton : undefined"
|
|
198
|
-
:outline="props.ssoOutline" :full-width="props.ssoAlign"
|
|
199
|
-
:align-txt="props.ssoAlign ? 'start' : undefined"
|
|
200
|
-
:style="props.ssoOutline ? 'color: #24292F;' : `color: #FFFFFF; ${props.ssoBrandBackground ? 'background: #24292F;' : ''}`"
|
|
201
|
-
icon="github" :class="{ 'px-075': props.ssoAlign ? 'px-075' : '' }"
|
|
202
|
-
:value="props.ssoShowValue ? texts.githubButton : undefined" :size="props.ssoSize"
|
|
203
|
-
@click="sso.github.redirect()"
|
|
204
|
-
/>
|
|
205
|
-
<Btn
|
|
206
|
-
v-if="props.google" v-tooltip="!props.ssoShowValue ? texts.googleButton : undefined"
|
|
207
|
-
:outline="props.ssoOutline" :full-width="props.ssoAlign"
|
|
208
|
-
:align-txt="props.ssoAlign ? 'start' : undefined"
|
|
209
|
-
:style="props.ssoOutline ? 'color: #DB4437;' : `color: #FFFFFF; ${props.ssoBrandBackground ? 'background: #DB4437;' : ''}`"
|
|
210
|
-
icon="google" :class="{ 'px-075': props.ssoAlign ? 'px-075' : '' }"
|
|
211
|
-
:value="props.ssoShowValue ? texts.googleButton : undefined" :size="props.ssoSize"
|
|
212
|
-
@click="sso.google.redirect()"
|
|
213
|
-
/>
|
|
214
|
-
<Btn
|
|
215
|
-
v-if="props.microsoft" v-tooltip="!props.ssoShowValue ? texts.microsoftButton : undefined"
|
|
216
|
-
:outline="props.ssoOutline" :full-width="props.ssoAlign"
|
|
217
|
-
:align-txt="props.ssoAlign ? 'start' : undefined"
|
|
218
|
-
:style="props.ssoOutline ? 'color: #2F2FEE;' : `color: #FFFFFF; ${props.ssoBrandBackground ? 'background: #2F2FEE;' : ''}`"
|
|
219
|
-
icon="microsoft" :class="{ 'px-075': props.ssoAlign ? 'px-075' : '' }"
|
|
220
|
-
:value="props.ssoShowValue ? texts.microsoftButton : undefined" :size="props.ssoSize"
|
|
221
|
-
@click="sso.microsoft.redirect()"
|
|
222
|
-
/>
|
|
223
|
-
<Btn
|
|
224
|
-
v-if="props.apple" v-tooltip="!props.ssoShowValue ? texts.appleButton : undefined"
|
|
225
|
-
:outline="props.ssoOutline" :full-width="props.ssoAlign"
|
|
226
|
-
:align-txt="props.ssoAlign ? 'start' : undefined"
|
|
227
|
-
:style="props.ssoOutline ? 'color: #000000;' : `color: #FFFFFF; ${props.ssoBrandBackground ? 'background: #000000;' : ''}`"
|
|
228
|
-
icon="apple" :class="{ 'px-075': props.ssoAlign ? 'px-075' : '' }"
|
|
229
|
-
:value="props.ssoShowValue ? texts.appleButton : undefined" :size="props.ssoSize"
|
|
230
|
-
@click="sso.apple.redirect()"
|
|
231
|
-
/>
|
|
232
|
-
<Btn
|
|
233
|
-
v-if="props.okta" v-tooltip="!props.ssoShowValue ? texts.oktaButton : undefined"
|
|
234
|
-
:outline="props.ssoOutline" :full-width="props.ssoAlign"
|
|
235
|
-
:align-txt="props.ssoAlign ? 'start' : undefined"
|
|
236
|
-
:style="props.ssoOutline ? 'color: #FFB600;' : `color: #FFFFFF; ${props.ssoBrandBackground ? 'background: #FFB600;' : ''}`"
|
|
237
|
-
icon="sun" :class="{ 'px-075': props.ssoAlign ? 'px-075' : '' }"
|
|
238
|
-
:value="props.ssoShowValue ? texts.oktaButton : undefined" :size="props.ssoSize"
|
|
239
|
-
@click="sso.okta.redirect()"
|
|
240
|
-
/>
|
|
241
|
-
<Btn
|
|
242
|
-
v-if="props.facebook" v-tooltip="!props.ssoShowValue ? texts.facebookButton : undefined"
|
|
243
|
-
:outline="props.ssoOutline" :full-width="props.ssoAlign"
|
|
244
|
-
:align-txt="props.ssoAlign ? 'start' : undefined"
|
|
245
|
-
:style="props.ssoOutline ? 'color: #1877F3;' : `color: #FFFFFF; ${props.ssoBrandBackground ? 'background: #1877F3;' : ''}`"
|
|
246
|
-
icon="facebook" :class="{ 'px-075': props.ssoAlign ? 'px-075' : '' }"
|
|
247
|
-
:value="props.ssoShowValue ? texts.facebookButton : undefined" :size="props.ssoSize"
|
|
248
|
-
@click="sso.facebook.redirect()"
|
|
249
|
-
/>
|
|
250
|
-
</div>
|
|
251
|
-
<div class="h-20px pt-075">
|
|
252
|
-
<p v-if="error" class="txt-center color-red txt-12">
|
|
253
|
-
{{ error }}
|
|
254
|
-
</p>
|
|
255
|
-
</div>
|
|
256
|
-
</div>
|
|
257
|
-
</form>
|
|
258
|
-
</template>
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { useAuth } from '@bagelink/auth'
|
|
3
|
-
import { Btn, Icon, PasswordInput } from '@bagelink/vue'
|
|
4
|
-
import { computed, ref } from 'vue'
|
|
5
|
-
|
|
6
|
-
export interface ResetPasswordTexts {
|
|
7
|
-
title?: string
|
|
8
|
-
invalidLinkTitle?: string
|
|
9
|
-
invalidLinkMessage?: string
|
|
10
|
-
newPasswordLabel?: string
|
|
11
|
-
confirmPasswordLabel?: string
|
|
12
|
-
submitButton?: string
|
|
13
|
-
backToLogin?: string
|
|
14
|
-
passwordMismatchError?: string
|
|
15
|
-
invalidTokenError?: string
|
|
16
|
-
successTitle?: string
|
|
17
|
-
successMessage?: string
|
|
18
|
-
goToLogin?: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface Props {
|
|
22
|
-
token?: string
|
|
23
|
-
showSuccess?: boolean
|
|
24
|
-
useHebrewDefaults?: boolean
|
|
25
|
-
texts?: Partial<ResetPasswordTexts>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
29
|
-
useHebrewDefaults: false
|
|
30
|
-
})
|
|
31
|
-
defineEmits<{
|
|
32
|
-
switchForm: [form: string]
|
|
33
|
-
}>()
|
|
34
|
-
|
|
35
|
-
const { resetPassword } = useAuth()
|
|
36
|
-
|
|
37
|
-
const error = ref<string>('')
|
|
38
|
-
const internalSuccess = ref<boolean>(false)
|
|
39
|
-
const newPassword = ref<string>('')
|
|
40
|
-
const confirmPassword = ref<string>('')
|
|
41
|
-
|
|
42
|
-
const isValidToken = computed(() => !!props.token)
|
|
43
|
-
const isSuccess = computed(() => props.showSuccess || internalSuccess.value)
|
|
44
|
-
|
|
45
|
-
// Text configuration with conditional defaults
|
|
46
|
-
const texts = computed(() => {
|
|
47
|
-
const hebrewDefaults = {
|
|
48
|
-
title: 'איפוס סיסמה',
|
|
49
|
-
invalidLinkTitle: 'קישור לא תקין',
|
|
50
|
-
invalidLinkMessage: 'קישור איפוס הסיסמה לא תקין או פג תוקף',
|
|
51
|
-
newPasswordLabel: 'סיסמה חדשה',
|
|
52
|
-
confirmPasswordLabel: 'אימות סיסמה',
|
|
53
|
-
submitButton: 'איפוס סיסמה',
|
|
54
|
-
backToLogin: 'חזרה להתחברות',
|
|
55
|
-
passwordMismatchError: 'הסיסמאות לא תואמות',
|
|
56
|
-
invalidTokenError: 'אסימון לא תקין',
|
|
57
|
-
successTitle: 'הסיסמה אופסה בהצלחה!',
|
|
58
|
-
successMessage: 'הסיסמה שלכם אופסה. כעת תוכלו להתחברות עם הסיסמה החדשה.',
|
|
59
|
-
goToLogin: 'מעבר להתחברות'
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const englishDefaults = {
|
|
63
|
-
title: 'Reset Password',
|
|
64
|
-
invalidLinkTitle: 'Invalid Link',
|
|
65
|
-
invalidLinkMessage: 'This reset link is invalid or has expired',
|
|
66
|
-
newPasswordLabel: 'New Password',
|
|
67
|
-
confirmPasswordLabel: 'Confirm Password',
|
|
68
|
-
submitButton: 'Reset Password',
|
|
69
|
-
backToLogin: 'Back to Login',
|
|
70
|
-
passwordMismatchError: 'Passwords do not match',
|
|
71
|
-
invalidTokenError: 'Invalid token',
|
|
72
|
-
successTitle: 'Password Reset Successfully!',
|
|
73
|
-
successMessage: 'Your password has been reset. You can now log in with your new password.',
|
|
74
|
-
goToLogin: 'Go to Login'
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const defaults = props.useHebrewDefaults ? hebrewDefaults : englishDefaults
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
title: props.texts?.title ?? defaults.title,
|
|
81
|
-
invalidLinkTitle: props.texts?.invalidLinkTitle ?? defaults.invalidLinkTitle,
|
|
82
|
-
invalidLinkMessage: props.texts?.invalidLinkMessage ?? defaults.invalidLinkMessage,
|
|
83
|
-
newPasswordLabel: props.texts?.newPasswordLabel ?? defaults.newPasswordLabel,
|
|
84
|
-
confirmPasswordLabel: props.texts?.confirmPasswordLabel ?? defaults.confirmPasswordLabel,
|
|
85
|
-
submitButton: props.texts?.submitButton ?? defaults.submitButton,
|
|
86
|
-
backToLogin: props.texts?.backToLogin ?? defaults.backToLogin,
|
|
87
|
-
passwordMismatchError: props.texts?.passwordMismatchError ?? defaults.passwordMismatchError,
|
|
88
|
-
invalidTokenError: props.texts?.invalidTokenError ?? defaults.invalidTokenError,
|
|
89
|
-
successTitle: props.texts?.successTitle ?? defaults.successTitle,
|
|
90
|
-
successMessage: props.texts?.successMessage ?? defaults.successMessage,
|
|
91
|
-
goToLogin: props.texts?.goToLogin ?? defaults.goToLogin
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const textDirection = computed(() => ({ 'auth-rtl': props.useHebrewDefaults }))
|
|
96
|
-
|
|
97
|
-
async function handleResetPassword() {
|
|
98
|
-
if (newPassword.value !== confirmPassword.value) {
|
|
99
|
-
error.value = texts.value.passwordMismatchError
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
if (!props.token) {
|
|
103
|
-
error.value = texts.value.invalidTokenError
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
await resetPassword(props.token, newPassword.value)
|
|
108
|
-
internalSuccess.value = true
|
|
109
|
-
error.value = ''
|
|
110
|
-
} catch (err: unknown) {
|
|
111
|
-
error.value = err instanceof Error ? err.message : 'Reset failed'
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
</script>
|
|
115
|
-
|
|
116
|
-
<template>
|
|
117
|
-
<div v-if="!isValidToken" class="txt-center" :class="textDirection">
|
|
118
|
-
<h1 class="txt20 bold mb-1">
|
|
119
|
-
{{ texts.invalidLinkTitle }}
|
|
120
|
-
</h1>
|
|
121
|
-
<p class="opacity-7 mb-2">
|
|
122
|
-
{{ texts.invalidLinkMessage }}
|
|
123
|
-
</p>
|
|
124
|
-
<Btn :value="texts.backToLogin" @click="$emit('switchForm', 'login')" />
|
|
125
|
-
</div>
|
|
126
|
-
<div v-else-if="isSuccess" class="txt-center" :class="textDirection">
|
|
127
|
-
<Icon name="check_circle" size="4" class="txt-green mb-1" />
|
|
128
|
-
<h1 class="txt20 bold mb-1">
|
|
129
|
-
{{ texts.successTitle }}
|
|
130
|
-
</h1>
|
|
131
|
-
<p class="opacity-7 mb-2">
|
|
132
|
-
{{ texts.successMessage }}
|
|
133
|
-
</p>
|
|
134
|
-
<Btn :value="texts.goToLogin" @click="$emit('switchForm', 'login')" />
|
|
135
|
-
</div>
|
|
136
|
-
<form v-else :class="textDirection" @submit.prevent="handleResetPassword">
|
|
137
|
-
<h1 class="txt20 bold txt-center mb-1">
|
|
138
|
-
{{ texts.title }}
|
|
139
|
-
</h1>
|
|
140
|
-
<PasswordInput
|
|
141
|
-
v-model="newPassword" class="mb-05" :label="texts.newPasswordLabel"
|
|
142
|
-
autocomplete="new-password"
|
|
143
|
-
/>
|
|
144
|
-
<PasswordInput v-model="confirmPassword" :label="texts.confirmPasswordLabel" autocomplete="new-password" />
|
|
145
|
-
<Btn type="submit" class="w-100 mt-2" :value="texts.submitButton" />
|
|
146
|
-
<Btn
|
|
147
|
-
thin flat class="txt-12 mt-075 underline block" :value="texts.backToLogin"
|
|
148
|
-
@click="$emit('switchForm', 'login')"
|
|
149
|
-
/>
|
|
150
|
-
<div class="h-20px pt-075">
|
|
151
|
-
<p v-if="error" class="txt-center color-red txt-12">
|
|
152
|
-
{{ error }}
|
|
153
|
-
</p>
|
|
154
|
-
</div>
|
|
155
|
-
</form>
|
|
156
|
-
</template>
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { useAuth } from '@bagelink/auth'
|
|
3
|
-
import { Btn, PasswordInput, TextInput } from '@bagelink/vue'
|
|
4
|
-
import { computed, ref } from 'vue'
|
|
5
|
-
|
|
6
|
-
export interface SignupTexts {
|
|
7
|
-
title?: string
|
|
8
|
-
firstNameLabel?: string
|
|
9
|
-
lastNameLabel?: string
|
|
10
|
-
emailLabel?: string
|
|
11
|
-
passwordLabel?: string
|
|
12
|
-
confirmPasswordLabel?: string
|
|
13
|
-
signupButton?: string
|
|
14
|
-
alreadyHaveAccount?: string
|
|
15
|
-
// Validation errors
|
|
16
|
-
emailRequiredError?: string
|
|
17
|
-
emailInvalidError?: string
|
|
18
|
-
passwordRequiredError?: string
|
|
19
|
-
passwordTooShortError?: string
|
|
20
|
-
passwordMismatchError?: string
|
|
21
|
-
firstNameRequiredError?: string
|
|
22
|
-
lastNameRequiredError?: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface Props {
|
|
26
|
-
showNames?: boolean
|
|
27
|
-
useHebrewDefaults?: boolean
|
|
28
|
-
errorState?: 'normal' | 'email-required' | 'email-invalid' | 'password-short' | 'password-mismatch' | 'name-required' | 'server-error'
|
|
29
|
-
texts?: Partial<SignupTexts>
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
33
|
-
showNames: true,
|
|
34
|
-
useHebrewDefaults: false,
|
|
35
|
-
errorState: 'normal',
|
|
36
|
-
texts: () => ({})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
defineEmits<{
|
|
40
|
-
switchForm: [form: string]
|
|
41
|
-
}>()
|
|
42
|
-
|
|
43
|
-
const { signup, login } = useAuth()
|
|
44
|
-
|
|
45
|
-
// Internal error for actual validation
|
|
46
|
-
const internalError = ref<string>('')
|
|
47
|
-
|
|
48
|
-
// Use computed for error that reacts to errorState changes
|
|
49
|
-
const error = computed(() => {
|
|
50
|
-
if (props.errorState !== 'normal') {
|
|
51
|
-
return getDevError()
|
|
52
|
-
}
|
|
53
|
-
return internalError.value
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
const form = ref({
|
|
57
|
-
email: '',
|
|
58
|
-
password: '',
|
|
59
|
-
confirmPassword: '',
|
|
60
|
-
first_name: '',
|
|
61
|
-
last_name: '',
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
// Text configuration with conditional defaults
|
|
65
|
-
const texts = computed(() => {
|
|
66
|
-
const hebrewDefaults = {
|
|
67
|
-
title: 'יצירת חשבון',
|
|
68
|
-
firstNameLabel: 'שם פרטי',
|
|
69
|
-
lastNameLabel: 'שם משפחה',
|
|
70
|
-
emailLabel: 'איימיל',
|
|
71
|
-
passwordLabel: 'סיסמה',
|
|
72
|
-
confirmPasswordLabel: 'אימות סיסמה',
|
|
73
|
-
signupButton: 'יצירת חשבון',
|
|
74
|
-
alreadyHaveAccount: 'יש לך כבר חשבון?',
|
|
75
|
-
// Validation errors
|
|
76
|
-
emailRequiredError: 'איימיל נדרש',
|
|
77
|
-
emailInvalidError: 'יש להזין כתובת איימיל תקינה',
|
|
78
|
-
passwordRequiredError: 'סיסמה נדרשת',
|
|
79
|
-
passwordTooShortError: 'הסיסמה חייבת להיות לפחות 8 תווים',
|
|
80
|
-
passwordMismatchError: 'הסיסמאות לא תואמות',
|
|
81
|
-
firstNameRequiredError: 'שם פרטי נדרש',
|
|
82
|
-
lastNameRequiredError: 'שם משפחה נדרש'
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const englishDefaults = {
|
|
86
|
-
title: 'Create Account',
|
|
87
|
-
firstNameLabel: 'First Name',
|
|
88
|
-
lastNameLabel: 'Last Name',
|
|
89
|
-
emailLabel: 'Email',
|
|
90
|
-
passwordLabel: 'Password',
|
|
91
|
-
confirmPasswordLabel: 'Confirm Password',
|
|
92
|
-
signupButton: 'Create Account',
|
|
93
|
-
alreadyHaveAccount: 'Already have an account?',
|
|
94
|
-
// Validation errors
|
|
95
|
-
emailRequiredError: 'Email is required',
|
|
96
|
-
emailInvalidError: 'Please enter a valid email address',
|
|
97
|
-
passwordRequiredError: 'Password is required',
|
|
98
|
-
passwordTooShortError: 'Password must be at least 8 characters',
|
|
99
|
-
passwordMismatchError: 'Passwords do not match',
|
|
100
|
-
firstNameRequiredError: 'First name is required',
|
|
101
|
-
lastNameRequiredError: 'Last name is required'
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const defaults = props.useHebrewDefaults ? hebrewDefaults : englishDefaults
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
title: props.texts.title || defaults.title,
|
|
108
|
-
firstNameLabel: props.texts.firstNameLabel || defaults.firstNameLabel,
|
|
109
|
-
lastNameLabel: props.texts.lastNameLabel || defaults.lastNameLabel,
|
|
110
|
-
emailLabel: props.texts.emailLabel || defaults.emailLabel,
|
|
111
|
-
passwordLabel: props.texts.passwordLabel || defaults.passwordLabel,
|
|
112
|
-
confirmPasswordLabel: props.texts.confirmPasswordLabel || defaults.confirmPasswordLabel,
|
|
113
|
-
signupButton: props.texts.signupButton || defaults.signupButton,
|
|
114
|
-
alreadyHaveAccount: props.texts.alreadyHaveAccount || defaults.alreadyHaveAccount,
|
|
115
|
-
// Validation errors
|
|
116
|
-
emailRequiredError: props.texts.emailRequiredError || defaults.emailRequiredError,
|
|
117
|
-
emailInvalidError: props.texts.emailInvalidError || defaults.emailInvalidError,
|
|
118
|
-
passwordRequiredError: props.texts.passwordRequiredError || defaults.passwordRequiredError,
|
|
119
|
-
passwordTooShortError: props.texts.passwordTooShortError || defaults.passwordTooShortError,
|
|
120
|
-
passwordMismatchError: props.texts.passwordMismatchError || defaults.passwordMismatchError,
|
|
121
|
-
firstNameRequiredError: props.texts.firstNameRequiredError || defaults.firstNameRequiredError,
|
|
122
|
-
lastNameRequiredError: props.texts.lastNameRequiredError || defaults.lastNameRequiredError
|
|
123
|
-
}
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
const textDirection = computed(() => ({ 'auth-rtl': props.useHebrewDefaults }))
|
|
127
|
-
|
|
128
|
-
function getDevError() {
|
|
129
|
-
switch (props.errorState) {
|
|
130
|
-
case 'email-required': return texts.value.emailRequiredError
|
|
131
|
-
case 'email-invalid': return texts.value.emailInvalidError
|
|
132
|
-
case 'password-short': return texts.value.passwordTooShortError
|
|
133
|
-
case 'password-mismatch': return texts.value.passwordMismatchError
|
|
134
|
-
case 'name-required': return props.showNames ? texts.value.firstNameRequiredError : null
|
|
135
|
-
case 'server-error': return props.useHebrewDefaults ? 'כתובת האימייל כבר קיימת או שגיאת שרת' : 'Email already exists or server error'
|
|
136
|
-
default: return null
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function validateForm() {
|
|
141
|
-
// Email validation
|
|
142
|
-
if (!form.value.email.trim()) {
|
|
143
|
-
return texts.value.emailRequiredError
|
|
144
|
-
}
|
|
145
|
-
const emailRegex = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/
|
|
146
|
-
if (!emailRegex.test(form.value.email)) {
|
|
147
|
-
return texts.value.emailInvalidError
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Password validation
|
|
151
|
-
if (!form.value.password) {
|
|
152
|
-
return texts.value.passwordRequiredError
|
|
153
|
-
}
|
|
154
|
-
if (form.value.password.length < 8) {
|
|
155
|
-
return texts.value.passwordTooShortError
|
|
156
|
-
}
|
|
157
|
-
if (form.value.password !== form.value.confirmPassword) {
|
|
158
|
-
return texts.value.passwordMismatchError
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Name validation (only if names are shown)
|
|
162
|
-
if (props.showNames) {
|
|
163
|
-
if (!form.value.first_name.trim()) {
|
|
164
|
-
return texts.value.firstNameRequiredError
|
|
165
|
-
}
|
|
166
|
-
if (!form.value.last_name.trim()) {
|
|
167
|
-
return texts.value.lastNameRequiredError
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return null
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function handleSignup() {
|
|
175
|
-
// Don't process if we're showing a development error
|
|
176
|
-
if (props.errorState !== 'normal') {
|
|
177
|
-
return
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Clear previous errors
|
|
181
|
-
internalError.value = ''
|
|
182
|
-
|
|
183
|
-
// Validate form first
|
|
184
|
-
const validationError = validateForm()
|
|
185
|
-
if (validationError) {
|
|
186
|
-
internalError.value = validationError
|
|
187
|
-
return
|
|
188
|
-
}
|
|
189
|
-
const { message, success } = await signup(form.value).catch((error) => {
|
|
190
|
-
console.error(error)
|
|
191
|
-
// Handle specific server errors
|
|
192
|
-
const errorMessage = error.response?.data?.detail as string
|
|
193
|
-
if (errorMessage?.toLowerCase().includes('email')) {
|
|
194
|
-
return { success: false, message: 'Email already exists or invalid' }
|
|
195
|
-
}
|
|
196
|
-
if (errorMessage?.toLowerCase().includes('password')) {
|
|
197
|
-
return { success: false, message: 'Password does not meet requirements' }
|
|
198
|
-
}
|
|
199
|
-
return { success: false, message: errorMessage || 'Registration failed. Please try again.' }
|
|
200
|
-
})
|
|
201
|
-
if (!success) {
|
|
202
|
-
internalError.value = message as string
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
await login({ email: form.value.email, password: form.value.password })
|
|
206
|
-
}
|
|
207
|
-
</script>
|
|
208
|
-
|
|
209
|
-
<template>
|
|
210
|
-
<form :class="textDirection" @submit.prevent="handleSignup">
|
|
211
|
-
<h1 class="txt20 bold txt-center mb-1">
|
|
212
|
-
{{ texts.title }}
|
|
213
|
-
</h1>
|
|
214
|
-
|
|
215
|
-
<TextInput v-if="props.showNames" v-model="form.first_name" :label="texts.firstNameLabel" />
|
|
216
|
-
<TextInput v-if="props.showNames" v-model="form.last_name" :label="texts.lastNameLabel" />
|
|
217
|
-
<TextInput v-model="form.email" type="email" :label="texts.emailLabel" autocomplete="username" />
|
|
218
|
-
<PasswordInput v-model="form.password" class="mb-05" :label="texts.passwordLabel" />
|
|
219
|
-
<PasswordInput v-model="form.confirmPassword" :label="texts.confirmPasswordLabel" />
|
|
220
|
-
<Btn class="w-100 mt-2" :value="texts.signupButton" type="submit" />
|
|
221
|
-
<Btn
|
|
222
|
-
thin flat class="txt-12 mt-075 underline block" :value="texts.alreadyHaveAccount"
|
|
223
|
-
@click="$emit('switchForm', 'login')"
|
|
224
|
-
/>
|
|
225
|
-
<div class="h-20px pt-075">
|
|
226
|
-
<p v-if="error" class="txt-center color-red txt-12">
|
|
227
|
-
{{ error }}
|
|
228
|
-
</p>
|
|
229
|
-
</div>
|
|
230
|
-
</form>
|
|
231
|
-
</template>
|
package/src/components/index.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// Export form components
|
|
2
|
-
export { default as ForgotPasswordForm } from './auth/ForgotPasswordForm.vue'
|
|
3
|
-
export { default as LoginForm } from './auth/LoginForm.vue'
|
|
4
|
-
export { default as ResetPasswordForm } from './auth/ResetPasswordForm.vue'
|
|
5
|
-
export { default as SignupForm } from './auth/SignupForm.vue'
|