Add coach handoff actions to dashboard

This commit is contained in:
axiomlogicnexus 2026-04-27 15:01:32 +02:00
parent 9157554304
commit d3658f490d
2 changed files with 218 additions and 0 deletions

View file

@ -603,6 +603,33 @@ void UHyperTwistCoachDashboardWidget::HandleDismissRecapClicked()
ClearRetainedRunRecap();
}
void UHyperTwistCoachDashboardWidget::HandleStartQueuedRunClicked()
{
StartNextQueuedCoachRun(DefaultCoachMaxCases);
SyncRetainedRunRecap();
SyncSelectedQueueEntry();
SyncSelectedAttempt();
UpdateDashboardPresentation();
}
void UHyperTwistCoachDashboardWidget::HandleStartFollowUpRunClicked()
{
StartCoachFollowUpRun(DefaultCoachMaxCases);
SyncRetainedRunRecap();
SyncSelectedQueueEntry();
SyncSelectedAttempt();
UpdateDashboardPresentation();
}
void UHyperTwistCoachDashboardWidget::HandleStartRecommendedRunClicked()
{
StartCoachRecommendedRun(DefaultCoachMaxCases);
SyncRetainedRunRecap();
SyncSelectedQueueEntry();
SyncSelectedAttempt();
UpdateDashboardPresentation();
}
void UHyperTwistCoachDashboardWidget::HandleCompleteRunClicked()
{
CompleteActiveTrainingRun(false);
@ -834,6 +861,18 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
TEXT("CoachRunRecapDetail"),
2
);
CoachHandoffHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
TEXT("CoachHandoffHeader"),
2
);
CoachHandoffStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
TEXT("CoachHandoffStatus"),
2
);
UHorizontalBox* RunRecapActionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
UHorizontalBox::StaticClass(),
TEXT("CoachDashboardRunRecapActions")
@ -867,6 +906,50 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
);
DismissRecapButtonLabel = DismissRecapLabelRaw;
}
UHorizontalBox* CoachHandoffActionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
UHorizontalBox::StaticClass(),
TEXT("CoachDashboardHandoffActions")
);
if (CoachHandoffActionRow != nullptr)
{
if (UVerticalBoxSlot* CoachHandoffActionRowSlot = RootLayout->AddChildToVerticalBox(CoachHandoffActionRow))
{
CoachHandoffActionRowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 0.0f));
}
UTextBlock* StartQueuedRunLabelRaw = nullptr;
StartQueuedRunButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
WidgetTree,
CoachHandoffActionRow,
TEXT("StartQueuedRunButton"),
TEXT("StartQueuedRunButtonLabel"),
TEXT("Start Queue"),
StartQueuedRunLabelRaw
);
StartQueuedRunButtonLabel = StartQueuedRunLabelRaw;
UTextBlock* StartFollowUpRunLabelRaw = nullptr;
StartFollowUpRunButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
WidgetTree,
CoachHandoffActionRow,
TEXT("StartFollowUpRunButton"),
TEXT("StartFollowUpRunButtonLabel"),
TEXT("Start Follow-Up"),
StartFollowUpRunLabelRaw
);
StartFollowUpRunButtonLabel = StartFollowUpRunLabelRaw;
UTextBlock* StartRecommendedRunLabelRaw = nullptr;
StartRecommendedRunButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
WidgetTree,
CoachHandoffActionRow,
TEXT("StartRecommendedRunButton"),
TEXT("StartRecommendedRunButtonLabel"),
TEXT("Start Recommended"),
StartRecommendedRunLabelRaw
);
StartRecommendedRunButtonLabel = StartRecommendedRunLabelRaw;
}
LastAttemptTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
@ -1384,6 +1467,18 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
{
DismissRecapButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleDismissRecapClicked);
}
if (StartQueuedRunButton != nullptr)
{
StartQueuedRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleStartQueuedRunClicked);
}
if (StartFollowUpRunButton != nullptr)
{
StartFollowUpRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleStartFollowUpRunClicked);
}
if (StartRecommendedRunButton != nullptr)
{
StartRecommendedRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleStartRecommendedRunClicked);
}
if (CompleteRunButton != nullptr)
{
CompleteRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleCompleteRunClicked);
@ -1770,6 +1865,58 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|| CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed
|| CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted);
const bool bCanDismissStoredRecap = !bHasCurrentRunRecap && bHasRetainedRunRecap;
const bool bCanUseCoachHandoff = !bHasLiveAttemptTimer && !bHasActiveRun;
FString CoachHandoffStatusLine;
if (bHasLiveAttemptTimer)
{
CoachHandoffStatusLine =
TEXT("Coach handoff: disabled while a live attempt is running or under inspection.");
}
else if (bHasActiveRun)
{
CoachHandoffStatusLine =
TEXT("Coach handoff: finish, clear, or complete the active run before launching another coach-directed run.");
}
else
{
const FString QueueLine = CachedCoachPanelState.bCanStartQueuedRun
? FString::Printf(
TEXT("queue ready from %s"),
CachedCoachPanelState.NextQueueSourceLabel.IsEmpty()
? TEXT("n/a")
: *CachedCoachPanelState.NextQueueSourceLabel
)
: TEXT("queue not ready");
const FString FollowUpLine = CachedCoachPanelState.bCanStartCoachFollowUpRun
? FString::Printf(
TEXT("follow-up %s (%d cases)"),
CachedCoachFollowUpBrief.Headline.IsEmpty() ? TEXT("ready") : *CachedCoachFollowUpBrief.Headline,
CachedCoachFollowUpBrief.FocusCaseIds.Num()
)
: TEXT("follow-up not ready");
const FString RecommendedLine = CachedCoachPanelState.bCanStartCoachRecommendedRun
? FString::Printf(
TEXT("recommended %s (%d cases)"),
CachedCoachBrief.Headline.IsEmpty() ? TEXT("ready") : *CachedCoachBrief.Headline,
CachedCoachBrief.FocusCaseIds.Num()
)
: TEXT("recommended not ready");
CoachHandoffStatusLine = FString::Printf(
TEXT("Coach handoff: %s | %s | %s"),
*QueueLine,
*FollowUpLine,
*RecommendedLine
);
}
const FString QueuedRunLabel = CachedCoachPanelState.NextQueueSourceLabel.IsEmpty()
? TEXT("Start Queue")
: FString::Printf(TEXT("Queue: %s"), *CachedCoachPanelState.NextQueueSourceLabel);
const FString FollowUpRunLabel = CachedCoachFollowUpBrief.PrimaryActionLabel.IsEmpty()
? TEXT("Start Follow-Up")
: CachedCoachFollowUpBrief.PrimaryActionLabel;
const FString RecommendedRunLabel = CachedCoachBrief.PrimaryActionLabel.IsEmpty()
? TEXT("Start Recommended")
: CachedCoachBrief.PrimaryActionLabel;
if (HeadlineTextBlock != nullptr)
{
@ -1899,6 +2046,44 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
{
DismissRecapButton->SetIsEnabled(bCanDismissStoredRecap);
}
if (CoachHandoffHeaderTextBlock != nullptr)
{
CoachHandoffHeaderTextBlock->SetText(FText::FromString(TEXT("[Coach Handoff]")));
}
if (CoachHandoffStatusTextBlock != nullptr)
{
CoachHandoffStatusTextBlock->SetText(FText::FromString(CoachHandoffStatusLine));
}
if (StartQueuedRunButtonLabel != nullptr)
{
StartQueuedRunButtonLabel->SetText(FText::FromString(QueuedRunLabel));
}
if (StartQueuedRunButton != nullptr)
{
StartQueuedRunButton->SetIsEnabled(bCanUseCoachHandoff && CachedCoachPanelState.bCanStartQueuedRun);
}
if (StartFollowUpRunButtonLabel != nullptr)
{
StartFollowUpRunButtonLabel->SetText(FText::FromString(FollowUpRunLabel));
}
if (StartFollowUpRunButton != nullptr)
{
StartFollowUpRunButton->SetIsEnabled(
bCanUseCoachHandoff
&& CachedCoachPanelState.bCanStartCoachFollowUpRun
&& CachedCoachPanelState.bHasFollowUpGuidance
);
}
if (StartRecommendedRunButtonLabel != nullptr)
{
StartRecommendedRunButtonLabel->SetText(FText::FromString(RecommendedRunLabel));
}
if (StartRecommendedRunButton != nullptr)
{
StartRecommendedRunButton->SetIsEnabled(
bCanUseCoachHandoff && CachedCoachPanelState.bCanStartCoachRecommendedRun
);
}
if (LastAttemptTextBlock != nullptr)
{
if (ReviewedAttempt != nullptr)

View file

@ -163,6 +163,12 @@ protected:
UPROPERTY(Transient)
TObjectPtr<UTextBlock> RunRecapDetailTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> CoachHandoffHeaderTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> CoachHandoffStatusTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> RepeatRunButton = nullptr;
@ -175,6 +181,24 @@ protected:
UPROPERTY(Transient)
TObjectPtr<UTextBlock> DismissRecapButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> StartQueuedRunButton = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> StartQueuedRunButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> StartFollowUpRunButton = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> StartFollowUpRunButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> StartRecommendedRunButton = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> StartRecommendedRunButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> LastAttemptTextBlock = nullptr;
@ -406,6 +430,15 @@ protected:
UFUNCTION()
void HandleDismissRecapClicked();
UFUNCTION()
void HandleStartQueuedRunClicked();
UFUNCTION()
void HandleStartFollowUpRunClicked();
UFUNCTION()
void HandleStartRecommendedRunClicked();
UFUNCTION()
void HandleCompleteRunClicked();