Show critical curation state in the editor and tables
This commit is contained in:
@@ -121,12 +121,14 @@
|
|||||||
@if (TryGetCell(rollBand.Label, group.Key, column.Key, out var groupedCell))
|
@if (TryGetCell(rollBand.Label, group.Key, column.Key, out var groupedCell))
|
||||||
{
|
{
|
||||||
<td
|
<td
|
||||||
class="critical-table-cell is-editable"
|
class="@GetCellCssClass(groupedCell)"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
title="Click to edit this cell"
|
title="@GetCellTitle(groupedCell)"
|
||||||
|
aria-label="@GetCellTitle(groupedCell)"
|
||||||
@onclick="() => OpenCellEditorAsync(groupedCell.ResultId)"
|
@onclick="() => OpenCellEditorAsync(groupedCell.ResultId)"
|
||||||
@onkeydown="args => HandleCellKeyDownAsync(args, groupedCell.ResultId)">
|
@onkeydown="args => HandleCellKeyDownAsync(args, groupedCell.ResultId)">
|
||||||
<CompactCriticalCell
|
<CompactCriticalCell
|
||||||
|
IsCurated="@groupedCell.IsCurated"
|
||||||
Description="@(groupedCell.Description ?? string.Empty)"
|
Description="@(groupedCell.Description ?? string.Empty)"
|
||||||
Effects="@(groupedCell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
|
Effects="@(groupedCell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
|
||||||
Branches="@(groupedCell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
|
Branches="@(groupedCell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
|
||||||
@@ -148,12 +150,14 @@
|
|||||||
@if (TryGetCell(rollBand.Label, null, column.Key, out var cell))
|
@if (TryGetCell(rollBand.Label, null, column.Key, out var cell))
|
||||||
{
|
{
|
||||||
<td
|
<td
|
||||||
class="critical-table-cell is-editable"
|
class="@GetCellCssClass(cell)"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
title="Click to edit this cell"
|
title="@GetCellTitle(cell)"
|
||||||
|
aria-label="@GetCellTitle(cell)"
|
||||||
@onclick="() => OpenCellEditorAsync(cell.ResultId)"
|
@onclick="() => OpenCellEditorAsync(cell.ResultId)"
|
||||||
@onkeydown="args => HandleCellKeyDownAsync(args, cell.ResultId)">
|
@onkeydown="args => HandleCellKeyDownAsync(args, cell.ResultId)">
|
||||||
<CompactCriticalCell
|
<CompactCriticalCell
|
||||||
|
IsCurated="@cell.IsCurated"
|
||||||
Description="@(cell.Description ?? string.Empty)"
|
Description="@(cell.Description ?? string.Empty)"
|
||||||
Effects="@(cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
|
Effects="@(cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
|
||||||
Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
|
Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
|
||||||
@@ -457,4 +461,14 @@
|
|||||||
await OpenCellEditorAsync(resultId);
|
await OpenCellEditorAsync(resultId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetCellCssClass(CriticalTableCellDetail cell) =>
|
||||||
|
cell.IsCurated
|
||||||
|
? "critical-table-cell is-editable is-curated"
|
||||||
|
: "critical-table-cell is-editable needs-curation";
|
||||||
|
|
||||||
|
private static string GetCellTitle(CriticalTableCellDetail cell) =>
|
||||||
|
cell.IsCurated
|
||||||
|
? "Curated cell. Click to edit this cell."
|
||||||
|
: "Needs curation. Click to edit this cell.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
@using RolemasterDb.App.Features
|
@using RolemasterDb.App.Features
|
||||||
|
|
||||||
<div class="critical-cell">
|
<div class="critical-cell">
|
||||||
|
<div class="critical-cell-status-row">
|
||||||
|
<span class="critical-cell-status-chip @(IsCurated ? "is-curated" : "needs-curation")">
|
||||||
|
@(IsCurated ? "Curated" : "Needs Curation")
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Description))
|
@if (!string.IsNullOrWhiteSpace(Description))
|
||||||
{
|
{
|
||||||
<p class="critical-cell-description">@Description</p>
|
<p class="critical-cell-description">@Description</p>
|
||||||
@@ -40,6 +46,9 @@
|
|||||||
[Parameter, EditorRequired]
|
[Parameter, EditorRequired]
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool IsCurated { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public IReadOnlyList<CriticalEffectLookupResponse>? Effects { get; set; }
|
public IReadOnlyList<CriticalEffectLookupResponse>? Effects { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,15 @@
|
|||||||
<span> · Variant <strong>@Model.GroupLabel</strong></span>
|
<span> · Variant <strong>@Model.GroupLabel</strong></span>
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
|
<div class="critical-editor-status-row">
|
||||||
|
<span class="critical-editor-curation-badge @(Model.IsCurated ? "is-curated" : "needs-curation")">
|
||||||
|
@(Model.IsCurated ? "Curated" : "Needs Curation")
|
||||||
|
</span>
|
||||||
|
@if (Model.SourcePageNumber is not null)
|
||||||
|
{
|
||||||
|
<span class="chip">Source page @Model.SourcePageNumber</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -67,6 +76,47 @@
|
|||||||
<p class="error-text critical-editor-error">@SaveErrorMessage</p>
|
<p class="error-text critical-editor-error">@SaveErrorMessage</p>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<section class="critical-editor-source-grid">
|
||||||
|
<div class="critical-editor-card critical-editor-status-card">
|
||||||
|
<div class="critical-editor-section-header">
|
||||||
|
<div>
|
||||||
|
<h4>Curation State</h4>
|
||||||
|
<p class="muted">Curated cells are protected from importer content overwrites until you unmark them.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label class="critical-editor-curation-toggle">
|
||||||
|
<InputCheckbox @bind-Value="Model.IsCurated" />
|
||||||
|
<span>Mark this result curated</span>
|
||||||
|
</label>
|
||||||
|
<p class="muted critical-editor-inline-copy">
|
||||||
|
@(Model.IsCurated
|
||||||
|
? "This result will keep its current text, branches, and effects on later imports."
|
||||||
|
: "This result will be refreshed from the importer on later imports.")
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="critical-editor-card critical-editor-source-card">
|
||||||
|
<div class="critical-editor-section-header">
|
||||||
|
<div>
|
||||||
|
<h4>Source Cell</h4>
|
||||||
|
<p class="muted">Use the importer crop as a visual reference while curating the result.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(Model.SourceImageUrl))
|
||||||
|
{
|
||||||
|
<img
|
||||||
|
class="critical-editor-source-image"
|
||||||
|
src="@Model.SourceImageUrl"
|
||||||
|
alt="@BuildSourceImageAltText(Model)" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p class="muted critical-editor-inline-copy">No source image is available for this cell yet.</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="critical-editor-section">
|
<section class="critical-editor-section">
|
||||||
<div class="critical-editor-section-header">
|
<div class="critical-editor-section-header">
|
||||||
<div>
|
<div>
|
||||||
@@ -692,6 +742,23 @@
|
|||||||
? "This is the result that will be saved."
|
? "This is the result that will be saved."
|
||||||
: "This is the edited card before the last re-parse.";
|
: "This is the edited card before the last re-parse.";
|
||||||
|
|
||||||
|
private static string BuildSourceImageAltText(CriticalCellEditorModel model)
|
||||||
|
{
|
||||||
|
var segments = new List<string>
|
||||||
|
{
|
||||||
|
model.TableName,
|
||||||
|
$"roll band {model.RollBand}",
|
||||||
|
$"column {model.ColumnLabel}"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(model.GroupLabel))
|
||||||
|
{
|
||||||
|
segments.Add($"variant {model.GroupLabel}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(", ", segments);
|
||||||
|
}
|
||||||
|
|
||||||
private static IReadOnlyList<CriticalEffectLookupResponse> BuildPreviewEffects(CriticalCellEditorModel model) =>
|
private static IReadOnlyList<CriticalEffectLookupResponse> BuildPreviewEffects(CriticalCellEditorModel model) =>
|
||||||
model.Effects
|
model.Effects
|
||||||
.Select(CreatePreviewEffect)
|
.Select(CreatePreviewEffect)
|
||||||
|
|||||||
@@ -314,6 +314,37 @@ textarea {
|
|||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-cell-status-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-cell-status-chip,
|
||||||
|
.critical-editor-curation-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 0.18rem 0.55rem;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-cell-status-chip.is-curated,
|
||||||
|
.critical-editor-curation-badge.is-curated {
|
||||||
|
background: rgba(102, 138, 83, 0.16);
|
||||||
|
border-color: rgba(102, 138, 83, 0.34);
|
||||||
|
color: #45613a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-cell-status-chip.needs-curation,
|
||||||
|
.critical-editor-curation-badge.needs-curation {
|
||||||
|
background: rgba(184, 121, 59, 0.16);
|
||||||
|
border-color: rgba(184, 121, 59, 0.34);
|
||||||
|
color: #8a5b21;
|
||||||
|
}
|
||||||
|
|
||||||
.critical-cell-footer {
|
.critical-cell-footer {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -629,6 +660,18 @@ textarea {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-table-cell.is-curated {
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(102, 138, 83, 0.16), transparent 34%),
|
||||||
|
rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-table-cell.needs-curation {
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(184, 121, 59, 0.18), transparent 34%),
|
||||||
|
rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
.critical-table td .critical-cell {
|
.critical-table td .critical-cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -763,6 +806,13 @@ textarea {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-editor-status-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
.critical-editor-body {
|
.critical-editor-body {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -778,6 +828,34 @@ textarea {
|
|||||||
gap: 0.85rem;
|
gap: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-editor-source-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.85rem;
|
||||||
|
grid-template-columns: minmax(240px, 0.8fr) minmax(320px, 1.2fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-status-card,
|
||||||
|
.critical-editor-source-card {
|
||||||
|
align-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-curation-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
color: #5b4327;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-source-image {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 340px;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 14px;
|
||||||
|
border: 1px solid rgba(127, 96, 55, 0.14);
|
||||||
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
}
|
||||||
|
|
||||||
.critical-editor-section h4,
|
.critical-editor-section h4,
|
||||||
.critical-editor-subsection h5 {
|
.critical-editor-subsection h5 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -1164,7 +1242,8 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.critical-editor-effect-row-main,
|
.critical-editor-effect-row-main,
|
||||||
.critical-editor-branch-line {
|
.critical-editor-branch-line,
|
||||||
|
.critical-editor-source-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user