@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.
Files changed (49) hide show
  1. package/README.md +284 -219
  2. package/dist/Callback-BHqVaZZm.cjs +4 -0
  3. package/dist/Callback-C-XghN_z.js +4 -0
  4. package/dist/ForgotPasswordPage-BV9tyhHl.cjs +4 -0
  5. package/dist/ForgotPasswordPage-DvttMGb0.js +4 -0
  6. package/dist/LoginPage-hv1wc54S.cjs +4 -0
  7. package/dist/LoginPage-klj1NV4J.js +4 -0
  8. package/dist/ResetPasswordPage-COPrJmW8.cjs +4 -0
  9. package/dist/ResetPasswordPage-nvQ4uupb.js +4 -0
  10. package/dist/SignupPage-m36w9PLJ.cjs +4 -0
  11. package/dist/SignupPage-oUFYApYW.js +4 -0
  12. package/dist/api.d.ts +115 -0
  13. package/dist/components/auth/ForgotPasswordForm.vue.d.ts +23 -0
  14. package/dist/components/auth/LoginForm.vue.d.ts +58 -0
  15. package/dist/components/auth/ResetPasswordForm.vue.d.ts +28 -0
  16. package/dist/components/auth/SignupForm.vue.d.ts +34 -0
  17. package/dist/components/index.d.ts +4 -0
  18. package/dist/constants.d.ts +34 -0
  19. package/dist/index.cjs +1227 -30
  20. package/dist/index.d.ts +16 -631
  21. package/dist/index.mjs +1242 -24
  22. package/dist/pages/Callback.vue.d.ts +2 -0
  23. package/dist/pages/ForgotPasswordPage.vue.d.ts +13 -0
  24. package/dist/pages/LoginPage.vue.d.ts +38 -0
  25. package/dist/pages/ResetPasswordPage.vue.d.ts +13 -0
  26. package/dist/pages/SignupPage.vue.d.ts +16 -0
  27. package/dist/routes.d.ts +49 -0
  28. package/dist/sso.d.ts +145 -0
  29. package/dist/types/index.d.ts +41 -0
  30. package/dist/types.d.ts +254 -0
  31. package/dist/useAuth.d.ts +112 -0
  32. package/dist/utils.d.ts +11 -0
  33. package/package.json +11 -13
  34. package/src/components/auth/ForgotPasswordForm.vue +97 -0
  35. package/src/components/auth/LoginForm.vue +257 -0
  36. package/src/components/auth/ResetPasswordForm.vue +146 -0
  37. package/src/components/auth/SignupForm.vue +224 -0
  38. package/src/components/index.ts +5 -0
  39. package/src/constants.ts +10 -0
  40. package/src/index.ts +26 -1
  41. package/src/pages/Callback.vue +183 -0
  42. package/src/pages/ForgotPasswordPage.vue +42 -0
  43. package/src/pages/LoginPage.vue +68 -0
  44. package/src/pages/ResetPasswordPage.vue +47 -0
  45. package/src/pages/SignupPage.vue +46 -0
  46. package/src/routes.ts +122 -0
  47. package/src/types/index.ts +45 -0
  48. package/dist/index.d.cts +0 -631
  49. package/dist/index.d.mts +0 -631
