Files
Investidor-V2/resources/js/components/tempo-brand.tsx
T
paulomassigue 2209b83820
tests / ci (push) Canceled after 0s
login de cadastro padronizado
2026-11-09 15:05:20 +02:00

475 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createContext, useContext, useEffect, useState } from 'react';
import { Facebook, Instagram, Linkedin, Youtube } from 'lucide-react';
import type { CSSProperties, ReactNode } from 'react';
export const tempoSocialLinks = [
{
name: 'Facebook',
href: 'https://www.facebook.com/revistatempo.mz/',
icon: Facebook,
color: '#1877f2',
},
{
name: 'Instagram',
href: 'https://www.instagram.com/revistatempo.mz/',
icon: Instagram,
color: '#e1306c',
},
{
name: 'LinkedIn',
href: 'https://www.linkedin.com/company/revistatempo/',
icon: Linkedin,
color: '#38a5df',
},
{
name: 'YouTube',
href: 'https://www.youtube.com/@RevistaTempoMz/',
icon: Youtube,
color: '#ff3333',
},
];
const menuTranslations = [
['Sobre nós', 'About us', 'À propos', 'Sobre nosotros'],
['Revista Tempo', 'Revista Tempo', 'Revista Tempo', 'Revista Tempo'],
[
'Abre num novo separador',
'Opens in a new tab',
'Ouvre un nouvel onglet',
'Abre en una nueva pestaña',
],
['O meu painel', 'My dashboard', 'Mon espace', 'Mi panel'],
[
'Manifestar interesse',
'Express interest',
'Manifester un intérêt',
'Manifestar interés',
],
[
'pedido recebido',
'request received',
'demande reçue',
'solicitud recibida',
],
[
'pedidos recebidos',
'requests received',
'demandes reçues',
'solicitudes recibidas',
],
[
'Porquê investir em Moçambique?',
'Why invest in Mozambique?',
'Pourquoi investir au Mozambique ?',
'Por qué invertir en Mozambique?',
],
[
'Partir de uma ideia',
'Start with an idea',
'Partir d’une idée',
'Partir de una idea',
],
[
'Consultar oportunidades',
'Browse opportunities',
'Consulter les opportunités',
'Consultar oportunidades',
],
[
'Simulador Fiscal',
'Tax simulator',
'Simulateur fiscal',
'Simulador fiscal',
],
[
'Oportunidades de investimento',
'Investment opportunities',
'Opportunités d’investissement',
'Oportunidades de inversión',
],
[
'Sectores de investimento',
'Investment sectors',
'Secteurs d’investissement',
'Sectores de inversión',
],
['Localizações', 'Locations', 'Localisations', 'Ubicaciones'],
[
'Iniciar investimento',
'Start an investment',
'Démarrer un investissement',
'Iniciar una inversión',
],
[
'Incentivos e simulador fiscal',
'Incentives and tax simulator',
'Incitations et simulateur fiscal',
'Incentivos y simulador fiscal',
],
['Licenciamento', 'Licensing', 'Autorisations', 'Licencias'],
['Acesso à terra', 'Land access', 'Accès à la terre', 'Acceso a la tierra'],
[
'Serviços ao investidor',
'Investor services',
'Services aux investisseurs',
'Servicios al inversor',
],
[
'Acompanhamento do projecto',
'Project follow-up',
'Suivi du projet',
'Seguimiento del proyecto',
],
[
'Apoio ao investidor',
'Investor support',
'Assistance aux investisseurs',
'Apoyo al inversor',
],
[
'Dossiers e percursos',
'Investment files and journeys',
'Dossiers et parcours',
'Expedientes y recorridos',
],
[
'Perfil e empresas',
'Profile and companies',
'Profil et entreprises',
'Perfil y empresas',
],
[
'Pedidos e tarefas de acompanhamento',
'Requests and follow-up tasks',
'Demandes et tâches de suivi',
'Solicitudes y tareas de seguimiento',
],
[
'Manifestações e favoritos',
'Expressions of interest and favourites',
'Manifestations d’intérêt et favoris',
'Manifestaciones de interés y favoritos',
],
['Notificações', 'Notifications', 'Notifications', 'Notificaciones'],
['A minha atividade', 'My activity', 'Mes activités', 'Mi actividad'],
[
'As minhas parcerias',
'My partnerships',
'Mes partenariats',
'Mis alianzas',
],
['Os meus negócios', 'My businesses', 'Mes entreprises', 'Mis negocios'],
];
const translatedMenu = (column: number) =>
Object.fromEntries(menuTranslations.map((row) => [row[0], row[column]]));
const translations: Record<string, Record<string, string>> = {
en: {
...translatedMenu(1),
Descoberta: 'Discover',
Realização: 'Invest',
Suporte: 'Support',
Entrar: 'Sign in',
'A minha área': 'My account',
'Terminar sessão': 'Sign out',
Contactos: 'Contacts',
Navegação: 'Navigation',
Início: 'Home',
Oportunidades: 'Opportunities',
Sectores: 'Sectors',
Legislações: 'Legislation',
'Guia do investidor': 'Investor guide',
Contacto: 'Contact',
'Política de Privacidade': 'Privacy Policy',
'Termos de Uso': 'Terms of Use',
'Todos os direitos reservados.': 'All rights reserved.',
'Siga a Revista Tempo para actualizações exclusivas.':
'Follow Revista Tempo for exclusive updates.',
'Número de registo': 'Registration number',
'A minha conta e atividade': 'My account and activity',
'Área de trabalho': 'Workspace',
},
fr: {
...translatedMenu(2),
Descoberta: 'Découvrir',
Realização: 'Investir',
Suporte: 'Assistance',
Entrar: 'Connexion',
'A minha área': 'Mon compte',
'Terminar sessão': 'Déconnexion',
Contactos: 'Contacts',
Navegação: 'Navigation',
Início: 'Accueil',
Oportunidades: 'Opportunités',
Sectores: 'Secteurs',
Legislações: 'Législation',
'Guia do investidor': 'Guide de l’investisseur',
Contacto: 'Contact',
'Política de Privacidade': 'Politique de confidentialité',
'Termos de Uso': 'Conditions d’utilisation',
'Todos os direitos reservados.': 'Tous droits réservés.',
'Siga a Revista Tempo para actualizações exclusivas.':
'Suivez Revista Tempo pour des actualités exclusives.',
'Número de registo': 'Numéro d’enregistrement',
'A minha conta e atividade': 'Mon compte et mes activités',
'Área de trabalho': 'Espace de travail',
},
es: {
...translatedMenu(3),
Descoberta: 'Descubrir',
Realização: 'Invertir',
Suporte: 'Soporte',
Entrar: 'Entrar',
'A minha área': 'Mi cuenta',
'Terminar sessão': 'Cerrar sesión',
Contactos: 'Contactos',
Navegação: 'Navegación',
Início: 'Inicio',
Oportunidades: 'Oportunidades',
Sectores: 'Sectores',
Legislações: 'Legislación',
'Guia do investidor': 'Guía del inversor',
Contacto: 'Contacto',
'Política de Privacidade': 'Política de privacidad',
'Termos de Uso': 'Términos de uso',
'Todos os direitos reservados.': 'Todos los derechos reservados.',
'Siga a Revista Tempo para actualizações exclusivas.':
'Siga Revista Tempo para recibir novedades exclusivas.',
'Número de registo': 'Número de registro',
'A minha conta e atividade': 'Mi cuenta y actividad',
'Área de trabalho': 'Área de trabajo',
},
};
const sharedTranslations: Record<string, [string, string, string]> = {
'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',
],
'Á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'],
'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',
],
'Navegação institucional': ['Language', 'Langue', 'Idioma'],
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',
],
};
for (const [source, values] of Object.entries(sharedTranslations)) {
(['en', 'fr', 'es'] as const).forEach((locale, index) => {
translations[locale][source] = values[index];
});
}
const TempoUi = createContext({
language: 'pt',
setLanguage: (_language: string) => {},
t: (text: string) => text,
requestCount: 0,
registerSubmittedInterest: () => {},
});
export function TempoUiProvider({ children }: { children: ReactNode }) {
const [language, setCurrentLanguage] = useState('pt');
const [requestCount, setRequestCount] = useState(0);
useEffect(() => {
try {
const saved = localStorage.getItem('tempo-navigation-language');
if (saved && /^[a-zA-Z][a-zA-Z0-9-]{0,19}$/.test(saved))
setCurrentLanguage(saved);
} catch {
/* Navigation remains usable without browser storage. */
}
}, []);
useEffect(() => {
document.documentElement.lang = language;
}, [language]);
const setLanguage = (next: string) => {
if (!/^[a-zA-Z][a-zA-Z0-9-]{0,19}$/.test(next)) return;
setCurrentLanguage(next);
try {
localStorage.setItem('tempo-navigation-language', next);
} catch {
/* Optional persistence. */
}
};
return (
<TempoUi.Provider
value={{
language,
setLanguage,
t: (text) => translations[language]?.[text] ?? text,
requestCount,
registerSubmittedInterest: () =>
setRequestCount((count) => count + 1),
}}
>
{children}
</TempoUi.Provider>
);
}
export const useTempoUi = () => useContext(TempoUi);
export function TempoSocialLinks({ footer = false }: { footer?: boolean }) {
return (
<div
className="flex items-center gap-1"
aria-label="Redes sociais da Revista Tempo"
>
{tempoSocialLinks.map(({ name, href, icon: Icon, color }) => (
<a
key={name}
href={href}
target="_blank"
rel="noopener noreferrer"
aria-label={`Revista Tempo — ${name}`}
title={name}
className={
footer ? 'tempo-footer-social' : 'tempo-header-social'
}
style={{ '--social-color': color } as CSSProperties}
>
<Icon size={footer ? 19 : 16} aria-hidden="true" />
</a>
))}
</div>
);
}