Add guidance preview to coach dashboard

This commit is contained in:
axiomlogicnexus 2026-04-27 15:05:54 +02:00
parent d3658f490d
commit 12c675d211
2 changed files with 203 additions and 0 deletions

View file

@ -303,6 +303,20 @@ bool UHyperTwistCoachDashboardWidget::SelectNextAttemptReview()
return true;
}
FHyperTwistTrainingDeck UHyperTwistCoachDashboardWidget::PreviewRecommendedCoachDeck()
{
const FHyperTwistTrainingDeck Deck = BuildActiveCoachRecommendedDeck(DefaultCoachMaxCases);
UpdateDashboardPresentation();
return Deck;
}
FHyperTwistTrainingDeck UHyperTwistCoachDashboardWidget::PreviewFollowUpCoachDeck()
{
const FHyperTwistTrainingDeck Deck = BuildActiveCoachFollowUpDeck(DefaultCoachMaxCases);
UpdateDashboardPresentation();
return Deck;
}
FHyperTwistTrainingRunState UHyperTwistCoachDashboardWidget::RestartRetainedRunRecapDeck()
{
if (!RetainedRunRecapDeck.IsStructurallyValid())
@ -630,6 +644,16 @@ void UHyperTwistCoachDashboardWidget::HandleStartRecommendedRunClicked()
UpdateDashboardPresentation();
}
void UHyperTwistCoachDashboardWidget::HandlePreviewRecommendedClicked()
{
PreviewRecommendedCoachDeck();
}
void UHyperTwistCoachDashboardWidget::HandlePreviewFollowUpClicked()
{
PreviewFollowUpCoachDeck();
}
void UHyperTwistCoachDashboardWidget::HandleCompleteRunClicked()
{
CompleteActiveTrainingRun(false);
@ -873,6 +897,24 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
TEXT("CoachHandoffStatus"),
2
);
GuidanceReviewHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
TEXT("CoachGuidanceReviewHeader"),
2
);
RecommendedDeckPreviewTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
TEXT("CoachRecommendedDeckPreview"),
2
);
FollowUpDeckPreviewTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
TEXT("CoachFollowUpDeckPreview"),
2
);
UHorizontalBox* RunRecapActionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
UHorizontalBox::StaticClass(),
TEXT("CoachDashboardRunRecapActions")
@ -950,6 +992,39 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
);
StartRecommendedRunButtonLabel = StartRecommendedRunLabelRaw;
}
UHorizontalBox* GuidancePreviewActionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
UHorizontalBox::StaticClass(),
TEXT("CoachDashboardGuidancePreviewActions")
);
if (GuidancePreviewActionRow != nullptr)
{
if (UVerticalBoxSlot* GuidancePreviewActionRowSlot = RootLayout->AddChildToVerticalBox(GuidancePreviewActionRow))
{
GuidancePreviewActionRowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 0.0f));
}
UTextBlock* PreviewRecommendedLabelRaw = nullptr;
PreviewRecommendedButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
WidgetTree,
GuidancePreviewActionRow,
TEXT("PreviewRecommendedButton"),
TEXT("PreviewRecommendedButtonLabel"),
TEXT("Preview Recommended"),
PreviewRecommendedLabelRaw
);
PreviewRecommendedButtonLabel = PreviewRecommendedLabelRaw;
UTextBlock* PreviewFollowUpLabelRaw = nullptr;
PreviewFollowUpButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
WidgetTree,
GuidancePreviewActionRow,
TEXT("PreviewFollowUpButton"),
TEXT("PreviewFollowUpButtonLabel"),
TEXT("Preview Follow-Up"),
PreviewFollowUpLabelRaw
);
PreviewFollowUpButtonLabel = PreviewFollowUpLabelRaw;
}
LastAttemptTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
WidgetTree,
RootLayout,
@ -1479,6 +1554,14 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
{
StartRecommendedRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleStartRecommendedRunClicked);
}
if (PreviewRecommendedButton != nullptr)
{
PreviewRecommendedButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePreviewRecommendedClicked);
}
if (PreviewFollowUpButton != nullptr)
{
PreviewFollowUpButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePreviewFollowUpClicked);
}
if (CompleteRunButton != nullptr)
{
CompleteRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleCompleteRunClicked);
@ -1917,6 +2000,59 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
const FString RecommendedRunLabel = CachedCoachBrief.PrimaryActionLabel.IsEmpty()
? TEXT("Start Recommended")
: CachedCoachBrief.PrimaryActionLabel;
const auto BuildDeckPreviewLine = [](const FHyperTwistTrainingDeck& Deck, const FHyperTwistCoachBrief& Brief, const TCHAR* EmptyPrefix)
{
if (Deck.IsStructurallyValid())
{
TArray<FString> PreviewCaseParts;
const int32 PreviewCaseCount = FMath::Min(Deck.Cases.Num(), 3);
for (int32 CaseIndex = 0; CaseIndex < PreviewCaseCount; ++CaseIndex)
{
const FHyperTwistTrainingCase& PreviewCase = Deck.Cases[CaseIndex];
PreviewCaseParts.Add(
PreviewCase.PromptLabel.IsEmpty() ? PreviewCase.CaseId : PreviewCase.PromptLabel
);
}
const FString PreviewCases = PreviewCaseParts.Num() > 0
? FString::Join(PreviewCaseParts, TEXT(" | "))
: TEXT("none");
return FString::Printf(
TEXT("Deck preview: %s | Deck: %s | Cases: %d | Selection: %s | Difficulty: %s | Subset: %s | Preview: %s"),
Deck.Title.IsEmpty() ? TEXT("n/a") : *Deck.Title,
Deck.DeckId.IsEmpty() ? TEXT("n/a") : *Deck.DeckId,
Deck.Cases.Num(),
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(Deck.SelectionPolicy),
Deck.DifficultyBand.IsEmpty() ? TEXT("n/a") : *Deck.DifficultyBand,
Deck.SubsetId.IsEmpty() ? TEXT("n/a") : *Deck.SubsetId,
*PreviewCases
);
}
if (!Brief.UserId.IsEmpty() && !Brief.Headline.IsEmpty())
{
return FString::Printf(
TEXT("%s: %s | Deck: %s | Method: %s | Focus cases: %d | Action: %s"),
EmptyPrefix,
*Brief.Headline,
Brief.FocusDeckId.IsEmpty() ? TEXT("n/a") : *Brief.FocusDeckId,
Brief.FocusMethodSegmentId.IsEmpty() ? TEXT("n/a") : *Brief.FocusMethodSegmentId,
Brief.FocusCaseIds.Num(),
Brief.PrimaryActionLabel.IsEmpty() ? TEXT("n/a") : *Brief.PrimaryActionLabel
);
}
return FString::Printf(TEXT("%s: no guidance available"), EmptyPrefix);
};
const FString RecommendedDeckPreviewLine = BuildDeckPreviewLine(
CachedCoachRecommendedDeck,
CachedCoachBrief,
TEXT("Recommended guidance")
);
const FString FollowUpDeckPreviewLine = BuildDeckPreviewLine(
CachedCoachFollowUpDeck,
CachedCoachFollowUpBrief,
TEXT("Follow-up guidance")
);
if (HeadlineTextBlock != nullptr)
{
@ -2054,6 +2190,18 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
{
CoachHandoffStatusTextBlock->SetText(FText::FromString(CoachHandoffStatusLine));
}
if (GuidanceReviewHeaderTextBlock != nullptr)
{
GuidanceReviewHeaderTextBlock->SetText(FText::FromString(TEXT("[Guidance Review]")));
}
if (RecommendedDeckPreviewTextBlock != nullptr)
{
RecommendedDeckPreviewTextBlock->SetText(FText::FromString(RecommendedDeckPreviewLine));
}
if (FollowUpDeckPreviewTextBlock != nullptr)
{
FollowUpDeckPreviewTextBlock->SetText(FText::FromString(FollowUpDeckPreviewLine));
}
if (StartQueuedRunButtonLabel != nullptr)
{
StartQueuedRunButtonLabel->SetText(FText::FromString(QueuedRunLabel));
@ -2084,6 +2232,28 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
bCanUseCoachHandoff && CachedCoachPanelState.bCanStartCoachRecommendedRun
);
}
if (PreviewRecommendedButtonLabel != nullptr)
{
PreviewRecommendedButtonLabel->SetText(FText::FromString(TEXT("Preview Recommended")));
}
if (PreviewRecommendedButton != nullptr)
{
PreviewRecommendedButton->SetIsEnabled(
!bHasLiveAttemptTimer && CachedCoachPanelState.bCanStartCoachRecommendedRun
);
}
if (PreviewFollowUpButtonLabel != nullptr)
{
PreviewFollowUpButtonLabel->SetText(FText::FromString(TEXT("Preview Follow-Up")));
}
if (PreviewFollowUpButton != nullptr)
{
PreviewFollowUpButton->SetIsEnabled(
!bHasLiveAttemptTimer
&& CachedCoachPanelState.bCanStartCoachFollowUpRun
&& CachedCoachPanelState.bHasFollowUpGuidance
);
}
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")
FHyperTwistTrainingDeck PreviewRecommendedCoachDeck();
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingDeck PreviewFollowUpCoachDeck();
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingRunState RestartRetainedRunRecapDeck();
@ -169,6 +175,15 @@ protected:
UPROPERTY(Transient)
TObjectPtr<UTextBlock> CoachHandoffStatusTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> GuidanceReviewHeaderTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> RecommendedDeckPreviewTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> FollowUpDeckPreviewTextBlock = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> RepeatRunButton = nullptr;
@ -199,6 +214,18 @@ protected:
UPROPERTY(Transient)
TObjectPtr<UTextBlock> StartRecommendedRunButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> PreviewRecommendedButton = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> PreviewRecommendedButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UButton> PreviewFollowUpButton = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> PreviewFollowUpButtonLabel = nullptr;
UPROPERTY(Transient)
TObjectPtr<UTextBlock> LastAttemptTextBlock = nullptr;
@ -439,6 +466,12 @@ protected:
UFUNCTION()
void HandleStartRecommendedRunClicked();
UFUNCTION()
void HandlePreviewRecommendedClicked();
UFUNCTION()
void HandlePreviewFollowUpClicked();
UFUNCTION()
void HandleCompleteRunClicked();