Files
perinco-Pulse/src/components/layout/Header.tsx
T
schakal00783andClaude Opus 5 e908db2788 feat(assistant): Fragen an die Daten im Kopf der Anwendung
Management und Administration koennen oben rechts eine Frage in Worten
stellen - "wie viele Stunden im August", "welches Projekt hat noch
Budget", "wie ist die Auslastung".

Aufbau: das Modell bekommt eine Uebersicht (Kunden, laufende Projekte,
Eckdaten) und vier Werkzeuge - auswertung, projekte, buchungen,
personen. Damit fragt es gezielt nach, statt dass ihm der ganze Bestand
vorgelegt wird; 19.000 Buchungen passen in keinen Kontext. Die Werkzeuge
rechnen mit loadEntries und loadProjects, also mit denselben Funktionen
wie die Reports - der Assistent kann so keine zweite Wahrheit erzeugen.
Was einfliesst, begrenzt visibleUserIds wie ueberall sonst.

Benutzt wird die Responses-API von OpenAI: die neueren Modelle nehmen
Werkzeuge in /v1/chat/completions nur ohne Nachdenken an. Mit
"store: false" bleibt nichts bei OpenAI liegen, der Verlauf geht deshalb
je Runde komplett mit. Der Schluessel liegt verschluesselt in den
Einstellungen (AES-256-GCM wie das SSO-Secret) und wird nie ausgeliefert;
das Modell ist einstellbar, Vorgabe gpt-5.6-luna.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 17:31:51 +02:00

73 lines
3.0 KiB
TypeScript

import React from 'react';
import { useTheme } from '../../lib/theme';
import { THEME_PALETTES, THEME_PALETTE_LABELS } from '../../types';
import { Icon } from '../ui/Icon';
import { PulseMark } from './PulseMark';
import { Assistant } from '../assistant/Assistant';
interface HeaderProps {
title: string;
subtitle?: string;
onToggleSidebar: () => void;
isMobile: boolean;
}
export const Header: React.FC<HeaderProps> = ({ title, subtitle, onToggleSidebar, isMobile }) => {
const { theme, preference, toggle, useSystem, palette, setPalette } = useTheme();
const isDark = theme === 'dark';
return (
/* Feste Hoehe (--app-bar-height), damit die Seitenleiste bündig anschließt. */
<header className="app-header relative flex h-[var(--app-bar-height)] shrink-0 items-center gap-3 border-b border-ink-200 bg-surface px-4 md:px-6">
{isMobile ? (
<PulseMark size={34} />
) : (
<button
onClick={onToggleSidebar}
className="rounded-lg p-2 text-ink-500 transition hover:bg-ink-100 hover:text-ink-800"
title="Menü ein-/ausklappen"
aria-label="Menü ein-/ausklappen"
>
<Icon name="menu" strokeWidth={2} />
</button>
)}
<div className="min-w-0 flex-1">
<h1 className="truncate text-lg leading-tight text-ink-900">{title}</h1>
{subtitle && <p className="truncate text-xs font-semibold text-ink-400">{subtitle}</p>}
</div>
<div className="flex shrink-0 items-center gap-2">
{/* Nur Management und Administration - zeigt Zahlen ueber alle Mitarbeiter. */}
<Assistant />
<label className="sr-only" htmlFor="theme-palette">
Design
</label>
<select
id="theme-palette"
value={palette}
onChange={(event) => setPalette(event.target.value as typeof palette)}
title={`Design: ${THEME_PALETTE_LABELS[palette].hint}`}
className="max-w-[110px] rounded-xl border border-ink-200 bg-surface px-2.5 py-2 text-xs font-bold text-ink-600 outline-none transition hover:border-brand-300 hover:text-brand-700 focus:border-brand-500 sm:max-w-none sm:px-3"
>
{THEME_PALETTES.map((option) => (
<option key={option} value={option}>
{THEME_PALETTE_LABELS[option].name}
</option>
))}
</select>
<button
onClick={toggle}
onDoubleClick={useSystem}
title={`${isDark ? 'Helles' : 'Dunkles'} Design${preference === null ? ' (folgt dem System)' : ' · Doppelklick: dem System folgen'}`}
aria-label={`${isDark ? 'Helles' : 'Dunkles'} Design einschalten`}
className="flex items-center gap-2 rounded-xl border border-ink-200 px-2.5 py-2 text-xs font-bold text-ink-600 transition hover:border-brand-300 hover:text-brand-700 sm:px-3"
>
<Icon name={isDark ? 'sun' : 'moon'} className="h-4 w-4" />
<span className="hidden lg:inline">{isDark ? 'Hell' : 'Dunkel'}</span>
</button>
</div>
</header>
);
};