@@ -0,0 +1,224 @@
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 thin flat class="txt-12 mt-075 underline block" :value="texts.alreadyHaveAccount" @click="$emit('switchForm', 'login')" />
222
+ <p v-if="error" class="txt-center color-red txt-12 mt-1" v-text="error" />
223
+ </form>
224
+ </template>
@@ -0,0 +1,5 @@
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'
@@ -0,0 +1,10 @@
1
+ export const INTAKE_WORKFLOW_ID = 'fdba1933-0964-4850-b52a-7a4324175790'
2
+ export const DEFAULT_AGENT_ID = 'fdba1933-0964-4850-b52a-7a4324175790'
3
+ export const providers = {
4
+ github: { name: 'GitHub', icon: 'github', color: '#24292F' },
5
+ google: { name: 'Google', icon: 'google', color: '#DB4437' },
6
+ microsoft: { name: 'Microsoft', icon: 'microsoft', color: '#00A4EF' },
7
+ apple: { name: 'Apple', icon: 'apple', color: '#000000' },
8
+ okta: { name: 'Okta', icon: 'sun', color: '#FFB600' },
9
+ facebook: { name: 'Facebook', icon: 'facebook', color: '#1877F3' },
10
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,30 @@
1
+ // Core auth functionality
1
2
  export * from './api'
3
+ // Components
4
+ export { default as ForgotPasswordForm } from './components/auth/ForgotPasswordForm.vue'
5
+ export { default as LoginForm } from './components/auth/LoginForm.vue'
6
+ export { default as ResetPasswordForm } from './components/auth/ResetPasswordForm.vue'
7
+
8
+ export { default as SignupForm } from './components/auth/SignupForm.vue'
9
+ export * from './constants'
10
+ // Page components
11
+ export { default as Callback } from './pages/Callback.vue'
12
+ export { default as ForgotPasswordPage } from './pages/ForgotPasswordPage.vue'
13
+
14
+ export { default as LoginPage } from './pages/LoginPage.vue'
15
+ export { default as ResetPasswordPage } from './pages/ResetPasswordPage.vue'
16
+ export { default as SignupPage } from './pages/SignupPage.vue'
17
+ // Routes
18
+ export * from './routes'
2
19
  export * from './sso'
20
+
21
+ // Types (re-export from types file and module)
3
22
  export * from './types'
4
- export { AuthState } from './types'
23
+
24
+ export type {
25
+ ForgotPasswordTexts,
26
+ LoginTexts,
27
+ ResetPasswordTexts,
28
+ SignupTexts,
29
+ } from './types/'
5
30
  export * from './useAuth'
@@ -0,0 +1,183 @@
1
+ <script setup lang="ts">
2
+ import type { AuthenticationResponse, SSOProvider } from '@bagelink/auth'
3
+ import { useAuth } from '@bagelink/auth'
4
+ import { Btn, Card, Icon, Loading } from '@bagelink/vue'
5
+ import { computed, onMounted, ref } from 'vue'
6
+ import { useRoute, useRouter } from 'vue-router'
7
+
8
+ import { providers } from '../constants'
9
+
10
+ const isLoading = ref(true)
11
+ const error = ref<string | null>(null)
12
+ const success = ref(false)
13
+ const isLinking = ref(false)
14
+ const provider = ref<SSOProvider | null>(null)
15
+ const authResponse = ref<AuthenticationResponse | null>(null)
16
+
17
+ const { sso, user, accountInfo } = useAuth()
18
+ const route = useRoute()
19
+ const router = useRouter()
20
+ const timeout = 4000
21
+ // Get provider info for better UI
22
+ const providerInfo = computed(() => {
23
+ if (provider.value === null) return null
24
+ return providers[provider.value]
25
+ })
26
+
27
+ async function linkCallback() {
28
+ isLinking.value = true
29
+ try {
30
+ await sso.handleLinkCallback()
31
+ success.value = true
32
+ setTimeout(() => router.push('/'), timeout)
33
+ } catch (err: unknown) {
34
+ const errorMessage = err instanceof Error ? err.message : 'Failed to link account'
35
+ error.value = errorMessage
36
+ } finally {
37
+ isLoading.value = false
38
+ }
39
+ }
40
+
41
+ async function handleCallback() {
42
+ const { state } = route.query
43
+ provider.value = sessionStorage.getItem(`oauth_provider:${state}`) as SSOProvider
44
+
45
+ try {
46
+ const response = await sso.handleCallback()
47
+ if (response === null) {
48
+ error.value = `Failed to authenticate with ${providerInfo.value?.name ?? 'provider'}`
49
+ } else {
50
+ authResponse.value = response
51
+ success.value = true
52
+ setTimeout(() => router.push('/'), timeout)
53
+ }
54
+ } catch (err: unknown) {
55
+ const errorMessage = err instanceof Error ? err.message : 'Authentication failed'
56
+ error.value = errorMessage
57
+ } finally {
58
+ isLoading.value = false
59
+ }
60
+ }
61
+
62
+ onMounted(async () => {
63
+ if (user.value) {
64
+ await linkCallback()
65
+ } else {
66
+ await handleCallback()
67
+ }
68
+ })
69
+ </script>
70
+
71
+ <template>
72
+ <div class="flex justify-content-center align-items-center vh-100 px-1">
73
+ <Card class="p-2 txt-center w450px shadow">
74
+ <!-- Loading State -->
75
+ <div v-if="isLoading" class="flex column align-items-center gap-1">
76
+ <Loading />
77
+ <div class="mt-1">
78
+ <h2 class="mb-05 pb-0 mt-0 txt24 m_txt20">
79
+ {{ isLinking ? 'Linking Account' : 'Authenticating' }}
80
+ </h2>
81
+ <p class="opacity-7 txt-14">
82
+ Please wait while we {{ isLinking ? 'link your' : 'complete the' }} {{ providerInfo?.name
83
+ || 'OAuth' }} {{ isLinking ? 'account' : 'authentication' }}.
84
+ </p>
85
+ </div>
86
+ </div>
87
+
88
+ <!-- Success State -->
89
+ <div v-else-if="success && !error" class="flex column align-items-center gap-1">
90
+ <div class="flex justify-content-center align-items-center mb-1">
91
+ <div class="relative">
92
+ <Icon name="check_circle" size="5" class="txt-green line-height-1" />
93
+ <div
94
+ v-if="providerInfo"
95
+ class="absolute flex justify-content-center align-items-center bg-white rounded"
96
+ style="bottom: -8px; right: -8px; width: 40px; height: 40px; border: 3px solid white;"
97
+ >
98
+ <Icon :name="providerInfo.icon" size="1.5" :style="{ color: providerInfo.color }" />
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ <div>
104
+ <h2 class="mb-05 pb-0 mt-0 txt24 m_txt20">
105
+ {{ isLinking ? 'Account Linked!' : 'Welcome Back!' }}
106
+ </h2>
107
+ <p class="opacity-7 txt-14 mb-1">
108
+ {{ isLinking
109
+ ? `Successfully linked your ${providerInfo?.name || 'account'}.`
110
+ : `You've successfully signed in with ${providerInfo?.name || 'your account'}.`
111
+ }}
112
+ </p>
113
+
114
+ <!-- Show user info if available -->
115
+ <div v-if="user || accountInfo" class="bg-gray-light rounded p-1 mt-1">
116
+ <div class="flex column gap-05">
117
+ <div v-if="user?.name" class="flex align-items-center gap-05 justify-content-center">
118
+ <Icon name="person" size="1.2" class="opacity-7" />
119
+ <span class="txt-14 bold">{{ user.name }}</span>
120
+ </div>
121
+ <div v-if="user?.email" class="flex align-items-center gap-05 justify-content-center">
122
+ <Icon name="email" size="1.2" class="opacity-7" />
123
+ <span class="txt-14 opacity-7">{{ user.email }}</span>
124
+ </div>
125
+ <div
126
+ v-if="accountInfo?.authentication_methods"
127
+ class="flex gap-05 justify-content-center mt-05"
128
+ >
129
+ <Icon
130
+ v-for="method in accountInfo.authentication_methods" :key="method.id"
131
+ :name="method.type === 'sso' ? (method.provider || 'link') : 'password'" size="1.2"
132
+ class="opacity-5"
133
+ />
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ <p class="txt-12 opacity-5 mt-1">
139
+ Redirecting you to home...
140
+ </p>
141
+ </div>
142
+ </div>
143
+
144
+ <!-- Error State -->
145
+ <div v-else-if="error" class="flex column align-items-center gap-1">
146
+ <Icon name="error" size="5" class="txt-red mb-1 line-height-1" />
147
+ <div>
148
+ <h2 class="mb-05 pb-0 mt-0 txt24 m_txt20">
149
+ Authentication Failed
150
+ </h2>
151
+ <p class="opacity-7 txt-14 mb-1">
152
+ {{ error }}
153
+ </p>
154
+
155
+ <div class="bg-red-light rounded p-1 mt-1 mb-1">
156
+ <p class="txt-12 opacity-7">
157
+ This could happen if:
158
+ </p>
159
+ <ul class="txt-12 opacity-7 txt-start mt-05" style="list-style: none; padding-left: 0;">
160
+ <li class="mb-025">
161
+ • The authorization was cancelled
162
+ </li>
163
+ <li class="mb-025">
164
+ • The state parameter was invalid
165
+ </li>
166
+ <li class="mb-025">
167
+ • The OAuth provider denied access
168
+ </li>
169
+ <li class="mb-025">
170
+ • Network connectivity issues
171
+ </li>
172
+ </ul>
173
+ </div>
174
+
175
+ <div class="flex gap-05 justify-content-center">
176
+ <Btn outline value="Try Again" to="/login" />
177
+ <Btn value="Go Home" to="/" />
178
+ </div>
179
+ </div>
180
+ </div>
181
+ </Card>
182
+ </div>
183
+ </template>
@@ -0,0 +1,42 @@
1
+ <script setup lang="ts">
2
+ import type { ForgotPasswordTexts } from '../components/auth/ForgotPasswordForm.vue'
3
+ import { Card } from '@bagelink/vue'
4
+ import { useRouter } from 'vue-router'
5
+ import ForgotPasswordForm from '../components/auth/ForgotPasswordForm.vue'
6
+
7
+ interface Props {
8
+ /** Custom texts for the forgot password form */
9
+ texts?: Partial<ForgotPasswordTexts>
10
+ /** Card styling */
11
+ cardWidth?: string
12
+ cardShadow?: boolean
13
+ }
14
+
15
+ const props = withDefaults(defineProps<Props>(), {
16
+ cardWidth: '450px',
17
+ cardShadow: true
18
+ })
19
+
20
+ const router = useRouter()
21
+
22
+ function switchForm(form: string) {
23
+ if (form === 'login') {
24
+ router.push('/login')
25
+ }
26
+ }
27
+ </script>
28
+
29
+ <template>
30
+ <div class="flex justify-content-center align-items-center min-vh-100 px-1 py-2">
31
+ <Card
32
+ :class="{ shadow: cardShadow }"
33
+ :style="{ width: cardWidth, maxWidth: '100%' }"
34
+ class="p-2"
35
+ >
36
+ <ForgotPasswordForm
37
+ :texts="texts"
38
+ @switch-form="switchForm"
39
+ />
40
+ </Card>
41
+ </div>
42
+ </template>
@@ -0,0 +1,68 @@
1
+ <script setup lang="ts">
2
+ import type { LoginTexts } from '../components/auth/LoginForm.vue'
3
+ import { Card } from '@bagelink/vue'
4
+ import { useRouter } from 'vue-router'
5
+ import LoginForm from '../components/auth/LoginForm.vue'
6
+
7
+ interface Props {
8
+ /** Custom texts for the login form */
9
+ texts?: Partial<LoginTexts>
10
+ /** Show/hide specific SSO providers */
11
+ showGithub?: boolean
12
+ showGoogle?: boolean
13
+ showMicrosoft?: boolean
14
+ showApple?: boolean
15
+ showOkta?: boolean
16
+ showFacebook?: boolean
17
+ /** SSO button styling */
18
+ ssoOutline?: boolean
19
+ ssoShowValue?: boolean
20
+ ssoBrandBackground?: boolean
21
+ /** Show/hide form elements */
22
+ showForgotPassword?: boolean
23
+ showSignupButton?: boolean
24
+ /** Card styling */
25
+ cardWidth?: string
26
+ cardShadow?: boolean
27
+ }
28
+
29
+ const props = withDefaults(defineProps<Props>(), {
30
+ showGithub: true,
31
+ showGoogle: true,
32
+ showMicrosoft: true,
33
+ showApple: true,
34
+ showOkta: true,
35
+ showFacebook: true,
36
+ ssoOutline: true,
37
+ ssoShowValue: true,
38
+ ssoBrandBackground: true,
39
+ showForgotPassword: true,
40
+ showSignupButton: true,
41
+ cardWidth: '450px',
42
+ cardShadow: true
43
+ })
44
+
45
+ const router = useRouter()
46
+
47
+ function switchForm(form: string) {
48
+ if (form === 'signup') {
49
+ router.push('/signup')
50
+ } else if (form === 'forgot-password') {
51
+ router.push('/forgot-password')
52
+ }
53
+ }
54
+ </script>
55
+
56
+ <template>
57
+ <div class="flex justify-content-center align-items-center min-vh-100 px-1 py-2">
58
+ <Card :class="{ shadow: cardShadow }" :style="{ width: cardWidth, maxWidth: '100%' }" class="p-2">
59
+ <LoginForm
60
+ :texts="texts" :github="showGithub" :google="showGoogle" :microsoft="showMicrosoft"
61
+ :apple="showApple" :okta="showOkta" :facebook="showFacebook" :sso-outline="ssoOutline"
62
+ :sso-show-value="ssoShowValue" :sso-brand-background="ssoBrandBackground"
63
+ :show-forgot-password="showForgotPassword" :show-signup-button="showSignupButton"
64
+ @switch-form="switchForm"
65
+ />
66
+ </Card>
67
+ </div>
68
+ </template>
@@ -0,0 +1,47 @@
1
+ <script setup lang="ts">
2
+ import type { ResetPasswordTexts } from '../components/auth/ResetPasswordForm.vue'
3
+ import { Card } from '@bagelink/vue'
4
+ import { computed } from 'vue'
5
+ import { useRouter, useRoute } from 'vue-router'
6
+ import ResetPasswordForm from '../components/auth/ResetPasswordForm.vue'
7
+
8
+ interface Props {
9
+ /** Custom texts for the reset password form */
10
+ texts?: Partial<ResetPasswordTexts>
11
+ /** Card styling */
12
+ cardWidth?: string
13
+ cardShadow?: boolean
14
+ }
15
+
16
+ const props = withDefaults(defineProps<Props>(), {
17
+ cardWidth: '450px',
18
+ cardShadow: true
19
+ })
20
+
21
+ const router = useRouter()
22
+ const route = useRoute()
23
+
24
+ const token = computed(() => route.query.token as string)
25
+
26
+ function switchForm(form: string) {
27
+ if (form === 'login') {
28
+ router.push('/login')
29
+ }
30
+ }
31
+ </script>
32
+
33
+ <template>
34
+ <div class="flex justify-content-center align-items-center min-vh-100 px-1 py-2">
35
+ <Card
36
+ :class="{ shadow: cardShadow }"
37
+ :style="{ width: cardWidth, maxWidth: '100%' }"
38
+ class="p-2"
39
+ >
40
+ <ResetPasswordForm
41
+ :texts="texts"
42
+ :token="token"
43
+ @switch-form="switchForm"
44
+ />
45
+ </Card>
46
+ </div>
47
+ </template>
@@ -0,0 +1,46 @@
1
+ <script setup lang="ts">
2
+ import type { SignupTexts } from '../components/auth/SignupForm.vue'
3
+ import { Card } from '@bagelink/vue'
4
+ import { useRouter } from 'vue-router'
5
+ import SignupForm from '../components/auth/SignupForm.vue'
6
+
7
+ interface Props {
8
+ /** Custom texts for the signup form */
9
+ texts?: Partial<SignupTexts>
10
+ /** Show name fields (first name, last name) */
11
+ showNames?: boolean
12
+ /** Card styling */
13
+ cardWidth?: string
14
+ cardShadow?: boolean
15
+ }
16
+
17
+ const props = withDefaults(defineProps<Props>(), {
18
+ showNames: true,
19
+ cardWidth: '450px',
20
+ cardShadow: true
21
+ })
22
+
23
+ const router = useRouter()
24
+
25
+ function switchForm(form: string) {
26
+ if (form === 'login') {
27
+ router.push('/login')
28
+ }
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <div class="flex justify-content-center align-items-center min-vh-100 px-1 py-2">
34
+ <Card
35
+ :class="{ shadow: cardShadow }"
36
+ :style="{ width: cardWidth, maxWidth: '100%' }"
37
+ class="p-2"
38
+ >
39
+ <SignupForm
40
+ :texts="texts"
41
+ :show-names="showNames"
42
+ @switch-form="switchForm"
43
+ />
44
+ </Card>
45
+ </div>
46
+ </template>