From 2209b8382019b0bbc2be57b3f6e665a3c94dd032 Mon Sep 17 00:00:00 2001 From: Paulo Massingue Date: Mon, 9 Nov 2026 15:05:20 +0200 Subject: [PATCH] login de cadastro padronizado --- resources/js/components/auth-tempo-card.tsx | 49 +++ resources/js/components/passkey-verify.tsx | 10 +- resources/js/components/tempo-brand.tsx | 170 ++++++++-- resources/js/layouts/auth-layout.tsx | 8 + .../js/layouts/auth/auth-tempo-layout.tsx | 9 + resources/js/pages/auth/login.tsx | 292 +++++++++++------- resources/js/pages/auth/register.tsx | 100 ++++-- 7 files changed, 463 insertions(+), 175 deletions(-) create mode 100644 resources/js/components/auth-tempo-card.tsx create mode 100644 resources/js/layouts/auth/auth-tempo-layout.tsx diff --git a/resources/js/components/auth-tempo-card.tsx b/resources/js/components/auth-tempo-card.tsx new file mode 100644 index 00000000..022e90d6 --- /dev/null +++ b/resources/js/components/auth-tempo-card.tsx @@ -0,0 +1,49 @@ +import type { ReactNode } from 'react'; + +type Props = { + badge: string; + title: string; + description: string; + children: ReactNode; + footer: ReactNode; +}; + +export default function AuthTempoCard({ + badge, + title, + description, + children, + footer, +}: Props) { + return ( +
+
+ Tempo + +
+ + {badge} + +

+ {title} +

+

+ {description} +

+
+ + {children} +
+ +
+ {footer} +
+
+ ); +} diff --git a/resources/js/components/passkey-verify.tsx b/resources/js/components/passkey-verify.tsx index c46fb278..10486d50 100644 --- a/resources/js/components/passkey-verify.tsx +++ b/resources/js/components/passkey-verify.tsx @@ -6,6 +6,7 @@ import InputError from '@/components/input-error'; import { Button } from '@/components/ui/button'; import { Separator } from '@/components/ui/separator'; import { Spinner } from '@/components/ui/spinner'; +import { cn } from '@/lib/utils'; type Props = { routes?: { @@ -15,6 +16,7 @@ type Props = { label?: string; loadingLabel?: string; separator?: string; + separatorClassName?: string; }; export default function PasskeyVerify({ @@ -22,6 +24,7 @@ export default function PasskeyVerify({ label, loadingLabel, separator, + separatorClassName, }: Props = {}) { const { verify, isLoading, error, isSupported } = usePasskeyVerify({ ...(routes && { @@ -64,7 +67,12 @@ export default function PasskeyVerify({
- + {separator ?? 'Ou continuar com email'}
diff --git a/resources/js/components/tempo-brand.tsx b/resources/js/components/tempo-brand.tsx index 7675e556..7a52f96e 100644 --- a/resources/js/components/tempo-brand.tsx +++ b/resources/js/components/tempo-brand.tsx @@ -243,39 +243,153 @@ const translations: Record> = { const sharedTranslations: Record = { 'Criar conta': ['Create account', 'Créer un compte', 'Crear cuenta'], - 'Entrar na sua conta': ['Sign in to your account', 'Connectez-vous à votre compte', 'Inicie sesión en su cuenta'], - 'Crie uma conta simples para continuar e acompanhar a sua operação. Não é necessário configurar MFA para participar.': ['Create an account to continue and follow your operation. You do not need to set up MFA to participate.', 'Créez un compte pour poursuivre et suivre votre opération. La configuration de l’authentification multifacteur n’est pas nécessaire pour participer.', 'Cree una cuenta para continuar y seguir su operación. No necesita configurar la autenticación multifactor para participar.'], - 'Entre para continuar a operação que escolheu. Os dados preenchidos permanecem guardados neste navegador.': ['Sign in to continue the operation you chose. Your entered data remains saved in this browser.', 'Connectez-vous pour poursuivre l’opération choisie. Les données saisies restent enregistrées dans ce navigateur.', 'Inicie sesión para continuar la operación elegida. Los datos introducidos permanecen guardados en este navegador.'], + 'Entrar na sua conta': [ + 'Sign in to your account', + 'Connectez-vous à votre compte', + 'Inicie sesión en su cuenta', + ], + 'Área reservada': ['Private area', 'Espace réservé', 'Área reservada'], + 'Bolsa de Parcerias, Investidores e Capital': [ + 'Partnership, Investor and Capital Exchange', + 'Bourse des partenariats, investisseurs et capitaux', + 'Bolsa de Alianzas, Inversores y Capital', + ], + 'Aceda à sua conta Tempo.': [ + 'Access your Tempo account.', + 'Accédez à votre compte Tempo.', + 'Acceda a su cuenta Tempo.', + ], + 'Crie uma conta simples para continuar e acompanhar a sua operação. Não é necessário configurar MFA para participar.': + [ + 'Create an account to continue and follow your operation. You do not need to set up MFA to participate.', + 'Créez un compte pour poursuivre et suivre votre opération. La configuration de l’authentification multifacteur n’est pas nécessaire pour participer.', + 'Cree una cuenta para continuar y seguir su operación. No necesita configurar la autenticación multifactor para participar.', + ], + 'Entre para continuar a operação que escolheu. Os dados preenchidos permanecem guardados neste navegador.': + [ + 'Sign in to continue the operation you chose. Your entered data remains saved in this browser.', + 'Connectez-vous pour poursuivre l’opération choisie. Les données saisies restent enregistrées dans ce navigateur.', + 'Inicie sesión para continuar la operación elegida. Los datos introducidos permanecen guardados en este navegador.', + ], 'Palavra-passe': ['Password', 'Mot de passe', 'Contraseña'], - 'Mostrar palavra-passe': ['Show password', 'Afficher le mot de passe', 'Mostrar contraseña'], - 'Ocultar palavra-passe': ['Hide password', 'Masquer le mot de passe', 'Ocultar contraseña'], - 'Confirmar palavra-passe': ['Confirm password', 'Confirmer le mot de passe', 'Confirmar contraseña'], - 'Nome': ['Name', 'Nom', 'Nombre'], + 'Mostrar palavra-passe': [ + 'Show password', + 'Afficher le mot de passe', + 'Mostrar contraseña', + ], + 'Ocultar palavra-passe': [ + 'Hide password', + 'Masquer le mot de passe', + 'Ocultar contraseña', + ], + 'Confirmar palavra-passe': [ + 'Confirm password', + 'Confirmer le mot de passe', + 'Confirmar contraseña', + ], + Nome: ['Name', 'Nom', 'Nombre'], 'Nome completo': ['Full name', 'Nom complet', 'Nombre completo'], - 'Esqueceu a palavra-passe?': ['Forgot your password?', 'Mot de passe oublié ?', '¿Olvidó su contraseña?'], - 'Manter sessão iniciada': ['Keep me signed in', 'Rester connecté', 'Mantener la sesión iniciada'], - 'Ainda não tem conta?': ['Don’t have an account?', 'Vous n’avez pas de compte ?', '¿Aún no tiene cuenta?'], - 'Já tem conta?': ['Already have an account?', 'Vous avez déjà un compte ?', '¿Ya tiene cuenta?'], - 'Entrar com chave de acesso': ['Sign in with a passkey', 'Se connecter avec une clé d’accès', 'Iniciar sesión con clave de acceso'], - 'A autenticar…': ['Signing in…', 'Connexion en cours…', 'Iniciando sesión…'], - 'Ou continuar com email': ['Or continue with email', 'Ou continuer avec l’e-mail', 'O continuar con correo electrónico'], - 'O email ou a palavra-passe não correspondem aos nossos registos.': ['The email or password does not match our records.', 'L’adresse e-mail ou le mot de passe ne correspond pas à nos informations.', 'El correo electrónico o la contraseña no coinciden con nuestros registros.'], - 'A palavra-passe está incorrecta.': ['The password is incorrect.', 'Le mot de passe est incorrect.', 'La contraseña es incorrecta.'], - 'Atalho para o conteúdo': ['Skip link', 'Lien d’accès rapide', 'Enlace de acceso rápido'], - 'Saltar para o conteúdo': ['Skip to content', 'Aller au contenu', 'Ir al contenido'], - 'Caminho de navegação': ['Breadcrumb', 'Fil d’Ariane', 'Ruta de navegación'], - 'Contacto de apoio ao investidor': ['Investor support contact', 'Contact de l’assistance aux investisseurs', 'Contacto de apoyo al inversor'], - 'Contactar Apoio ao Investidor': ['Contact investor support', 'Contacter l’assistance aux investisseurs', 'Contactar con apoyo al inversor'], - 'Ambiente de homologação': ['Review environment', 'Environnement de validation', 'Entorno de validación'], - 'Homologação reservada à revisão da Tempo. Consulte o conteúdo público; entre na sua conta quando iniciar uma operação.': ['This review environment is reserved for Tempo. Browse public content; sign in when starting an operation.', 'Cet environnement de validation est réservé à Tempo. Consultez le contenu public ; connectez-vous pour commencer une opération.', 'Este entorno de validación está reservado para Tempo. Consulte el contenido público; inicie sesión al comenzar una operación.'], - 'Idioma da navegação': ['Navigation language', 'Langue de navigation', 'Idioma de navegación'], + 'Esqueceu a palavra-passe?': [ + 'Forgot your password?', + 'Mot de passe oublié ?', + '¿Olvidó su contraseña?', + ], + 'Manter sessão iniciada': [ + 'Keep me signed in', + 'Rester connecté', + 'Mantener la sesión iniciada', + ], + 'Ainda não tem conta?': [ + 'Don’t have an account?', + 'Vous n’avez pas de compte ?', + '¿Aún no tiene cuenta?', + ], + 'Já tem conta?': [ + 'Already have an account?', + 'Vous avez déjà un compte ?', + '¿Ya tiene cuenta?', + ], + 'Entrar com chave de acesso': [ + 'Sign in with a passkey', + 'Se connecter avec une clé d’accès', + 'Iniciar sesión con clave de acceso', + ], + 'A autenticar…': [ + 'Signing in…', + 'Connexion en cours…', + 'Iniciando sesión…', + ], + 'Ou continuar com email': [ + 'Or continue with email', + 'Ou continuer avec l’e-mail', + 'O continuar con correo electrónico', + ], + 'O email ou a palavra-passe não correspondem aos nossos registos.': [ + 'The email or password does not match our records.', + 'L’adresse e-mail ou le mot de passe ne correspond pas à nos informations.', + 'El correo electrónico o la contraseña no coinciden con nuestros registros.', + ], + 'A palavra-passe está incorrecta.': [ + 'The password is incorrect.', + 'Le mot de passe est incorrect.', + 'La contraseña es incorrecta.', + ], + 'Atalho para o conteúdo': [ + 'Skip link', + 'Lien d’accès rapide', + 'Enlace de acceso rápido', + ], + 'Saltar para o conteúdo': [ + 'Skip to content', + 'Aller au contenu', + 'Ir al contenido', + ], + 'Caminho de navegação': [ + 'Breadcrumb', + 'Fil d’Ariane', + 'Ruta de navegación', + ], + 'Contacto de apoio ao investidor': [ + 'Investor support contact', + 'Contact de l’assistance aux investisseurs', + 'Contacto de apoyo al inversor', + ], + 'Contactar Apoio ao Investidor': [ + 'Contact investor support', + 'Contacter l’assistance aux investisseurs', + 'Contactar con apoyo al inversor', + ], + 'Ambiente de homologação': [ + 'Review environment', + 'Environnement de validation', + 'Entorno de validación', + ], + 'Homologação reservada à revisão da Tempo. Consulte o conteúdo público; entre na sua conta quando iniciar uma operação.': + [ + 'This review environment is reserved for Tempo. Browse public content; sign in when starting an operation.', + 'Cet environnement de validation est réservé à Tempo. Consultez le contenu public ; connectez-vous pour commencer une opération.', + 'Este entorno de validación está reservado para Tempo. Consulte el contenido público; inicie sesión al comenzar una operación.', + ], + 'Idioma da navegação': [ + 'Navigation language', + 'Langue de navigation', + 'Idioma de navegación', + ], 'Navegação institucional': ['Language', 'Langue', 'Idioma'], - 'Valores': ['Values', 'Valeurs', 'Valores'], + Valores: ['Values', 'Valeurs', 'Valores'], 'Ver menos': ['Show less', 'Voir moins', 'Ver menos'], 'Ver mais': ['Show more', 'Voir plus', 'Ver más'], - 'Voltar': ['Back', 'Retour', 'Volver'], - 'Anos de Experiência': ['Years of experience', 'Années d’expérience', 'Años de experiencia'], - 'Projetos Realizados': ['Projects completed', 'Projets réalisés', 'Proyectos realizados'], + Voltar: ['Back', 'Retour', 'Volver'], + 'Anos de Experiência': [ + 'Years of experience', + 'Années d’expérience', + 'Años de experiencia', + ], + 'Projetos Realizados': [ + 'Projects completed', + 'Projets réalisés', + 'Proyectos realizados', + ], }; for (const [source, values] of Object.entries(sharedTranslations)) { (['en', 'fr', 'es'] as const).forEach((locale, index) => { diff --git a/resources/js/layouts/auth-layout.tsx b/resources/js/layouts/auth-layout.tsx index d20add3c..a75eda3d 100644 --- a/resources/js/layouts/auth-layout.tsx +++ b/resources/js/layouts/auth-layout.tsx @@ -1,16 +1,24 @@ import AuthLayoutTemplate from '@/layouts/auth/auth-simple-layout'; +import AuthTempoLayout from '@/layouts/auth/auth-tempo-layout'; import { useTempoUi } from '@/components/tempo-brand'; export default function AuthLayout({ title = '', description = '', + variant = 'simple', children, }: { title?: string; description?: string; + variant?: 'simple' | 'tempo-card'; children: React.ReactNode; }) { const { t } = useTempoUi(); + + if (variant === 'tempo-card') { + return {children}; + } + return ( {children} diff --git a/resources/js/layouts/auth/auth-tempo-layout.tsx b/resources/js/layouts/auth/auth-tempo-layout.tsx new file mode 100644 index 00000000..ca0a28a1 --- /dev/null +++ b/resources/js/layouts/auth/auth-tempo-layout.tsx @@ -0,0 +1,9 @@ +import type { PropsWithChildren } from 'react'; + +export default function AuthTempoLayout({ children }: PropsWithChildren) { + return ( +
+
{children}
+
+ ); +} diff --git a/resources/js/pages/auth/login.tsx b/resources/js/pages/auth/login.tsx index 2f2d5bca..5142baaf 100644 --- a/resources/js/pages/auth/login.tsx +++ b/resources/js/pages/auth/login.tsx @@ -1,6 +1,7 @@ import { participationIdentity } from '@/lib/participation-identity'; import { useTempoUi } from '@/components/tempo-brand'; import { Form, Head, usePage } from '@inertiajs/react'; +import { Check, LogIn } from 'lucide-react'; import InputError from '@/components/input-error'; import PasswordInput from '@/components/password-input'; import TextLink from '@/components/text-link'; @@ -24,6 +25,13 @@ type Props = { qaAccount?: { id: number; email: string }; }; +const controlClass = + 'h-auto rounded-xl border-slate-300 bg-white px-3.5 py-3 text-sm text-slate-900 shadow-sm placeholder:text-slate-400 hover:border-slate-400 focus-visible:border-[#ed1c24] focus-visible:ring-4 focus-visible:ring-[#ed1c24]/10'; +const labelClass = + 'mb-1.5 block text-xs leading-normal font-bold text-slate-800'; +const inlineLabelClass = + 'block text-xs leading-normal font-bold text-slate-800'; + export default function Login({ status, canResetPassword, qaAccount }: Props) { const { t } = useTempoUi(); // A redirect from another page (e.g. MFA) creates a new Form with empty local errors. @@ -32,130 +40,186 @@ export default function Login({ status, canResetPassword, qaAccount }: Props) { <> - {qaAccount && ( - - )} +
+
+ Tempo - {/* @chisel-passkeys */} - - {/* @end-chisel-passkeys */} +
+ + {t('Área reservada')} + +

+ {t('Entrar na sua conta')} +

+

+ {t('Aceda à sua conta Tempo.')} +

+
-
- {({ processing, errors }) => ( - <> -
-
- - - -
+ {qaAccount && ( + + )} -
-
- - {canResetPassword && ( - + + {status} +
+ )} + + {/* @chisel-passkeys */} + + {/* @end-chisel-passkeys */} + + + {({ processing, errors }) => ( + <> +
+
+ + + +
+ +
+
+ + {canResetPassword && ( + + {t( + 'Esqueceu a palavra-passe?', + )} + + )} +
+ + +
+ +
+ + +
+ +
- - -
- -
- - -
- - -
- - {/* @chisel-registration */} -
- {t('Ainda não tem conta?')}{' '} - - {t('Criar conta')} - -
- {/* @end-chisel-registration */} - - )} -
- - {status && ( -
- {status} + + )} +
- )} + + {/* @chisel-registration */} +
+ {t('Ainda não tem conta?')}{' '} + + {t('Criar conta')} + +
+ {/* @end-chisel-registration */} +
); } Login.layout = { + variant: 'tempo-card', title: 'Entrar na sua conta', - description: - 'Entre para continuar a operação que escolheu. Os dados preenchidos permanecem guardados neste navegador.', + description: 'Aceda à sua conta Tempo.', }; diff --git a/resources/js/pages/auth/register.tsx b/resources/js/pages/auth/register.tsx index 75b98678..dfd6f167 100644 --- a/resources/js/pages/auth/register.tsx +++ b/resources/js/pages/auth/register.tsx @@ -1,6 +1,8 @@ import { participationIdentity } from '@/lib/participation-identity'; import { useTempoUi } from '@/components/tempo-brand'; import { Form, Head } from '@inertiajs/react'; +import { UserPlus } from 'lucide-react'; +import AuthTempoCard from '@/components/auth-tempo-card'; import InputError from '@/components/input-error'; import PasswordInput from '@/components/password-input'; import TextLink from '@/components/text-link'; @@ -15,22 +17,47 @@ type Props = { passwordRules: string; }; +const controlClass = + 'h-auto rounded-xl border-slate-300 bg-white px-3.5 py-3 text-sm text-slate-900 shadow-sm placeholder:text-slate-400 hover:border-slate-400 focus-visible:border-[#ed1c24] focus-visible:ring-4 focus-visible:ring-[#ed1c24]/10'; +const labelClass = + 'mb-1.5 block text-xs leading-normal font-bold text-slate-800'; + export default function Register({ passwordRules }: Props) { const { t } = useTempoUi(); return ( <> -
- {({ processing, errors }) => ( + -
-
- + {t('Já tem conta?')}{' '} + + {t('Entrar')} + + + } + > + + {({ processing, errors }) => ( +
+
+ - +
-
- +
+
-
- +
+
-
-