diff --git a/packages/core-service/src/core/settings/settings.controller.ts b/packages/core-service/src/core/settings/settings.controller.ts index 3e6edfb..f762637 100644 --- a/packages/core-service/src/core/settings/settings.controller.ts +++ b/packages/core-service/src/core/settings/settings.controller.ts @@ -15,18 +15,16 @@ import { RedisService } from '../../redis/redis.service'; /** * Ein externer Link fuer die Sidebar-Navigation. + * Icons werden im Frontend automatisch als Favicon der URL geladen. */ interface ExternalLink { id: string; label: string; url: string; - /** Base64-encodiertes Icon (data URI), z.B. "data:image/png;base64,..." */ - icon?: string; sortOrder: number; } const EXTERNAL_LINKS_KEY = 'platform_external_links'; -const MAX_ICON_SIZE = 100_000; // ~100KB Base64 @ApiTags('Settings') @Controller('settings') @@ -76,11 +74,6 @@ export class SettingsController { if (!link.url?.trim()) { throw new BadRequestException('Jeder Link braucht eine URL'); } - if (link.icon && link.icon.length > MAX_ICON_SIZE) { - throw new BadRequestException( - `Icon fuer "${link.label}" ist zu gross (max ~75KB)`, - ); - } } // Sortierung sicherstellen @@ -88,7 +81,6 @@ export class SettingsController { id: link.id || randomUUID(), label: link.label.trim(), url: link.url.trim(), - icon: link.icon || undefined, sortOrder: link.sortOrder ?? index, })); diff --git a/packages/frontend/src/admin/AdminExternalLinksPage.tsx b/packages/frontend/src/admin/AdminExternalLinksPage.tsx index e3ff546..8633765 100644 --- a/packages/frontend/src/admin/AdminExternalLinksPage.tsx +++ b/packages/frontend/src/admin/AdminExternalLinksPage.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import api from '../api/client'; @@ -7,11 +7,20 @@ function generateId(): string { return Date.now().toString(36) + Math.random().toString(36).slice(2, 10); } +/** Favicon-URL aus einer Website-URL ableiten (Google Favicon Service) */ +function getFaviconUrl(url: string): string | null { + try { + const domain = new URL(url).hostname; + return `https://www.google.com/s2/favicons?domain=${domain}&sz=32`; + } catch { + return null; + } +} + interface ExternalLink { id: string; label: string; url: string; - icon?: string; sortOrder: number; } @@ -84,60 +93,43 @@ function LinkRow({ isFirst: boolean; isLast: boolean; }) { - const fileInputRef = useRef(null); - - const handleIconUpload = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - - // Max 75KB - if (file.size > 75_000) { - alert('Icon darf max. 75KB gross sein'); - return; - } - - const reader = new FileReader(); - reader.onload = () => { - onChange({ ...link, icon: reader.result as string }); - }; - reader.readAsDataURL(file); - }; + const faviconUrl = getFaviconUrl(link.url); return (
- {/* Icon */} + {/* Favicon-Vorschau */}
fileInputRef.current?.click()} style={{ - width: 40, - height: 40, + width: 36, + height: 36, borderRadius: 'var(--radius-sm)', - border: '1px dashed var(--color-border)', + border: '1px solid var(--color-border)', display: 'flex', alignItems: 'center', justifyContent: 'center', - cursor: 'pointer', overflow: 'hidden', background: 'var(--color-bg)', }} - title="Icon hochladen (PNG, SVG, max 75KB)" > - {link.icon ? ( + {faviconUrl ? ( { + (e.target as HTMLImageElement).style.display = 'none'; + }} /> ) : ( - + + )}
-
{/* Label */} @@ -308,7 +294,6 @@ export function AdminExternalLinksPage() { id: generateId(), label: '', url: '', - icon: undefined, sortOrder: prev.length, }, ]); @@ -374,7 +359,7 @@ export function AdminExternalLinksPage() { }} > Links zu externen Anwendungen, die in der Sidebar fuer alle Benutzer - angezeigt werden. + angezeigt werden. Das Icon wird automatisch von der Webseite geladen.

@@ -398,11 +383,21 @@ export function AdminExternalLinksPage() { style={{ margin: '0 auto 1rem' }} > - +

Noch keine externen Links konfiguriert.

+

+ Klicke auf “Link hinzufuegen” um einen externen Link + zur Sidebar hinzuzufuegen. +

) : (
@@ -509,9 +504,18 @@ export function AdminExternalLinksPage() { color: '#1e40af', }} > - Hinweis: Externe Links werden in der Sidebar fuer alle - angemeldeten Benutzer angezeigt. Icons sollten quadratisch sein (z.B. - 32x32px) und als PNG, SVG oder JPEG hochgeladen werden (max. 75KB). + Hinweis: Das Icon wird automatisch als Favicon der + jeweiligen Webseite geladen. Gib eine vollstaendige URL inkl.{' '} + + https:// + {' '} + ein.
); diff --git a/packages/frontend/src/shell/AppLayout.tsx b/packages/frontend/src/shell/AppLayout.tsx index e5208b6..0e586df 100644 --- a/packages/frontend/src/shell/AppLayout.tsx +++ b/packages/frontend/src/shell/AppLayout.tsx @@ -9,10 +9,19 @@ interface ExternalLink { id: string; label: string; url: string; - icon?: string; sortOrder: number; } +/** Favicon-URL aus einer Website-URL ableiten (Google Favicon Service) */ +function getFaviconUrl(url: string): string | null { + try { + const domain = new URL(url).hostname; + return `https://www.google.com/s2/favicons?domain=${domain}&sz=32`; + } catch { + return null; + } +} + export function AppLayout() { const { user, logout } = useAuth(); const navigate = useNavigate(); @@ -67,56 +76,59 @@ export function AppLayout() { {externalLinks && externalLinks.length > 0 && ( <>
Anwendungen
- {externalLinks.map((link) => ( - - {link.icon ? ( - - ) : ( + {externalLinks.map((link) => { + const favicon = getFaviconUrl(link.url); + return ( + + {favicon ? ( + + ) : ( + + + + + + )} + {link.label} - - - + + - )} - {link.label} - - - - - - ))} + + ); + })} )}