feat: T4 wave 5 - colony end cleanup, withdrawal chain, next steps, inspection takeover UI
- Colony end releases the queen in the same transaction; open suggestions become obsolete and a CLEANUP suggestion lists open tasks and the queen; apply-cleanup validates evidence and blocks reactivated colonies - Withdrawal chain: plausibility warnings on harvest and bottling, conflict notifications also when a treatment is entered or corrected after the harvest (naming bottled lots), withdrawal spans in the calendar and M365 sync, Berlin-day end events - Next 3 steps per colony and the "Heute" endpoint with ranking and an explicit merge table by intent - Inspection-triggered rule suggestions follow the colony's latest inspection (resolve when the finding is gone, refresh when it stays) - Frontend: colony card takeover in the inspection form, feed status as condition, occupied frames 0-40; legacy offline entries keep syncing - Notifications and preferences are deleted with their user Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
02eb16924e
commit
ef33e5549b
122 files changed
+10707
-504
No files matched your search
@@ -0,0 +1,100 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { SuggestionIntent } from '../../../generated/prisma';
|
||||
import {
|
||||
PHASE_SOURCES,
|
||||
PHENOLOGY_PHASES,
|
||||
SEASON_SEGMENTS,
|
||||
} from '../../season/season-types';
|
||||
import type { ColonyNextSteps, NextStep } from '../next-step-ranking';
|
||||
import type { AutopilotToday } from '../next-steps.service';
|
||||
|
||||
const FORMS = [
|
||||
'inspection',
|
||||
'varroa',
|
||||
'feeding',
|
||||
'treatment',
|
||||
'winter-check',
|
||||
] as const;
|
||||
|
||||
class NextStepActionDto {
|
||||
@ApiProperty({ enum: ['CAPTURE', 'ACCEPT', 'CLEANUP', 'OPEN_TASK'] })
|
||||
type!: NextStep['action']['type'];
|
||||
@ApiProperty({ enum: FORMS, required: false }) form?: (typeof FORMS)[number];
|
||||
@ApiProperty({ required: false }) href?: string;
|
||||
@ApiProperty({ required: false }) suggestionId?: string;
|
||||
@ApiProperty({ required: false }) taskId?: string;
|
||||
}
|
||||
|
||||
export class NextStepDto {
|
||||
@ApiProperty() key!: string;
|
||||
@ApiProperty({ enum: ['TASK', 'SUGGESTION'] }) kind!: NextStep['kind'];
|
||||
@ApiProperty({ enum: SuggestionIntent, nullable: true })
|
||||
intent!: SuggestionIntent | null;
|
||||
@ApiProperty() title!: string;
|
||||
@ApiProperty({ type: [String] }) reasons!: string[];
|
||||
@ApiProperty({ nullable: true, type: String }) dueOn!: string | null;
|
||||
@ApiProperty({ enum: ['OVERDUE', 'TODAY', 'SOON', 'LATER', 'NONE'] })
|
||||
urgency!: NextStep['urgency'];
|
||||
@ApiProperty({ type: [String] }) suggestionIds!: string[];
|
||||
@ApiProperty({ type: NextStepActionDto }) action!: NextStepActionDto;
|
||||
@ApiProperty() dismissible!: boolean;
|
||||
}
|
||||
|
||||
export class ColonyNextStepsDto {
|
||||
@ApiProperty() colonyId!: string;
|
||||
@ApiProperty({ enum: SEASON_SEGMENTS }) segment!: ColonyNextSteps['segment'];
|
||||
@ApiProperty({ enum: PHENOLOGY_PHASES, nullable: true })
|
||||
phase!: ColonyNextSteps['phase'];
|
||||
@ApiProperty({ enum: PHASE_SOURCES, nullable: true })
|
||||
phaseSource!: ColonyNextSteps['phaseSource'];
|
||||
@ApiProperty({ nullable: true, type: String }) inspectionDueOn!:
|
||||
string | null;
|
||||
@ApiProperty({ nullable: true, type: String }) withdrawalUntil!:
|
||||
string | null;
|
||||
@ApiProperty({ type: [NextStepDto] }) steps!: NextStepDto[];
|
||||
}
|
||||
|
||||
class TodayNextStepDto extends NextStepDto {
|
||||
@ApiProperty() colonyId!: string;
|
||||
@ApiProperty() colonyCode!: string;
|
||||
}
|
||||
|
||||
class DueColonyDto {
|
||||
@ApiProperty() id!: string;
|
||||
@ApiProperty() code!: string;
|
||||
@ApiProperty() dueOn!: string;
|
||||
@ApiProperty() overdueDays!: number;
|
||||
}
|
||||
|
||||
class InspectionDueDto {
|
||||
@ApiProperty() apiaryId!: string;
|
||||
@ApiProperty() apiaryName!: string;
|
||||
@ApiProperty({ enum: SEASON_SEGMENTS })
|
||||
segment!: AutopilotToday['seasons'][number]['segment'];
|
||||
@ApiProperty({ nullable: true, type: Number }) intervalDays!: number | null;
|
||||
@ApiProperty({ type: [DueColonyDto] }) colonies!: DueColonyDto[];
|
||||
}
|
||||
|
||||
class SeasonOfApiaryDto {
|
||||
@ApiProperty() apiaryId!: string;
|
||||
@ApiProperty() apiaryName!: string;
|
||||
@ApiProperty({ enum: SEASON_SEGMENTS })
|
||||
segment!: AutopilotToday['seasons'][number]['segment'];
|
||||
@ApiProperty({ enum: PHENOLOGY_PHASES, nullable: true })
|
||||
phase!: AutopilotToday['seasons'][number]['phase'];
|
||||
@ApiProperty({ enum: PHASE_SOURCES, nullable: true })
|
||||
phaseSource!: AutopilotToday['seasons'][number]['phaseSource'];
|
||||
}
|
||||
|
||||
export class AutopilotTodayDto {
|
||||
@ApiProperty({ type: TodayNextStepDto, nullable: true })
|
||||
nextStep!: TodayNextStepDto | null;
|
||||
@ApiProperty({
|
||||
type: [InspectionDueDto],
|
||||
description: 'Colonies whose inspection is due today or overdue.',
|
||||
})
|
||||
inspectionDue!: InspectionDueDto[];
|
||||
@ApiProperty() suggestionsOpen!: number;
|
||||
@ApiProperty({ type: [SeasonOfApiaryDto] }) seasons!: SeasonOfApiaryDto[];
|
||||
@ApiProperty() tasksVisible!: boolean;
|
||||
}
|
||||
Reference in new issue
Block a user