@fayz-ai/plugin-auth 0.9.0 → 0.9.1

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 (44) hide show
  1. package/dist/{AmbassadorBubbles.d.ts → components/AmbassadorBubbles.d.ts} +1 -1
  2. package/dist/components/AmbassadorBubbles.d.ts.map +1 -0
  3. package/dist/components/forms.d.ts.map +1 -0
  4. package/dist/index.d.ts +9 -9
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/{runtime.d.ts → lib/runtime.d.ts} +1 -1
  9. package/dist/lib/runtime.d.ts.map +1 -0
  10. package/dist/{pages.d.ts → views/pages.d.ts} +1 -1
  11. package/dist/views/pages.d.ts.map +1 -0
  12. package/dist/views/set-password.d.ts.map +1 -0
  13. package/dist/website/components/AuthModalProvider.d.ts.map +1 -0
  14. package/dist/website/components/PhoneAuthModal.d.ts.map +1 -0
  15. package/dist/website/index.d.ts +5 -5
  16. package/dist/website/index.d.ts.map +1 -1
  17. package/dist/website/index.js +1 -1
  18. package/dist/website/index.js.map +1 -1
  19. package/dist/website/lib/phone.d.ts.map +1 -0
  20. package/package.json +5 -8
  21. package/dist/AmbassadorBubbles.d.ts.map +0 -1
  22. package/dist/forms.d.ts.map +0 -1
  23. package/dist/pages.d.ts.map +0 -1
  24. package/dist/runtime.d.ts.map +0 -1
  25. package/dist/set-password.d.ts.map +0 -1
  26. package/dist/website/AuthModalProvider.d.ts.map +0 -1
  27. package/dist/website/PhoneAuthModal.d.ts.map +0 -1
  28. package/dist/website/phone.d.ts.map +0 -1
  29. package/src/AmbassadorBubbles.tsx +0 -61
  30. package/src/forms.tsx +0 -307
  31. package/src/index.ts +0 -56
  32. package/src/pages.tsx +0 -209
  33. package/src/runtime.ts +0 -67
  34. package/src/set-password.tsx +0 -229
  35. package/src/types.ts +0 -65
  36. package/src/website/AuthModalProvider.tsx +0 -48
  37. package/src/website/PhoneAuthModal.tsx +0 -178
  38. package/src/website/index.tsx +0 -17
  39. package/src/website/phone.ts +0 -12
  40. /package/dist/{forms.d.ts → components/forms.d.ts} +0 -0
  41. /package/dist/{set-password.d.ts → views/set-password.d.ts} +0 -0
  42. /package/dist/website/{AuthModalProvider.d.ts → components/AuthModalProvider.d.ts} +0 -0
  43. /package/dist/website/{PhoneAuthModal.d.ts → components/PhoneAuthModal.d.ts} +0 -0
  44. /package/dist/website/{phone.d.ts → lib/phone.d.ts} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../src/pages.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAIjD,OAAO,KAAK,EAAgB,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAExE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC3B,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;yFACqF;IACrF,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAA;IACpC,mFAAmF;IACnF,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,CAAA;IACjD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,SAAS,CAAC,EACxB,OAAe,EACf,IAAI,EACJ,SAAS,EACT,MAAgB,EAChB,OAAO,EACP,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,SAAS,GACV,EAAE,cAAc,qBAmGhB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B;AAED,wBAAgB,QAAQ,CAAC,EAAE,WAAkB,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,UAAU,EAAE,EAAE,aAAa,qBAevG;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACjC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,qBAAqB,qBA8B7E;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED;gFACgF;AAChF,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,EAAE,sBAAsB,qBAEtE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAQpE,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,iBAAsB,GAAG,WAAW,CA0B/E;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,iBAAsB,GAAG,kBAAkB,CAwBrF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,kBAAkB,CAEpF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"set-password.d.ts","sourceRoot":"","sources":["../src/set-password.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqB9B,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,CAAA;AAuBrD,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAChC,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAA;IAChB,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAA;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CAmBxD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,iBAAiB,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,OAAe,EACf,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,SAAS,GACV,EAAE,oBAAoB,qBA+HtB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"AuthModalProvider.d.ts","sourceRoot":"","sources":["../../src/website/AuthModalProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3E,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAE5E,UAAU,qBAAqB;IAC7B,aAAa,EAAE,MAAM,IAAI,CAAA;IACzB,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,eAAe,EAAE,OAAO,CAAA;CACzB;AAID,MAAM,WAAW,sBAAsB;IACrC,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,sBAAsB,+BAa/F;AAED,wFAAwF;AACxF,wBAAgB,YAAY,IAAI,qBAAqB,CAQpD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PhoneAuthModal.d.ts","sourceRoot":"","sources":["../../src/website/PhoneAuthModal.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EAAE,OAAO,EAAE,MAAM,GACtB,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,oBAAoB,CAAA;CAAE,+BAyJvE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"phone.d.ts","sourceRoot":"","sources":["../../src/website/phone.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAG1D"}
@@ -1,61 +0,0 @@
1
- import * as React from 'react'
2
- import type { LoginAmbassador } from './types'
3
-
4
- /**
5
- * Social-proof "ambassador" strip for the split-login brand panel. Restores
6
- * (modernized) the old login avatar cluster: a short row of small, softly
7
- * overlapping circular portraits sitting just above the tagline, optionally
8
- * followed by a microtext label ("+2 mil profissionais").
9
- *
10
- * Small (40px), subtle, no flashy motion — a very gentle idle float only, which
11
- * fully disables under prefers-reduced-motion. Degrades to nothing when no
12
- * usable images are supplied, so it is always safe to render. Purely
13
- * presentational — never touches the auth flow.
14
- */
15
-
16
- const FLOAT_STYLES = `
17
- @keyframes fayz-amb-drift {
18
- 0%, 100% { transform: translateY(0); }
19
- 50% { transform: translateY(-2px); }
20
- }
21
- @media (prefers-reduced-motion: reduce) {
22
- .fayz-amb-avatar { animation: none !important; }
23
- }
24
- `
25
-
26
- export interface AmbassadorBubblesProps {
27
- ambassadors?: LoginAmbassador[]
28
- /** Optional social-proof caption shown beside the avatars, e.g. "+2 mil profissionais". */
29
- label?: string
30
- }
31
-
32
- export function AmbassadorBubbles({ ambassadors, label }: AmbassadorBubblesProps) {
33
- const items = (ambassadors ?? []).filter((a) => a && typeof a.image === 'string' && a.image.trim() !== '')
34
- if (items.length === 0) return null
35
- const shown = items.slice(0, 5)
36
-
37
- return (
38
- <div className="flex items-center gap-3">
39
- <style>{FLOAT_STYLES}</style>
40
- <div className="flex -space-x-2">
41
- {shown.map((ambassador, index) => (
42
- <div
43
- key={`${ambassador.name}-${index}`}
44
- title={ambassador.role ? `${ambassador.name} · ${ambassador.role}` : ambassador.name}
45
- className="fayz-amb-avatar relative h-10 w-10 shrink-0 overflow-hidden rounded-full border-2 border-sidebar bg-sidebar-accent shadow-md ring-1 ring-sidebar-foreground/10 transition-transform duration-200 hover:z-10 hover:scale-110"
46
- style={{ animation: `fayz-amb-drift 4s ease-in-out ${index * 0.4}s infinite` }}
47
- >
48
- <img
49
- src={ambassador.image}
50
- alt={ambassador.name}
51
- className="h-full w-full object-cover"
52
- loading="lazy"
53
- draggable={false}
54
- />
55
- </div>
56
- ))}
57
- </div>
58
- {label ? <p className="text-xs font-medium text-sidebar-foreground/80">{label}</p> : null}
59
- </div>
60
- )
61
- }
package/src/forms.tsx DELETED
@@ -1,307 +0,0 @@
1
- import * as React from 'react'
2
- import { useAuth } from '@fayz-ai/auth'
3
- import type { AuthProvider } from '@fayz-ai/core'
4
-
5
- function providerLabel(provider: string): string {
6
- return provider.charAt(0).toUpperCase() + provider.slice(1)
7
- }
8
-
9
- function fieldClass() {
10
- return 'h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50'
11
- }
12
-
13
- function buttonClass(variant: 'primary' | 'outline' | 'ghost' = 'primary') {
14
- if (variant === 'outline') {
15
- return 'inline-flex h-10 w-full items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-muted disabled:pointer-events-none disabled:opacity-50'
16
- }
17
- if (variant === 'ghost') return 'text-sm font-medium text-primary hover:underline'
18
- return 'inline-flex h-10 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50'
19
- }
20
-
21
- export interface OAuthButtonsProps {
22
- providers?: Exclude<AuthProvider, 'email'>[]
23
- disabled?: boolean
24
- onProviderClick?: (provider: AuthProvider) => void | Promise<void>
25
- }
26
-
27
- export function OAuthButtons({ providers = ['google'], disabled, onProviderClick }: OAuthButtonsProps) {
28
- const { signInWithOAuth } = useAuth()
29
- return (
30
- <div className="space-y-2">
31
- {providers.map((provider) => (
32
- <button
33
- key={provider}
34
- type="button"
35
- className={buttonClass('outline')}
36
- disabled={disabled}
37
- onClick={() => {
38
- void (onProviderClick ? onProviderClick(provider) : signInWithOAuth(provider))
39
- }}
40
- >
41
- Continue with {providerLabel(provider)}
42
- </button>
43
- ))}
44
- </div>
45
- )
46
- }
47
-
48
- export interface LoginFormProps {
49
- onSuccess?: () => void
50
- onForgotPassword?: () => void
51
- showOAuth?: boolean
52
- oauthProviders?: Exclude<AuthProvider, 'email'>[]
53
- /** Seeds the field, so switching between login and recovery never asks for the
54
- * e-mail the user already typed. */
55
- initialEmail?: string
56
- onEmailChange?: (email: string) => void
57
- }
58
-
59
- export function LoginForm({
60
- onSuccess,
61
- onForgotPassword,
62
- showOAuth,
63
- oauthProviders,
64
- initialEmail = '',
65
- onEmailChange,
66
- }: LoginFormProps) {
67
- const { signIn, error, isLoading } = useAuth()
68
- const [email, setEmail] = React.useState(initialEmail)
69
- const [password, setPassword] = React.useState('')
70
- const [busy, setBusy] = React.useState(false)
71
- const disabled = busy || isLoading
72
-
73
- async function submit(event: React.FormEvent) {
74
- event.preventDefault()
75
- setBusy(true)
76
- try {
77
- await signIn(email, password)
78
- onSuccess?.()
79
- } finally {
80
- setBusy(false)
81
- }
82
- }
83
-
84
- return (
85
- <form onSubmit={submit} className="space-y-4">
86
- <label className="block space-y-1.5 text-sm font-medium">
87
- <span>Email</span>
88
- <input
89
- className={fieldClass()}
90
- type="email"
91
- autoComplete="username"
92
- required
93
- value={email}
94
- onChange={(e) => {
95
- setEmail(e.target.value)
96
- onEmailChange?.(e.target.value)
97
- }}
98
- disabled={disabled}
99
- />
100
- </label>
101
- <label className="block space-y-1.5 text-sm font-medium">
102
- <span>Password</span>
103
- <input className={fieldClass()} type="password" autoComplete="current-password" required value={password} onChange={(e) => setPassword(e.target.value)} disabled={disabled} />
104
- </label>
105
- {onForgotPassword && (
106
- <div className="-mt-1 flex justify-end">
107
- <button type="button" className={buttonClass('ghost')} onClick={onForgotPassword}>
108
- Forgot password?
109
- </button>
110
- </div>
111
- )}
112
- {error && <p className="text-sm text-destructive">{error.message}</p>}
113
- <button className={buttonClass()} type="submit" disabled={disabled}>
114
- {disabled ? 'Signing in...' : 'Sign in'}
115
- </button>
116
- {showOAuth && <OAuthButtons providers={oauthProviders} disabled={disabled} />}
117
- </form>
118
- )
119
- }
120
-
121
- export interface SignupFormProps {
122
- onSuccess?: () => void
123
- }
124
-
125
- export function SignupForm({ onSuccess }: SignupFormProps) {
126
- const { signUp, error, isLoading } = useAuth()
127
- const [fullName, setFullName] = React.useState('')
128
- const [email, setEmail] = React.useState('')
129
- const [password, setPassword] = React.useState('')
130
- const [confirmPassword, setConfirmPassword] = React.useState('')
131
- const [localError, setLocalError] = React.useState<string | null>(null)
132
- const [busy, setBusy] = React.useState(false)
133
- const disabled = busy || isLoading
134
-
135
- async function submit(event: React.FormEvent) {
136
- event.preventDefault()
137
- setLocalError(null)
138
- if (password.length < 8) {
139
- setLocalError('Password must be at least 8 characters.')
140
- return
141
- }
142
- if (password !== confirmPassword) {
143
- setLocalError('Passwords do not match.')
144
- return
145
- }
146
- setBusy(true)
147
- try {
148
- await signUp(email, password, fullName)
149
- onSuccess?.()
150
- } finally {
151
- setBusy(false)
152
- }
153
- }
154
-
155
- return (
156
- <form onSubmit={submit} className="space-y-4">
157
- <label className="block space-y-1.5 text-sm font-medium">
158
- <span>Full name</span>
159
- <input className={fieldClass()} required value={fullName} onChange={(e) => setFullName(e.target.value)} disabled={disabled} />
160
- </label>
161
- <label className="block space-y-1.5 text-sm font-medium">
162
- <span>Email</span>
163
- <input className={fieldClass()} type="email" autoComplete="username" required value={email} onChange={(e) => setEmail(e.target.value)} disabled={disabled} />
164
- </label>
165
- <label className="block space-y-1.5 text-sm font-medium">
166
- <span>Password</span>
167
- <input className={fieldClass()} type="password" autoComplete="new-password" required value={password} onChange={(e) => setPassword(e.target.value)} disabled={disabled} />
168
- </label>
169
- <label className="block space-y-1.5 text-sm font-medium">
170
- <span>Confirm password</span>
171
- <input className={fieldClass()} type="password" autoComplete="new-password" required value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} disabled={disabled} />
172
- </label>
173
- {(localError || error) && <p className="text-sm text-destructive">{localError ?? error?.message}</p>}
174
- <button className={buttonClass()} type="submit" disabled={disabled}>
175
- {disabled ? 'Creating account...' : 'Create account'}
176
- </button>
177
- </form>
178
- )
179
- }
180
-
181
- export interface ForgotPasswordFormProps {
182
- redirectTo?: string
183
- onSent?: (email: string) => void
184
- onBackToLogin?: () => void
185
- /** Pre-filled with whatever the user already typed on the login form. */
186
- initialEmail?: string
187
- onEmailChange?: (email: string) => void
188
- /** Copy overrides — the set-password screen reuses this form to hand out a
189
- * fresh link, where "Send reset link" is the wrong label. */
190
- emailLabel?: string
191
- submitLabel?: string
192
- sendingLabel?: string
193
- sentLabel?: string
194
- backLabel?: string
195
- }
196
-
197
- export function ForgotPasswordForm({
198
- redirectTo,
199
- onSent,
200
- onBackToLogin,
201
- initialEmail = '',
202
- onEmailChange,
203
- emailLabel = 'Email',
204
- submitLabel = 'Send reset link',
205
- sendingLabel = 'Sending...',
206
- sentLabel = 'Password reset instructions sent.',
207
- backLabel = 'Back to login',
208
- }: ForgotPasswordFormProps) {
209
- const { resetPassword, error, isLoading } = useAuth()
210
- const [email, setEmail] = React.useState(initialEmail)
211
- const [sent, setSent] = React.useState(false)
212
- const [busy, setBusy] = React.useState(false)
213
- const disabled = busy || isLoading
214
-
215
- async function submit(event: React.FormEvent) {
216
- event.preventDefault()
217
- setBusy(true)
218
- try {
219
- await resetPassword(email, { redirectTo })
220
- setSent(true)
221
- onSent?.(email)
222
- } finally {
223
- setBusy(false)
224
- }
225
- }
226
-
227
- return (
228
- <form onSubmit={submit} className="space-y-4">
229
- <label className="block space-y-1.5 text-sm font-medium">
230
- <span>{emailLabel}</span>
231
- <input
232
- className={fieldClass()}
233
- type="email"
234
- autoComplete="username"
235
- required
236
- autoFocus={!email}
237
- value={email}
238
- onChange={(e) => {
239
- setEmail(e.target.value)
240
- onEmailChange?.(e.target.value)
241
- }}
242
- disabled={disabled}
243
- />
244
- </label>
245
- {error && <p className="text-sm text-destructive">{error.message}</p>}
246
- {sent && <p className="text-sm text-emerald-600">{sentLabel}</p>}
247
- <button className={buttonClass()} type="submit" disabled={disabled}>
248
- {disabled ? sendingLabel : submitLabel}
249
- </button>
250
- {onBackToLogin && (
251
- <button type="button" className={buttonClass('ghost')} onClick={onBackToLogin}>
252
- {backLabel}
253
- </button>
254
- )}
255
- </form>
256
- )
257
- }
258
-
259
- export interface ResetPasswordFormProps {
260
- onSuccess?: () => void
261
- }
262
-
263
- export function ResetPasswordForm({ onSuccess }: ResetPasswordFormProps) {
264
- const { updatePassword, error, isLoading } = useAuth()
265
- const [password, setPassword] = React.useState('')
266
- const [confirmPassword, setConfirmPassword] = React.useState('')
267
- const [localError, setLocalError] = React.useState<string | null>(null)
268
- const [busy, setBusy] = React.useState(false)
269
- const disabled = busy || isLoading
270
-
271
- async function submit(event: React.FormEvent) {
272
- event.preventDefault()
273
- setLocalError(null)
274
- if (password.length < 8) {
275
- setLocalError('Password must be at least 8 characters.')
276
- return
277
- }
278
- if (password !== confirmPassword) {
279
- setLocalError('Passwords do not match.')
280
- return
281
- }
282
- setBusy(true)
283
- try {
284
- await updatePassword(password)
285
- onSuccess?.()
286
- } finally {
287
- setBusy(false)
288
- }
289
- }
290
-
291
- return (
292
- <form onSubmit={submit} className="space-y-4">
293
- <label className="block space-y-1.5 text-sm font-medium">
294
- <span>New password</span>
295
- <input className={fieldClass()} type="password" autoComplete="new-password" required value={password} onChange={(e) => setPassword(e.target.value)} disabled={disabled} />
296
- </label>
297
- <label className="block space-y-1.5 text-sm font-medium">
298
- <span>Confirm password</span>
299
- <input className={fieldClass()} type="password" autoComplete="new-password" required value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} disabled={disabled} />
300
- </label>
301
- {(localError || error) && <p className="text-sm text-destructive">{localError ?? error?.message}</p>}
302
- <button className={buttonClass()} type="submit" disabled={disabled}>
303
- {disabled ? 'Saving...' : 'Update password'}
304
- </button>
305
- </form>
306
- )
307
- }
package/src/index.ts DELETED
@@ -1,56 +0,0 @@
1
- export {
2
- createAuthPlugin,
3
- createAuthRuntime,
4
- resolveAuthAdapter,
5
- } from './runtime'
6
- export {
7
- AuthGate,
8
- LoginPage,
9
- AuthCallbackPage,
10
- ResetPasswordPage,
11
- } from './pages'
12
- export type {
13
- AuthCallbackPageProps,
14
- AuthGateProps,
15
- LoginPageProps,
16
- ResetPasswordPageProps,
17
- } from './pages'
18
- export { SetPasswordPage, usePasswordSetupGate } from './set-password'
19
- export type { SetPasswordPageProps, SetPasswordReason, PasswordSetupGate } from './set-password'
20
- export { AmbassadorBubbles } from './AmbassadorBubbles'
21
- export type { AmbassadorBubblesProps } from './AmbassadorBubbles'
22
- export {
23
- LoginForm,
24
- SignupForm,
25
- ForgotPasswordForm,
26
- ResetPasswordForm,
27
- OAuthButtons,
28
- } from './forms'
29
- export type {
30
- ForgotPasswordFormProps,
31
- LoginFormProps,
32
- OAuthButtonsProps,
33
- ResetPasswordFormProps,
34
- SignupFormProps,
35
- } from './forms'
36
- export {
37
- createMockAuthAdapter,
38
- createSupabaseAuthAdapter,
39
- AuthProvider,
40
- useAuth,
41
- useAuthStore,
42
- readAuthUrlIntent,
43
- clearAuthUrlTokens,
44
- } from '@fayz-ai/auth'
45
- export type {
46
- AuthPluginOptions,
47
- ResolvedAuthPlugin,
48
- AuthRoutesConfig,
49
- AuthOAuthConfig,
50
- AuthSupabaseConfig,
51
- AuthProviderKey,
52
- AuthLayout,
53
- AuthFormView,
54
- LoginAmbassador,
55
- } from './types'
56
- export type { AuthAdapter, AuthSession, AuthUser, AuthProvider as OAuthProvider } from '@fayz-ai/core'
package/src/pages.tsx DELETED
@@ -1,209 +0,0 @@
1
- import * as React from 'react'
2
- import { useAuth } from '@fayz-ai/auth'
3
- import type { AuthProvider } from '@fayz-ai/core'
4
- import { ForgotPasswordForm, LoginForm, SignupForm } from './forms'
5
- import { SetPasswordPage } from './set-password'
6
- import { AmbassadorBubbles } from './AmbassadorBubbles'
7
- import type { AuthFormView, AuthLayout, LoginAmbassador } from './types'
8
-
9
- export interface LoginPageProps {
10
- appName?: string
11
- logo?: React.ReactNode
12
- loginLogo?: React.ReactNode
13
- layout?: AuthLayout
14
- tagline?: string
15
- description?: string
16
- /** Product ambassadors shown as a small social-proof avatar strip above the
17
- * tagline on the split-layout brand panel. Renders nothing when empty/imageless. */
18
- loginAmbassadors?: LoginAmbassador[]
19
- /** Optional microtext beside the ambassador strip, e.g. "+2 mil profissionais". */
20
- loginAmbassadorsLabel?: string
21
- showOAuth?: boolean
22
- oauthProviders?: Exclude<AuthProvider, 'email'>[]
23
- resetPasswordUrl?: string
24
- onSuccess?: () => void
25
- }
26
-
27
- export function LoginPage({
28
- appName = 'App',
29
- logo,
30
- loginLogo,
31
- layout = 'split',
32
- tagline,
33
- description,
34
- loginAmbassadors,
35
- loginAmbassadorsLabel,
36
- showOAuth,
37
- oauthProviders,
38
- resetPasswordUrl,
39
- onSuccess,
40
- }: LoginPageProps) {
41
- const [view, setView] = React.useState<AuthFormView>('login')
42
- // Lifted out of the forms so switching view carries the e-mail across: someone
43
- // who typed it and then remembered they forgot the password should not have to
44
- // type it again (and back on the login form it is still there).
45
- const [email, setEmail] = React.useState('')
46
- const brandLogo = loginLogo ?? logo
47
- const heading = view === 'login' ? 'Welcome back' : view === 'signup' ? 'Create account' : 'Reset password'
48
- const subtitle =
49
- view === 'login'
50
- ? 'Enter your credentials to continue'
51
- : view === 'signup'
52
- ? 'Create your account to get started'
53
- : 'Enter your email and we will send reset instructions'
54
- const form =
55
- view === 'login' ? (
56
- <LoginForm
57
- onSuccess={onSuccess}
58
- onForgotPassword={() => setView('recovery')}
59
- showOAuth={showOAuth}
60
- oauthProviders={oauthProviders}
61
- initialEmail={email}
62
- onEmailChange={setEmail}
63
- />
64
- ) : view === 'signup' ? (
65
- <SignupForm onSuccess={onSuccess} />
66
- ) : (
67
- <ForgotPasswordForm
68
- redirectTo={resetPasswordUrl}
69
- onBackToLogin={() => setView('login')}
70
- initialEmail={email}
71
- onEmailChange={setEmail}
72
- />
73
- )
74
- const switcher =
75
- view === 'login' ? (
76
- <p className="text-center text-sm text-muted-foreground">
77
- Do not have an account?{' '}
78
- <button type="button" className="font-medium text-primary hover:underline" onClick={() => setView('signup')}>
79
- Sign up
80
- </button>
81
- </p>
82
- ) : (
83
- <p className="text-center text-sm text-muted-foreground">
84
- {view === 'signup' ? 'Already have an account?' : 'Remembered your password?'}{' '}
85
- <button type="button" className="font-medium text-primary hover:underline" onClick={() => setView('login')}>
86
- Sign in
87
- </button>
88
- </p>
89
- )
90
-
91
- if (layout === 'centered') {
92
- return (
93
- <div className="flex min-h-screen items-center justify-center bg-sidebar p-4">
94
- <div className="w-full max-w-md rounded-2xl border border-border/50 bg-card p-8 shadow-2xl sm:p-10">
95
- <div className="mb-8 text-center">
96
- {brandLogo && <div className="mb-4 flex justify-center">{brandLogo}</div>}
97
- <h1 className="text-2xl font-bold text-foreground">{heading}</h1>
98
- <p className="mt-1 text-sm text-muted-foreground">{subtitle}</p>
99
- </div>
100
- <div className="space-y-6">
101
- {form}
102
- {switcher}
103
- </div>
104
- </div>
105
- </div>
106
- )
107
- }
108
-
109
- return (
110
- <div className="flex min-h-screen bg-sidebar">
111
- <div className="relative hidden w-1/2 overflow-hidden lg:block">
112
- <div className="absolute inset-0 bg-gradient-to-br from-sidebar-accent/50 via-transparent to-sidebar/80" />
113
- <div className="relative z-20 flex h-full flex-col justify-end p-12">
114
- <div className="space-y-4">
115
- <AmbassadorBubbles ambassadors={loginAmbassadors} label={loginAmbassadorsLabel} />
116
- <p className="text-sm font-medium text-sidebar-muted">{appName}</p>
117
- <h2 className="text-3xl font-bold text-sidebar-foreground">{tagline ?? `Welcome to ${appName}`}</h2>
118
- {description && <p className="max-w-md text-sm leading-relaxed text-sidebar-muted">{description}</p>}
119
- </div>
120
- </div>
121
- </div>
122
- <div className="flex flex-1 flex-col lg:p-3 lg:pl-3">
123
- <div className="flex flex-1 flex-col justify-center bg-background px-6 py-12 lg:rounded-[1.25rem] lg:px-16">
124
- <div className="mx-auto w-full max-w-sm">
125
- {brandLogo && <div className="mb-8">{brandLogo}</div>}
126
- <div className="mb-8">
127
- <h1 className="text-2xl font-bold text-foreground">{heading}</h1>
128
- <p className="mt-1 text-sm text-muted-foreground">{subtitle}</p>
129
- </div>
130
- <div className="space-y-6">
131
- {form}
132
- {switcher}
133
- </div>
134
- </div>
135
- </div>
136
- </div>
137
- </div>
138
- )
139
- }
140
-
141
- export interface AuthGateProps extends LoginPageProps {
142
- requireAuth?: boolean
143
- loadingFallback?: React.ReactNode
144
- children: React.ReactNode
145
- }
146
-
147
- export function AuthGate({ requireAuth = true, loadingFallback, children, ...loginProps }: AuthGateProps) {
148
- const { isAuthenticated, isLoading } = useAuth()
149
- if (!requireAuth || isAuthenticated) return <>{children}</>
150
- if (isLoading) {
151
- return (
152
- <>
153
- {loadingFallback ?? (
154
- <div className="flex min-h-screen items-center justify-center bg-background">
155
- <span className="text-sm text-muted-foreground">Loading...</span>
156
- </div>
157
- )}
158
- </>
159
- )
160
- }
161
- return <LoginPage {...loginProps} />
162
- }
163
-
164
- export interface AuthCallbackPageProps {
165
- onSuccess?: () => void
166
- onError?: (error: Error) => void
167
- }
168
-
169
- export function AuthCallbackPage({ onSuccess, onError }: AuthCallbackPageProps) {
170
- const { handleCallback } = useAuth()
171
- const [error, setError] = React.useState<string | null>(null)
172
-
173
- React.useEffect(() => {
174
- let cancelled = false
175
- handleCallback(typeof window === 'undefined' ? undefined : window.location.href)
176
- .then(() => {
177
- if (!cancelled) onSuccess?.()
178
- })
179
- .catch((err: unknown) => {
180
- const errorObject = err instanceof Error ? err : new Error(String(err))
181
- if (!cancelled) {
182
- setError(errorObject.message)
183
- onError?.(errorObject)
184
- }
185
- })
186
- return () => {
187
- cancelled = true
188
- }
189
- }, [handleCallback, onError, onSuccess])
190
-
191
- return (
192
- <div className="flex min-h-screen items-center justify-center bg-background p-6 text-center">
193
- <div>
194
- <h1 className="text-xl font-semibold text-foreground">{error ? 'Authentication failed' : 'Completing sign in...'}</h1>
195
- {error && <p className="mt-2 text-sm text-destructive">{error}</p>}
196
- </div>
197
- </div>
198
- )
199
- }
200
-
201
- export interface ResetPasswordPageProps {
202
- onSuccess?: () => void
203
- }
204
-
205
- /** Kept for apps that route `/auth/reset-password` themselves. The shell no
206
- * longer needs a route: SetPasswordPage is mounted by usePasswordSetupGate. */
207
- export function ResetPasswordPage({ onSuccess }: ResetPasswordPageProps) {
208
- return <SetPasswordPage reason="recovery" onSuccess={onSuccess} />
209
- }