diff --git a/frontend/src/lib/components/TranscriptionPanelHeader.svelte b/frontend/src/lib/components/TranscriptionPanelHeader.svelte
new file mode 100644
index 00000000..6b001378
--- /dev/null
+++ b/frontend/src/lib/components/TranscriptionPanelHeader.svelte
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+ {#if blockCount === 1}
+ {m.transcription_status_section()}
+ {:else}
+ {m.transcription_status_sections({ count: blockCount })}
+ {/if}
+ {#if formattedDate}
+ · {m.transcription_status_last_edited({ time: formattedDate })}
+ {/if}
+
+
+
+
+
diff --git a/frontend/src/lib/components/TranscriptionPanelHeader.svelte.test.ts b/frontend/src/lib/components/TranscriptionPanelHeader.svelte.test.ts
new file mode 100644
index 00000000..c2d12e9d
--- /dev/null
+++ b/frontend/src/lib/components/TranscriptionPanelHeader.svelte.test.ts
@@ -0,0 +1,108 @@
+import { describe, it, expect, vi } from 'vitest';
+import { render } from 'vitest-browser-svelte';
+import { page } from 'vitest/browser';
+import TranscriptionPanelHeader from './TranscriptionPanelHeader.svelte';
+
+describe('TranscriptionPanelHeader', () => {
+ it('should render Lesen and Bearbeiten buttons', async () => {
+ render(TranscriptionPanelHeader, {
+ mode: 'read',
+ hasBlocks: true,
+ blockCount: 3,
+ lastEditedAt: null,
+ onModeChange: () => {},
+ onClose: () => {}
+ });
+
+ await expect.element(page.getByText('Lesen')).toBeInTheDocument();
+ await expect.element(page.getByText('Bearbeiten')).toBeInTheDocument();
+ });
+
+ it('should disable Lesen button when hasBlocks is false', async () => {
+ render(TranscriptionPanelHeader, {
+ mode: 'edit',
+ hasBlocks: false,
+ blockCount: 0,
+ lastEditedAt: null,
+ onModeChange: () => {},
+ onClose: () => {}
+ });
+
+ const lesenBtn = document.querySelector('[data-testid="mode-read"]') as HTMLButtonElement;
+ expect(lesenBtn.getAttribute('aria-disabled')).toBe('true');
+ });
+
+ it('should call onModeChange when clicking Bearbeiten', async () => {
+ const onModeChange = vi.fn();
+ render(TranscriptionPanelHeader, {
+ mode: 'read',
+ hasBlocks: true,
+ blockCount: 3,
+ lastEditedAt: null,
+ onModeChange,
+ onClose: () => {}
+ });
+
+ const editBtn = document.querySelector('[data-testid="mode-edit"]')!;
+ editBtn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ expect(onModeChange).toHaveBeenCalledWith('edit');
+ });
+
+ it('should not call onModeChange when clicking disabled Lesen', async () => {
+ const onModeChange = vi.fn();
+ render(TranscriptionPanelHeader, {
+ mode: 'edit',
+ hasBlocks: false,
+ blockCount: 0,
+ lastEditedAt: null,
+ onModeChange,
+ onClose: () => {}
+ });
+
+ const readBtn = document.querySelector('[data-testid="mode-read"]')!;
+ readBtn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ expect(onModeChange).not.toHaveBeenCalled();
+ });
+
+ it('should call onClose when clicking close button', async () => {
+ const onClose = vi.fn();
+ render(TranscriptionPanelHeader, {
+ mode: 'read',
+ hasBlocks: true,
+ blockCount: 3,
+ lastEditedAt: null,
+ onModeChange: () => {},
+ onClose
+ });
+
+ const closeBtn = document.querySelector('[data-testid="panel-close"]')!;
+ closeBtn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ expect(onClose).toHaveBeenCalled();
+ });
+
+ it('should show singular block count for 1 block', async () => {
+ render(TranscriptionPanelHeader, {
+ mode: 'read',
+ hasBlocks: true,
+ blockCount: 1,
+ lastEditedAt: null,
+ onModeChange: () => {},
+ onClose: () => {}
+ });
+
+ await expect.element(page.getByText('1 Abschnitt')).toBeInTheDocument();
+ });
+
+ it('should show plural block count for multiple blocks', async () => {
+ render(TranscriptionPanelHeader, {
+ mode: 'read',
+ hasBlocks: true,
+ blockCount: 5,
+ lastEditedAt: null,
+ onModeChange: () => {},
+ onClose: () => {}
+ });
+
+ await expect.element(page.getByText('5 Abschnitte')).toBeInTheDocument();
+ });
+});