960696745f
- Migration 20261008180000: NextcloudFilesConfig (Organisation) und NextcloudFilesAccount (Mandant UND Benutzer per Zeilenschutz, nur verschlüsseltes App-Passwort) - Transportschicht ncRequest: feste Pfadanfänge, Segmentcodierung, keine Weiterleitungen, keine Cookies, Zeitgrenzen; Aufrufsperre pausiert den Ursprung nach jedem 429 und sperrt Zugangsschlüssel nach dem ersten 401 - Einstellungen: Adresse speichern/prüfen, Adresswechsel läuft alle Konten ab (mit Bestätigung) - Controller mit Verwalten nur auf Einstellungen, Seed, Modul, Registrierung in Web (Ladefunktion, Symbol folder, Navigation, Layout) und Reiter Einstellungen - RLS-Inventar fortgeschrieben, Testaufbau (tessera-nc-test) und E2E-Skripte Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
133 lines
4.3 KiB
TypeScript
133 lines
4.3 KiB
TypeScript
import type { ReactNode } from 'react';
|
|
import { getModuleIcon, type ModuleIconId } from '@/lib/module-identity';
|
|
|
|
const GLYPHS: Record<ModuleIconId, ReactNode> = {
|
|
radar: (
|
|
<>
|
|
<path d="M19.07 4.93A10 10 0 0 0 6.99 3.34" />
|
|
<path d="M4 6h.01" />
|
|
<path d="M2.29 9.62A10 10 0 1 0 21.31 8.35" />
|
|
<path d="M16.24 7.76A6 6 0 1 0 8.23 16.67" />
|
|
<path d="M12 18h.01" />
|
|
<path d="M17.99 11.66A6 6 0 0 1 15.77 16.67" />
|
|
<circle cx="12" cy="12" r="2" />
|
|
<path d="m13.41 10.59 5.66-5.66" />
|
|
</>
|
|
),
|
|
fuel: (
|
|
<>
|
|
<line x1="3" x2="15" y1="22" y2="22" />
|
|
<line x1="4" x2="14" y1="9" y2="9" />
|
|
<path d="M14 22V4a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v18" />
|
|
<path d="M14 13h2a2 2 0 0 1 2 2v2a2 2 0 0 0 2 2a2 2 0 0 0 2-2V9.83a2 2 0 0 0-.59-1.42L18 5" />
|
|
</>
|
|
),
|
|
certificate: (
|
|
<>
|
|
<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
|
|
<path d="m9 12 2 2 4-4" />
|
|
</>
|
|
),
|
|
globe: (
|
|
<>
|
|
<circle cx="12" cy="12" r="10" />
|
|
<path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" />
|
|
<path d="M2 12h20" />
|
|
</>
|
|
),
|
|
server: (
|
|
<>
|
|
<rect x="2" y="2" width="20" height="8" rx="2" />
|
|
<rect x="2" y="14" width="20" height="8" rx="2" />
|
|
<line x1="6" x2="6.01" y1="6" y2="6" />
|
|
<line x1="6" x2="6.01" y1="18" y2="18" />
|
|
</>
|
|
),
|
|
utensils: (
|
|
<>
|
|
<path d="M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2" />
|
|
<path d="M7 2v20" />
|
|
<path d="M21 15V2a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7" />
|
|
</>
|
|
),
|
|
'shopping-bag': (
|
|
<>
|
|
<path d="M16 10a4 4 0 0 1-8 0" />
|
|
<path d="M3.103 6.034h17.794" />
|
|
<path d="M3.4 5.467a2 2 0 0 0-.4 1.2V20a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6.667a2 2 0 0 0-.4-1.2l-2-2.667A2 2 0 0 0 17 2H7a2 2 0 0 0-1.6.8z" />
|
|
</>
|
|
),
|
|
cloud: <path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" />,
|
|
earth: (
|
|
<>
|
|
<circle cx="12" cy="12" r="10" />
|
|
<path d="M21.54 15H17a2 2 0 0 0-2 2v4.54" />
|
|
<path d="M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17" />
|
|
<path d="M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05" />
|
|
</>
|
|
),
|
|
folder: (
|
|
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
|
|
),
|
|
tile: (
|
|
<>
|
|
<path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z" />
|
|
<path d="m3.3 7 8.7 5 8.7-5" />
|
|
<path d="M12 22V12" />
|
|
</>
|
|
),
|
|
};
|
|
|
|
interface ModuleTileProps {
|
|
slug: string;
|
|
/** Kantenlaenge in Pixeln (Seitenleiste 20, Karten 40, Seitenkopf 36). */
|
|
size?: number;
|
|
/**
|
|
* `neutral` (Vorgabe): graue Flaeche, dunkelgraues Symbol.
|
|
* `accent`: Akzentflaeche mit passender Schrift — fuer das Modul im Fokus.
|
|
*/
|
|
tone?: 'neutral' | 'accent';
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* App-Kachel eines Moduls: abgerundetes Quadrat mit Symbol (siehe
|
|
* lib/module-identity.ts). Kontrast Symbol/Flaeche: neutral hell 7,4:1,
|
|
* dunkel 8,9:1; Akzent ueber `--primary-foreground`, das fuer jede
|
|
* persoenliche Akzentfarbe hell oder dunkel gewaehlt wird (lib/color.ts).
|
|
* Rein dekorativ — der Modulname steht immer als Text daneben.
|
|
*/
|
|
export function ModuleTile({ slug, size = 20, tone = 'neutral', className = '' }: ModuleTileProps) {
|
|
const icon = getModuleIcon(slug);
|
|
const glyph = Math.round(size * 0.6);
|
|
return (
|
|
<span
|
|
aria-hidden="true"
|
|
data-module-tile={slug}
|
|
data-tone={tone}
|
|
className={`inline-flex shrink-0 items-center justify-center ${
|
|
tone === 'accent' ? 'bg-primary text-primary-foreground' : 'bg-tile text-tile-foreground'
|
|
} ${className}`}
|
|
style={{
|
|
width: size,
|
|
height: size,
|
|
borderRadius: Math.max(4, Math.round(size * 0.24)),
|
|
}}
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width={glyph}
|
|
height={glyph}
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth={size <= 24 ? 2.2 : 1.9}
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
{GLYPHS[icon]}
|
|
</svg>
|
|
</span>
|
|
);
|
|
}
|