Add queue browsing to coach dashboard
This commit is contained in:
parent
ead48b43aa
commit
23fa842616
12 changed files with 693 additions and 6 deletions
|
|
@ -11,6 +11,17 @@
|
|||
|
||||
namespace HyperTwistCoachDashboardWidgetInternal
|
||||
{
|
||||
template <typename TEnum>
|
||||
FString EnumDisplayName(const TEnum Value)
|
||||
{
|
||||
if (const UEnum* Enum = StaticEnum<TEnum>())
|
||||
{
|
||||
return Enum->GetDisplayNameTextByValue(static_cast<int64>(Value)).ToString();
|
||||
}
|
||||
|
||||
return TEXT("Unknown");
|
||||
}
|
||||
|
||||
UTextBlock* AddTextRow(
|
||||
UWidgetTree* WidgetTree,
|
||||
UVerticalBox* Parent,
|
||||
|
|
@ -75,6 +86,7 @@ void UHyperTwistCoachDashboardWidget::NativeConstruct()
|
|||
void UHyperTwistCoachDashboardWidget::RefreshCoachDashboardView()
|
||||
{
|
||||
RefreshTrainingState();
|
||||
SyncSelectedQueueEntry();
|
||||
UpdateDashboardPresentation();
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +106,7 @@ FHyperTwistTrainingRunState UHyperTwistCoachDashboardWidget::ExecutePrimaryCoach
|
|||
RunState = StartCoachRecommendedRun(DefaultCoachMaxCases);
|
||||
}
|
||||
|
||||
SyncSelectedQueueEntry();
|
||||
UpdateDashboardPresentation();
|
||||
return RunState;
|
||||
}
|
||||
|
|
@ -105,23 +118,90 @@ bool UHyperTwistCoachDashboardWidget::DeferDisplayedQueueEntryByDefaultWindow()
|
|||
TEXT("dashboard-defer"),
|
||||
true
|
||||
);
|
||||
SyncSelectedQueueEntry();
|
||||
UpdateDashboardPresentation();
|
||||
return bSucceeded;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::PromoteFirstDeferredQueueEntry()
|
||||
{
|
||||
const FString DeferredEntryId = FindFirstDeferredQueueEntryId();
|
||||
FString DeferredEntryId;
|
||||
if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedEntry = FindSelectedQueueEntry();
|
||||
SelectedEntry != nullptr
|
||||
&& SelectedEntry->EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Deferred)
|
||||
{
|
||||
DeferredEntryId = SelectedEntry->EntryId;
|
||||
}
|
||||
else
|
||||
{
|
||||
DeferredEntryId = FindFirstDeferredQueueEntryId();
|
||||
}
|
||||
if (DeferredEntryId.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool bSucceeded = PromoteCoachQueueEntry(DeferredEntryId);
|
||||
SyncSelectedQueueEntry();
|
||||
UpdateDashboardPresentation();
|
||||
return bSucceeded;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::SelectPreviousQueueEntry()
|
||||
{
|
||||
if (CachedCoachSessionQueueState.Entries.Num() <= 0)
|
||||
{
|
||||
SelectedQueueEntryIndex = INDEX_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SelectedQueueEntryIndex == INDEX_NONE)
|
||||
{
|
||||
SyncSelectedQueueEntry();
|
||||
return SelectedQueueEntryIndex != INDEX_NONE;
|
||||
}
|
||||
|
||||
SelectedQueueEntryIndex =
|
||||
(SelectedQueueEntryIndex - 1 + CachedCoachSessionQueueState.Entries.Num())
|
||||
% CachedCoachSessionQueueState.Entries.Num();
|
||||
UpdateDashboardPresentation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::SelectNextQueueEntry()
|
||||
{
|
||||
if (CachedCoachSessionQueueState.Entries.Num() <= 0)
|
||||
{
|
||||
SelectedQueueEntryIndex = INDEX_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SelectedQueueEntryIndex == INDEX_NONE)
|
||||
{
|
||||
SyncSelectedQueueEntry();
|
||||
return SelectedQueueEntryIndex != INDEX_NONE;
|
||||
}
|
||||
|
||||
SelectedQueueEntryIndex = (SelectedQueueEntryIndex + 1) % CachedCoachSessionQueueState.Entries.Num();
|
||||
UpdateDashboardPresentation();
|
||||
return true;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistCoachDashboardWidget::StartSelectedQueueEntry()
|
||||
{
|
||||
if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedEntry = FindSelectedQueueEntry();
|
||||
SelectedEntry != nullptr)
|
||||
{
|
||||
FHyperTwistTrainingRunState RunState =
|
||||
StartCoachQueueEntry(SelectedEntry->EntryId, DefaultCoachMaxCases);
|
||||
SyncSelectedQueueEntry();
|
||||
UpdateDashboardPresentation();
|
||||
return RunState;
|
||||
}
|
||||
|
||||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandlePrimaryActionClicked()
|
||||
{
|
||||
ExecutePrimaryCoachAction();
|
||||
|
|
@ -135,7 +215,21 @@ void UHyperTwistCoachDashboardWidget::HandleVerifyClicked()
|
|||
|
||||
void UHyperTwistCoachDashboardWidget::HandleDeferClicked()
|
||||
{
|
||||
DeferDisplayedQueueEntryByDefaultWindow();
|
||||
if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedEntry = FindSelectedQueueEntry())
|
||||
{
|
||||
DeferCoachQueueEntry(
|
||||
SelectedEntry->EntryId,
|
||||
BuildDefaultDeferredUntilUtc(),
|
||||
TEXT("dashboard-defer"),
|
||||
true
|
||||
);
|
||||
SyncSelectedQueueEntry();
|
||||
UpdateDashboardPresentation();
|
||||
}
|
||||
else
|
||||
{
|
||||
DeferDisplayedQueueEntryByDefaultWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandlePromoteClicked()
|
||||
|
|
@ -143,6 +237,21 @@ void UHyperTwistCoachDashboardWidget::HandlePromoteClicked()
|
|||
PromoteFirstDeferredQueueEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandlePreviousQueueClicked()
|
||||
{
|
||||
SelectPreviousQueueEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandleStartSelectedClicked()
|
||||
{
|
||||
StartSelectedQueueEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandleNextQueueClicked()
|
||||
{
|
||||
SelectNextQueueEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
||||
{
|
||||
if (WidgetTree == nullptr || WidgetTree->RootWidget != nullptr)
|
||||
|
|
@ -176,18 +285,42 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
TEXT("CoachFocus"),
|
||||
8
|
||||
);
|
||||
FollowUpTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachFollowUp"),
|
||||
4
|
||||
);
|
||||
QueueStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachQueueStatus"),
|
||||
2
|
||||
);
|
||||
QueueSelectionTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachQueueSelection"),
|
||||
2
|
||||
);
|
||||
QueueEntryDetailTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachQueueEntryDetail"),
|
||||
2
|
||||
);
|
||||
QueueCountsTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachQueueCounts"),
|
||||
2
|
||||
);
|
||||
QueueExecutionTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachQueueExecution"),
|
||||
2
|
||||
);
|
||||
VerificationStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
|
|
@ -251,6 +384,51 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
PromoteButtonLabel = PromoteLabelRaw;
|
||||
}
|
||||
|
||||
UHorizontalBox* QueueNavRow = WidgetTree->ConstructWidget<UHorizontalBox>(
|
||||
UHorizontalBox::StaticClass(),
|
||||
TEXT("CoachDashboardQueueNavButtons")
|
||||
);
|
||||
if (QueueNavRow != nullptr)
|
||||
{
|
||||
if (UVerticalBoxSlot* QueueNavRowSlot = RootLayout->AddChildToVerticalBox(QueueNavRow))
|
||||
{
|
||||
QueueNavRowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
UTextBlock* PreviousQueueLabelRaw = nullptr;
|
||||
PreviousQueueButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
QueueNavRow,
|
||||
TEXT("PreviousQueueButton"),
|
||||
TEXT("PreviousQueueButtonLabel"),
|
||||
TEXT("Prev Queue"),
|
||||
PreviousQueueLabelRaw
|
||||
);
|
||||
PreviousQueueButtonLabel = PreviousQueueLabelRaw;
|
||||
|
||||
UTextBlock* StartSelectedLabelRaw = nullptr;
|
||||
StartSelectedButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
QueueNavRow,
|
||||
TEXT("StartSelectedQueueButton"),
|
||||
TEXT("StartSelectedQueueButtonLabel"),
|
||||
TEXT("Start Selected"),
|
||||
StartSelectedLabelRaw
|
||||
);
|
||||
StartSelectedButtonLabel = StartSelectedLabelRaw;
|
||||
|
||||
UTextBlock* NextQueueLabelRaw = nullptr;
|
||||
NextQueueButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
QueueNavRow,
|
||||
TEXT("NextQueueButton"),
|
||||
TEXT("NextQueueButtonLabel"),
|
||||
TEXT("Next Queue"),
|
||||
NextQueueLabelRaw
|
||||
);
|
||||
NextQueueButtonLabel = NextQueueLabelRaw;
|
||||
}
|
||||
|
||||
if (PrimaryActionButton != nullptr)
|
||||
{
|
||||
PrimaryActionButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePrimaryActionClicked);
|
||||
|
|
@ -267,6 +445,72 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
{
|
||||
PromoteButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePromoteClicked);
|
||||
}
|
||||
if (PreviousQueueButton != nullptr)
|
||||
{
|
||||
PreviousQueueButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePreviousQueueClicked);
|
||||
}
|
||||
if (StartSelectedButton != nullptr)
|
||||
{
|
||||
StartSelectedButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleStartSelectedClicked);
|
||||
}
|
||||
if (NextQueueButton != nullptr)
|
||||
{
|
||||
NextQueueButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleNextQueueClicked);
|
||||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::SyncSelectedQueueEntry()
|
||||
{
|
||||
if (CachedCoachSessionQueueState.Entries.Num() <= 0)
|
||||
{
|
||||
SelectedQueueEntryIndex = INDEX_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedQueueEntryIndex >= 0
|
||||
&& SelectedQueueEntryIndex < CachedCoachSessionQueueState.Entries.Num())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedQueueEntryIndex = INDEX_NONE;
|
||||
|
||||
if (bPreferActiveQueueSelectionOnRefresh && !CachedCoachPanelState.ActiveQueueEntryId.IsEmpty())
|
||||
{
|
||||
SelectedQueueEntryIndex = CachedCoachSessionQueueState.Entries.IndexOfByPredicate(
|
||||
[this](const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate)
|
||||
{
|
||||
return Candidate.EntryId == CachedCoachPanelState.ActiveQueueEntryId;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (SelectedQueueEntryIndex == INDEX_NONE)
|
||||
{
|
||||
SelectedQueueEntryIndex = CachedCoachSessionQueueState.Entries.IndexOfByPredicate(
|
||||
[](const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate)
|
||||
{
|
||||
return Candidate.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Ready
|
||||
|| (Candidate.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Pending
|
||||
&& Candidate.bCanStartImmediately);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (SelectedQueueEntryIndex == INDEX_NONE)
|
||||
{
|
||||
SelectedQueueEntryIndex = CachedCoachSessionQueueState.Entries.IndexOfByPredicate(
|
||||
[](const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate)
|
||||
{
|
||||
return Candidate.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Deferred;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (SelectedQueueEntryIndex == INDEX_NONE)
|
||||
{
|
||||
SelectedQueueEntryIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
||||
|
|
@ -288,17 +532,96 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
CachedCoachPanelState.FocusCaseCount
|
||||
)));
|
||||
}
|
||||
if (FollowUpTextBlock != nullptr)
|
||||
{
|
||||
const FString FollowUpLine =
|
||||
(!CachedCoachFollowUpBrief.UserId.IsEmpty() && !CachedCoachFollowUpBrief.Headline.IsEmpty())
|
||||
? FString::Printf(
|
||||
TEXT("Follow-up: %s | Action: %s | Cases: %d"),
|
||||
*CachedCoachFollowUpBrief.Headline,
|
||||
CachedCoachFollowUpBrief.PrimaryActionLabel.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *CachedCoachFollowUpBrief.PrimaryActionLabel,
|
||||
CachedCoachFollowUpBrief.FocusCaseIds.Num()
|
||||
)
|
||||
: TEXT("Follow-up: none queued beyond the current coach chain");
|
||||
FollowUpTextBlock->SetText(FText::FromString(FollowUpLine));
|
||||
}
|
||||
if (QueueStatusTextBlock != nullptr)
|
||||
{
|
||||
QueueStatusTextBlock->SetText(FText::FromString(CachedCoachPanelState.QueueStatusLine));
|
||||
}
|
||||
if (QueueSelectionTextBlock != nullptr)
|
||||
{
|
||||
if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedEntry = FindSelectedQueueEntry())
|
||||
{
|
||||
QueueSelectionTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
TEXT("Selected queue item %d/%d | Source: %s | State: %s | Priority: %s (%.1f)"),
|
||||
SelectedQueueEntryIndex + 1,
|
||||
CachedCoachSessionQueueState.Entries.Num(),
|
||||
SelectedEntry->SourceLabel.IsEmpty() ? TEXT("n/a") : *SelectedEntry->SourceLabel,
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(SelectedEntry->EntryState),
|
||||
SelectedEntry->PriorityLabel.IsEmpty() ? TEXT("n/a") : *SelectedEntry->PriorityLabel,
|
||||
SelectedEntry->PriorityScore
|
||||
)));
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueSelectionTextBlock->SetText(FText::FromString(TEXT("Selected queue item: none")));
|
||||
}
|
||||
}
|
||||
if (QueueEntryDetailTextBlock != nullptr)
|
||||
{
|
||||
if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedEntry = FindSelectedQueueEntry())
|
||||
{
|
||||
const FString DeferredLabel = SelectedEntry->DeferredUntilUtc.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: SelectedEntry->DeferredUntilUtc;
|
||||
const FString DeferralReason = SelectedEntry->DeferralReasonLabel.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: SelectedEntry->DeferralReasonLabel;
|
||||
QueueEntryDetailTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
TEXT("Headline: %s | Deck: %s | Method: %s | Cases: %d | Mode: %s | Policy: %s | Deferred until: %s | Reason: %s | Follow-up: %s | Archive tune: %s | Pinned: %s"),
|
||||
*SelectedEntry->Headline,
|
||||
SelectedEntry->FocusDeckId.IsEmpty() ? TEXT("n/a") : *SelectedEntry->FocusDeckId,
|
||||
SelectedEntry->FocusMethodSegmentId.IsEmpty() ? TEXT("n/a") : *SelectedEntry->FocusMethodSegmentId,
|
||||
SelectedEntry->FocusCaseIds.Num(),
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(SelectedEntry->SuggestedMode),
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(SelectedEntry->SuggestedSelectionPolicy),
|
||||
*DeferredLabel,
|
||||
*DeferralReason,
|
||||
SelectedEntry->bRequiresFollowUp ? TEXT("yes") : TEXT("no"),
|
||||
SelectedEntry->bRequiresArchiveRetentionTuning ? TEXT("yes") : TEXT("no"),
|
||||
SelectedEntry->bPinned ? TEXT("yes") : TEXT("no")
|
||||
)));
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueEntryDetailTextBlock->SetText(FText::FromString(TEXT("Queue entry detail: no queue entry selected")));
|
||||
}
|
||||
}
|
||||
if (QueueCountsTextBlock != nullptr)
|
||||
{
|
||||
QueueCountsTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
TEXT("Signals: %d | Follow-up queue entries: %d | Runnable: %s"),
|
||||
TEXT("Signals: %d | Ready: %d | Deferred: %d | In-progress: %d | Completed: %d"),
|
||||
CachedCoachPanelState.SignalCount,
|
||||
CachedCoachPanelState.FollowUpQueueEntryCount,
|
||||
CachedCoachPanelState.bHasRunnableQueueEntry ? TEXT("yes") : TEXT("no")
|
||||
CachedCoachSessionQueueState.ReadyEntryCount,
|
||||
CachedCoachSessionQueueState.DeferredEntryCount,
|
||||
CachedCoachSessionQueueState.InProgressEntryCount,
|
||||
CachedCoachSessionQueueState.CompletedEntryCount
|
||||
)));
|
||||
}
|
||||
if (QueueExecutionTextBlock != nullptr)
|
||||
{
|
||||
QueueExecutionTextBlock->SetText(FText::FromString(FString::Printf(
|
||||
TEXT("Execution: queued %d | started %d | completed %d | deferred %d | manual reschedules %d | outstanding deferred: %s | completion gap: %s"),
|
||||
CachedCoachQueueExecutionSummary.QueuedEventCount,
|
||||
CachedCoachQueueExecutionSummary.StartedEventCount,
|
||||
CachedCoachQueueExecutionSummary.CompletedEventCount,
|
||||
CachedCoachQueueExecutionSummary.DeferredEventCount,
|
||||
CachedCoachQueueExecutionSummary.ManualRescheduleEventCount,
|
||||
CachedCoachQueueExecutionSummary.bHasOutstandingDeferredWork ? TEXT("yes") : TEXT("no"),
|
||||
CachedCoachQueueExecutionSummary.bHasCompletionGap ? TEXT("yes") : TEXT("no")
|
||||
)));
|
||||
}
|
||||
if (VerificationStatusTextBlock != nullptr)
|
||||
|
|
@ -334,7 +657,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
}
|
||||
if (DeferButton != nullptr)
|
||||
{
|
||||
DeferButton->SetIsEnabled(CachedCoachPanelState.bHasRunnableQueueEntry);
|
||||
DeferButton->SetIsEnabled(FindSelectedQueueEntry() != nullptr);
|
||||
}
|
||||
if (PromoteButtonLabel != nullptr)
|
||||
{
|
||||
|
|
@ -344,6 +667,35 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
{
|
||||
PromoteButton->SetIsEnabled(!FindFirstDeferredQueueEntryId().IsEmpty());
|
||||
}
|
||||
if (PreviousQueueButtonLabel != nullptr)
|
||||
{
|
||||
PreviousQueueButtonLabel->SetText(FText::FromString(TEXT("Prev Queue")));
|
||||
}
|
||||
if (PreviousQueueButton != nullptr)
|
||||
{
|
||||
PreviousQueueButton->SetIsEnabled(CachedCoachSessionQueueState.Entries.Num() > 1);
|
||||
}
|
||||
if (StartSelectedButtonLabel != nullptr)
|
||||
{
|
||||
StartSelectedButtonLabel->SetText(FText::FromString(TEXT("Start Selected")));
|
||||
}
|
||||
if (StartSelectedButton != nullptr)
|
||||
{
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedEntry = FindSelectedQueueEntry();
|
||||
const bool bSelectedRunnable = SelectedEntry != nullptr
|
||||
&& (SelectedEntry->EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Ready
|
||||
|| (SelectedEntry->EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Pending
|
||||
&& SelectedEntry->bCanStartImmediately));
|
||||
StartSelectedButton->SetIsEnabled(bSelectedRunnable);
|
||||
}
|
||||
if (NextQueueButtonLabel != nullptr)
|
||||
{
|
||||
NextQueueButtonLabel->SetText(FText::FromString(TEXT("Next Queue")));
|
||||
}
|
||||
if (NextQueueButton != nullptr)
|
||||
{
|
||||
NextQueueButton->SetIsEnabled(CachedCoachSessionQueueState.Entries.Num() > 1);
|
||||
}
|
||||
}
|
||||
|
||||
FString UHyperTwistCoachDashboardWidget::BuildDefaultDeferredUntilUtc() const
|
||||
|
|
@ -365,3 +717,13 @@ FString UHyperTwistCoachDashboardWidget::FindFirstDeferredQueueEntryId() const
|
|||
|
||||
return FString();
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* UHyperTwistCoachDashboardWidget::FindSelectedQueueEntry() const
|
||||
{
|
||||
if (SelectedQueueEntryIndex < 0 || SelectedQueueEntryIndex >= CachedCoachSessionQueueState.Entries.Num())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &CachedCoachSessionQueueState.Entries[SelectedQueueEntryIndex];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,21 @@ FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartNextQueuedCoach
|
|||
return CachedRunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachQueueEntry(
|
||||
const FString& EntryId,
|
||||
const int32 MaxCases
|
||||
)
|
||||
{
|
||||
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartCoachQueueEntry(
|
||||
this,
|
||||
EntryId,
|
||||
DefaultSessionId + TEXT("_queue_") + EntryId,
|
||||
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
|
||||
);
|
||||
RefreshTrainingState();
|
||||
return CachedRunState;
|
||||
}
|
||||
|
||||
bool UHyperTwistTrainingPanelWidget::DeferCoachQueueEntry(
|
||||
const FString& EntryId,
|
||||
const FString& DeferredUntilUtc,
|
||||
|
|
|
|||
|
|
@ -6236,6 +6236,60 @@ FHyperTwistTrainingCoachSessionQueueState UHyperTwistTrainingRepositoryLibrary::
|
|||
return HyperTwistTrainingRepositoryLibraryInternal::NormalizeCoachSessionQueueState(UpdatedQueueState);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueState UHyperTwistTrainingRepositoryLibrary::StartCoachSessionQueueEntryById(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
|
||||
const FString& EntryId,
|
||||
const FString& TrainingSessionId,
|
||||
const FString& StartedAtUtc
|
||||
)
|
||||
{
|
||||
FHyperTwistTrainingCoachSessionQueueState UpdatedQueueState =
|
||||
HyperTwistTrainingRepositoryLibraryInternal::NormalizeCoachSessionQueueState(QueueState);
|
||||
if (!UpdatedQueueState.IsStructurallyValid() || EntryId.IsEmpty())
|
||||
{
|
||||
return UpdatedQueueState;
|
||||
}
|
||||
|
||||
const FString ResolvedStartedAtUtc =
|
||||
HyperTwistTrainingRepositoryLibraryInternal::ResolveReferenceUtc(StartedAtUtc);
|
||||
const int32 ActivatedIndex = UpdatedQueueState.Entries.IndexOfByPredicate(
|
||||
[&EntryId](const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate)
|
||||
{
|
||||
return Candidate.EntryId == EntryId
|
||||
&& Candidate.EntryState != EHyperTwistTrainingCoachSessionQueueEntryState::Deferred
|
||||
&& !HyperTwistTrainingRepositoryLibraryInternal::IsTerminalCoachQueueEntryState(Candidate.EntryState);
|
||||
}
|
||||
);
|
||||
if (ActivatedIndex == INDEX_NONE)
|
||||
{
|
||||
return UpdatedQueueState;
|
||||
}
|
||||
|
||||
for (FHyperTwistTrainingCoachSessionQueueStateEntry& Entry : UpdatedQueueState.Entries)
|
||||
{
|
||||
if (Entry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::InProgress)
|
||||
{
|
||||
Entry.EntryState = EHyperTwistTrainingCoachSessionQueueEntryState::Pending;
|
||||
Entry.SourceTrainingSessionId.Reset();
|
||||
Entry.StartedAtUtc.Reset();
|
||||
Entry.bCanStartImmediately = false;
|
||||
}
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueStateEntry& ActiveEntry =
|
||||
UpdatedQueueState.Entries[ActivatedIndex];
|
||||
ActiveEntry.EntryState = EHyperTwistTrainingCoachSessionQueueEntryState::InProgress;
|
||||
ActiveEntry.SourceTrainingSessionId = TrainingSessionId;
|
||||
ActiveEntry.ScheduledAtUtc = !ActiveEntry.ScheduledAtUtc.IsEmpty()
|
||||
? ActiveEntry.ScheduledAtUtc
|
||||
: ResolvedStartedAtUtc;
|
||||
ActiveEntry.DeferredUntilUtc.Reset();
|
||||
ActiveEntry.StartedAtUtc = ResolvedStartedAtUtc;
|
||||
ActiveEntry.bCanStartImmediately = false;
|
||||
UpdatedQueueState.LastUpdatedAtUtc = ResolvedStartedAtUtc;
|
||||
return HyperTwistTrainingRepositoryLibraryInternal::NormalizeCoachSessionQueueState(UpdatedQueueState);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueState UHyperTwistTrainingRepositoryLibrary::FinalizeCoachSessionQueueEntry(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
|
||||
const FString& TrainingSessionId,
|
||||
|
|
|
|||
|
|
@ -1043,6 +1043,21 @@ FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartNextQueuedCo
|
|||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartCoachQueueEntry(
|
||||
UObject* WorldContextObject,
|
||||
const FString& EntryId,
|
||||
const FString& SessionId,
|
||||
int32 MaxCases
|
||||
)
|
||||
{
|
||||
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
|
||||
{
|
||||
return TrainingSubsystem->StartCoachQueueEntry(EntryId, SessionId, MaxCases);
|
||||
}
|
||||
|
||||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartSampleClassicTrainingRun(
|
||||
UObject* WorldContextObject,
|
||||
const FString& UserId,
|
||||
|
|
|
|||
|
|
@ -161,6 +161,21 @@ FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartNextQueuedCoac
|
|||
return CachedRunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachQueueEntryTrainingRun(
|
||||
const FString& EntryId,
|
||||
const int32 MaxCases
|
||||
)
|
||||
{
|
||||
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartCoachQueueEntry(
|
||||
this,
|
||||
EntryId,
|
||||
AutoStartSessionId + TEXT("_queue_") + EntryId,
|
||||
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
|
||||
);
|
||||
RefreshCachedTrainingState();
|
||||
return CachedRunState;
|
||||
}
|
||||
|
||||
bool AHyperTwistTrainingSessionActor::DeferCoachQueueEntry(
|
||||
const FString& EntryId,
|
||||
const FString& DeferredUntilUtc,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,24 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
return BestEntry;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* FindCoachQueueEntryById(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
|
||||
const FString& EntryId
|
||||
)
|
||||
{
|
||||
if (!QueueState.IsStructurallyValid() || EntryId.IsEmpty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return QueueState.Entries.FindByPredicate(
|
||||
[&EntryId](const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate)
|
||||
{
|
||||
return Candidate.EntryId == EntryId;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingDeck BuildCoachQueueDeckFromEntry(
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry& QueueEntry,
|
||||
const FHyperTwistTrainingRunState& ActiveRunState,
|
||||
|
|
@ -1469,6 +1487,123 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartNextQueuedCoachRu
|
|||
return RunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachQueueEntry(
|
||||
const FString& EntryId,
|
||||
const FString& SessionId,
|
||||
int32 MaxCases
|
||||
)
|
||||
{
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedEntry =
|
||||
HyperTwistTrainingSubsystemInternal::FindCoachQueueEntryById(ActiveCoachSessionQueueState, EntryId);
|
||||
if (SelectedEntry == nullptr)
|
||||
{
|
||||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
const bool bIsRunnable = SelectedEntry->EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Ready
|
||||
|| (SelectedEntry->EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Pending
|
||||
&& SelectedEntry->bCanStartImmediately);
|
||||
if (!bIsRunnable)
|
||||
{
|
||||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingDeck QueueDeck;
|
||||
FHyperTwistCoachBrief QueueBrief;
|
||||
const bool bHasPrimaryCoachBrief = !ActiveCoachBrief.UserId.IsEmpty() && !ActiveCoachBrief.Headline.IsEmpty();
|
||||
const bool bHasFollowUpCoachBrief =
|
||||
!ActiveCoachFollowUpBrief.UserId.IsEmpty() && !ActiveCoachFollowUpBrief.Headline.IsEmpty();
|
||||
FString UserId = !ActiveCoachSessionQueueState.UserId.IsEmpty()
|
||||
? ActiveCoachSessionQueueState.UserId
|
||||
: (ActiveRunState.Session.UserId.IsEmpty() ? TEXT("local-user") : ActiveRunState.Session.UserId);
|
||||
|
||||
if (SelectedEntry->SourceLabel == TEXT("primary-brief") && bHasPrimaryCoachBrief)
|
||||
{
|
||||
QueueDeck = BuildActiveCoachRecommendedDeck(MaxCases);
|
||||
QueueBrief = ActiveCoachBrief;
|
||||
UserId = ActiveCoachBrief.UserId.IsEmpty() ? UserId : ActiveCoachBrief.UserId;
|
||||
}
|
||||
else if (SelectedEntry->SourceLabel == TEXT("follow-up-brief") && bHasFollowUpCoachBrief)
|
||||
{
|
||||
QueueDeck = BuildActiveCoachFollowUpDeck(MaxCases);
|
||||
QueueBrief = ActiveCoachFollowUpBrief;
|
||||
UserId = ActiveCoachFollowUpBrief.UserId.IsEmpty() ? UserId : ActiveCoachFollowUpBrief.UserId;
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueDeck = HyperTwistTrainingSubsystemInternal::BuildCoachQueueDeckFromEntry(
|
||||
*SelectedEntry,
|
||||
ActiveRunState,
|
||||
MaxCases
|
||||
);
|
||||
QueueBrief = HyperTwistTrainingSubsystemInternal::BuildCoachBriefFromQueueEntry(
|
||||
ActiveCoachSessionQueueState,
|
||||
*SelectedEntry,
|
||||
ActiveCoachSessionQueueState.ReferenceUtc
|
||||
);
|
||||
}
|
||||
|
||||
if (!QueueDeck.IsStructurallyValid())
|
||||
{
|
||||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingCoachSessionQueueState QueueStateBeforeStart = ActiveCoachSessionQueueState;
|
||||
const FHyperTwistTrainingCoachActionPlan DraftActionPlan =
|
||||
(!QueueBrief.UserId.IsEmpty() && !QueueBrief.Headline.IsEmpty())
|
||||
? UHyperTwistTrainingRepositoryLibrary::BuildCoachActionPlan(
|
||||
QueueBrief,
|
||||
FString::Printf(TEXT("coach_queue_%s_%s"), *SelectedEntry->EntryId, *SessionId),
|
||||
FString()
|
||||
)
|
||||
: FHyperTwistTrainingCoachActionPlan();
|
||||
FHyperTwistTrainingRunState RunState = StartTrainingRunFromDeck(
|
||||
QueueDeck,
|
||||
UserId,
|
||||
SessionId,
|
||||
SelectedEntry->SuggestedMode
|
||||
);
|
||||
if (!RunState.IsStructurallyValid())
|
||||
{
|
||||
return RunState;
|
||||
}
|
||||
|
||||
if (DraftActionPlan.IsStructurallyValid())
|
||||
{
|
||||
ActiveCoachActionPlan = UHyperTwistTrainingRepositoryLibrary::ActivateCoachActionPlan(
|
||||
DraftActionPlan,
|
||||
RunState.Session.TrainingSessionId,
|
||||
RunState.Session.StartedAtUtc
|
||||
);
|
||||
TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachActionPlan(
|
||||
TrainingRepositoryState,
|
||||
ActiveCoachActionPlan
|
||||
);
|
||||
}
|
||||
|
||||
if (QueueStateBeforeStart.IsStructurallyValid())
|
||||
{
|
||||
const FHyperTwistTrainingCoachSessionQueueState UpdatedQueueState =
|
||||
UHyperTwistTrainingRepositoryLibrary::StartCoachSessionQueueEntryById(
|
||||
QueueStateBeforeStart,
|
||||
SelectedEntry->EntryId,
|
||||
RunState.Session.TrainingSessionId,
|
||||
RunState.Session.StartedAtUtc
|
||||
);
|
||||
if (UpdatedQueueState.IsStructurallyValid())
|
||||
{
|
||||
TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
|
||||
TrainingRepositoryState,
|
||||
UpdatedQueueState
|
||||
);
|
||||
ActiveCoachSessionQueueState = UpdatedQueueState;
|
||||
}
|
||||
}
|
||||
|
||||
RefreshRepositoryViews();
|
||||
return RunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartTrainingRunFromDeck(
|
||||
const FHyperTwistTrainingDeck& Deck,
|
||||
const FString& UserId,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ public:
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
int32 DefaultDeferredHours = 24;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool bPreferActiveQueueSelectionOnRefresh = true;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
int32 SelectedQueueEntryIndex = INDEX_NONE;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
void RefreshCoachDashboardView();
|
||||
|
||||
|
|
@ -31,6 +37,15 @@ public:
|
|||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool PromoteFirstDeferredQueueEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectPreviousQueueEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectNextQueueEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
FHyperTwistTrainingRunState StartSelectedQueueEntry();
|
||||
|
||||
protected:
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UVerticalBox> RootLayout = nullptr;
|
||||
|
|
@ -53,6 +68,18 @@ protected:
|
|||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> QueueCountsTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> FollowUpTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> QueueSelectionTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> QueueEntryDetailTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> QueueExecutionTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> PrimaryActionButton = nullptr;
|
||||
|
||||
|
|
@ -77,6 +104,24 @@ protected:
|
|||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> PromoteButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> PreviousQueueButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> PreviousQueueButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> StartSelectedButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> StartSelectedButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> NextQueueButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> NextQueueButtonLabel = nullptr;
|
||||
|
||||
UFUNCTION()
|
||||
void HandlePrimaryActionClicked();
|
||||
|
||||
|
|
@ -89,9 +134,20 @@ protected:
|
|||
UFUNCTION()
|
||||
void HandlePromoteClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandlePreviousQueueClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleStartSelectedClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleNextQueueClicked();
|
||||
|
||||
private:
|
||||
void EnsureDefaultDashboardBuilt();
|
||||
void SyncSelectedQueueEntry();
|
||||
void UpdateDashboardPresentation();
|
||||
FString BuildDefaultDeferredUntilUtc() const;
|
||||
FString FindFirstDeferredQueueEntryId() const;
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* FindSelectedQueueEntry() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -118,6 +118,12 @@ public:
|
|||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartNextQueuedCoachRun(int32 MaxCases);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachQueueEntry(
|
||||
const FString& EntryId,
|
||||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
bool DeferCoachQueueEntry(
|
||||
const FString& EntryId,
|
||||
|
|
|
|||
|
|
@ -369,6 +369,14 @@ public:
|
|||
const FString& StartedAtUtc
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Coach")
|
||||
static FHyperTwistTrainingCoachSessionQueueState StartCoachSessionQueueEntryById(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
|
||||
const FString& EntryId,
|
||||
const FString& TrainingSessionId,
|
||||
const FString& StartedAtUtc
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Coach")
|
||||
static FHyperTwistTrainingCoachSessionQueueState FinalizeCoachSessionQueueEntry(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
|
||||
|
|
|
|||
|
|
@ -424,6 +424,14 @@ public:
|
|||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
|
||||
static FHyperTwistTrainingRunState StartCoachQueueEntry(
|
||||
UObject* WorldContextObject,
|
||||
const FString& EntryId,
|
||||
const FString& SessionId,
|
||||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject"))
|
||||
static FHyperTwistTrainingRunState StartSampleClassicTrainingRun(
|
||||
UObject* WorldContextObject,
|
||||
|
|
|
|||
|
|
@ -120,6 +120,12 @@ public:
|
|||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartNextQueuedCoachTrainingRun(int32 MaxCases);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachQueueEntryTrainingRun(
|
||||
const FString& EntryId,
|
||||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
bool DeferCoachQueueEntry(
|
||||
const FString& EntryId,
|
||||
|
|
|
|||
|
|
@ -342,6 +342,13 @@ public:
|
|||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachQueueEntry(
|
||||
const FString& EntryId,
|
||||
const FString& SessionId,
|
||||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training")
|
||||
FHyperTwistTrainingRunState StartTrainingRunFromDeck(
|
||||
const FHyperTwistTrainingDeck& Deck,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue