Lots of improvements
Some checks failed
CI / Python (lint + test) (push) Failing after 1m39s
CI / Frontend (lint + typecheck) (push) Failing after 1m49s
CI / Rust (lint + test) (push) Failing after 1m50s
Build and publish Docker image / build-and-push (push) Failing after 3m9s

This commit is contained in:
Andras Schmelczer 2026-04-04 10:45:48 +01:00
parent 3853b5dce7
commit b94cf17d75
33 changed files with 2587 additions and 1866 deletions

View file

@ -0,0 +1,60 @@
import { useState, useRef, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { SUPPORTED_LANGUAGES, type LanguageCode } from '../../i18n';
export default function LanguageDropdown() {
const { i18n } = useTranslation();
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const current = SUPPORTED_LANGUAGES.find((l) => l.code === i18n.language) ?? SUPPORTED_LANGUAGES[0];
useEffect(() => {
if (!open) return;
const handler = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, [open]);
const changeLanguage = (code: LanguageCode) => {
i18n.changeLanguage(code);
localStorage.setItem('language', code);
setOpen(false);
};
return (
<div className="relative" ref={ref}>
<button
onClick={() => setOpen((v) => !v)}
className="flex items-center gap-1 px-2 py-1.5 rounded bg-navy-800 hover:bg-navy-700 transition-colors text-sm"
aria-label="Language"
>
<span className="text-base leading-none">{current.flag}</span>
<svg className="w-3 h-3 text-warm-400" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M3 5l3 3 3-3" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</button>
{open && (
<div className="absolute right-0 top-9 w-40 bg-white dark:bg-warm-800 border border-warm-200 dark:border-warm-700 rounded-lg shadow-lg z-50 py-1">
{SUPPORTED_LANGUAGES.map((lang) => (
<button
key={lang.code}
onClick={() => changeLanguage(lang.code)}
className={`w-full flex items-center gap-2.5 px-3 py-1.5 text-sm ${
i18n.language === lang.code
? 'text-teal-600 dark:text-teal-400 font-medium bg-teal-50 dark:bg-teal-900/30'
: 'text-warm-700 dark:text-warm-300 hover:bg-warm-50 dark:hover:bg-warm-700'
}`}
>
<span className="text-base leading-none">{lang.flag}</span>
{lang.label}
</button>
))}
</div>
)}
</div>
);
}

View file

@ -16,7 +16,7 @@ export function Slider({ className, ...props }: SliderProps) {
{props.value?.map((_, i) => (
<SliderPrimitive.Thumb
key={i}
className="block h-6 w-6 rounded-full border-2 border-teal-600 dark:border-teal-500 bg-white dark:bg-navy-800 ring-offset-white dark:ring-offset-navy-950 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-teal-600 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 before:absolute before:left-1/2 before:top-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:h-11 before:w-11 before:rounded-full before:content-['']"
className="block h-6 w-6 cursor-pointer rounded-full border-2 border-teal-600 dark:border-teal-500 bg-white dark:bg-navy-800 ring-offset-white dark:ring-offset-navy-950 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-teal-600 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 before:absolute before:left-1/2 before:top-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:h-11 before:w-11 before:rounded-full before:content-['']"
/>
))}
</SliderPrimitive.Root>