develop #83
@ -3,6 +3,7 @@ import { useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { useAuth } from '@/hooks/useAuth'
|
||||
import { sendMagicLink } from '@/lib/api'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
@ -12,6 +13,9 @@ import { AuthenticationError } from '@/lib/auth'
|
||||
export default function LoginAdminPage() {
|
||||
const [credentials, setCredentials] = useState({ email: '', password: '' })
|
||||
const [error, setError] = useState('')
|
||||
const [magicMessage, setMagicMessage] = useState('')
|
||||
const [magicError, setMagicError] = useState('')
|
||||
const [magicLoading, setMagicLoading] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const router = useRouter()
|
||||
const { login } = useAuth()
|
||||
@ -44,6 +48,27 @@ export default function LoginAdminPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleSendMagicLink = async () => {
|
||||
if (!credentials.email) {
|
||||
setMagicError('Por favor, preencha o email antes de solicitar o magic link.')
|
||||
return
|
||||
}
|
||||
|
||||
setMagicLoading(true)
|
||||
setMagicError('')
|
||||
setMagicMessage('')
|
||||
|
||||
try {
|
||||
const res = await sendMagicLink(credentials.email, { target: 'admin' })
|
||||
setMagicMessage(res?.message ?? 'Magic link enviado. Verifique seu email.')
|
||||
} catch (err: any) {
|
||||
console.error('[MAGIC-LINK ADMIN] erro ao enviar:', err)
|
||||
setMagicError(err?.message ?? String(err))
|
||||
} finally {
|
||||
setMagicLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-background py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-md w-full space-y-8">
|
||||
@ -108,6 +133,25 @@ export default function LoginAdminPage() {
|
||||
{loading ? 'Entrando...' : 'Entrar no Sistema Administrativo'}
|
||||
</Button>
|
||||
</form>
|
||||
<div className="mt-4 space-y-2">
|
||||
<div className="text-sm text-muted-foreground mb-2">Ou entre usando um magic link (sem senha)</div>
|
||||
|
||||
{magicError && (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>{magicError}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{magicMessage && (
|
||||
<Alert>
|
||||
<AlertDescription>{magicMessage}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Button className="w-full" onClick={handleSendMagicLink} disabled={magicLoading}>
|
||||
{magicLoading ? 'Enviando magic link...' : 'Enviar magic link'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-center">
|
||||
<Button variant="outline" asChild className="w-full hover:!bg-primary hover:!text-white hover:!border-primary transition-all duration-200">
|
||||
|
||||
@ -66,7 +66,7 @@ export default function LoginPacientePage() {
|
||||
setMagicMessage('')
|
||||
|
||||
try {
|
||||
const res = await sendMagicLink(credentials.email, { emailRedirectTo: `${window.location.origin}/` })
|
||||
const res = await sendMagicLink(credentials.email, { target: 'paciente' })
|
||||
setMagicMessage(res?.message ?? 'Magic link enviado. Verifique seu email.')
|
||||
} catch (err: any) {
|
||||
console.error('[MAGIC-LINK PACIENTE] erro ao enviar:', err)
|
||||
|
||||
@ -69,7 +69,7 @@ export default function LoginPage() {
|
||||
setMagicMessage('')
|
||||
|
||||
try {
|
||||
const res = await sendMagicLink(credentials.email, { emailRedirectTo: `${window.location.origin}/` })
|
||||
const res = await sendMagicLink(credentials.email, { target: 'medico' })
|
||||
setMagicMessage(res?.message ?? 'Magic link enviado. Verifique seu email.')
|
||||
} catch (err: any) {
|
||||
console.error('[MAGIC-LINK] erro ao enviar:', err)
|
||||
|
||||
@ -603,6 +603,27 @@ export async function deletarExcecao(id: string): Promise<void> {
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? ENV_CONFIG.SUPABASE_URL;
|
||||
const REST = `${API_BASE}/rest/v1`;
|
||||
|
||||
// Helper to build/normalize redirect URLs
|
||||
function buildRedirectUrl(target?: 'paciente' | 'medico' | 'admin' | 'default', explicit?: string, redirectBase?: string) {
|
||||
const DEFAULT_REDIRECT_BASE = redirectBase ?? 'https://mediconecta-app-liart.vercel.app';
|
||||
if (explicit) {
|
||||
// If explicit is already absolute, return trimmed
|
||||
try {
|
||||
const u = new URL(explicit);
|
||||
return u.toString().replace(/\/$/, '');
|
||||
} catch (e) {
|
||||
// Not an absolute URL, fall through to build from base
|
||||
}
|
||||
}
|
||||
|
||||
const base = DEFAULT_REDIRECT_BASE.replace(/\/$/, '');
|
||||
let path = '/';
|
||||
if (target === 'paciente') path = '/paciente';
|
||||
else if (target === 'medico') path = '/profissional';
|
||||
else if (target === 'admin') path = '/dashboard';
|
||||
return `${base}${path}`;
|
||||
}
|
||||
|
||||
// Token salvo no browser (aceita auth_token ou token)
|
||||
function getAuthToken(): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
@ -1587,6 +1608,13 @@ export type CreateUserInput = {
|
||||
full_name: string;
|
||||
phone?: string | null;
|
||||
role: UserRoleEnum;
|
||||
// Optional: when provided, backend can use this to send magic links that redirect
|
||||
// to the given URL or interpret `target` to build a role-specific redirect.
|
||||
emailRedirectTo?: string;
|
||||
// Compatibility: some integrations expect `redirect_url` as the parameter name
|
||||
// for the post-auth redirect. Include it so backend/functions receive it.
|
||||
redirect_url?: string;
|
||||
target?: 'paciente' | 'medico' | 'admin' | 'default';
|
||||
};
|
||||
|
||||
export type CreatedUser = {
|
||||
@ -1767,28 +1795,35 @@ export async function criarUsuarioMedico(medico: { email: string; full_name: str
|
||||
// Rely on the server-side create-user endpoint (POST /create-user). The
|
||||
// backend is responsible for role assignment and sending the magic link.
|
||||
// Any error should be surfaced to the caller so it can be handled there.
|
||||
return await criarUsuario({ email: medico.email, password: '', full_name: medico.full_name, phone: medico.phone_mobile, role: 'medico' as any });
|
||||
const redirectBase = 'https://mediconecta-app-liart.vercel.app';
|
||||
const emailRedirectTo = `${redirectBase.replace(/\/$/, '')}/profissional`;
|
||||
const redirect_url = emailRedirectTo;
|
||||
return await criarUsuario({ email: medico.email, password: '', full_name: medico.full_name, phone: medico.phone_mobile, role: 'medico' as any, emailRedirectTo, redirect_url, target: 'medico' });
|
||||
}
|
||||
|
||||
// Criar usuário para PACIENTE no Supabase Auth (sistema de autenticação)
|
||||
export async function criarUsuarioPaciente(paciente: { email: string; full_name: string; phone_mobile: string; }): Promise<any> {
|
||||
// Rely on the server-side create-user endpoint (POST /create-user).
|
||||
return await criarUsuario({ email: paciente.email, password: '', full_name: paciente.full_name, phone: paciente.phone_mobile, role: 'paciente' as any });
|
||||
const redirectBase = 'https://mediconecta-app-liart.vercel.app';
|
||||
const emailRedirectTo = `${redirectBase.replace(/\/$/, '')}/paciente`;
|
||||
const redirect_url = emailRedirectTo;
|
||||
return await criarUsuario({ email: paciente.email, password: '', full_name: paciente.full_name, phone: paciente.phone_mobile, role: 'paciente' as any, emailRedirectTo, redirect_url, target: 'paciente' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Envia um magic link (OTP) diretamente via Supabase Auth (cliente)
|
||||
* Sem componente server-side adicional. Use quando quiser autenticar
|
||||
* o usuário por email (senha não necessária).
|
||||
*
|
||||
* Observação: isto apenas envia o link de login. A atribuição de roles
|
||||
* continua sendo operação server-side e deve ser feita pelo backend.
|
||||
*/
|
||||
export async function sendMagicLink(email: string, options?: { emailRedirectTo?: string }): Promise<{ success: boolean; message?: string }> {
|
||||
|
||||
export async function sendMagicLink(
|
||||
email: string,
|
||||
options?: { emailRedirectTo?: string; target?: 'paciente' | 'medico' | 'admin' | 'default'; redirectBase?: string }
|
||||
): Promise<{ success: boolean; message?: string }> {
|
||||
if (!email) throw new Error('Email obrigatório para enviar magic link');
|
||||
const url = `${API_BASE}/auth/v1/otp`;
|
||||
const payload: any = { email };
|
||||
if (options && options.emailRedirectTo) payload.options = { emailRedirectTo: options.emailRedirectTo };
|
||||
|
||||
const redirectUrl = buildRedirectUrl(options?.target, options?.emailRedirectTo, options?.redirectBase);
|
||||
if (redirectUrl) {
|
||||
// include both keys for broader compatibility across different Supabase setups
|
||||
payload.options = { emailRedirectTo: redirectUrl, redirect_to: redirectUrl, redirect_url: redirectUrl };
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user