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; }