Files
Investidor-V2/resources/js/components/tempo-header-tools.tsx
T
2026-11-03 15:31:25 +02:00

157 lines
6.2 KiB
TypeScript

import { useEffect, useState } from 'react';
import { ChevronDown, CloudSun, Globe, MapPin } from 'lucide-react';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useTempoUi } from '@/components/tempo-brand';
import { getLanguages } from '@/translation/service';
import { withPortugueseFirst } from '@/translation/utils';
import type { TranslationLanguage } from '@/translation/types';
type Weather = { temp: number; description: string; humidity?: number };
export function TempoWeather() {
const { language } = useTempoUi();
const [weather, setWeather] = useState<Weather | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 8000);
fetch(`https://teste-info.tempo.co.mz/api/weather?lang=${language}`, {
signal: controller.signal,
credentials: 'omit',
})
.then((response) => (response.ok ? response.json() : null))
.then((data: { current?: Weather } | null) => {
if (data?.current && Number.isFinite(data.current.temp))
setWeather(data.current);
})
.catch(() => {
/* No guessed temperatures when the source is unavailable. */
})
.finally(() => {
clearTimeout(timer);
setLoading(false);
});
return () => {
clearTimeout(timer);
controller.abort();
};
}, [language]);
return (
<DropdownMenu modal={false}>
<DropdownMenuTrigger
className="tempo-utility-button"
aria-label="Temperatura em Maputo"
>
<CloudSun
size={19}
aria-hidden="true"
className="text-amber-300"
/>
<span>
{weather
? `${Math.round(weather.temp)}°C`
: loading
? '…'
: 'Maputo'}
</span>
<ChevronDown size={12} aria-hidden="true" />
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
sideOffset={12}
className="tempo-dropdown w-60 p-4"
>
<p className="mb-3 flex items-center gap-2 text-xs font-semibold text-slate-500">
<MapPin size={14} /> Maputo, Moçambique
</p>
{weather ? (
<>
<p className="text-3xl font-semibold text-slate-950">
{Math.round(weather.temp)}°C
</p>
<p className="mt-1 text-sm text-slate-600">
{weather.description}
</p>
{weather.humidity !== undefined && (
<p className="mt-3 text-xs text-slate-500">
Humidade: {weather.humidity}%
</p>
)}
</>
) : (
<p className="text-sm text-slate-600">
{loading
? 'A consultar a temperatura…'
: 'Temperatura temporariamente indisponível.'}
</p>
)}
<p className="mt-3 text-[11px] text-slate-400">
Meteorologia · Tempo
</p>
</DropdownMenuContent>
</DropdownMenu>
);
}
export function TempoLanguageSelector() {
const { language, setLanguage, t } = useTempoUi();
const [languages, setLanguages] = useState<TranslationLanguage[]>([
{ code: 'pt', name: 'Português' },
{ code: 'en', name: 'English' },
{ code: 'fr', name: 'Français' },
{ code: 'es', name: 'Español' },
]);
const [available, setAvailable] = useState(true);
useEffect(() => {
const controller = new AbortController();
getLanguages(controller.signal).then((items) => {
const portuguese = items.find((item) => item.code === 'pt');
if (!portuguese) throw new Error('Portuguese model unavailable');
setLanguages(withPortugueseFirst(items.filter((item) => item.code === 'pt' || !portuguese.targets || portuguese.targets.includes(item.code))));
}).catch(() => { if (!controller.signal.aborted) setAvailable(false); });
return () => controller.abort();
}, []);
return (
<DropdownMenu modal={false}>
<DropdownMenuTrigger
className="tempo-utility-button"
aria-label={t('Idioma da navegação')}
>
<Globe size={16} aria-hidden="true" />
<span className="text-xs font-semibold uppercase">
{language}
</span>
<ChevronDown size={12} aria-hidden="true" />
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
sideOffset={12}
className="tempo-dropdown min-w-44"
>
<DropdownMenuLabel className="text-xs text-slate-500">
{t('Navegação institucional')}
</DropdownMenuLabel>
{!available && <DropdownMenuLabel className="text-xs text-slate-500">Tradução temporariamente indisponível</DropdownMenuLabel>}
{languages.map(({ code, name }) => (
<DropdownMenuItem
key={code}
onSelect={() => setLanguage(code)}
className={`tempo-menu-item ${language === code ? 'bg-red-50 text-red-700' : ''}`}
>
<span className="w-6 text-[10px] font-bold uppercase">
{code}
</span>
{name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}