@fayz-ai/plugin-auth 0.9.0 → 0.10.0

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 (46) 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/{forms.d.ts → components/forms.d.ts} +12 -2
  4. package/dist/components/forms.d.ts.map +1 -0
  5. package/dist/index.d.ts +9 -9
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +42 -19
  8. package/dist/index.js.map +1 -1
  9. package/dist/{runtime.d.ts → lib/runtime.d.ts} +1 -1
  10. package/dist/lib/runtime.d.ts.map +1 -0
  11. package/dist/types.d.ts +6 -0
  12. package/dist/types.d.ts.map +1 -1
  13. package/dist/{pages.d.ts → views/pages.d.ts} +4 -2
  14. package/dist/views/pages.d.ts.map +1 -0
  15. package/dist/views/set-password.d.ts.map +1 -0
  16. package/dist/website/components/AuthModalProvider.d.ts.map +1 -0
  17. package/dist/website/components/PhoneAuthModal.d.ts.map +1 -0
  18. package/dist/website/index.d.ts +5 -5
  19. package/dist/website/index.d.ts.map +1 -1
  20. package/dist/website/index.js +1 -1
  21. package/dist/website/index.js.map +1 -1
  22. package/dist/website/lib/phone.d.ts.map +1 -0
  23. package/package.json +5 -8
  24. package/dist/AmbassadorBubbles.d.ts.map +0 -1
  25. package/dist/forms.d.ts.map +0 -1
  26. package/dist/pages.d.ts.map +0 -1
  27. package/dist/runtime.d.ts.map +0 -1
  28. package/dist/set-password.d.ts.map +0 -1
  29. package/dist/website/AuthModalProvider.d.ts.map +0 -1
  30. package/dist/website/PhoneAuthModal.d.ts.map +0 -1
  31. package/dist/website/phone.d.ts.map +0 -1
  32. package/src/AmbassadorBubbles.tsx +0 -61
  33. package/src/forms.tsx +0 -307
  34. package/src/index.ts +0 -56
  35. package/src/pages.tsx +0 -209
  36. package/src/runtime.ts +0 -67
  37. package/src/set-password.tsx +0 -229
  38. package/src/types.ts +0 -65
  39. package/src/website/AuthModalProvider.tsx +0 -48
  40. package/src/website/PhoneAuthModal.tsx +0 -178
  41. package/src/website/index.tsx +0 -17
  42. package/src/website/phone.ts +0 -12
  43. /package/dist/{set-password.d.ts → views/set-password.d.ts} +0 -0
  44. /package/dist/website/{AuthModalProvider.d.ts → components/AuthModalProvider.d.ts} +0 -0
  45. /package/dist/website/{PhoneAuthModal.d.ts → components/PhoneAuthModal.d.ts} +0 -0
  46. /package/dist/website/{phone.d.ts → lib/phone.d.ts} +0 -0
