Add manual critical table cell editor

This commit is contained in:
2026-03-14 15:09:16 +01:00
parent 4e518244a2
commit 6e28ad975f
16 changed files with 1105 additions and 27 deletions

View File

@@ -231,6 +231,7 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
.ThenBy(result => result.CriticalGroup?.SortOrder ?? 0)
.ThenBy(result => result.CriticalColumn.SortOrder)
.Select(result => new CriticalTableCellDetail(
result.Id,
result.CriticalRollBand.Label,
result.CriticalColumn.ColumnKey,
result.CriticalColumn.Label,
@@ -262,6 +263,70 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
cells,
legend);
}
public async Task<CriticalCellEditorResponse?> GetCriticalCellEditorAsync(string slug, int resultId, CancellationToken cancellationToken = default)
{
await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
var normalizedSlug = NormalizeSlug(slug);
var result = await dbContext.CriticalResults
.AsNoTracking()
.AsSplitQuery()
.Include(item => item.CriticalTable)
.Include(item => item.CriticalColumn)
.Include(item => item.CriticalGroup)
.Include(item => item.CriticalRollBand)
.Include(item => item.Effects)
.Include(item => item.Branches)
.ThenInclude(branch => branch.Effects)
.SingleOrDefaultAsync(
item => item.Id == resultId && item.CriticalTable.Slug == normalizedSlug,
cancellationToken);
return result is null ? null : CreateCellEditorResponse(result);
}
public async Task<CriticalCellEditorResponse?> UpdateCriticalCellAsync(
string slug,
int resultId,
CriticalCellUpdateRequest request,
CancellationToken cancellationToken = default)
{
await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
var normalizedSlug = NormalizeSlug(slug);
var result = await dbContext.CriticalResults
.AsSplitQuery()
.Include(item => item.CriticalTable)
.Include(item => item.CriticalColumn)
.Include(item => item.CriticalGroup)
.Include(item => item.CriticalRollBand)
.Include(item => item.Effects)
.Include(item => item.Branches)
.ThenInclude(branch => branch.Effects)
.SingleOrDefaultAsync(
item => item.Id == resultId && item.CriticalTable.Slug == normalizedSlug,
cancellationToken);
if (result is null)
{
return null;
}
result.RawCellText = request.RawCellText.Trim();
result.DescriptionText = request.DescriptionText.Trim();
result.RawAffixText = NormalizeOptionalText(request.RawAffixText);
result.ParseStatus = request.ParseStatus.Trim();
result.ParsedJson = string.IsNullOrWhiteSpace(request.ParsedJson) ? "{}" : request.ParsedJson.Trim();
ReplaceBaseEffects(dbContext, result, request.Effects);
ReplaceBranches(dbContext, result, request.Branches);
await dbContext.SaveChangesAsync(cancellationToken);
return CreateCellEditorResponse(result);
}
private static IReadOnlyList<CriticalTableLegendEntry> BuildLegend(IReadOnlyList<CriticalTableCellDetail> cells)
{
var seenCodes = new HashSet<string>(StringComparer.Ordinal);
@@ -320,6 +385,32 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
effect.SourceType,
effect.SourceText);
private static CriticalCellEditorResponse CreateCellEditorResponse(CriticalResult result) =>
new(
result.Id,
result.CriticalTable.Slug,
result.CriticalTable.DisplayName,
result.CriticalTable.SourceDocument,
result.CriticalRollBand.Label,
result.CriticalGroup?.GroupKey,
result.CriticalGroup?.Label,
result.CriticalColumn.ColumnKey,
result.CriticalColumn.Label,
result.CriticalColumn.Role,
result.RawCellText,
result.DescriptionText,
result.RawAffixText,
result.ParseStatus,
result.ParsedJson,
result.Effects
.OrderBy(effect => effect.Id)
.Select(CreateEffectEditorItem)
.ToList(),
result.Branches
.OrderBy(branch => branch.SortOrder)
.Select(CreateBranchEditorItem)
.ToList());
private static CriticalBranchLookupResponse CreateBranchLookupResponse(CriticalBranch branch) =>
new(
branch.BranchKind,
@@ -334,6 +425,108 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
branch.RawText,
branch.SortOrder);
private static CriticalBranchEditorItem CreateBranchEditorItem(CriticalBranch branch) =>
new(
branch.BranchKind,
branch.ConditionKey,
branch.ConditionText,
branch.ConditionJson,
branch.RawText,
branch.DescriptionText,
branch.RawAffixText,
branch.ParsedJson,
branch.SortOrder,
(branch.Effects ?? Enumerable.Empty<CriticalEffect>())
.OrderBy(effect => effect.Id)
.Select(CreateEffectEditorItem)
.ToList());
private static CriticalEffectEditorItem CreateEffectEditorItem(CriticalEffect effect) =>
new(
effect.EffectCode,
effect.Target,
effect.ValueInteger,
effect.ValueDecimal,
effect.ValueExpression,
effect.DurationRounds,
effect.PerRound,
effect.Modifier,
effect.BodyPart,
effect.IsPermanent,
effect.SourceType,
effect.SourceText);
private static void ReplaceBaseEffects(
RolemasterDbContext dbContext,
CriticalResult result,
IReadOnlyList<CriticalEffectEditorItem>? effects)
{
dbContext.CriticalEffects.RemoveRange(result.Effects);
result.Effects.Clear();
foreach (var effect in effects ?? Array.Empty<CriticalEffectEditorItem>())
{
result.Effects.Add(CreateEffectEntity(effect));
}
}
private static void ReplaceBranches(
RolemasterDbContext dbContext,
CriticalResult result,
IReadOnlyList<CriticalBranchEditorItem>? branches)
{
foreach (var branch in result.Branches)
{
dbContext.CriticalEffects.RemoveRange(branch.Effects);
}
dbContext.CriticalBranches.RemoveRange(result.Branches);
result.Branches.Clear();
foreach (var branch in branches ?? Array.Empty<CriticalBranchEditorItem>())
{
var branchEntity = new CriticalBranch
{
BranchKind = branch.BranchKind.Trim(),
ConditionKey = NormalizeOptionalText(branch.ConditionKey),
ConditionText = branch.ConditionText.Trim(),
ConditionJson = string.IsNullOrWhiteSpace(branch.ConditionJson) ? "{}" : branch.ConditionJson.Trim(),
RawText = branch.RawText.Trim(),
DescriptionText = branch.DescriptionText.Trim(),
RawAffixText = NormalizeOptionalText(branch.RawAffixText),
ParsedJson = string.IsNullOrWhiteSpace(branch.ParsedJson) ? "{}" : branch.ParsedJson.Trim(),
SortOrder = branch.SortOrder
};
foreach (var effect in branch.Effects ?? Array.Empty<CriticalEffectEditorItem>())
{
branchEntity.Effects.Add(CreateEffectEntity(effect));
}
result.Branches.Add(branchEntity);
}
}
private static CriticalEffect CreateEffectEntity(CriticalEffectEditorItem effect) =>
new()
{
EffectCode = effect.EffectCode.Trim(),
Target = NormalizeOptionalText(effect.Target),
ValueInteger = effect.ValueInteger,
ValueDecimal = effect.ValueDecimal,
ValueExpression = NormalizeOptionalText(effect.ValueExpression),
DurationRounds = effect.DurationRounds,
PerRound = effect.PerRound,
Modifier = effect.Modifier,
BodyPart = NormalizeOptionalText(effect.BodyPart),
IsPermanent = effect.IsPermanent,
SourceType = effect.SourceType.Trim(),
SourceText = NormalizeOptionalText(effect.SourceText)
};
private static string? NormalizeOptionalText(string? value) =>
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
private static string NormalizeSlug(string value) =>
value.Trim().Replace(' ', '_').ToLowerInvariant();
}