Surface closure recovery on coach dashboard

This commit is contained in:
axiomlogicnexus 2026-04-27 20:40:38 +02:00
parent c8c879dc7e
commit 77c8e66539
2 changed files with 113 additions and 0 deletions

View file

@ -2018,6 +2018,24 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
TEXT("CoachSchedulePolicy"),
2
);
ClosureRecoveryHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
TEXT("CoachClosureRecoveryHeader"),
2
);
ClosureRecoveryStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
TEXT("CoachClosureRecoveryStatus"),
2
);
ClosureRecoveryDetailTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
TEXT("CoachClosureRecoveryDetail"),
8
);
QueueRecoveryHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
@ -5301,6 +5319,92 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
: *CachedCoachSchedulePolicySummary.ClosurePolicyLine
)));
}
const int32 ClosureKeepHistoryCount =
CachedCoachSchedulePolicySummary.HistoricalFollowUpContinuationCount;
const int32 ClosureSuppressHistoryCount =
CachedCoachSchedulePolicySummary.HistoricalBorderlineFollowUpSuppressionCount;
const bool bHasClosureRecoveryHistory =
ClosureKeepHistoryCount > 0 || ClosureSuppressHistoryCount > 0;
const FHyperTwistCoachSignal* ClosureRecoverySignal = CachedCoachSignals.FindByPredicate(
[](const FHyperTwistCoachSignal& Signal)
{
return Signal.SignalId == TEXT("closure_history_follow_up_continuation")
|| Signal.SignalId == TEXT("closure_history_recommended_suppression");
}
);
const bool bClosureRecoverySignalPromoted =
ClosureRecoverySignal != nullptr
&& CachedCoachBrief.Signals.Num() > 0
&& CachedCoachBrief.Signals[0].SignalId == ClosureRecoverySignal->SignalId;
const FString ClosureGuidanceLaneLabel =
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
? TEXT("follow-up")
: (CachedCoachPanelState.PreferredGuidanceLane
== EHyperTwistTrainingCoachGuidanceLane::Recommended
? TEXT("recommended")
: TEXT("none"));
const FString ClosureRecoveryHistoryLabel =
ClosureKeepHistoryCount > ClosureSuppressHistoryCount
? TEXT("follow-up continuity history is dominant")
: (ClosureSuppressHistoryCount > ClosureKeepHistoryCount
? TEXT("recommended-side suppression history is dominant")
: (bHasClosureRecoveryHistory
? TEXT("retained closure history is balanced")
: TEXT("no retained closure history")));
const FString ClosureRecoveryStatusLine =
ClosureRecoverySignal != nullptr
? FString::Printf(
TEXT("Closure recovery: %s | promoted: %s | headline: %s | priority: %s | score %.1f"),
*ClosureRecoveryHistoryLabel,
bClosureRecoverySignalPromoted ? TEXT("yes") : TEXT("no"),
ClosureRecoverySignal->Headline.IsEmpty()
? TEXT("n/a")
: *ClosureRecoverySignal->Headline,
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(ClosureRecoverySignal->Priority),
ClosureRecoverySignal->Score
)
: (bHasClosureRecoveryHistory
? FString::Printf(
TEXT("Closure recovery: retained history is present without an active closure-history signal | policy: %s"),
CachedCoachSchedulePolicySummary.ClosurePolicyLine.IsEmpty()
? TEXT("n/a")
: *CachedCoachSchedulePolicySummary.ClosurePolicyLine
)
: TEXT("Closure recovery: no retained closure-recovery history is recorded yet."));
const FString ClosureRecoveryDetailLine = FString::Printf(
TEXT("Policy: %s | keep %d | suppress %d | continuation active: %s | borderline suppression active: %s | guidance lane: %s | source: %s%s%s"),
CachedCoachSchedulePolicySummary.ClosurePolicyLine.IsEmpty()
? TEXT("n/a")
: *CachedCoachSchedulePolicySummary.ClosurePolicyLine,
ClosureKeepHistoryCount,
ClosureSuppressHistoryCount,
CachedCoachSchedulePolicySummary.bFollowUpContinuationFromRecoveryHistory
? TEXT("yes")
: TEXT("no"),
CachedCoachSchedulePolicySummary.bBorderlineFollowUpSuppressedByRecoveryHistory
? TEXT("yes")
: TEXT("no"),
*ClosureGuidanceLaneLabel,
CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty()
? TEXT("n/a")
: *CachedCoachPanelState.PreferredGuidanceSourceLabel,
ClosureRecoverySignal != nullptr ? TEXT(" || Signal detail: ") : TEXT(""),
ClosureRecoverySignal != nullptr
? *ClosureRecoverySignal->Detail
: TEXT("")
);
if (ClosureRecoveryHeaderTextBlock != nullptr)
{
ClosureRecoveryHeaderTextBlock->SetText(FText::FromString(TEXT("[Closure Recovery]")));
}
if (ClosureRecoveryStatusTextBlock != nullptr)
{
ClosureRecoveryStatusTextBlock->SetText(FText::FromString(ClosureRecoveryStatusLine));
}
if (ClosureRecoveryDetailTextBlock != nullptr)
{
ClosureRecoveryDetailTextBlock->SetText(FText::FromString(ClosureRecoveryDetailLine));
}
if (QueueRecoveryHeaderTextBlock != nullptr)
{
QueueRecoveryHeaderTextBlock->SetText(FText::FromString(TEXT("[Queue Recovery]")));

View file

@ -584,6 +584,15 @@ protected:
UPROPERTY(Transient)
TObjectPtr<UTextBlock> SchedulePolicyTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> ClosureRecoveryHeaderTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> ClosureRecoveryStatusTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> ClosureRecoveryDetailTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> QueueRecoveryHeaderTextBlock = nullptr;