fix: Feinschliff nach Browserprüfung (Sprachnotiz-Text, offline Volk, Beute-SVG)
- „Gesagt wurde:“ zeigt an, dass der Text aufklappbar ist - /voice/new nennt offline das Volk aus den Offline-Paketen - Einstellungen: Überschrift „Sprachnotizen“ ohne „in Vorbereitung“ - Beute-SVG (role=img) ohne fokussierbare Kinder (axe nested- interactive); Tastaturweg bleibt die Gliederung. Dabei behoben: der Editor fokussierte nach dem Entfernen die SVG-Form statt der Listenzeile. Gates: Frontend lint, typecheck, 3110 Tests, build. Refs #27 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
6c33a6de3b
commit
1763d953b6
9 files changed
+76
-23
No files matched your search
@@ -213,6 +213,13 @@ describe("VoiceSettingsPage", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("has no leftover „in Vorbereitung“ heading", () => {
|
||||
renderPage();
|
||||
|
||||
expect(screen.getByRole("heading", { name: "Sprachnotizen" })).toBeInTheDocument();
|
||||
expect(document.body.textContent).not.toContain("in Vorbereitung");
|
||||
});
|
||||
|
||||
it("names the retention periods in words", () => {
|
||||
renderPage();
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export const VOICE_PRIVACY_SECTIONS: readonly PrivacySection[] = [
|
||||
},
|
||||
{
|
||||
id: "notes",
|
||||
title: "Sprachnotizen (in Vorbereitung)",
|
||||
title: "Sprachnotizen",
|
||||
paragraphs: [
|
||||
`Der Ton bleibt in deinem Cluster. Er wird nach dem Abschreiben gelöscht, spätestens nach ${VOICE_RETENTION.audio}.`,
|
||||
`Der abgeschriebene Text geht zur Auswertung an Anthropic. Er bleibt, bis du ihn übernimmst oder verwirfst, höchstens ${VOICE_RETENTION.content}.`,
|
||||
|
||||
@@ -6,6 +6,8 @@ import { toLocalIsoDate, todayLocalIsoDate } from "@/lib/dates";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { StandRound } from "@/lib/rounds/round-state";
|
||||
import { fetchVoiceConfig } from "@/lib/voice/api";
|
||||
import { fetchColonies } from "@/lib/colonies/api";
|
||||
import { listPacks } from "@/lib/apiaries/offline-pack";
|
||||
import type { VoiceRecorderProps } from "@/components/voice/voice-recorder";
|
||||
import NewVoiceNotePage from "./page";
|
||||
|
||||
@@ -76,6 +78,11 @@ vi.mock("@/components/voice/voice-recorder", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/apiaries/offline-pack", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("@/lib/apiaries/offline-pack")>()),
|
||||
listPacks: vi.fn().mockResolvedValue([]),
|
||||
}));
|
||||
|
||||
const CONFIG = {
|
||||
recordingEnabled: true,
|
||||
extractionEnabled: true,
|
||||
@@ -111,6 +118,22 @@ describe("NewVoiceNotePage", () => {
|
||||
expect(screen.getByText("Sprich in ganzen Sätzen, nur du.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("names the colony from the offline packs when the colony list cannot be loaded", async () => {
|
||||
query = new URLSearchParams({ colonyId: "c-9", date: "2026-10-14" });
|
||||
vi.mocked(fetchColonies).mockRejectedValueOnce(new TypeError("Failed to fetch"));
|
||||
vi.mocked(listPacks).mockResolvedValueOnce([
|
||||
{
|
||||
apiaryId: "a-1",
|
||||
ownerUserId: "user-1",
|
||||
savedAt: "2026-10-15T08:00:00.000Z",
|
||||
pack: { colonies: [{ id: "c-9", code: "V-02", status: "ACTIVE" }] },
|
||||
},
|
||||
] as never);
|
||||
renderPage();
|
||||
|
||||
expect(await screen.findByText("V-02 · 14.10.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const daysAgo = (days: number) => toLocalIsoDate(new Date(Date.now() - days * 86_400_000));
|
||||
|
||||
it.each([
|
||||
|
||||
@@ -6,7 +6,9 @@ import { useSearchParams } from "next/navigation";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ArrowRight, Mic } from "lucide-react";
|
||||
import { fetchColonies } from "@/lib/colonies/api";
|
||||
import { useAuth } from "@/lib/auth/auth-context";
|
||||
import { useAuth, useCurrentUserId } from "@/lib/auth/auth-context";
|
||||
import { OFFLINE_PACKS_QUERY_KEY, listPacks, livingColoniesFromPacks } from "@/lib/apiaries/offline-pack";
|
||||
import { isNetworkError } from "@/lib/http/fetch-with-timeout";
|
||||
import { toLocalIsoDate, todayLocalIsoDate } from "@/lib/dates";
|
||||
import { useStandRound } from "@/lib/rounds/use-stand-round";
|
||||
import type { StandRound } from "@/lib/rounds/round-state";
|
||||
@@ -119,7 +121,17 @@ function VoiceCaptureForm({ initialColonyId, initialDate, roundApiaryId }: Captu
|
||||
const locked = busy || saved;
|
||||
const [savedRound, setSavedRound] = useState<StandRound | null>(null);
|
||||
const stand = useStandRound(roundApiaryId, colonyId);
|
||||
const { data: colonies } = useQuery({ queryKey: ["colonies"], queryFn: () => fetchColonies() });
|
||||
const userId = useCurrentUserId();
|
||||
const server = useQuery({ queryKey: ["colonies"], queryFn: () => fetchColonies() });
|
||||
// Without network the loaded stands know the colony's code (never the shared cache: pack colonies are partial).
|
||||
const isOffline = server.data === undefined && isNetworkError(server.error ?? server.failureReason);
|
||||
const packs = useQuery({
|
||||
queryKey: [...OFFLINE_PACKS_QUERY_KEY, userId, "living-colonies"],
|
||||
queryFn: async () => livingColoniesFromPacks(await listPacks(userId ?? "")),
|
||||
enabled: isOffline && userId !== null && colonyId !== "",
|
||||
networkMode: "always",
|
||||
});
|
||||
const colonies = server.data ?? packs.data;
|
||||
const colonyCode = colonies?.find((colony) => colony.id === colonyId)?.code;
|
||||
|
||||
const heading = colonyId ? `${colonyCode ?? "Volk"} · ${shortDay(date)}` : "Volk wählen";
|
||||
|
||||
Reference in new issue
Block a user