Add authored coach dashboard widget
This commit is contained in:
parent
2f70bdfc4f
commit
3d67ba1df7
2 changed files with 464 additions and 0 deletions
|
|
@ -0,0 +1,367 @@
|
|||
#include "HyperTwistTraining/HyperTwistCoachDashboardWidget.h"
|
||||
|
||||
#include "Blueprint/WidgetTree.h"
|
||||
#include "Components/Button.h"
|
||||
#include "Components/ButtonSlot.h"
|
||||
#include "Components/HorizontalBox.h"
|
||||
#include "Components/HorizontalBoxSlot.h"
|
||||
#include "Components/TextBlock.h"
|
||||
#include "Components/VerticalBox.h"
|
||||
#include "Components/VerticalBoxSlot.h"
|
||||
|
||||
namespace HyperTwistCoachDashboardWidgetInternal
|
||||
{
|
||||
UTextBlock* AddTextRow(
|
||||
UWidgetTree* WidgetTree,
|
||||
UVerticalBox* Parent,
|
||||
const FName WidgetName,
|
||||
const int32 PaddingBottom)
|
||||
{
|
||||
UTextBlock* TextBlock = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), WidgetName);
|
||||
if (TextBlock == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (UVerticalBoxSlot* Slot = Parent->AddChildToVerticalBox(TextBlock))
|
||||
{
|
||||
Slot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, static_cast<float>(PaddingBottom)));
|
||||
}
|
||||
return TextBlock;
|
||||
}
|
||||
|
||||
UButton* AddButton(
|
||||
UWidgetTree* WidgetTree,
|
||||
UHorizontalBox* Parent,
|
||||
const FName ButtonName,
|
||||
const FName LabelName,
|
||||
const FString& LabelText,
|
||||
UTextBlock*& OutLabel)
|
||||
{
|
||||
UButton* Button = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), ButtonName);
|
||||
if (Button == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OutLabel = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), LabelName);
|
||||
if (OutLabel != nullptr)
|
||||
{
|
||||
OutLabel->SetText(FText::FromString(LabelText));
|
||||
if (UButtonSlot* ButtonSlot = Cast<UButtonSlot>(Button->AddChild(OutLabel)))
|
||||
{
|
||||
ButtonSlot->SetPadding(FMargin(8.0f, 4.0f));
|
||||
ButtonSlot->SetHorizontalAlignment(HAlign_Center);
|
||||
ButtonSlot->SetVerticalAlignment(VAlign_Center);
|
||||
}
|
||||
}
|
||||
|
||||
if (UHorizontalBoxSlot* Slot = Parent->AddChildToHorizontalBox(Button))
|
||||
{
|
||||
Slot->SetPadding(FMargin(0.0f, 0.0f, 8.0f, 0.0f));
|
||||
}
|
||||
|
||||
return Button;
|
||||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::NativeConstruct()
|
||||
{
|
||||
EnsureDefaultDashboardBuilt();
|
||||
Super::NativeConstruct();
|
||||
RefreshCoachDashboardView();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::RefreshCoachDashboardView()
|
||||
{
|
||||
RefreshTrainingState();
|
||||
UpdateDashboardPresentation();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistCoachDashboardWidget::ExecutePrimaryCoachAction()
|
||||
{
|
||||
FHyperTwistTrainingRunState RunState;
|
||||
if (CachedCoachPanelState.bCanStartQueuedRun)
|
||||
{
|
||||
RunState = StartNextQueuedCoachRun(DefaultCoachMaxCases);
|
||||
}
|
||||
else if (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
|
||||
{
|
||||
RunState = StartCoachFollowUpRun(DefaultCoachMaxCases);
|
||||
}
|
||||
else if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
|
||||
{
|
||||
RunState = StartCoachRecommendedRun(DefaultCoachMaxCases);
|
||||
}
|
||||
|
||||
UpdateDashboardPresentation();
|
||||
return RunState;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::DeferDisplayedQueueEntryByDefaultWindow()
|
||||
{
|
||||
const bool bSucceeded = DeferDisplayedCoachQueueEntry(
|
||||
BuildDefaultDeferredUntilUtc(),
|
||||
TEXT("dashboard-defer"),
|
||||
true
|
||||
);
|
||||
UpdateDashboardPresentation();
|
||||
return bSucceeded;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::PromoteFirstDeferredQueueEntry()
|
||||
{
|
||||
const FString DeferredEntryId = FindFirstDeferredQueueEntryId();
|
||||
if (DeferredEntryId.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool bSucceeded = PromoteCoachQueueEntry(DeferredEntryId);
|
||||
UpdateDashboardPresentation();
|
||||
return bSucceeded;
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandlePrimaryActionClicked()
|
||||
{
|
||||
ExecutePrimaryCoachAction();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandleVerifyClicked()
|
||||
{
|
||||
VerifyTrainingRepositoryRoundTrip();
|
||||
UpdateDashboardPresentation();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandleDeferClicked()
|
||||
{
|
||||
DeferDisplayedQueueEntryByDefaultWindow();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandlePromoteClicked()
|
||||
{
|
||||
PromoteFirstDeferredQueueEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
||||
{
|
||||
if (WidgetTree == nullptr || WidgetTree->RootWidget != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RootLayout = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("CoachDashboardRoot"));
|
||||
if (RootLayout == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WidgetTree->RootWidget = RootLayout;
|
||||
|
||||
HeadlineTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachHeadline"),
|
||||
6
|
||||
);
|
||||
SecondaryTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachSecondary"),
|
||||
4
|
||||
);
|
||||
FocusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachFocus"),
|
||||
8
|
||||
);
|
||||
QueueStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachQueueStatus"),
|
||||
2
|
||||
);
|
||||
QueueCountsTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachQueueCounts"),
|
||||
2
|
||||
);
|
||||
VerificationStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachVerificationStatus"),
|
||||
10
|
||||
);
|
||||
|
||||
UHorizontalBox* ButtonRow = WidgetTree->ConstructWidget<UHorizontalBox>(
|
||||
UHorizontalBox::StaticClass(),
|
||||
TEXT("CoachDashboardButtons")
|
||||
);
|
||||
if (ButtonRow != nullptr)
|
||||
{
|
||||
if (UVerticalBoxSlot* ButtonRowSlot = RootLayout->AddChildToVerticalBox(ButtonRow))
|
||||
{
|
||||
ButtonRowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
UTextBlock* PrimaryActionLabelRaw = nullptr;
|
||||
PrimaryActionButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
ButtonRow,
|
||||
TEXT("PrimaryActionButton"),
|
||||
TEXT("PrimaryActionButtonLabel"),
|
||||
TEXT("Start"),
|
||||
PrimaryActionLabelRaw
|
||||
);
|
||||
PrimaryActionButtonLabel = PrimaryActionLabelRaw;
|
||||
|
||||
UTextBlock* VerifyLabelRaw = nullptr;
|
||||
VerifyButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
ButtonRow,
|
||||
TEXT("VerifyButton"),
|
||||
TEXT("VerifyButtonLabel"),
|
||||
TEXT("Verify"),
|
||||
VerifyLabelRaw
|
||||
);
|
||||
VerifyButtonLabel = VerifyLabelRaw;
|
||||
|
||||
UTextBlock* DeferLabelRaw = nullptr;
|
||||
DeferButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
ButtonRow,
|
||||
TEXT("DeferButton"),
|
||||
TEXT("DeferButtonLabel"),
|
||||
TEXT("Defer"),
|
||||
DeferLabelRaw
|
||||
);
|
||||
DeferButtonLabel = DeferLabelRaw;
|
||||
|
||||
UTextBlock* PromoteLabelRaw = nullptr;
|
||||
PromoteButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
ButtonRow,
|
||||
TEXT("PromoteButton"),
|
||||
TEXT("PromoteButtonLabel"),
|
||||
TEXT("Promote"),
|
||||
PromoteLabelRaw
|
||||
);
|
||||
PromoteButtonLabel = PromoteLabelRaw;
|
||||
}
|
||||
|
||||
if (PrimaryActionButton != nullptr)
|
||||
{
|
||||
PrimaryActionButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePrimaryActionClicked);
|
||||
}
|
||||
if (VerifyButton != nullptr)
|
||||
{
|
||||
VerifyButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleVerifyClicked);
|
||||
}
|
||||
if (DeferButton != nullptr)
|
||||
{
|
||||
DeferButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleDeferClicked);
|
||||
}
|
||||
if (PromoteButton != nullptr)
|
||||
{
|
||||
PromoteButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePromoteClicked);
|
||||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
||||
{
|
||||
if (HeadlineTextBlock != nullptr)
|
||||
{
|
||||
HeadlineTextBlock->SetText(FText::FromString(CachedCoachPanelState.Headline));
|
||||
}
|
||||
if (SecondaryTextBlock != nullptr)
|
||||
{
|
||||
SecondaryTextBlock->SetText(FText::FromString(CachedCoachPanelState.SecondaryLine));
|
||||
}
|
||||
if (FocusTextBlock != nullptr)
|
||||
{
|
||||
FocusTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
TEXT("Focus deck: %s | Method: %s | Cases: %d"),
|
||||
CachedCoachPanelState.FocusDeckId.IsEmpty() ? TEXT("n/a") : *CachedCoachPanelState.FocusDeckId,
|
||||
CachedCoachPanelState.FocusMethodSegmentId.IsEmpty() ? TEXT("n/a") : *CachedCoachPanelState.FocusMethodSegmentId,
|
||||
CachedCoachPanelState.FocusCaseCount
|
||||
)));
|
||||
}
|
||||
if (QueueStatusTextBlock != nullptr)
|
||||
{
|
||||
QueueStatusTextBlock->SetText(FText::FromString(CachedCoachPanelState.QueueStatusLine));
|
||||
}
|
||||
if (QueueCountsTextBlock != nullptr)
|
||||
{
|
||||
QueueCountsTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
TEXT("Signals: %d | Follow-up queue entries: %d | Runnable: %s"),
|
||||
CachedCoachPanelState.SignalCount,
|
||||
CachedCoachPanelState.FollowUpQueueEntryCount,
|
||||
CachedCoachPanelState.bHasRunnableQueueEntry ? TEXT("yes") : TEXT("no")
|
||||
)));
|
||||
}
|
||||
if (VerificationStatusTextBlock != nullptr)
|
||||
{
|
||||
VerificationStatusTextBlock->SetText(FText::FromString(CachedCoachPanelState.VerificationStatusLine));
|
||||
}
|
||||
|
||||
FString PrimaryLabel = CachedCoachPanelState.PrimaryActionLabel;
|
||||
if (PrimaryLabel.IsEmpty())
|
||||
{
|
||||
PrimaryLabel = TEXT("Refresh");
|
||||
}
|
||||
if (PrimaryActionButtonLabel != nullptr)
|
||||
{
|
||||
PrimaryActionButtonLabel->SetText(FText::FromString(PrimaryLabel));
|
||||
}
|
||||
if (PrimaryActionButton != nullptr)
|
||||
{
|
||||
PrimaryActionButton->SetIsEnabled(
|
||||
CachedCoachPanelState.bCanStartQueuedRun
|
||||
|| CachedCoachPanelState.bCanStartCoachFollowUpRun
|
||||
|| CachedCoachPanelState.bCanStartCoachRecommendedRun
|
||||
);
|
||||
}
|
||||
|
||||
if (VerifyButtonLabel != nullptr)
|
||||
{
|
||||
VerifyButtonLabel->SetText(FText::FromString(TEXT("Verify")));
|
||||
}
|
||||
if (DeferButtonLabel != nullptr)
|
||||
{
|
||||
DeferButtonLabel->SetText(FText::FromString(TEXT("Defer 24h")));
|
||||
}
|
||||
if (DeferButton != nullptr)
|
||||
{
|
||||
DeferButton->SetIsEnabled(CachedCoachPanelState.bHasRunnableQueueEntry);
|
||||
}
|
||||
if (PromoteButtonLabel != nullptr)
|
||||
{
|
||||
PromoteButtonLabel->SetText(FText::FromString(TEXT("Promote Deferred")));
|
||||
}
|
||||
if (PromoteButton != nullptr)
|
||||
{
|
||||
PromoteButton->SetIsEnabled(!FindFirstDeferredQueueEntryId().IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
FString UHyperTwistCoachDashboardWidget::BuildDefaultDeferredUntilUtc() const
|
||||
{
|
||||
const FDateTime DeferredTimeUtc =
|
||||
FDateTime::UtcNow() + FTimespan::FromHours(FMath::Max(DefaultDeferredHours, 1));
|
||||
return DeferredTimeUtc.ToIso8601();
|
||||
}
|
||||
|
||||
FString UHyperTwistCoachDashboardWidget::FindFirstDeferredQueueEntryId() const
|
||||
{
|
||||
for (const FHyperTwistTrainingCoachSessionQueueStateEntry& Entry : CachedCoachSessionQueueState.Entries)
|
||||
{
|
||||
if (Entry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Deferred)
|
||||
{
|
||||
return Entry.EntryId;
|
||||
}
|
||||
}
|
||||
|
||||
return FString();
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "HyperTwistTraining/HyperTwistTrainingPanelWidget.h"
|
||||
#include "HyperTwistCoachDashboardWidget.generated.h"
|
||||
|
||||
class UButton;
|
||||
class UTextBlock;
|
||||
class UVerticalBox;
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class UNREALHYPERTWIST_API UHyperTwistCoachDashboardWidget : public UHyperTwistTrainingPanelWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
int32 DefaultDeferredHours = 24;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
void RefreshCoachDashboardView();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
FHyperTwistTrainingRunState ExecutePrimaryCoachAction();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool DeferDisplayedQueueEntryByDefaultWindow();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool PromoteFirstDeferredQueueEntry();
|
||||
|
||||
protected:
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UVerticalBox> RootLayout = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> HeadlineTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> SecondaryTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> FocusTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> QueueStatusTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> VerificationStatusTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> QueueCountsTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> PrimaryActionButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> PrimaryActionButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> VerifyButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> VerifyButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> DeferButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> DeferButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> PromoteButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> PromoteButtonLabel = nullptr;
|
||||
|
||||
UFUNCTION()
|
||||
void HandlePrimaryActionClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleVerifyClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleDeferClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandlePromoteClicked();
|
||||
|
||||
private:
|
||||
void EnsureDefaultDashboardBuilt();
|
||||
void UpdateDashboardPresentation();
|
||||
FString BuildDefaultDeferredUntilUtc() const;
|
||||
FString FindFirstDeferredQueueEntryId() const;
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue