diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 1ee1a9b..2ef09f7 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -1773,6 +1773,24 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt() WidgetTree, RootLayout, TEXT("CoachSchedulePolicy"), + 2 + ); + QueueRecoveryHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachQueueRecoveryHeader"), + 2 + ); + QueueRecoveryStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachQueueRecoveryStatus"), + 2 + ); + QueueRecoveryDetailTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachQueueRecoveryDetail"), 8 ); VerificationStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( @@ -3407,6 +3425,50 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() GuidanceLanePreference.FollowUpAverageSuccessRate ) : TEXT("Preference detail: build recommended or follow-up guidance to let the dashboard choose a next surfaced lane."); + const bool bQueueRecoveryMemorySource = + CachedCoachPanelState.PreferredGuidanceSourceLabel.StartsWith(TEXT("coach-memory-queue-")); + const FString QueueRecoveryLaneLabel = + CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp + ? TEXT("follow-up") + : (CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended + ? TEXT("recommended") + : TEXT("none")); + const FString QueueRecoverySourceLabel = CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + ? TEXT("n/a") + : CachedCoachPanelState.PreferredGuidanceSourceLabel; + const bool bHasQueueRecoveryFriction = + CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals + || CachedCoachSchedulePolicySummary.bHasArchiveAwareDeferrals + || CachedCoachSchedulePolicySummary.bNeedsManualRescheduleReview + || CachedCoachQueueExecutionSummary.bHasOutstandingDeferredWork + || CachedCoachQueueExecutionSummary.bHasCompletionGap; + const FString QueueRecoveryStatusLine = bQueueRecoveryMemorySource + ? FString::Printf( + TEXT("Recovery: %s lane | Source: %s | Action: %s | Mode: %s | Selection: %s"), + *QueueRecoveryLaneLabel, + *QueueRecoverySourceLabel, + CachedCoachPanelState.PrimaryActionLabel.IsEmpty() + ? TEXT("n/a") + : *CachedCoachPanelState.PrimaryActionLabel, + *HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(CachedCoachPanelState.SuggestedMode), + *HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(CachedCoachPanelState.SuggestedSelectionPolicy) + ) + : (bHasQueueRecoveryFriction + ? TEXT("Recovery: queue friction is present, but no queue-memory recovery override is currently active.") + : TEXT("Recovery: no queue-memory recovery override is currently active.")); + const FString QueueRecoveryDetailLine = FString::Printf( + TEXT("Deferred events %d | Promoted %d | Manual %d | Pressure %.2f | Dominant reason: %s | Follow-up deferrals: %s | Archive-aware deferrals: %s | Completion gap: %s"), + CachedCoachQueueExecutionSummary.DeferredEventCount, + CachedCoachQueueExecutionSummary.PromotedEventCount, + CachedCoachQueueExecutionSummary.ManualRescheduleEventCount, + CachedCoachSchedulePolicySummary.ReschedulePressureScore, + CachedCoachSchedulePolicySummary.DominantDeferralReasonLabel.IsEmpty() + ? TEXT("n/a") + : *CachedCoachSchedulePolicySummary.DominantDeferralReasonLabel, + CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals ? TEXT("yes") : TEXT("no"), + CachedCoachSchedulePolicySummary.bHasArchiveAwareDeferrals ? TEXT("yes") : TEXT("no"), + CachedCoachQueueExecutionSummary.bHasCompletionGap ? TEXT("yes") : TEXT("no") + ); const FString GuidancePreviewSelectionLine = SelectedGuidancePreviewCase != nullptr && ActivePreviewDeck != nullptr ? FString::Printf( @@ -4430,6 +4492,18 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals ? TEXT("yes") : TEXT("no") ))); } + if (QueueRecoveryHeaderTextBlock != nullptr) + { + QueueRecoveryHeaderTextBlock->SetText(FText::FromString(TEXT("[Queue Recovery]"))); + } + if (QueueRecoveryStatusTextBlock != nullptr) + { + QueueRecoveryStatusTextBlock->SetText(FText::FromString(QueueRecoveryStatusLine)); + } + if (QueueRecoveryDetailTextBlock != nullptr) + { + QueueRecoveryDetailTextBlock->SetText(FText::FromString(QueueRecoveryDetailLine)); + } if (VerificationStatusTextBlock != nullptr) { VerificationStatusTextBlock->SetText(FText::FromString(CachedCoachPanelState.VerificationStatusLine)); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index c48140b..59399af 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -495,6 +495,15 @@ protected: UPROPERTY(Transient) TObjectPtr SchedulePolicyTextBlock = nullptr; + UPROPERTY(Transient) + TObjectPtr QueueRecoveryHeaderTextBlock = nullptr; + + UPROPERTY(Transient) + TObjectPtr QueueRecoveryStatusTextBlock = nullptr; + + UPROPERTY(Transient) + TObjectPtr QueueRecoveryDetailTextBlock = nullptr; + UPROPERTY(Transient) TObjectPtr VerificationStatusTextBlock = nullptr;