(null);
+ const [failed, setFailed] = useState(false);
+
+ const fetchFavicon = useCallback(async () => {
+ try {
+ new URL(url);
+ const res = await api.get<{ faviconUrl: string | null }>(
+ `/settings/favicon?url=${encodeURIComponent(url)}`,
+ );
+ if (res.data.faviconUrl) {
+ setFaviconUrl(res.data.faviconUrl);
+ setFailed(false);
+ } else {
+ setFailed(true);
+ }
+ } catch {
+ setFailed(true);
+ }
+ }, [url]);
+
+ useEffect(() => {
+ fetchFavicon();
+ }, [fetchFavicon]);
+
+ if (failed || !faviconUrl) {
+ return (
+
+ {label.charAt(0).toUpperCase()}
+
+ );
}
+
+ return (
+

setFailed(true)}
+ />
+ );
}
export function AppLayout() {
const { user, logout } = useAuth();
const navigate = useNavigate();
+ const [appsOpen, setAppsOpen] = useState(true);
const { data: externalLinks } = useQuery
({
queryKey: ['settings', 'external-links'],
@@ -72,13 +127,28 @@ export function AppLayout() {
Dashboard
- {/* Externe Links */}
+ {/* Externe Links (aufklappbar) */}
{externalLinks && externalLinks.length > 0 && (
<>
- Anwendungen
- {externalLinks.map((link) => {
- const favicon = getFaviconUrl(link.url);
- return (
+
+ {appsOpen &&
+ externalLinks.map((link) => (
-
{
- // Fallback: erstes Zeichen des Labels als Text-Icon
- const el = e.target as HTMLImageElement;
- el.style.display = 'none';
- const fallback = el.nextElementSibling;
- if (fallback)
- (fallback as HTMLElement).style.display =
- 'flex';
- }}
- />
-
- {link.label.charAt(0).toUpperCase()}
-
+
{link.label}
- );
- })}
+ ))}
>
)}