@@ -1,178 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
- import * as Dialog from '@radix-ui/react-dialog'
3
- import { X, Smartphone, ArrowLeft } from 'lucide-react'
4
- import { COUNTRIES, DEFAULT_COUNTRY, getCountry, maskPhone, unmaskPhone } from '@fayz-ai/core'
5
- import { useAuth } from '@fayz-ai/auth'
6
- import { phoneToEmail } from './phone'
7
-
8
- export interface PhoneAuthModalConfig {
9
- /** Business/brand name shown in the copy. */
10
- brand?: string
11
- title?: string
12
- }
13
-
14
- type Mode = 'signin' | 'signup'
15
-
16
- /**
17
- * Default phone sign-in / sign-up modal for dogfood Fayz sites. Built on Radix
18
- * Dialog (backdrop, focus-trap, Esc, scroll-lock, enter/exit animation via
19
- * tailwindcss-animate) — no reinvented modal chrome. Entrar / Criar conta
20
- * toggle, phone → WhatsApp code (POC bypass `0000`) → signs the user in via the
21
- * mounted auth adapter (phone = account). Token-only; render inside <AuthProvider>.
22
- */
23
- export function PhoneAuthModal({
24
- open, onClose, config,
25
- }: { open: boolean; onClose: () => void; config?: PhoneAuthModalConfig }) {
26
- const { signUp } = useAuth()
27
-
28
- const [mode, setMode] = useState<Mode>('signup')
29
- const [step, setStep] = useState<'phone' | 'code'>('phone')
30
- const [countryIso, setCountryIso] = useState(DEFAULT_COUNTRY)
31
- const country = getCountry(countryIso)
32
- const [phone, setPhone] = useState('')
33
- const [name, setName] = useState('')
34
- const [code, setCode] = useState('')
35
- const [codeError, setCodeError] = useState(false)
36
- const [busy, setBusy] = useState(false)
37
-
38
- // Fresh flow each time it opens.
39
- useEffect(() => {
40
- if (open) { setStep('phone'); setCode(''); setCodeError(false); setBusy(false) }
41
- }, [open])
42
-
43
- function sendCode() {
44
- if (unmaskPhone(phone).length < 8) return
45
- setCode(''); setCodeError(false); setStep('code')
46
- }
47
-
48
- async function confirmCode() {
49
- if (code.trim() !== '0000') { setCodeError(true); return }
50
- setBusy(true)
51
- try {
52
- await signUp(phoneToEmail(`${country.dial} ${phone}`), unmaskPhone(phone), name.trim() || 'Cliente')
53
- onClose()
54
- } catch {
55
- setCodeError(true)
56
- } finally {
57
- setBusy(false)
58
- }
59
- }
60
-
61
- const brandLabel = config?.brand ? ` na ${config.brand}` : ''
62
-
63
- return (
64
- <Dialog.Root open={open} onOpenChange={(o) => { if (!o) onClose() }}>
65
- <Dialog.Portal>
66
- <Dialog.Overlay className="fixed inset-0 z-[100] bg-foreground/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0" />
67
- <Dialog.Content
68
- onOpenAutoFocus={(e) => e.preventDefault()}
69
- className="fixed left-1/2 top-1/2 z-[101] w-[calc(100%-2rem)] max-w-[26rem] -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-border bg-card p-6 shadow-2xl duration-200 focus:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[state=open]:slide-in-from-bottom-2 motion-reduce:animate-none"
70
- >
71
- {/* Header */}
72
- <div className="flex items-center gap-3">
73
- <span className="flex h-10 w-10 items-center justify-center rounded-xl bg-accent text-primary">
74
- <Smartphone className="h-5 w-5" />
75
- </span>
76
- <div className="min-w-0">
77
- <Dialog.Title className="text-base font-semibold text-foreground">
78
- {config?.title ?? 'Acessar sua conta'}
79
- </Dialog.Title>
80
- <Dialog.Description className="text-xs text-muted-foreground">
81
- {mode === 'signup' ? `Crie sua conta${brandLabel} em segundos` : `Bem-vindo de volta${brandLabel}`}
82
- </Dialog.Description>
83
- </div>
84
- </div>
85
-
86
- <Dialog.Close
87
- aria-label="Fechar"
88
- className="absolute right-4 top-4 flex h-7 w-7 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
89
- >
90
- <X className="h-4 w-4" />
91
- </Dialog.Close>
92
-
93
- {/* Mode toggle (only on the phone step) */}
94
- {step === 'phone' ? (
95
- <div className="mt-5 grid grid-cols-2 gap-1 rounded-lg bg-muted p-1 text-sm">
96
- {(['signin', 'signup'] as Mode[]).map((m) => (
97
- <button
98
- key={m}
99
- type="button"
100
- onClick={() => setMode(m)}
101
- className={`rounded-md py-1.5 font-medium transition-colors ${mode === m ? 'bg-card text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'}`}
102
- >
103
- {m === 'signin' ? 'Entrar' : 'Criar conta'}
104
- </button>
105
- ))}
106
- </div>
107
- ) : null}
108
-
109
- {step === 'phone' ? (
110
- <form onSubmit={(e) => { e.preventDefault(); sendCode() }} className="mt-4 space-y-3">
111
- {mode === 'signup' ? (
112
- <div>
113
- <label htmlFor="auth-name" className="mb-1 block text-sm font-medium text-foreground">Nome</label>
114
- <input
115
- id="auth-name" type="text" autoComplete="name" value={name}
116
- onChange={(e) => setName(e.target.value)}
117
- className="w-full rounded-xl border border-border bg-background px-3.5 py-2.5 text-foreground outline-none transition-colors focus:border-primary focus-visible:ring-2 focus-visible:ring-ring"
118
- placeholder="Seu nome"
119
- />
120
- </div>
121
- ) : null}
122
- <div>
123
- <label htmlFor="auth-phone" className="mb-1 block text-sm font-medium text-foreground">Celular</label>
124
- <div className="flex">
125
- <select
126
- aria-label="País"
127
- value={countryIso}
128
- onChange={(e) => { const c = getCountry(e.target.value); setCountryIso(c.iso2); setPhone(maskPhone(unmaskPhone(phone), c.mask)) }}
129
- className="appearance-none rounded-l-xl border border-r-0 border-border bg-muted py-2.5 pl-3 pr-6 text-sm font-medium text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring"
130
- >
131
- {COUNTRIES.map((c) => <option key={c.iso2} value={c.iso2}>{c.flag} {c.dial}</option>)}
132
- </select>
133
- <input
134
- id="auth-phone" type="tel" autoComplete="tel" inputMode="tel" value={phone} autoFocus
135
- onChange={(e) => setPhone(maskPhone(e.target.value, country.mask))}
136
- className="w-full rounded-r-xl border border-border bg-background px-3.5 py-2.5 text-foreground outline-none transition-colors focus:border-primary focus-visible:ring-2 focus-visible:ring-ring"
137
- placeholder={`Ex.: ${maskPhone('11987654321', country.mask)}`}
138
- />
139
- </div>
140
- </div>
141
- <button
142
- type="submit"
143
- disabled={unmaskPhone(phone).length < 8}
144
- className="w-full rounded-xl bg-primary py-2.5 font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
145
- >
146
- Enviar código via WhatsApp
147
- </button>
148
- <p className="text-center text-xs text-muted-foreground">Enviaremos um código para confirmar seu número.</p>
149
- </form>
150
- ) : (
151
- <form onSubmit={(e) => { e.preventDefault(); void confirmCode() }} className="mt-5 space-y-3">
152
- <p className="text-sm text-muted-foreground">
153
- Enviamos um código via WhatsApp para <span className="font-medium text-foreground">{country.flag} {country.dial} {phone}</span>.
154
- </p>
155
- <input
156
- type="text" inputMode="numeric" value={code} autoFocus
157
- onChange={(e) => { setCode(e.target.value); setCodeError(false) }}
158
- className="w-full rounded-xl border border-border bg-background px-4 py-3 text-center text-lg tracking-[0.4em] text-foreground outline-none transition-colors focus:border-primary focus-visible:ring-2 focus-visible:ring-ring placeholder:text-sm placeholder:tracking-normal"
159
- placeholder="Digite o código"
160
- />
161
- {codeError ? <p role="alert" className="text-sm text-destructive">Código inválido. Para testar, use 0000.</p> : null}
162
- <button
163
- type="submit"
164
- disabled={busy}
165
- className="w-full rounded-xl bg-primary py-2.5 font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
166
- >
167
- {busy ? 'Entrando…' : mode === 'signup' ? 'Criar conta e entrar' : 'Confirmar e entrar'}
168
- </button>
169
- <button type="button" onClick={() => setStep('phone')} className="mx-auto flex items-center gap-1 text-sm font-medium text-primary hover:underline">
170
- <ArrowLeft className="h-3.5 w-3.5" /> Trocar número
171
- </button>
172
- </form>
173
- )}
174
- </Dialog.Content>
175
- </Dialog.Portal>
176
- </Dialog.Root>
177
- )
178
- }
@@ -1,17 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // @fayz-ai/plugin-auth/website — lean website auth surface (no @fayz-ai/ui).
3
- //
4
- // Ships the DEFAULT phone sign-in/sign-up modal for dogfood Fayz sites +
5
- // AuthModalProvider (openAuthModal control) + the canonical phone→email helper.
6
- // Re-exports the auth runtime from @fayz-ai/auth for one-stop host wiring.
7
- // ---------------------------------------------------------------------------
8
-
9
- export { AuthModalProvider, useAuthModal } from './AuthModalProvider'
10
- export type { AuthModalProviderProps } from './AuthModalProvider'
11
- export { PhoneAuthModal } from './PhoneAuthModal'
12
- export type { PhoneAuthModalConfig } from './PhoneAuthModal'
13
- export { phoneToEmail } from './phone'
14
-
15
- // Convenience re-exports so hosts import auth from one place.
16
- export { AuthProvider, useAuth, createMockAuthAdapter, createSupabaseAuthAdapter } from '@fayz-ai/auth'
17
- export type { AuthAdapter, AuthUser, AuthSession } from '@fayz-ai/core'
@@ -1,12 +0,0 @@
1
- import { unmaskPhone } from '@fayz-ai/core'
2
-
3
- /**
4
- * Canonical phone → auth email. Used by BOTH the phone-auth modal and any host
5
- * booking→auth bridge so the same number always maps to the same account
6
- * (Quaddro model: phone = identity). Pass the phone WITH its country dial so the
7
- * digits are globally unique.
8
- */
9
- export function phoneToEmail(phoneWithDial: string): string {
10
- const digits = unmaskPhone(phoneWithDial)
11
- return `${digits || 'anon'}@phone.fayz.local`
12
- }
File without changes