diff --git a/src/assets/team/bruno.jpg b/src/assets/team/bruno.jpg new file mode 100644 index 0000000..36e878c Binary files /dev/null and b/src/assets/team/bruno.jpg differ diff --git a/src/assets/team/ferdi.jpg b/src/assets/team/ferdi.jpg new file mode 100644 index 0000000..f1a0566 Binary files /dev/null and b/src/assets/team/ferdi.jpg differ diff --git a/src/assets/team/lea.jpg b/src/assets/team/lea.jpg new file mode 100644 index 0000000..4c284da Binary files /dev/null and b/src/assets/team/lea.jpg differ diff --git a/src/assets/team/livia.jpg b/src/assets/team/livia.jpg new file mode 100644 index 0000000..130ffa7 Binary files /dev/null and b/src/assets/team/livia.jpg differ diff --git a/src/assets/team/nora.jpg b/src/assets/team/nora.jpg new file mode 100644 index 0000000..5036413 Binary files /dev/null and b/src/assets/team/nora.jpg differ diff --git a/src/assets/team/reto.jpg b/src/assets/team/reto.jpg new file mode 100644 index 0000000..2caea47 Binary files /dev/null and b/src/assets/team/reto.jpg differ diff --git a/src/assets/team/sina.jpg b/src/assets/team/sina.jpg new file mode 100644 index 0000000..0e9a2bf Binary files /dev/null and b/src/assets/team/sina.jpg differ diff --git a/src/components/team/AgentAvatar.tsx b/src/components/team/AgentAvatar.tsx index fd1e62f..dd04d96 100644 --- a/src/components/team/AgentAvatar.tsx +++ b/src/components/team/AgentAvatar.tsx @@ -4,6 +4,7 @@ import { Check } from 'lucide-react' import type { TeamAgent } from '../../domain/teamAgent' import { AgentStatus } from '../../domain/teamAgent' import { AVATAR_TONE_COLOR } from './teamTokens' +import { AGENT_PHOTOS } from './agentPhotos' import { AGENT_STATUS_LABELS } from '../../lib/constants' import { DS_BG, DS_TEXT } from '../../lib/ds' @@ -39,15 +40,20 @@ export const AgentAvatar = memo(function AgentAvatar({ agent, size = 'medium', s const isActive = agent.status === AgentStatus.ACTIVE const statusLabel = AGENT_STATUS_LABELS[agent.status] ?? agent.status const badgePx = size === 'large' ? 20 : 14 + const photo = AGENT_PHOTOS[agent.id] + const label = `${agent.name}, ${agent.role}` return ( - {/* `alt` reicht MUI ausschliesslich an den img-Slot weiter, und ohne `src` - rendert Avatar gar kein — die Beschriftung landete damit nirgends - im DOM. Der zugängliche Name muss deshalb direkt auf das Element. */} + {/* Der zugängliche Name hängt an genau einem Element, nie an zweien: + mit Porträt trägt ihn das von MUI erzeugte über `alt`, ohne + Porträt der Container über role="img" und aria-label. `alt` allein + genügt nämlich nicht — ohne `src` rendert MUI gar kein , und der + Wert landet nirgends im DOM. */} + {/* Fällt das Bild aus, zeigt MUI wieder die Initialen statt eines Lochs. */} {initialsOf(agent.name)} diff --git a/src/components/team/__tests__/AgentCard.test.tsx b/src/components/team/__tests__/AgentCard.test.tsx index e410503..7d64d3c 100644 --- a/src/components/team/__tests__/AgentCard.test.tsx +++ b/src/components/team/__tests__/AgentCard.test.tsx @@ -139,9 +139,20 @@ describe('AgentCard', () => { expect(within(card).getByText(/Zuletzt aktiv|Noch nicht gelaufen/)).toBeInTheDocument() }) - it('rendert den Avatar mit den Initialen des Mitarbeiters', () => { + it('rendert das Porträt des Mitarbeiters', () => { renderWithProviders() + const portrait = screen.getByRole('img', { name: `${ferdi.name}, ${ferdi.role}` }) + expect(portrait).toHaveAttribute('src') + expect(portrait.getAttribute('src')).toBeTruthy() + }) + + it('fällt ohne hinterlegtes Porträt auf die Initialen zurück', () => { + // Ein Mitarbeiter, zu dem in `agentPhotos.ts` kein Bild liegt — so verhält + // sich die Karte, falls der Katalog später um jemanden ohne Foto wächst. + const ohneFoto = { ...ferdi, id: 'unbekannt' } + renderWithProviders() + const card = screen.getByRole('article') expect(within(card).getByText(ferdi.name.slice(0, 2).toUpperCase())).toBeInTheDocument() }) @@ -149,9 +160,8 @@ describe('AgentCard', () => { it('gibt dem Avatar einen zugänglichen Namen aus Name und Rolle', () => { renderWithProviders() - // `alt` allein genügt bei MUI nicht: ohne `src` rendert Avatar kein , - // und der Wert landet nirgends im DOM. Der Name muss deshalb über - // role="img" + aria-label direkt am Element hängen (§18). + // Der Name hängt an genau einem Element: mit Porträt am über `alt`, + // ohne Porträt am Container über role="img" + aria-label (§18). expect( screen.getByRole('img', { name: `${ferdi.name}, ${ferdi.role}` }), ).toBeInTheDocument() diff --git a/src/components/team/agentPhotos.ts b/src/components/team/agentPhotos.ts new file mode 100644 index 0000000..b022c73 --- /dev/null +++ b/src/components/team/agentPhotos.ts @@ -0,0 +1,33 @@ +/** + * Property On — Porträts der digitalen Mitarbeiter. + * + * Die Bilder stammen aus dem Konzeptdokument «Agenten_Bewirtschaftung_v2.html», + * wo sie als eingebettete JPEGs am jeweiligen Agenten hängen. Sie sind damit + * Teil der fachlichen Vorgabe, nicht eine gestalterische Zutat: das digitale + * Personalbüro zeigt Mitarbeiter mit Gesicht, nicht abstrakte Werkzeuge. + * + * Die Zuordnung liegt bewusst in der Darstellungsschicht und nicht in + * `domain/teamAgent.ts` oder den Mockdaten. Ein Domain-Typ, der Binärdateien + * importiert, wäre an den Bundler gekoppelt und liesse sich weder in einem Test + * noch später gegen ein echtes Backend sauber verwenden. + * + * Fehlt zu einer ID ein Bild, fällt `AgentAvatar` auf die Initialen zurück. + */ + +import bruno from '../../assets/team/bruno.jpg' +import ferdi from '../../assets/team/ferdi.jpg' +import lea from '../../assets/team/lea.jpg' +import livia from '../../assets/team/livia.jpg' +import nora from '../../assets/team/nora.jpg' +import reto from '../../assets/team/reto.jpg' +import sina from '../../assets/team/sina.jpg' + +export const AGENT_PHOTOS: Record = { + bruno, + ferdi, + lea, + livia, + nora, + reto, + sina, +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..88d9b17 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,5 @@ +/// + +// Ohne diese Referenz kennt TypeScript die von Vite bereitgestellten +// Modul-Deklarationen für Assets nicht — ein `import portrait from './x.jpg'` +// würde als Fehler gemeldet, obwohl der Bundler es korrekt auflöst.