@bagelink/auth 1.6.53 → 1.6.61
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/README.md +284 -219
- package/dist/Callback-BHqVaZZm.cjs +4 -0
- package/dist/Callback-C-XghN_z.js +4 -0
- package/dist/ForgotPasswordPage-BV9tyhHl.cjs +4 -0
- package/dist/ForgotPasswordPage-DvttMGb0.js +4 -0
- package/dist/LoginPage-hv1wc54S.cjs +4 -0
- package/dist/LoginPage-klj1NV4J.js +4 -0
- package/dist/ResetPasswordPage-COPrJmW8.cjs +4 -0
- package/dist/ResetPasswordPage-nvQ4uupb.js +4 -0
- package/dist/SignupPage-m36w9PLJ.cjs +4 -0
- package/dist/SignupPage-oUFYApYW.js +4 -0
- package/dist/api.d.ts +115 -0
- package/dist/components/auth/ForgotPasswordForm.vue.d.ts +23 -0
- package/dist/components/auth/LoginForm.vue.d.ts +58 -0
- package/dist/components/auth/ResetPasswordForm.vue.d.ts +28 -0
- package/dist/components/auth/SignupForm.vue.d.ts +34 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/constants.d.ts +34 -0
- package/dist/index.cjs +1227 -30
- package/dist/index.d.ts +16 -631
- package/dist/index.mjs +1242 -24
- package/dist/pages/Callback.vue.d.ts +2 -0
- package/dist/pages/ForgotPasswordPage.vue.d.ts +13 -0
- package/dist/pages/LoginPage.vue.d.ts +38 -0
- package/dist/pages/ResetPasswordPage.vue.d.ts +13 -0
- package/dist/pages/SignupPage.vue.d.ts +16 -0
- package/dist/routes.d.ts +49 -0
- package/dist/sso.d.ts +145 -0
- package/dist/types/index.d.ts +41 -0
- package/dist/types.d.ts +254 -0
- package/dist/useAuth.d.ts +112 -0
- package/dist/utils.d.ts +11 -0
- package/package.json +11 -13
- package/src/components/auth/ForgotPasswordForm.vue +97 -0
- package/src/components/auth/LoginForm.vue +257 -0
- package/src/components/auth/ResetPasswordForm.vue +146 -0
- package/src/components/auth/SignupForm.vue +224 -0
- package/src/components/index.ts +5 -0
- package/src/constants.ts +10 -0
- package/src/index.ts +26 -1
- package/src/pages/Callback.vue +183 -0
- package/src/pages/ForgotPasswordPage.vue +42 -0
- package/src/pages/LoginPage.vue +68 -0
- package/src/pages/ResetPasswordPage.vue +47 -0
- package/src/pages/SignupPage.vue +46 -0
- package/src/routes.ts +122 -0
- package/src/types/index.ts +45 -0
- package/dist/index.d.cts +0 -631
- package/dist/index.d.mts +0 -631
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
import { AuthEventMap, AuthState } from './types';
|
|
3
|
+
export declare function createAxiosInstance(baseURL: string): AxiosInstance;
|
|
4
|
+
export declare class EventEmitter {
|
|
5
|
+
private listeners;
|
|
6
|
+
on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
|
|
7
|
+
off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
|
|
8
|
+
emit<K extends AuthState>(event: K): void;
|
|
9
|
+
removeAllListeners<K extends AuthState>(event?: K): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function queryParams(): Record<string, string>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bagelink/auth",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.61",
|
|
5
5
|
"description": "Bagelink auth package",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Bagel Studio",
|
|
@@ -40,23 +40,21 @@
|
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^24.0.0",
|
|
43
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
43
44
|
"typescript": "^5.8.3",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
45
|
+
"vite": "^5.0.0",
|
|
46
|
+
"vite-plugin-dts": "^4.0.0",
|
|
47
|
+
"vite-tsconfig-paths": "^5.0.0",
|
|
48
|
+
"vue": "^3.5.16",
|
|
49
|
+
"vue-router": "^4.6.3",
|
|
50
|
+
"vue-tsc": "^2.0.0"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
53
|
"vue": "^3.0.0"
|
|
53
54
|
},
|
|
54
55
|
"scripts": {
|
|
55
|
-
"dev": "
|
|
56
|
-
"build": "
|
|
57
|
-
"
|
|
58
|
-
"watch": "tsx watch src/index.ts",
|
|
59
|
-
"lint": "eslint . --ext .ts",
|
|
60
|
-
"clean": "rimraf dist"
|
|
56
|
+
"dev": "vite build --watch",
|
|
57
|
+
"build": "vite build",
|
|
58
|
+
"typecheck": "vue-tsc --noEmit"
|
|
61
59
|
}
|
|
62
60
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { useAuth } from '@bagelink/auth'
|
|
3
|
+
import { Btn, Icon, TextInput } from '@bagelink/vue'
|
|
4
|
+
import { computed, ref } from 'vue'
|
|
5
|
+
|
|
6
|
+
export interface ForgotPasswordTexts {
|
|
7
|
+
title?: string
|
|
8
|
+
emailLabel?: string
|
|
9
|
+
submitButton?: string
|
|
10
|
+
backToLogin?: string
|
|
11
|
+
emailSentTitle?: string
|
|
12
|
+
emailSentMessage?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
emailSent?: boolean
|
|
17
|
+
useHebrewDefaults?: boolean
|
|
18
|
+
texts?: Partial<ForgotPasswordTexts>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
22
|
+
emailSent: false,
|
|
23
|
+
useHebrewDefaults: false,
|
|
24
|
+
texts: () => ({})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
defineEmits<{
|
|
28
|
+
switchForm: [form: string]
|
|
29
|
+
}>()
|
|
30
|
+
|
|
31
|
+
const { forgotPassword } = useAuth()
|
|
32
|
+
|
|
33
|
+
const email = ref<string>('')
|
|
34
|
+
const internalEmailSent = ref(false)
|
|
35
|
+
|
|
36
|
+
const showEmailSent = computed(() => props.emailSent || internalEmailSent.value)
|
|
37
|
+
|
|
38
|
+
// Text configuration with conditional defaults
|
|
39
|
+
const texts = computed(() => {
|
|
40
|
+
const hebrewDefaults = {
|
|
41
|
+
title: 'שכחתם סיסמה',
|
|
42
|
+
emailLabel: 'איימיל',
|
|
43
|
+
submitButton: 'שליחת איימיל איפוס',
|
|
44
|
+
backToLogin: 'חזרה להתחברות',
|
|
45
|
+
emailSentTitle: 'איימיל נשלח!',
|
|
46
|
+
emailSentMessage: 'יש לבדוק את תיבת הדואר שלכם לקישור איפוס הסיסמה'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const englishDefaults = {
|
|
50
|
+
title: 'Forgot Password',
|
|
51
|
+
emailLabel: 'Email',
|
|
52
|
+
submitButton: 'Send Reset Email',
|
|
53
|
+
backToLogin: 'Back to Login',
|
|
54
|
+
emailSentTitle: 'Email Sent!',
|
|
55
|
+
emailSentMessage: 'Check your inbox for a password reset link'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const defaults = props.useHebrewDefaults ? hebrewDefaults : englishDefaults
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
title: props.texts?.title ?? defaults.title,
|
|
62
|
+
emailLabel: props.texts?.emailLabel ?? defaults.emailLabel,
|
|
63
|
+
submitButton: props.texts?.submitButton ?? defaults.submitButton,
|
|
64
|
+
backToLogin: props.texts?.backToLogin ?? defaults.backToLogin,
|
|
65
|
+
emailSentTitle: props.texts?.emailSentTitle ?? defaults.emailSentTitle,
|
|
66
|
+
emailSentMessage: props.texts?.emailSentMessage ?? defaults.emailSentMessage
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const textDirection = computed(() => ({ 'auth-rtl': props.useHebrewDefaults }))
|
|
71
|
+
|
|
72
|
+
async function handleRecoverPassword() {
|
|
73
|
+
await forgotPassword(email.value)
|
|
74
|
+
internalEmailSent.value = true
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<template>
|
|
79
|
+
<div v-if="showEmailSent" class="txt-center" :class="[textDirection]">
|
|
80
|
+
<Icon name="email" size="4" class="opacity-3 mb-1" />
|
|
81
|
+
<h1 class="txt20 bold mb-1">
|
|
82
|
+
{{ texts.emailSentTitle }}
|
|
83
|
+
</h1>
|
|
84
|
+
<p class="txt-center opacity-7">
|
|
85
|
+
{{ texts.emailSentMessage }}
|
|
86
|
+
</p>
|
|
87
|
+
<Btn thin flat class="txt-12 mt-2 underline" :value="texts.backToLogin" @click="$emit('switchForm', 'login')" />
|
|
88
|
+
</div>
|
|
89
|
+
<form v-else :class="textDirection" @submit.prevent="handleRecoverPassword">
|
|
90
|
+
<h1 class="txt20 bold txt-center mb-1">
|
|
91
|
+
{{ texts.title }}
|
|
92
|
+
</h1>
|
|
93
|
+
<TextInput v-model="email" :label="texts.emailLabel" autocomplete="email" type="email" />
|
|
94
|
+
<Btn type="submit" class="w-100 mt-05" :value="texts.submitButton" />
|
|
95
|
+
<Btn thin flat class="txt-12 mt-075 underline block" :value="texts.backToLogin" @click="$emit('switchForm', 'login')" />
|
|
96
|
+
</form>
|
|
97
|
+
</template>
|
|
@@ -0,0 +1,257 @@
|
|
|
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: true,
|
|
47
|
+
google: true,
|
|
48
|
+
microsoft: true,
|
|
49
|
+
apple: true,
|
|
50
|
+
okta: true,
|
|
51
|
+
facebook: true,
|
|
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 In',
|
|
94
|
+
emailLabel: 'Email',
|
|
95
|
+
passwordLabel: 'Password',
|
|
96
|
+
forgotPassword: 'Forgot Password?',
|
|
97
|
+
loginButton: 'Login In',
|
|
98
|
+
signupButton: 'Create Account',
|
|
99
|
+
orText: 'or',
|
|
100
|
+
githubButton: 'Login with GitHub',
|
|
101
|
+
googleButton: 'Login with Google',
|
|
102
|
+
microsoftButton: 'Login with Microsoft',
|
|
103
|
+
appleButton: 'Login with Apple',
|
|
104
|
+
oktaButton: 'Login with Okta',
|
|
105
|
+
facebookButton: 'Login 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>
|
|
252
|
+
|
|
253
|
+
<p v-if="error" class="txt-center color-red txt-12 mt-1">
|
|
254
|
+
{{ error }}
|
|
255
|
+
</p>
|
|
256
|
+
</form>
|
|
257
|
+
</template>
|
|
@@ -0,0 +1,146 @@
|
|
|
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 v-model="newPassword" class="mb-05" :label="texts.newPasswordLabel" autocomplete="new-password" />
|
|
141
|
+
<PasswordInput v-model="confirmPassword" :label="texts.confirmPasswordLabel" autocomplete="new-password" />
|
|
142
|
+
<Btn type="submit" class="w-100 mt-2" :value="texts.submitButton" />
|
|
143
|
+
<Btn thin flat class="txt-12 mt-075 underline block" :value="texts.backToLogin" @click="$emit('switchForm', 'login')" />
|
|
144
|
+
<p v-if="error" class="txt-center color-red txt-12 mt-1" v-text="error" />
|
|
145
|
+
</form>
|
|
146
|
+
</template>
|