Files
ApiCore/ApiCore-Backend/src/autopilot/dto/next-steps-response.dto.ts
T
Nick.RosenloecherandClaude Opus 5.5 bcc7cbfd13 feat(autopilot): /autopilot/today liefert Saison-Zusätze für das Bienenjahr-Rad (T5b B5)
- seasons[] bekommt segmentSince, reportedOn und note
  (NO_COORDINATES | WEATHER_GAPS | WEATHER_STALE).
- Neues seasonCalendar: calendarFallback (8 Phasen, MM-DD), swarmSeason und
  winterVarroaWindow aus den Regel-Configs, auch bei inaktiver Regel.
- Nur Zusätze, keine Migration, Recht bleibt colonies:read;
  SEASON_AUTOPILOT wird je Anfrage einmal gelesen.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-19 15:55:24 +02:00

153 lines
5.3 KiB
TypeScript

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'];
@ApiProperty({ description: 'Day the current segment began (YYYY-MM-DD).' })
segmentSince!: string;
@ApiProperty({
nullable: true,
type: String,
format: 'date',
description:
'Day of the phase report the phase rests on; null without one.',
})
reportedOn!: string | null;
@ApiProperty({
enum: ['NO_COORDINATES', 'WEATHER_GAPS', 'WEATHER_STALE'],
nullable: true,
})
note!: AutopilotToday['seasons'][number]['note'];
}
class SwarmSeasonDto {
@ApiProperty({ enum: PHENOLOGY_PHASES })
from!: AutopilotToday['seasonCalendar']['swarmSeason']['from'];
@ApiProperty({ enum: PHENOLOGY_PHASES })
until!: AutopilotToday['seasonCalendar']['swarmSeason']['until'];
}
class MonthDayWindowDto {
@ApiProperty({ example: '11-15' }) from!: string;
@ApiProperty({ example: '12-20' }) to!: string;
}
/** Calendar day ('MM-DD') of each phenology phase. */
class CalendarFallbackDto implements Record<
(typeof PHENOLOGY_PHASES)[number],
string
> {
@ApiProperty({ example: '03-20' }) SALWEIDE!: string;
@ApiProperty({ example: '04-15' }) KIRSCHE!: string;
@ApiProperty({ example: '04-28' }) APFEL!: string;
@ApiProperty({ example: '05-30' }) HOLUNDER!: string;
@ApiProperty({ example: '06-20' }) LINDE!: string;
@ApiProperty({ example: '08-01' }) TRACHTENDE!: string;
@ApiProperty({ example: '09-15' }) HERBST!: string;
@ApiProperty({ example: '11-15' }) WINTER!: string;
}
class SeasonCalendarDto {
@ApiProperty({ type: CalendarFallbackDto })
calendarFallback!: CalendarFallbackDto;
@ApiProperty({ type: SwarmSeasonDto }) swarmSeason!: SwarmSeasonDto;
@ApiProperty({ type: MonthDayWindowDto })
winterVarroaWindow!: MonthDayWindowDto;
}
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({ type: SeasonCalendarDto })
seasonCalendar!: SeasonCalendarDto;
@ApiProperty() tasksVisible!: boolean;
}