Phase 20AQ – Story Intelligence Review UX Polish
This commit is contained in:
parent
d44217162c
commit
ab3b591e4d
@ -137,12 +137,12 @@ public sealed class StoryIntelligenceAssetImportService(
|
||||
|
||||
var requestedName = Clean(choice.ImportName);
|
||||
var importName = string.IsNullOrWhiteSpace(requestedName) ? candidate.DisplayName : requestedName;
|
||||
var forceCreateSeparate = string.Equals(choice.Action, StoryIntelligenceAssetImportActions.CreateSeparate, StringComparison.OrdinalIgnoreCase);
|
||||
var matchedExistingId = forceCreateSeparate
|
||||
? null
|
||||
: candidate.ExistingAssetID
|
||||
var linkExisting = string.Equals(choice.Action, StoryIntelligenceAssetImportActions.LinkExisting, StringComparison.OrdinalIgnoreCase);
|
||||
var matchedExistingId = linkExisting
|
||||
? candidate.ExistingAssetID
|
||||
?? existingIndex.Find(importName)?.StoryAssetID
|
||||
?? existingIndex.Find(candidate.DisplayName)?.StoryAssetID;
|
||||
?? existingIndex.Find(candidate.DisplayName)?.StoryAssetID
|
||||
: null;
|
||||
var assetId = matchedExistingId;
|
||||
|
||||
if (!assetId.HasValue)
|
||||
|
||||
@ -142,12 +142,12 @@ public sealed class StoryIntelligenceCharacterImportService(
|
||||
|
||||
var requestedName = Clean(choice.ImportName);
|
||||
var importName = string.IsNullOrWhiteSpace(requestedName) ? candidate.DisplayName : requestedName;
|
||||
var forceCreateSeparate = string.Equals(choice.Action, StoryIntelligenceCharacterImportActions.CreateSeparate, StringComparison.OrdinalIgnoreCase);
|
||||
var matchedExistingId = forceCreateSeparate
|
||||
? null
|
||||
: candidate.ExistingCharacterID
|
||||
var linkExisting = string.Equals(choice.Action, StoryIntelligenceCharacterImportActions.LinkExisting, StringComparison.OrdinalIgnoreCase);
|
||||
var matchedExistingId = linkExisting
|
||||
? candidate.ExistingCharacterID
|
||||
?? existingIndex.Find(importName)?.CharacterID
|
||||
?? existingIndex.Find(candidate.DisplayName)?.CharacterID;
|
||||
?? existingIndex.Find(candidate.DisplayName)?.CharacterID
|
||||
: null;
|
||||
var characterId = matchedExistingId;
|
||||
|
||||
if (!characterId.HasValue)
|
||||
|
||||
@ -132,12 +132,12 @@ public sealed class StoryIntelligenceLocationImportService(
|
||||
|
||||
var requestedName = Clean(choice.ImportName);
|
||||
var importName = string.IsNullOrWhiteSpace(requestedName) ? candidate.DisplayName : requestedName;
|
||||
var forceCreateSeparate = string.Equals(choice.Action, StoryIntelligenceLocationImportActions.CreateSeparate, StringComparison.OrdinalIgnoreCase);
|
||||
var matchedExistingId = forceCreateSeparate
|
||||
? null
|
||||
: candidate.ExistingLocationID
|
||||
var linkExisting = string.Equals(choice.Action, StoryIntelligenceLocationImportActions.LinkExisting, StringComparison.OrdinalIgnoreCase);
|
||||
var matchedExistingId = linkExisting
|
||||
? candidate.ExistingLocationID
|
||||
?? existingIndex.Find(importName)?.LocationID
|
||||
?? existingIndex.Find(candidate.DisplayName)?.LocationID;
|
||||
?? existingIndex.Find(candidate.DisplayName)?.LocationID
|
||||
: null;
|
||||
var locationId = matchedExistingId;
|
||||
|
||||
if (!locationId.HasValue)
|
||||
|
||||
@ -212,16 +212,16 @@ public sealed class StoryIntelligenceCharacterImportForm
|
||||
public sealed class StoryIntelligenceCharacterImportChoiceForm
|
||||
{
|
||||
public string Key { get; set; } = string.Empty;
|
||||
public string Action { get; set; } = StoryIntelligenceCharacterImportActions.Approve;
|
||||
public string Action { get; set; } = StoryIntelligenceCharacterImportActions.CreateNew;
|
||||
public string? ImportName { get; set; }
|
||||
public string? AliasTargetKey { get; set; }
|
||||
}
|
||||
|
||||
public static class StoryIntelligenceCharacterImportActions
|
||||
{
|
||||
public const string Approve = "Approve";
|
||||
public const string CreateNew = "CreateNew";
|
||||
public const string LinkExisting = "LinkExisting";
|
||||
public const string Ignore = "Ignore";
|
||||
public const string CreateSeparate = "CreateSeparate";
|
||||
public const string Alias = "Alias";
|
||||
}
|
||||
|
||||
@ -260,16 +260,16 @@ public sealed class StoryIntelligenceLocationImportForm
|
||||
public sealed class StoryIntelligenceLocationImportChoiceForm
|
||||
{
|
||||
public string Key { get; set; } = string.Empty;
|
||||
public string Action { get; set; } = StoryIntelligenceLocationImportActions.Approve;
|
||||
public string Action { get; set; } = StoryIntelligenceLocationImportActions.CreateNew;
|
||||
public string? ImportName { get; set; }
|
||||
public string? AliasTargetKey { get; set; }
|
||||
}
|
||||
|
||||
public static class StoryIntelligenceLocationImportActions
|
||||
{
|
||||
public const string Approve = "Approve";
|
||||
public const string CreateNew = "CreateNew";
|
||||
public const string LinkExisting = "LinkExisting";
|
||||
public const string Ignore = "Ignore";
|
||||
public const string CreateSeparate = "CreateSeparate";
|
||||
public const string Alias = "Alias";
|
||||
}
|
||||
|
||||
@ -309,16 +309,16 @@ public sealed class StoryIntelligenceAssetImportForm
|
||||
public sealed class StoryIntelligenceAssetImportChoiceForm
|
||||
{
|
||||
public string Key { get; set; } = string.Empty;
|
||||
public string Action { get; set; } = StoryIntelligenceAssetImportActions.Approve;
|
||||
public string Action { get; set; } = StoryIntelligenceAssetImportActions.CreateNew;
|
||||
public string? ImportName { get; set; }
|
||||
public string? AliasTargetKey { get; set; }
|
||||
}
|
||||
|
||||
public static class StoryIntelligenceAssetImportActions
|
||||
{
|
||||
public const string Approve = "Approve";
|
||||
public const string CreateNew = "CreateNew";
|
||||
public const string LinkExisting = "LinkExisting";
|
||||
public const string Ignore = "Ignore";
|
||||
public const string CreateSeparate = "CreateSeparate";
|
||||
public const string Alias = "Alias";
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
<section class="onboarding-shell" aria-labelledby="story-asset-complete-title">
|
||||
<div class="onboarding-panel onboarding-review-panel">
|
||||
<partial name="_StoryIntelligencePipelineHeader" model="@(new StoryIntelligencePipelineHeaderViewModel { CurrentStage = "Create Assets" })" />
|
||||
<partial name="_StoryIntelligencePipelineHeader" model="@(new StoryIntelligencePipelineHeaderViewModel { CurrentStage = "Complete" })" />
|
||||
<div class="onboarding-copy">
|
||||
<p class="eyebrow">Build Story Database</p>
|
||||
<div class="onboarding-success-heading">
|
||||
|
||||
@ -47,9 +47,9 @@
|
||||
else
|
||||
{
|
||||
<div class="story-review-card-actions">
|
||||
<button class="btn btn-outline-primary btn-sm" type="button" data-asset-bulk="approve-all">Approve all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-asset-bulk="approve-selected">Approve selected</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-asset-bulk="ignore-selected">Ignore selected</button>
|
||||
<button class="btn btn-outline-primary btn-sm" type="button" data-asset-bulk="create-all">Create all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-asset-bulk="link-existing">Link existing matches</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-asset-bulk="ignore-all">Ignore all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-asset-bulk="expand">Expand all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-asset-bulk="collapse">Collapse all</button>
|
||||
</div>
|
||||
@ -62,7 +62,6 @@
|
||||
var candidate = Model.AssetReview.Candidates[i];
|
||||
<details class="story-character-card" open data-asset-card>
|
||||
<summary>
|
||||
<input type="checkbox" checked data-asset-selected aria-label="Select @candidate.AssetName" />
|
||||
<span>
|
||||
<strong>@candidate.AssetName</strong>
|
||||
<small>@candidate.Category · @candidate.AppearsInScenes.ToString("N0") scene@(candidate.AppearsInScenes == 1 ? string.Empty : "s")</small>
|
||||
@ -106,19 +105,16 @@
|
||||
<fieldset class="story-character-actions">
|
||||
<legend>Decision</legend>
|
||||
<label>
|
||||
<input type="radio" name="Assets[@i].Action" value="@StoryIntelligenceAssetImportActions.Approve" checked data-asset-action />
|
||||
@(candidate.IsExistingMatch ? "Link to existing" : "Create new asset")
|
||||
<input type="radio" name="Assets[@i].Action" value="@StoryIntelligenceAssetImportActions.CreateNew" checked data-asset-action />
|
||||
Create new asset
|
||||
</label>
|
||||
@if (candidate.IsExistingMatch)
|
||||
{
|
||||
<label>
|
||||
<input type="radio" name="Assets[@i].Action" value="@StoryIntelligenceAssetImportActions.CreateSeparate" data-asset-action />
|
||||
Create separate asset
|
||||
<input type="radio" name="Assets[@i].Action" value="@StoryIntelligenceAssetImportActions.LinkExisting" data-asset-action @(candidate.IsExistingMatch ? string.Empty : "disabled") />
|
||||
Link existing asset
|
||||
</label>
|
||||
}
|
||||
<label>
|
||||
<input type="radio" name="Assets[@i].Action" value="@StoryIntelligenceAssetImportActions.Alias" data-asset-action />
|
||||
Alias/variant of another asset
|
||||
Alias of another asset
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="Assets[@i].Action" value="@StoryIntelligenceAssetImportActions.Ignore" data-asset-action />
|
||||
@ -170,7 +166,7 @@
|
||||
updateCard(card);
|
||||
}
|
||||
};
|
||||
const actionFor = (card) => card.querySelector("[data-asset-action]:checked")?.value || "@StoryIntelligenceAssetImportActions.Approve";
|
||||
const actionFor = (card) => card.querySelector("[data-asset-action]:checked")?.value || "@StoryIntelligenceAssetImportActions.CreateNew";
|
||||
const isCanonicalTarget = (card) => {
|
||||
const action = actionFor(card);
|
||||
return action !== "@StoryIntelligenceAssetImportActions.Ignore" && action !== "@StoryIntelligenceAssetImportActions.Alias";
|
||||
@ -207,10 +203,13 @@
|
||||
document.querySelectorAll("[data-asset-bulk]").forEach(button => {
|
||||
button.addEventListener("click", () => {
|
||||
const action = button.getAttribute("data-asset-bulk");
|
||||
const selectedCards = cards().filter(card => card.querySelector("[data-asset-selected]")?.checked);
|
||||
if (action === "approve-all") cards().forEach(card => setAction(card, "@StoryIntelligenceAssetImportActions.Approve"));
|
||||
if (action === "approve-selected") selectedCards.forEach(card => setAction(card, "@StoryIntelligenceAssetImportActions.Approve"));
|
||||
if (action === "ignore-selected") selectedCards.forEach(card => setAction(card, "@StoryIntelligenceAssetImportActions.Ignore"));
|
||||
if (action === "create-all") cards().forEach(card => setAction(card, "@StoryIntelligenceAssetImportActions.CreateNew"));
|
||||
if (action === "link-existing") cards().forEach(card => {
|
||||
if (card.querySelector(`[data-asset-action][value="@StoryIntelligenceAssetImportActions.LinkExisting"]:not(:disabled)`)) {
|
||||
setAction(card, "@StoryIntelligenceAssetImportActions.LinkExisting");
|
||||
}
|
||||
});
|
||||
if (action === "ignore-all") cards().forEach(card => setAction(card, "@StoryIntelligenceAssetImportActions.Ignore"));
|
||||
if (action === "expand") cards().forEach(card => card.open = true);
|
||||
if (action === "collapse") cards().forEach(card => card.open = false);
|
||||
});
|
||||
@ -219,11 +218,6 @@
|
||||
input.addEventListener("change", () => updateCard(input.closest("[data-asset-card]")));
|
||||
});
|
||||
cards().forEach(updateCard);
|
||||
form.addEventListener("submit", () => {
|
||||
cards()
|
||||
.filter(card => !card.querySelector("[data-asset-selected]")?.checked)
|
||||
.forEach(card => setAction(card, "@StoryIntelligenceAssetImportActions.Ignore"));
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
<section class="onboarding-shell" aria-labelledby="story-character-complete-title">
|
||||
<div class="onboarding-panel onboarding-review-panel">
|
||||
<partial name="_StoryIntelligencePipelineHeader" model="@(new StoryIntelligencePipelineHeaderViewModel { CurrentStage = "Create Characters" })" />
|
||||
<partial name="_StoryIntelligencePipelineHeader" model="@(new StoryIntelligencePipelineHeaderViewModel { CurrentStage = "Review Locations" })" />
|
||||
<div class="onboarding-copy">
|
||||
<p class="eyebrow">Build Story Database</p>
|
||||
<div class="onboarding-success-heading">
|
||||
|
||||
@ -38,15 +38,15 @@
|
||||
</div>
|
||||
<div class="onboarding-actions">
|
||||
<a class="btn btn-outline-secondary" asp-action="StoryIntelligenceReview" asp-route-batchId="@Model.BatchID">Back to scenes</a>
|
||||
<a class="btn btn-primary" asp-action="StoryIntelligenceComplete" asp-route-batchId="@Model.BatchID">Continue</a>
|
||||
<a class="btn btn-primary" asp-action="StoryIntelligenceLocations" asp-route-batchId="@Model.BatchID">Continue</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="story-review-card-actions">
|
||||
<button class="btn btn-outline-primary btn-sm" type="button" data-character-bulk="approve-all">Approve all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-character-bulk="approve-selected">Approve selected</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-character-bulk="ignore-selected">Ignore selected</button>
|
||||
<button class="btn btn-outline-primary btn-sm" type="button" data-character-bulk="create-all">Create all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-character-bulk="link-existing">Link existing matches</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-character-bulk="ignore-all">Ignore all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-character-bulk="expand">Expand all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-character-bulk="collapse">Collapse all</button>
|
||||
</div>
|
||||
@ -59,7 +59,6 @@
|
||||
var candidate = Model.CharacterReview.Candidates[i];
|
||||
<details class="story-character-card" open data-character-card>
|
||||
<summary>
|
||||
<input type="checkbox" checked data-character-selected aria-label="Select @candidate.CharacterName" />
|
||||
<span>
|
||||
<strong>@candidate.CharacterName</strong>
|
||||
<small>@candidate.AppearsInScenes.ToString("N0") scene@(candidate.AppearsInScenes == 1 ? string.Empty : "s")</small>
|
||||
@ -87,24 +86,21 @@
|
||||
<fieldset class="story-character-actions">
|
||||
<legend>Decision</legend>
|
||||
<label>
|
||||
<input type="radio" name="Characters[@i].Action" value="@StoryIntelligenceCharacterImportActions.Approve" checked data-character-action />
|
||||
@(candidate.IsExistingMatch ? "Link to existing" : "Create new character")
|
||||
<input type="radio" name="Characters[@i].Action" value="@StoryIntelligenceCharacterImportActions.CreateNew" checked data-character-action />
|
||||
Create new character
|
||||
</label>
|
||||
@if (candidate.IsExistingMatch)
|
||||
{
|
||||
<label>
|
||||
<input type="radio" name="Characters[@i].Action" value="@StoryIntelligenceCharacterImportActions.CreateSeparate" data-character-action />
|
||||
Create separate character
|
||||
</label>
|
||||
}
|
||||
<label>
|
||||
<input type="radio" name="Characters[@i].Action" value="@StoryIntelligenceCharacterImportActions.Ignore" data-character-action />
|
||||
Ignore
|
||||
<input type="radio" name="Characters[@i].Action" value="@StoryIntelligenceCharacterImportActions.LinkExisting" data-character-action @(candidate.IsExistingMatch ? string.Empty : "disabled") />
|
||||
Link existing character
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="Characters[@i].Action" value="@StoryIntelligenceCharacterImportActions.Alias" data-character-action />
|
||||
Alias of another character
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="Characters[@i].Action" value="@StoryIntelligenceCharacterImportActions.Ignore" data-character-action />
|
||||
Ignore
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div data-character-import-name-panel>
|
||||
@ -151,7 +147,7 @@
|
||||
updateCard(card);
|
||||
}
|
||||
};
|
||||
const actionFor = (card) => card.querySelector("[data-character-action]:checked")?.value || "@StoryIntelligenceCharacterImportActions.Approve";
|
||||
const actionFor = (card) => card.querySelector("[data-character-action]:checked")?.value || "@StoryIntelligenceCharacterImportActions.CreateNew";
|
||||
const isCanonicalTarget = (card) => {
|
||||
const action = actionFor(card);
|
||||
return action !== "@StoryIntelligenceCharacterImportActions.Ignore" && action !== "@StoryIntelligenceCharacterImportActions.Alias";
|
||||
@ -188,10 +184,13 @@
|
||||
document.querySelectorAll("[data-character-bulk]").forEach(button => {
|
||||
button.addEventListener("click", () => {
|
||||
const action = button.getAttribute("data-character-bulk");
|
||||
const selectedCards = cards().filter(card => card.querySelector("[data-character-selected]")?.checked);
|
||||
if (action === "approve-all") cards().forEach(card => setAction(card, "@StoryIntelligenceCharacterImportActions.Approve"));
|
||||
if (action === "approve-selected") selectedCards.forEach(card => setAction(card, "@StoryIntelligenceCharacterImportActions.Approve"));
|
||||
if (action === "ignore-selected") selectedCards.forEach(card => setAction(card, "@StoryIntelligenceCharacterImportActions.Ignore"));
|
||||
if (action === "create-all") cards().forEach(card => setAction(card, "@StoryIntelligenceCharacterImportActions.CreateNew"));
|
||||
if (action === "link-existing") cards().forEach(card => {
|
||||
if (card.querySelector(`[data-character-action][value="@StoryIntelligenceCharacterImportActions.LinkExisting"]:not(:disabled)`)) {
|
||||
setAction(card, "@StoryIntelligenceCharacterImportActions.LinkExisting");
|
||||
}
|
||||
});
|
||||
if (action === "ignore-all") cards().forEach(card => setAction(card, "@StoryIntelligenceCharacterImportActions.Ignore"));
|
||||
if (action === "expand") cards().forEach(card => card.open = true);
|
||||
if (action === "collapse") cards().forEach(card => card.open = false);
|
||||
});
|
||||
@ -200,11 +199,6 @@
|
||||
input.addEventListener("change", () => updateCard(input.closest("[data-character-card]")));
|
||||
});
|
||||
cards().forEach(updateCard);
|
||||
form.addEventListener("submit", () => {
|
||||
cards()
|
||||
.filter(card => !card.querySelector("[data-character-selected]")?.checked)
|
||||
.forEach(card => setAction(card, "@StoryIntelligenceCharacterImportActions.Ignore"));
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
<section class="onboarding-shell" aria-labelledby="story-location-complete-title">
|
||||
<div class="onboarding-panel onboarding-review-panel">
|
||||
<partial name="_StoryIntelligencePipelineHeader" model="@(new StoryIntelligencePipelineHeaderViewModel { CurrentStage = "Create Locations" })" />
|
||||
<partial name="_StoryIntelligencePipelineHeader" model="@(new StoryIntelligencePipelineHeaderViewModel { CurrentStage = "Review Assets" })" />
|
||||
<div class="onboarding-copy">
|
||||
<p class="eyebrow">Build Story Database</p>
|
||||
<div class="onboarding-success-heading">
|
||||
|
||||
@ -44,9 +44,9 @@
|
||||
else
|
||||
{
|
||||
<div class="story-review-card-actions">
|
||||
<button class="btn btn-outline-primary btn-sm" type="button" data-location-bulk="approve-all">Approve all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-location-bulk="approve-selected">Approve selected</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-location-bulk="ignore-selected">Ignore selected</button>
|
||||
<button class="btn btn-outline-primary btn-sm" type="button" data-location-bulk="create-all">Create all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-location-bulk="link-existing">Link existing matches</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-location-bulk="ignore-all">Ignore all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-location-bulk="expand">Expand all</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-location-bulk="collapse">Collapse all</button>
|
||||
</div>
|
||||
@ -59,7 +59,6 @@
|
||||
var candidate = Model.LocationReview.Candidates[i];
|
||||
<details class="story-character-card" open data-location-card>
|
||||
<summary>
|
||||
<input type="checkbox" checked data-location-selected aria-label="Select @candidate.LocationName" />
|
||||
<span>
|
||||
<strong>@candidate.LocationName</strong>
|
||||
<small>@candidate.Category · @candidate.AppearsInScenes.ToString("N0") scene@(candidate.AppearsInScenes == 1 ? string.Empty : "s")</small>
|
||||
@ -95,19 +94,16 @@
|
||||
<fieldset class="story-character-actions">
|
||||
<legend>Decision</legend>
|
||||
<label>
|
||||
<input type="radio" name="Locations[@i].Action" value="@StoryIntelligenceLocationImportActions.Approve" checked data-location-action />
|
||||
@(candidate.IsExistingMatch ? "Link to existing" : "Create new location")
|
||||
<input type="radio" name="Locations[@i].Action" value="@StoryIntelligenceLocationImportActions.CreateNew" checked data-location-action />
|
||||
Create new location
|
||||
</label>
|
||||
@if (candidate.IsExistingMatch)
|
||||
{
|
||||
<label>
|
||||
<input type="radio" name="Locations[@i].Action" value="@StoryIntelligenceLocationImportActions.CreateSeparate" data-location-action />
|
||||
Create separate location
|
||||
<input type="radio" name="Locations[@i].Action" value="@StoryIntelligenceLocationImportActions.LinkExisting" data-location-action @(candidate.IsExistingMatch ? string.Empty : "disabled") />
|
||||
Link existing location
|
||||
</label>
|
||||
}
|
||||
<label>
|
||||
<input type="radio" name="Locations[@i].Action" value="@StoryIntelligenceLocationImportActions.Alias" data-location-action />
|
||||
Alias/variant of another location
|
||||
Alias of another location
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="Locations[@i].Action" value="@StoryIntelligenceLocationImportActions.Ignore" data-location-action />
|
||||
@ -159,7 +155,7 @@
|
||||
updateCard(card);
|
||||
}
|
||||
};
|
||||
const actionFor = (card) => card.querySelector("[data-location-action]:checked")?.value || "@StoryIntelligenceLocationImportActions.Approve";
|
||||
const actionFor = (card) => card.querySelector("[data-location-action]:checked")?.value || "@StoryIntelligenceLocationImportActions.CreateNew";
|
||||
const isCanonicalTarget = (card) => {
|
||||
const action = actionFor(card);
|
||||
return action !== "@StoryIntelligenceLocationImportActions.Ignore" && action !== "@StoryIntelligenceLocationImportActions.Alias";
|
||||
@ -196,10 +192,13 @@
|
||||
document.querySelectorAll("[data-location-bulk]").forEach(button => {
|
||||
button.addEventListener("click", () => {
|
||||
const action = button.getAttribute("data-location-bulk");
|
||||
const selectedCards = cards().filter(card => card.querySelector("[data-location-selected]")?.checked);
|
||||
if (action === "approve-all") cards().forEach(card => setAction(card, "@StoryIntelligenceLocationImportActions.Approve"));
|
||||
if (action === "approve-selected") selectedCards.forEach(card => setAction(card, "@StoryIntelligenceLocationImportActions.Approve"));
|
||||
if (action === "ignore-selected") selectedCards.forEach(card => setAction(card, "@StoryIntelligenceLocationImportActions.Ignore"));
|
||||
if (action === "create-all") cards().forEach(card => setAction(card, "@StoryIntelligenceLocationImportActions.CreateNew"));
|
||||
if (action === "link-existing") cards().forEach(card => {
|
||||
if (card.querySelector(`[data-location-action][value="@StoryIntelligenceLocationImportActions.LinkExisting"]:not(:disabled)`)) {
|
||||
setAction(card, "@StoryIntelligenceLocationImportActions.LinkExisting");
|
||||
}
|
||||
});
|
||||
if (action === "ignore-all") cards().forEach(card => setAction(card, "@StoryIntelligenceLocationImportActions.Ignore"));
|
||||
if (action === "expand") cards().forEach(card => card.open = true);
|
||||
if (action === "collapse") cards().forEach(card => card.open = false);
|
||||
});
|
||||
@ -208,11 +207,6 @@
|
||||
input.addEventListener("change", () => updateCard(input.closest("[data-location-card]")));
|
||||
});
|
||||
cards().forEach(updateCard);
|
||||
form.addEventListener("submit", () => {
|
||||
cards()
|
||||
.filter(card => !card.querySelector("[data-location-selected]")?.checked)
|
||||
.forEach(card => setAction(card, "@StoryIntelligenceLocationImportActions.Ignore"));
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
}
|
||||
|
||||
@ -2,14 +2,18 @@
|
||||
@{
|
||||
var phases = new[]
|
||||
{
|
||||
new { Name = "Phase 1", Label = "Prepare manuscript", Stages = new[] { "Connect Word Companion", "Scan Manuscript", "Review Chapters" } },
|
||||
new { Name = "Phase 1", Label = "Prepare manuscript", Stages = new[] { "Welcome", "Writing Preferences", "Project", "Book", "Connect Word", "Scan Manuscript", "Review Chapters" } },
|
||||
new { Name = "Phase 2", Label = "Analyse manuscript", Stages = new[] { "Analyse Manuscript" } },
|
||||
new { Name = "Phase 3", Label = "Review Story Intelligence", Stages = new[] { "Review Scenes", "Review Characters", "Review Locations", "Review Assets", "Review Relationships", "Review Knowledge" } },
|
||||
new { Name = "Phase 4", Label = "Build Story Database", Stages = new[] { "Create Scenes", "Create Characters", "Create Locations", "Create Assets", "Complete" } }
|
||||
new { Name = "Phase 3", Label = "Review Story Intelligence", Stages = new[] { "Review Scenes", "Review Characters", "Review Locations", "Review Assets" } },
|
||||
new { Name = "Phase 4", Label = "Build Story Database", Stages = new[] { "Complete" } }
|
||||
};
|
||||
var flatStages = phases.SelectMany(phase => phase.Stages).ToList();
|
||||
var currentIndex = Math.Max(0, flatStages.FindIndex(stage => string.Equals(stage, Model.CurrentStage, StringComparison.OrdinalIgnoreCase)));
|
||||
var percent = flatStages.Count <= 1 ? 100 : Math.Clamp((currentIndex * 100) / (flatStages.Count - 1), 0, 100);
|
||||
var implementedReviewStages = new[] { "Review Scenes", "Review Characters", "Review Locations", "Review Assets", "Complete" };
|
||||
var reviewIndex = Array.FindIndex(implementedReviewStages, stage => string.Equals(stage, Model.CurrentStage, StringComparison.OrdinalIgnoreCase));
|
||||
var percent = reviewIndex >= 0
|
||||
? Math.Clamp((reviewIndex * 100) / (implementedReviewStages.Length - 1), 0, 100)
|
||||
: flatStages.Count <= 1 ? 100 : Math.Clamp((currentIndex * 100) / (flatStages.Count - 1), 0, 100);
|
||||
}
|
||||
|
||||
<div class="onboarding-progress story-pipeline-progress" aria-label="Story Intelligence pipeline progress">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user