feat: advance legacy UI behavior parity

This commit is contained in:
2026-07-18 10:18:29 +09:00
parent d7ec571343
commit 1302b1b92f
71 changed files with 11244 additions and 1153 deletions

View File

@@ -132,6 +132,27 @@ public sealed class LegacyExpertWorkflowController
}
}
public LegacyExpertWorkflowSnapshot RemoveSelectedResultPreservingPreview()
{
lock (_gate)
{
if (_selected is null)
{
return CreateSnapshot();
}
var removed = _searchResults.RemoveAll(row => row.Identity == _selected.Identity);
if (removed > 0)
{
// UC6 removed the visible expert row but intentionally retained m_code,
// m_name and the recommendation grid until the operator selected again.
Touch();
}
return CreateSnapshot();
}
}
public async Task<LegacyExpertWorkflowSnapshot> SearchAsync(
string query,
CancellationToken cancellationToken = default)
@@ -304,14 +325,14 @@ public sealed class LegacyExpertWorkflowController
}
var itemCount = _previewItems.Count;
if (itemCount is <= 0 or > MaximumAccessibleItems)
if (itemCount > MaximumAccessibleItems)
{
throw new InvalidOperationException(
"An expert cut requires one through 100 accessible recommendation rows.");
"An expert cut supports zero through 100 accessible recommendation rows.");
}
var pageCount = checked((itemCount + PageSize - 1) / PageSize);
if (pageCount is <= 0 or > MaximumPageCount)
if (pageCount > MaximumPageCount)
{
throw new InvalidOperationException("The expert PageN plan exceeds 20 pages.");
}
@@ -361,7 +382,7 @@ public sealed class LegacyExpertWorkflowController
: (previewRows.Length + PageSize - 1) / PageSize;
var actionAvailable = _selected is not null &&
_previewStatus == LegacyExpertRequestStatus.Ready &&
previewRows.Length is > 0 and <= MaximumAccessibleItems &&
previewRows.Length <= MaximumAccessibleItems &&
pageCount <= MaximumPageCount;
return new LegacyExpertWorkflowSnapshot(
_revision,
@@ -405,7 +426,6 @@ public sealed class LegacyExpertWorkflowController
var states = new List<ExpertState>(result.Items.Count);
var codes = new HashSet<string>(StringComparer.Ordinal);
var names = new HashSet<string>(StringComparer.Ordinal);
ExpertSelectionIdentity? previous = null;
for (var index = 0; index < result.Items.Count; index++)
{
@@ -417,11 +437,11 @@ public sealed class LegacyExpertWorkflowController
}
ValidateExpertIdentity(item.Identity);
if (!codes.Add(item.Identity.ExpertCode) || !names.Add(item.Identity.ExpertName) ||
if (!codes.Add(item.Identity.ExpertCode) ||
previous is not null && CompareIdentities(previous, item.Identity) > 0)
{
throw new ExpertSelectionDataException(
"Expert identities must be unique and sorted by name then code.");
"Expert codes must be unique and identities sorted by name then code.");
}
states.Add(new ExpertState(
@@ -448,8 +468,6 @@ public sealed class LegacyExpertWorkflowController
var items = new List<ExpertRecommendationPreview>(result.Items.Count);
var indexes = new HashSet<int>();
var codes = new HashSet<string>(StringComparer.Ordinal);
var names = new HashSet<string>(StringComparer.Ordinal);
var previousIndex = -1;
foreach (var item in result.Items)
{
@@ -459,9 +477,7 @@ public sealed class LegacyExpertWorkflowController
!IsCanonicalText(item.StockName, 200) ||
item.BuyAmount <= 0 || item.BuyAmount > 9_007_199_254_740_991m ||
decimal.Truncate(item.BuyAmount) != item.BuyAmount ||
!indexes.Add(item.PlayIndex) ||
!codes.Add(item.StockCode) ||
!names.Add(item.StockName))
!indexes.Add(item.PlayIndex))
{
throw new ExpertSelectionDataException(
"The expert preview contains an invalid or duplicate recommendation.");