Add recap handoff actions to coach dashboard

This commit is contained in:
axiomlogicnexus 2026-04-27 14:56:51 +02:00
parent 8895f8b48a
commit 9157554304
2 changed files with 149 additions and 0 deletions

View file

@ -303,6 +303,50 @@ bool UHyperTwistCoachDashboardWidget::SelectNextAttemptReview()
return true;
}
FHyperTwistTrainingRunState UHyperTwistCoachDashboardWidget::RestartRetainedRunRecapDeck()
{
if (!RetainedRunRecapDeck.IsStructurallyValid())
{
UpdateDashboardPresentation();
return FHyperTwistTrainingRunState();
}
const FString BaseSessionId = RetainedRunRecapSessionId.IsEmpty()
? DefaultSessionId
: RetainedRunRecapSessionId;
const FString RestartSessionId = FString::Printf(
TEXT("%s_repeat_%s"),
*BaseSessionId,
*FGuid::NewGuid().ToString(EGuidFormats::Digits)
);
const FString RestartUserId = RetainedRunRecapUserId.IsEmpty()
? DefaultUserId
: RetainedRunRecapUserId;
const FHyperTwistTrainingRunState RunState = StartTrainingRunFromDeck(
RetainedRunRecapDeck,
RestartUserId,
RestartSessionId,
RetainedRunRecapMode
);
SyncRetainedRunRecap();
SyncSelectedQueueEntry();
SyncSelectedAttempt();
UpdateDashboardPresentation();
return RunState;
}
void UHyperTwistCoachDashboardWidget::ClearRetainedRunRecap()
{
bHasRetainedRunRecap = false;
RetainedRunRecapSummary = FHyperTwistTrainingSessionSummary();
RetainedRunRecapDeck = FHyperTwistTrainingDeck();
RetainedRunRecapUserId.Reset();
RetainedRunRecapSessionId.Reset();
RetainedRunRecapMode = EHyperTwistTrainingDeliveryMode::Timer;
UpdateDashboardPresentation();
}
FHyperTwistTrainingRunStepResult UHyperTwistCoachDashboardWidget::SubmitSyntheticCurrentAttempt(
const EHyperTwistTrainingAttemptResult Result,
const int32 TimeMs
@ -549,6 +593,16 @@ void UHyperTwistCoachDashboardWidget::HandlePromoteClicked()
PromoteFirstDeferredQueueEntry();
}
void UHyperTwistCoachDashboardWidget::HandleRepeatRunClicked()
{
RestartRetainedRunRecapDeck();
}
void UHyperTwistCoachDashboardWidget::HandleDismissRecapClicked()
{
ClearRetainedRunRecap();
}
void UHyperTwistCoachDashboardWidget::HandleCompleteRunClicked()
{
CompleteActiveTrainingRun(false);
@ -780,6 +834,39 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
TEXT("CoachRunRecapDetail"),
2
);
UHorizontalBox* RunRecapActionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
UHorizontalBox::StaticClass(),
TEXT("CoachDashboardRunRecapActions")
);
if (RunRecapActionRow != nullptr)
{
if (UVerticalBoxSlot* RunRecapActionRowSlot = RootLayout->AddChildToVerticalBox(RunRecapActionRow))
{
RunRecapActionRowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 0.0f));
}
UTextBlock* RepeatRunLabelRaw = nullptr;
RepeatRunButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
WidgetTree,
RunRecapActionRow,
TEXT("RepeatRunButton"),
TEXT("RepeatRunButtonLabel"),
TEXT("Repeat Run"),
RepeatRunLabelRaw
);
RepeatRunButtonLabel = RepeatRunLabelRaw;
UTextBlock* DismissRecapLabelRaw = nullptr;
DismissRecapButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
WidgetTree,
RunRecapActionRow,
TEXT("DismissRecapButton"),
TEXT("DismissRecapButtonLabel"),
TEXT("Dismiss Recap"),
DismissRecapLabelRaw
);
DismissRecapButtonLabel = DismissRecapLabelRaw;
}
LastAttemptTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
@ -1289,6 +1376,14 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
{
PromoteButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePromoteClicked);
}
if (RepeatRunButton != nullptr)
{
RepeatRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleRepeatRunClicked);
}
if (DismissRecapButton != nullptr)
{
DismissRecapButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleDismissRecapClicked);
}
if (CompleteRunButton != nullptr)
{
CompleteRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleCompleteRunClicked);
@ -1450,6 +1545,10 @@ void UHyperTwistCoachDashboardWidget::SyncRetainedRunRecap()
{
RetainedRunRecapSummary = CachedRunSummary;
bHasRetainedRunRecap = !RetainedRunRecapSummary.TrainingSessionId.IsEmpty();
RetainedRunRecapDeck = CachedRunState.ActiveDeck;
RetainedRunRecapUserId = CachedRunState.Session.UserId;
RetainedRunRecapSessionId = CachedRunState.Session.TrainingSessionId;
RetainedRunRecapMode = CachedRunState.Session.Mode;
}
}
@ -1665,6 +1764,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
const FString RunRecapSourceLabel = bHasCurrentRunRecap
? TEXT("current run")
: TEXT("last completed run");
const bool bCanRepeatDisplayedRun = !bHasLiveAttemptTimer
&& RetainedRunRecapDeck.IsStructurallyValid()
&& (!bHasActiveRun
|| CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed
|| CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted);
const bool bCanDismissStoredRecap = !bHasCurrentRunRecap && bHasRetainedRunRecap;
if (HeadlineTextBlock != nullptr)
{
@ -1778,6 +1883,22 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
));
}
}
if (RepeatRunButtonLabel != nullptr)
{
RepeatRunButtonLabel->SetText(FText::FromString(TEXT("Repeat Run")));
}
if (RepeatRunButton != nullptr)
{
RepeatRunButton->SetIsEnabled(bCanRepeatDisplayedRun);
}
if (DismissRecapButtonLabel != nullptr)
{
DismissRecapButtonLabel->SetText(FText::FromString(TEXT("Dismiss Recap")));
}
if (DismissRecapButton != nullptr)
{
DismissRecapButton->SetIsEnabled(bCanDismissStoredRecap);
}
if (LastAttemptTextBlock != nullptr)
{
if (ReviewedAttempt != nullptr)

View file

@ -97,6 +97,12 @@ public:
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
bool SelectNextAttemptReview();
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingRunState RestartRetainedRunRecapDeck();
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
void ClearRetainedRunRecap();
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingRunStepResult SubmitSyntheticCurrentAttempt(
EHyperTwistTrainingAttemptResult Result,
@ -157,6 +163,18 @@ protected:
UPROPERTY(Transient)
TObjectPtr<UTextBlock> RunRecapDetailTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> RepeatRunButton = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> RepeatRunButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> DismissRecapButton = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> DismissRecapButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> LastAttemptTextBlock = nullptr;
@ -382,6 +400,12 @@ protected:
UFUNCTION()
void HandlePromoteClicked();
UFUNCTION()
void HandleRepeatRunClicked();
UFUNCTION()
void HandleDismissRecapClicked();
UFUNCTION()
void HandleCompleteRunClicked();
@ -461,4 +485,8 @@ private:
int32 LastObservedAttemptCount = INDEX_NONE;
bool bHasRetainedRunRecap = false;
FHyperTwistTrainingSessionSummary RetainedRunRecapSummary;
FHyperTwistTrainingDeck RetainedRunRecapDeck;
FString RetainedRunRecapUserId;
FString RetainedRunRecapSessionId;
EHyperTwistTrainingDeliveryMode RetainedRunRecapMode = EHyperTwistTrainingDeliveryMode::Timer;
};