Apply queue recovery from dashboard

This commit is contained in:
axiomlogicnexus 2026-04-27 18:22:03 +02:00
parent df446d8ca4
commit 1e104702f3
2 changed files with 143 additions and 2 deletions

View file

@ -252,6 +252,76 @@ bool UHyperTwistCoachDashboardWidget::PromoteFirstDeferredQueueEntry()
return bSucceeded;
}
bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
{
if (bHasLiveAttemptTimer)
{
return false;
}
const FString DeferredEntryId = FindFirstDeferredQueueEntryId();
if (!DeferredEntryId.IsEmpty())
{
return PromoteFirstDeferredQueueEntry();
}
const bool bQueueRecoveryMemorySource =
CachedCoachPanelState.PreferredGuidanceSourceLabel.StartsWith(TEXT("coach-memory-queue-"));
const bool bPrefersFollowUp =
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
const bool bPrefersRecommended =
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended;
auto StartRunAndRefresh = [this](const FHyperTwistTrainingRunState& RunState)
{
SyncRetainedRunRecap();
SyncSelectedQueueEntry();
SyncSelectedAttempt();
UpdateDashboardPresentation();
return RunState.Session.IsStructurallyValid();
};
if (bQueueRecoveryMemorySource && CachedCoachPanelState.bCanStartQueuedRun)
{
const bool bQueueMatchesPreference =
(!bPrefersFollowUp && !bPrefersRecommended)
|| (bPrefersFollowUp
&& CachedCoachPanelState.NextQueueSourceLabel == TEXT("follow-up-brief"))
|| (bPrefersRecommended
&& CachedCoachPanelState.NextQueueSourceLabel == TEXT("primary-brief"));
if (bQueueMatchesPreference)
{
return StartRunAndRefresh(StartNextQueuedCoachRun(DefaultCoachMaxCases));
}
}
if (bPrefersFollowUp
&& CachedCoachPanelState.bCanStartCoachFollowUpRun
&& CachedCoachPanelState.bHasFollowUpGuidance)
{
return StartRunAndRefresh(StartCoachFollowUpRun(DefaultCoachMaxCases));
}
if (bPrefersRecommended && CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
return StartRunAndRefresh(StartCoachRecommendedRun(DefaultCoachMaxCases));
}
if (CachedCoachPanelState.bCanStartQueuedRun)
{
return StartRunAndRefresh(StartNextQueuedCoachRun(DefaultCoachMaxCases));
}
if (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
{
return StartRunAndRefresh(StartCoachFollowUpRun(DefaultCoachMaxCases));
}
if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
return StartRunAndRefresh(StartCoachRecommendedRun(DefaultCoachMaxCases));
}
const FHyperTwistTrainingRunState FallbackRunState = ExecutePrimaryCoachAction();
return FallbackRunState.Session.IsStructurallyValid();
}
bool UHyperTwistCoachDashboardWidget::SelectPreviousQueueEntry()
{
if (CachedCoachSessionQueueState.Entries.Num() <= 0)
@ -987,6 +1057,11 @@ void UHyperTwistCoachDashboardWidget::HandlePromoteClicked()
PromoteFirstDeferredQueueEntry();
}
void UHyperTwistCoachDashboardWidget::HandleApplyQueueRecoveryClicked()
{
ApplyQueueRecoveryPosture();
}
void UHyperTwistCoachDashboardWidget::HandleRepeatRunClicked()
{
RestartRetainedRunRecapDeck();
@ -1866,6 +1941,17 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
PromoteLabelRaw
);
PromoteButtonLabel = PromoteLabelRaw;
UTextBlock* QueueRecoveryActionLabelRaw = nullptr;
QueueRecoveryActionButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
WidgetTree,
ButtonRow,
TEXT("QueueRecoveryActionButton"),
TEXT("QueueRecoveryActionButtonLabel"),
TEXT("Apply Recovery"),
QueueRecoveryActionLabelRaw
);
QueueRecoveryActionButtonLabel = QueueRecoveryActionLabelRaw;
}
UHorizontalBox* AttemptInputRow = WidgetTree->ConstructWidget<UHorizontalBox>(
@ -2164,6 +2250,10 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
{
PromoteButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePromoteClicked);
}
if (QueueRecoveryActionButton != nullptr)
{
QueueRecoveryActionButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleApplyQueueRecoveryClicked);
}
if (RepeatRunButton != nullptr)
{
RepeatRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleRepeatRunClicked);
@ -3427,10 +3517,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
: 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 bool bQueueRecoveryFollowUp =
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
const bool bQueueRecoveryRecommended =
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended;
const FString QueueRecoveryLaneLabel =
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
bQueueRecoveryFollowUp
? TEXT("follow-up")
: (CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended
: (bQueueRecoveryRecommended
? TEXT("recommended")
: TEXT("none"));
const FString QueueRecoverySourceLabel = CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty()
@ -3469,6 +3563,33 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
CachedCoachSchedulePolicySummary.bHasArchiveAwareDeferrals ? TEXT("yes") : TEXT("no"),
CachedCoachQueueExecutionSummary.bHasCompletionGap ? TEXT("yes") : TEXT("no")
);
const FString PreferredDeferredRecoveryEntryId = FindFirstDeferredQueueEntryId();
const bool bCanApplyQueueRecovery =
!bHasLiveAttemptTimer
&& (bQueueRecoveryMemorySource || bHasQueueRecoveryFriction)
&& (!PreferredDeferredRecoveryEntryId.IsEmpty()
|| CachedCoachPanelState.bCanStartQueuedRun
|| (bQueueRecoveryFollowUp
&& CachedCoachPanelState.bCanStartCoachFollowUpRun
&& CachedCoachPanelState.bHasFollowUpGuidance)
|| (bQueueRecoveryRecommended
&& CachedCoachPanelState.bCanStartCoachRecommendedRun)
|| (!bQueueRecoveryMemorySource
&& (CachedCoachPanelState.bCanStartCoachFollowUpRun
|| CachedCoachPanelState.bCanStartCoachRecommendedRun)));
const FString QueueRecoveryActionLabel = !PreferredDeferredRecoveryEntryId.IsEmpty()
? (bQueueRecoveryFollowUp
? TEXT("Promote Follow-Up Recovery")
: (bQueueRecoveryRecommended
? TEXT("Promote Recommended Recovery")
: TEXT("Promote Recovery")))
: (bQueueRecoveryFollowUp
? TEXT("Start Follow-Up Recovery")
: (bQueueRecoveryRecommended
? TEXT("Start Recommended Recovery")
: (CachedCoachPanelState.bCanStartQueuedRun
? TEXT("Launch Queue Recovery")
: TEXT("Apply Recovery"))));
const FString GuidancePreviewSelectionLine =
SelectedGuidancePreviewCase != nullptr && ActivePreviewDeck != nullptr
? FString::Printf(
@ -4582,6 +4703,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
{
PromoteButton->SetIsEnabled(!bHasLiveAttemptTimer && !FindFirstDeferredQueueEntryId().IsEmpty());
}
if (QueueRecoveryActionButtonLabel != nullptr)
{
QueueRecoveryActionButtonLabel->SetText(FText::FromString(QueueRecoveryActionLabel));
}
if (QueueRecoveryActionButton != nullptr)
{
QueueRecoveryActionButton->SetIsEnabled(bCanApplyQueueRecovery);
}
if (CompleteRunButtonLabel != nullptr)
{
CompleteRunButtonLabel->SetText(FText::FromString(TEXT("Complete Run")));

View file

@ -171,6 +171,9 @@ public:
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
bool PromoteFirstDeferredQueueEntry();
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
bool ApplyQueueRecoveryPosture();
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
bool SelectPreviousQueueEntry();
@ -552,6 +555,12 @@ protected:
UPROPERTY(Transient)
TObjectPtr<UTextBlock> PromoteButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> QueueRecoveryActionButton = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> QueueRecoveryActionButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> CompleteRunButton = nullptr;
@ -672,6 +681,9 @@ protected:
UFUNCTION()
void HandlePromoteClicked();
UFUNCTION()
void HandleApplyQueueRecoveryClicked();
UFUNCTION()
void HandleRepeatRunClicked();