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
@@ -2,6 +2,8 @@ import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PermissionResolverService } from '../auth/permissions/permission-resolver.service';
|
||||
import type { ColonyNextSteps } from '../autopilot/next-step-ranking';
|
||||
import { NextStepsService } from '../autopilot/next-steps.service';
|
||||
import { HiveCatalogService } from '../hive/hive-catalog.service';
|
||||
import { HiveService } from '../hive/hive.service';
|
||||
import { LIVING_COLONY_WHERE } from '../colonies/colony-status';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import type {
|
||||
@@ -12,6 +14,15 @@ import type {
|
||||
|
||||
const INSPECTIONS_PER_COLONY = 3;
|
||||
|
||||
/** Active systems plus the ones the pack's hives use (a hidden system still builds an old hive). */
|
||||
function activeOrUsed<S extends { id: string; active: boolean }>(
|
||||
systems: S[],
|
||||
hives: Record<string, { systemId: string }>,
|
||||
): S[] {
|
||||
const used = new Set(Object.values(hives).map((hive) => hive.systemId));
|
||||
return systems.filter((system) => system.active || used.has(system.id));
|
||||
}
|
||||
|
||||
type PackColony = OfflinePackV1['colonies'][number];
|
||||
|
||||
interface FieldData {
|
||||
@@ -33,6 +44,8 @@ export class ApiaryFieldService {
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly permissionResolver: PermissionResolverService,
|
||||
private readonly nextSteps: NextStepsService,
|
||||
private readonly hives: HiveService,
|
||||
private readonly hiveCatalogs: HiveCatalogService,
|
||||
) {}
|
||||
|
||||
async colonyOverview(
|
||||
@@ -45,12 +58,14 @@ export class ApiaryFieldService {
|
||||
async offlinePack(apiaryId: string, userId: string): Promise<OfflinePackV1> {
|
||||
const { apiary, colonies, permissions, overview, nextSteps } =
|
||||
await this.load(apiaryId, userId);
|
||||
const [queens, inspections, tasks] = await Promise.all([
|
||||
const [queens, inspections, tasks, hives, hiveCatalog] = await Promise.all([
|
||||
permissions.has('queens:read') ? this.loadQueens(colonies) : null,
|
||||
permissions.has('inspections:read')
|
||||
? this.loadInspections(colonies)
|
||||
: null,
|
||||
permissions.has('tasks:read') ? this.loadTasks(apiaryId, colonies) : null,
|
||||
this.hives.getMany(colonies.map((colony) => colony.id)),
|
||||
this.hiveCatalogs.listSystems(),
|
||||
]);
|
||||
return {
|
||||
version: 1,
|
||||
@@ -61,6 +76,8 @@ export class ApiaryFieldService {
|
||||
queens,
|
||||
inspections,
|
||||
tasks,
|
||||
hives,
|
||||
hiveCatalog: activeOrUsed(hiveCatalog, hives),
|
||||
nextSteps: Object.fromEntries(
|
||||
colonies.map((colony) => [
|
||||
colony.id,
|
||||
|
||||
Reference in new issue
Block a user