feat: Beute L3–L5 (3D-Szene, Umbauen, Durchsicht, offline) und Diktat (T7 L1)
Beute L3 Runden 2+3 und Review-Fixes: - Glas-Holo ohne warmes Leuchten, Wanderboden-Fuss, Fokusflug mit Geist-Ansicht, Inhalte im Fokus, Rähmchen mit Art-Farbe beim Anheben - Hinweis-Geometrie je Zarge nur einmal je Layout gebaut; Drehen mit Pointer-Capture, endet auch bei Loslassen ausserhalb (rig-pointer.ts) - Tiefenunschärfe ab 55 fps, Leistungsprobe bei jeder Bewegungsstufe, Context-Loss-Listener aufgeräumt, Trefferboxen als skalierte Einheitsbox Beute L4 Umbauen (/colonies/[id]/hive): Entwurf mit Basis-Schnappschuss beim ersten Schritt, Rückgängig/Wiederholen, Hinweis bei Serverstand- Änderung, Konfliktblatt. Beute L5: - Durchsicht-Abschnitt „Beute“: Aufbau-Änderungen in der Durchsicht, Blatt-Formulare stoppen das Submit-Bubbling durch Portale (sonst speicherte „Speichern“ im Blatt die ganze Durchsicht), Fokus nach Rückgängig, Folgevorschläge beim Speichern - Offline: Beute im Offline-Paket, Warteschlange pending-hive-changes (DB v5), Abarbeiten in Reihenfolge, keine Online-Änderung überholt wartende, Hinweis bei abgelehnter Änderung mit „Warteschlange öffnen“ T7 L1 Diktat: Spracherkennungs-Kapsel (Web Speech API), Einwilligung standardmässig aus, Diktat-Knopf an Freitextfeldern, Seite „Sprache & Datenschutz“; axe-core als exakte devDependency. Backend: NotificationsService liest Empfänger und schreibt in einer Transaktion mit FOR KEY SHARE (paralleles Löschen eines Benutzers führte zu P2003 und verzögerten Vorschlägen; Ursache der wackeligen Endtests). Zwei e2e-Prüfungen gegen geteilten DB-Zustand entschärft. Gates: Frontend lint, typecheck, 2740 Tests, build; Backend lint, typecheck, 4266 Unit-Tests, build; parallele e2e 3x grün (978). Browser-Prüfung 1280/390 px, Tastatur, offline, reduced motion. Refs #19 #20 #21 #22 #27 #32 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
ab45d6e24e
commit
1a57de8573
123 files changed
+7639
-355
No files matched your search
@@ -4,6 +4,8 @@ import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PermissionResolverService } from '../auth/permissions/permission-resolver.service';
|
||||
import type { ColonyNextSteps } from '../autopilot/next-step-ranking';
|
||||
import { NextStepsService } from '../autopilot/next-steps.service';
|
||||
import { HiveService } from '../hive/hive.service';
|
||||
import { HiveCatalogService } from '../hive/hive-catalog.service';
|
||||
|
||||
describe('ApiaryFieldService', () => {
|
||||
let service: ApiaryFieldService;
|
||||
@@ -17,6 +19,8 @@ describe('ApiaryFieldService', () => {
|
||||
};
|
||||
let permissionResolver: { getPermissions: jest.Mock };
|
||||
let nextSteps: { forColonies: jest.Mock; warningCounts: jest.Mock };
|
||||
let hives: { getMany: jest.Mock };
|
||||
let hiveCatalogs: { listSystems: jest.Mock };
|
||||
|
||||
const stepsOf = (
|
||||
colonyId: string,
|
||||
@@ -87,10 +91,14 @@ describe('ApiaryFieldService', () => {
|
||||
),
|
||||
warningCounts: jest.fn().mockResolvedValue(new Map()),
|
||||
};
|
||||
hives = { getMany: jest.fn().mockResolvedValue({}) };
|
||||
hiveCatalogs = { listSystems: jest.fn().mockResolvedValue([]) };
|
||||
service = new ApiaryFieldService(
|
||||
prisma as unknown as PrismaService,
|
||||
permissionResolver as unknown as PermissionResolverService,
|
||||
nextSteps as unknown as NextStepsService,
|
||||
hives as unknown as HiveService,
|
||||
hiveCatalogs as unknown as HiveCatalogService,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -129,6 +137,40 @@ describe('ApiaryFieldService', () => {
|
||||
expect(prisma.task.findMany).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('carries the hives of the apiary colonies and the hive catalog', async () => {
|
||||
grant('colonies:read');
|
||||
const view = { colonyId: 'c2', systemId: 's1', version: 3 };
|
||||
hives.getMany.mockResolvedValue({ c2: view });
|
||||
const systems = [{ id: 's1', name: 'Zander', active: true, partTypes: [] }];
|
||||
hiveCatalogs.listSystems.mockResolvedValue(systems);
|
||||
|
||||
const pack = await service.offlinePack('apiary-1', 'u1');
|
||||
|
||||
expect(hives.getMany).toHaveBeenCalledWith(['c2', 'c10']);
|
||||
expect(pack.hives).toEqual({ c2: view });
|
||||
expect(pack.hiveCatalog).toEqual(systems);
|
||||
});
|
||||
|
||||
it('limits the hive catalog to active systems and those the hives use', async () => {
|
||||
grant('colonies:read');
|
||||
hives.getMany.mockResolvedValue({ c2: { colonyId: 'c2', systemId: 's2' } });
|
||||
hiveCatalogs.listSystems.mockResolvedValue([
|
||||
{ id: 's1', active: true },
|
||||
{ id: 's2', active: false },
|
||||
{ id: 's3', active: false },
|
||||
]);
|
||||
|
||||
const pack = await service.offlinePack('apiary-1', 'u1');
|
||||
|
||||
expect(pack.hiveCatalog?.map((system) => system.id)).toEqual(['s1', 's2']);
|
||||
});
|
||||
|
||||
it('keeps colonies without a hive setup out of the hives', async () => {
|
||||
grant('colonies:read');
|
||||
const pack = await service.offlinePack('apiary-1', 'u1');
|
||||
expect(pack.hives).toEqual({});
|
||||
});
|
||||
|
||||
it('fills the permitted sections and the last inspection date', async () => {
|
||||
grant('colonies:read', 'inspections:read', 'queens:read', 'tasks:read');
|
||||
const date = new Date('2026-05-01T10:00:00.000Z');
|
||||
|
||||
Reference in new issue
Block a user