Integrar navbar e sobre dos programadores preservando identidade e acessos

This commit is contained in:
2026-10-20 17:23:45 +02:00
3 changed files with 275 additions and 228 deletions
+62 -10
View File
@@ -301,23 +301,42 @@
}
}
@media (min-width: 1280px) {
.tempo-header-inner {
display: flex;
min-height: 88px;
gap: 0.75rem;
/* Shared columns keep each portal area below its institutional link. */
.tempo-header-layout {
display: grid;
grid-template-columns: auto repeat(4, max-content) minmax(0, 1fr) auto;
column-gap: 0.25rem;
max-width: 1440px;
margin-inline: auto;
padding-inline: 2rem;
}
.tempo-header-inner {
grid-column: 1 / -1;
grid-template-columns: subgrid;
min-height: 88px;
column-gap: normal;
max-width: none;
margin: 0;
padding: 0;
}
.tempo-logo-link {
margin-right: 0.75rem;
}
.tempo-header-logo {
height: 64px;
}
.tempo-header[data-expanded] .tempo-primary-navigation,
.tempo-primary-navigation {
display: block;
flex: 1;
display: grid;
grid-area: 1 / 2 / 2 / 6;
grid-template-columns: subgrid;
border: 0;
}
.tempo-primary-navigation > ul {
justify-content: center;
gap: 0.25rem;
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
gap: normal;
padding: 0;
}
.tempo-nav-trigger {
@@ -332,6 +351,8 @@
}
.tempo-header-extras {
display: flex;
grid-area: 1 / 6;
justify-self: end;
flex-shrink: 0;
padding: 0 0.75rem;
border-top: 0;
@@ -347,16 +368,47 @@
.tempo-header-toggle {
display: none;
}
.tempo-header-actions {
grid-area: 1 / 7;
}
.tempo-section-bar {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
}
.tempo-section-inner {
padding-inline: 2rem;
grid-column: 1 / -1;
grid-template-columns: subgrid;
gap: normal;
max-width: none;
margin: 0;
padding-inline: 0;
}
.tempo-section-navigation {
display: grid;
grid-area: 1 / 2 / 2 / 5;
grid-template-columns: subgrid;
}
.tempo-section-navigation > ul {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
gap: normal;
}
.tempo-section-navigation .tempo-nav-trigger {
padding-inline: 1.25rem;
padding-inline: 0.625rem;
}
.tempo-primary-navigation .tempo-nav-trigger,
.tempo-section-navigation .tempo-nav-trigger {
justify-content: flex-start;
}
.tempo-interest-button {
padding-inline: 0.85rem;
font-size: 0.8125rem;
}
.tempo-section-inner .tempo-interest-button {
grid-area: 1 / 6 / 2 / -1;
}
}
.tempo-header :is(a, button):focus-visible {
outline: 2px solid #fff;
+213 -199
View File
@@ -127,223 +127,237 @@ export function PortalGroupedMenu() {
}
}}
>
<div className="tempo-header-inner">
<Link
href="/"
aria-label="InvesteMoz — início"
className="tempo-logo-link"
>
<PortalProductIdentity />
</Link>
<nav
id={institutionalId}
aria-label="Navegação institucional"
className="tempo-primary-navigation"
>
<ul>
<li>
<Link
href="/"
className="tempo-nav-trigger"
aria-current={
url.split('?')[0] === '/'
? 'page'
: undefined
}
data-current-group={
url.split('?')[0] === '/' || undefined
}
>
{t('Início')}
</Link>
</li>
<li>
<a
href="https://revista.tempo.co.mz/"
target="_blank"
rel="noopener noreferrer"
className="tempo-nav-trigger"
aria-label={`${t('Revista Tempo')} — ${t('Abre num novo separador')}`}
>
{t('Revista Tempo')}
<ArrowUpRight size={14} aria-hidden="true" />
</a>
</li>
{[
['/sobre', 'Sobre nós'],
['/contacto', 'Contacto'],
].map(([href, label]) => (
<li key={href}>
<div className="tempo-header-layout">
<div className="tempo-header-inner">
<Link
href="/"
aria-label="InvesteMoz — início"
className="tempo-logo-link"
>
<PortalProductIdentity />
</Link>
<nav
id={institutionalId}
aria-label="Navegação institucional"
className="tempo-primary-navigation"
>
<ul>
<li>
<Link
href={href}
href="/"
className="tempo-nav-trigger"
aria-current={
url.split('?')[0] === href
url.split('?')[0] === '/'
? 'page'
: undefined
}
data-current-group={
url.split('?')[0] === href || undefined
url.split('?')[0] === '/' || undefined
}
>
{t(label)}
{t('Início')}
</Link>
</li>
))}
</ul>
</nav>
<div id={toolsId} className="tempo-header-extras">
<div className="tempo-header-utilities">
<div className="flex items-center gap-1">
<TempoWeather />
<TempoLanguageSelector />
</div>
<TempoSocialLinks />
</div>
</div>
<div className="tempo-header-actions">
{props.auth?.user ? (
<DropdownMenu modal={false}>
<DropdownMenuTrigger className="tempo-login-button">
<UserRound size={16} aria-hidden="true" />
<span className="max-w-24 truncate">
{props.auth.user.name || t('A minha área')}
</span>
<ChevronDown size={12} aria-hidden="true" />
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
sideOffset={12}
collisionPadding={12}
className="tempo-dropdown max-h-[75vh] w-72 max-w-[calc(100vw-1.5rem)] overflow-y-auto p-2"
>
<DropdownMenuLabel className="truncate text-xs text-slate-500">
{props.auth.user.email}
</DropdownMenuLabel>
{items(accountGroups)}
<DropdownMenuSeparator />
<DropdownMenuItem
asChild
className="tempo-menu-item text-red-700"
<li>
<a
href="https://revista.tempo.co.mz/"
target="_blank"
rel="noopener noreferrer"
className="tempo-nav-trigger"
aria-label={`${t('Revista Tempo')} — ${t('Abre num novo separador')}`}
>
{t('Revista Tempo')}
<ArrowUpRight
size={14}
aria-hidden="true"
/>
</a>
</li>
{[
['/sobre', 'Sobre nós'],
['/contacto', 'Contacto'],
].map(([href, label]) => (
<li key={href}>
<Link
href={logout()}
as="button"
type="button"
disabled={loggingOut}
data-test="portal-logout-button"
onClick={() => router.flushAll()}
onStart={() => setLoggingOut(true)}
onFinish={() => setLoggingOut(false)}
href={href}
className="tempo-nav-trigger"
aria-current={
url.split('?')[0] === href
? 'page'
: undefined
}
data-current-group={
url.split('?')[0] === href ||
undefined
}
>
<LogOut size={16} aria-hidden="true" />
{loggingOut
? 'A terminar sessão…'
: t('Terminar sessão')}
{t(label)}
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : (
<Link href="/login" className="tempo-login-button">
<UserRound size={15} aria-hidden="true" />
{t('Entrar')}
</Link>
)}
</div>
<button
ref={toggle}
type="button"
className="tempo-header-toggle"
aria-label={
expanded
? 'Fechar menu e ferramentas'
: 'Abrir menu e ferramentas'
}
aria-expanded={expanded}
aria-controls={`${institutionalId} ${toolsId}`}
onClick={() => setExpanded(!expanded)}
>
{expanded ? (
<X size={22} aria-hidden="true" />
) : (
<Menu size={22} aria-hidden="true" />
)}
</button>
</div>
<div className="tempo-section-bar">
<div className="tempo-section-inner">
<Link
href="/investidor/manifestacoes"
className="tempo-panel-summary"
>
<span>{t('O meu painel')}</span>
<span
className="tempo-panel-count"
aria-live="polite"
aria-atomic="true"
>
{requestCount}{' '}
{t(
requestCount === 1
? 'pedido recebido'
: 'pedidos recebidos',
)}
</span>
</Link>
<nav
aria-label="Áreas do portal"
className="tempo-section-navigation"
>
<ul>
{groups
.filter((group) =>
[
'discovery',
'realization',
'support',
].includes(group.id),
)
.map((group) => (
<li key={group.id}>
<DropdownMenu modal={false}>
<DropdownMenuTrigger
className="tempo-nav-trigger"
data-current-group={
current === group.id ||
undefined
}
>
{t(group.label)}
<ChevronDown
size={14}
aria-hidden="true"
/>
{current === group.id && (
<span className="sr-only">
Grupo actual
</span>
)}
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
sideOffset={8}
collisionPadding={12}
className="tempo-dropdown max-h-[min(75vh,36rem)] w-72 max-w-[calc(100vw-1.5rem)] overflow-y-auto p-2"
>
{items(group.children ?? [])}
</DropdownMenuContent>
</DropdownMenu>
</li>
))}
</li>
))}
</ul>
</nav>
<Link
href="/manifestar-interesse"
className="tempo-interest-button"
<div id={toolsId} className="tempo-header-extras">
<div className="tempo-header-utilities">
<div className="flex items-center gap-1">
<TempoWeather />
<TempoLanguageSelector />
</div>
<TempoSocialLinks />
</div>
</div>
<div className="tempo-header-actions">
{props.auth?.user ? (
<DropdownMenu modal={false}>
<DropdownMenuTrigger className="tempo-login-button">
<UserRound size={16} aria-hidden="true" />
<span className="max-w-24 truncate">
{props.auth.user.name ||
t('A minha área')}
</span>
<ChevronDown size={12} aria-hidden="true" />
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
sideOffset={12}
collisionPadding={12}
className="tempo-dropdown max-h-[75vh] w-72 max-w-[calc(100vw-1.5rem)] overflow-y-auto p-2"
>
<DropdownMenuLabel className="truncate text-xs text-slate-500">
{props.auth.user.email}
</DropdownMenuLabel>
{items(accountGroups)}
<DropdownMenuSeparator />
<DropdownMenuItem
asChild
className="tempo-menu-item text-red-700"
>
<Link
href={logout()}
as="button"
type="button"
disabled={loggingOut}
data-test="portal-logout-button"
onClick={() => router.flushAll()}
onStart={() => setLoggingOut(true)}
onFinish={() =>
setLoggingOut(false)
}
>
<LogOut
size={16}
aria-hidden="true"
/>
{loggingOut
? 'A terminar sessão…'
: t('Terminar sessão')}
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : (
<Link href="/login" className="tempo-login-button">
<UserRound size={15} aria-hidden="true" />
{t('Entrar')}
</Link>
)}
</div>
<button
ref={toggle}
type="button"
className="tempo-header-toggle"
aria-label={
expanded
? 'Fechar menu e ferramentas'
: 'Abrir menu e ferramentas'
}
aria-expanded={expanded}
aria-controls={`${institutionalId} ${toolsId}`}
onClick={() => setExpanded(!expanded)}
>
{t('Manifestar interesse')}
</Link>
{expanded ? (
<X size={22} aria-hidden="true" />
) : (
<Menu size={22} aria-hidden="true" />
)}
</button>
</div>
<div className="tempo-section-bar">
<div className="tempo-section-inner">
<Link
href="/investidor/manifestacoes"
className="tempo-panel-summary"
>
<span>{t('O meu painel')}</span>
<span
className="tempo-panel-count"
aria-live="polite"
aria-atomic="true"
>
{requestCount}{' '}
{t(
requestCount === 1
? 'pedido recebido'
: 'pedidos recebidos',
)}
</span>
</Link>
<nav
aria-label="Áreas do portal"
className="tempo-section-navigation"
>
<ul>
{groups
.filter((group) =>
[
'discovery',
'realization',
'support',
].includes(group.id),
)
.map((group) => (
<li key={group.id}>
<DropdownMenu modal={false}>
<DropdownMenuTrigger
className="tempo-nav-trigger"
data-current-group={
current === group.id ||
undefined
}
>
{t(group.label)}
<ChevronDown
size={14}
aria-hidden="true"
/>
{current === group.id && (
<span className="sr-only">
Grupo actual
</span>
)}
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
sideOffset={8}
collisionPadding={12}
className="tempo-dropdown max-h-[min(75vh,36rem)] w-72 max-w-[calc(100vw-1.5rem)] overflow-y-auto p-2"
>
{items(
group.children ?? [],
)}
</DropdownMenuContent>
</DropdownMenu>
</li>
))}
</ul>
</nav>
<Link
href="/manifestar-interesse"
className="tempo-interest-button"
>
{t('Manifestar interesse')}
</Link>
</div>
</div>
</div>
</header>
-19
View File
@@ -5,7 +5,6 @@ import { AboutStory } from '@/components/institutional-source/AboutStory';
import { MissionVision } from '@/components/institutional-source/MissionVision';
import { CompanyValues } from '@/components/institutional-source/CompanyValues';
import BackButton from '@/components/institutional-source/BackButton';
import { AdvertisementSlot } from '@/components/institutional-source/source-advertisement-slot';
const AboutPage: React.FC = () => {
const [loading, setLoading] = useState(true);
@@ -45,29 +44,11 @@ const AboutPage: React.FC = () => {
</div>
</header>
<AdvertisementSlot
page="about"
position="top"
slot="about_top"
limit={1}
fallbackToUnslotted
className=""
/>
<div className="text-white">
<AboutStory />
<MissionVision />
<CompanyValues />
</div>
<AdvertisementSlot
page="about"
position="bottom"
slot="about_bottom"
limit={2}
fallbackToUnslotted
className="py-4"
/>
</div>
);
};