diff --git a/web/src/pages/AuthLogin.tsx b/web/src/pages/AuthLogin.tsx index ac35159..1183f11 100644 --- a/web/src/pages/AuthLogin.tsx +++ b/web/src/pages/AuthLogin.tsx @@ -25,7 +25,7 @@ export default function AuthLogin() { const { data, error } = await supabase.auth.signInWithPassword({ email, password }) if (error) throw error const role = data.user?.user_metadata?.role - if (role) navigate(role === 'barbearia' ? '/painel' : '/explorar', { replace: true }) + navigate(role === 'barbearia' ? '/painel' : '/explorar', { replace: true }) } catch { setError('Email ou palavra-passe incorretos.') setLoading(false) @@ -33,18 +33,18 @@ export default function AuthLogin() { } return ( -
-
- {/* Logo top */} -
-
- +
+
+ {/* Logo */} +
+
+
-

Bem-vindo de volta

-

Aceda à sua conta SmartAgenda

+

Bem-vindo de volta

+

Aceda à sua conta SmartAgenda

-
+
{error && (
{error} @@ -60,7 +60,8 @@ export default function AuthLogin() { onChange={e => setEmail(e.target.value)} placeholder="seu@email.com" required - className="w-full px-3.5 py-2.5 rounded-xl border border-slate-200 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent placeholder:text-slate-400" + className="w-full px-3.5 py-2.5 rounded-xl border border-slate-200 text-sm focus:outline-none focus:ring-2 focus:border-transparent placeholder:text-slate-400" + style={{ '--tw-ring-color': '#10b981' } as any} />
@@ -73,7 +74,7 @@ export default function AuthLogin() { onChange={e => setPassword(e.target.value)} placeholder="••••••••" required - className="w-full px-3.5 py-2.5 pr-10 rounded-xl border border-slate-200 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent placeholder:text-slate-400" + className="w-full px-3.5 py-2.5 pr-10 rounded-xl border border-slate-200 text-sm focus:outline-none focus:ring-2 focus:border-transparent placeholder:text-slate-400" /> -
-

- Não tem conta?{' '} - - Criar conta grátis - -

-
+

+ Não tem conta?{' '} + + Criar conta grátis + +

diff --git a/web/src/pages/AuthRegister.tsx b/web/src/pages/AuthRegister.tsx index 9d2a5a8..129d264 100644 --- a/web/src/pages/AuthRegister.tsx +++ b/web/src/pages/AuthRegister.tsx @@ -1,14 +1,5 @@ -/** - * @file AuthRegister.tsx - * @description Página de Registo para a versão Web. Permite a criação de - * novas contas segmentadas por perfil ('cliente' ou 'barbearia'). Interage - * diretamente com o serviço de autenticação do Supabase. - */ import { FormEvent, useEffect, useState } from 'react' import { Link, useNavigate } from 'react-router-dom' -import { Input } from '../components/ui/input' -import { Button } from '../components/ui/button' -import { Card } from '../components/ui/card' import { UserPlus, User, Scissors } from 'lucide-react' import { supabase } from '../lib/supabase' @@ -16,58 +7,32 @@ export default function AuthRegister() { const [name, setName] = useState('') const [email, setEmail] = useState('') const [password, setPassword] = useState('') - - // Estado local para definir as permissões associadas à conta nova const [role, setRole] = useState<'cliente' | 'barbearia'>('cliente') - - // Condicional, só inserido no metadados se o tipo de utilizador for 'barbearia' const [shopName, setShopName] = useState('') const [error, setError] = useState('') const [loading, setLoading] = useState(false) - const navigate = useNavigate() - /** - * Previne acesso à página de registo por utilizadores com Sessão ativa. - * Utiliza um padrão `mounted` para não alterar state se o componente for desmontado. - */ - // 🔐 Se já estiver logado, não pode ver o registo useEffect(() => { let mounted = true ; (async () => { - // Efetua um fetch à sessão existente injetada pelo SDK da Supabase const { data } = await supabase.auth.getSession() if (!mounted) return - if (data.session) { - navigate('/explorar', { replace: true }) - } + if (data.session) navigate('/explorar', { replace: true }) })() - return () => { - mounted = false - } + return () => { mounted = false } }, [navigate]) - /** - * Processa a criação e envio do novo perfil e das informações para a BD via Supabase Auth. - * @param {FormEvent} e - Evento de submissão do formulário. - */ async function onSubmit(e: FormEvent) { e.preventDefault() setError('') - - // Regras básicas de negócio à submissão (proteção pre-API) if (!name.trim()) return setError('Preencha o nome completo') if (!email.trim()) return setError('Preencha o email') if (!password.trim()) return setError('Preencha a senha') - if (role === 'barbearia' && !shopName.trim()) { - return setError('Informe o nome da barbearia') - } + if (role === 'barbearia' && !shopName.trim()) return setError('Informe o nome da barbearia') try { setLoading(true) - - // Registo propriamente dito perante serviço do Supabase - // Encaminha, adicionalmente, opções nos metadados (Data) da auth do serviço para mapeamento da "profile" table na BD const { data, error } = await supabase.auth.signUp({ email, password, @@ -75,31 +40,17 @@ export default function AuthRegister() { data: { name: name.trim(), role, - // Apenas vincula shopName no json object se a rule aplicável confirmar que a role o exige - // Envia ambas as formatações (camelCase para web, snake_case para o trigger SQL) shopName: role === 'barbearia' ? shopName.trim() : null, shop_name: role === 'barbearia' ? shopName.trim() : null, }, }, }) - if (error) throw error - - // 🔔 Se confirmação de email estiver ON (verificação pendente via SMTP em Supabase Configs) if (!data.session) { - navigate('/login', { - replace: true, - state: { - msg: 'Conta criada! Confirma o email antes de fazer login.', - }, - }) + navigate('/login', { replace: true, state: { msg: 'Conta criada! Confirme o email antes de fazer login.' } }) return } - - // ✅ Login automático: Rotas divergentes de acordo com a função Role - navigate(role === 'barbearia' ? '/painel' : '/explorar', { - replace: true, - }) + navigate(role === 'barbearia' ? '/painel' : '/explorar', { replace: true }) } catch (err: any) { setError(err?.message || 'Erro ao criar conta') } finally { @@ -107,115 +58,91 @@ export default function AuthRegister() { } } + const field = (label: string, node: React.ReactNode) => ( +
+ + {node} +
+ ) + const inputCls = "w-full px-3.5 py-2.5 rounded-xl border border-slate-200 text-sm focus:outline-none placeholder:text-slate-400" + return ( -
- -
-
- +
+
+ {/* Logo */} +
+
+
-

- Criar conta -

-

- Escolha o tipo de acesso -

+

Criar conta

+

Escolha o seu perfil e comece já

- {error && ( -
- {error} -
- )} - -
- {/* Tipo de conta */} -
- -
- {(['cliente', 'barbearia'] as const).map((r) => ( - - ))} +
+ {error && ( +
+ {error}
-
- - setName(e.target.value)} - placeholder="João Silva" - required - /> - - setEmail(e.target.value)} - placeholder="seu@email.com" - required - /> - - setPassword(e.target.value)} - placeholder="••••••••" - required - /> - - {role === 'barbearia' && ( - setShopName(e.target.value)} - placeholder="Barbearia XPTO" - required - /> )} - - +
+ {/* Tipo de conta */} +
+ +
+ {(['cliente', 'barbearia'] as const).map(r => ( + + ))} +
+
-
-

- Já tem conta?{' '} - setName(e.target.value)} placeholder="João Silva" required className={inputCls} /> + )} + {field('Email', + setEmail(e.target.value)} placeholder="seu@email.com" required className={inputCls} /> + )} + {field('Palavra-passe', + setPassword(e.target.value)} placeholder="••••••••" required className={inputCls} /> + )} + {role === 'barbearia' && field('Nome da barbearia', + setShopName(e.target.value)} placeholder="Barbearia XPTO" required className={inputCls} /> + )} + + +

+ +

+ Já tem conta?{' '} + Entrar

- +
) } diff --git a/web/src/pages/Landing.tsx b/web/src/pages/Landing.tsx index cd6c499..beb6a21 100644 --- a/web/src/pages/Landing.tsx +++ b/web/src/pages/Landing.tsx @@ -173,22 +173,20 @@ export default function Landing() { {/* CTA */} -
-
+
+

Pronto para começar?

-

+

Junte-se a centenas de barbearias que já usam a SmartAgenda.

-
- - Criar conta grátis - - - Explorar barbearias - -
+ + Criar conta grátis +
);