diff --git a/Pages/Admin.php b/Pages/Admin.php
index d1ad375..731959d 100644
--- a/Pages/Admin.php
+++ b/Pages/Admin.php
@@ -95,6 +95,8 @@ $PageSection = "Admin";
@@ -102,6 +104,10 @@ $PageSection = "Admin";
Mutations
+
+
+
+
diff --git a/Pages/AdminMutations.php b/Pages/AdminMutations.php
index e07d399..02abf3b 100644
--- a/Pages/AdminMutations.php
+++ b/Pages/AdminMutations.php
@@ -133,7 +133,6 @@ $PageSection = "Admin";
diff --git a/Pages/AdminPlants.php b/Pages/AdminPlants.php
index 2377a9b..29f70fb 100644
--- a/Pages/AdminPlants.php
+++ b/Pages/AdminPlants.php
@@ -140,7 +140,6 @@ $PageSection = "Admin";
id="AdminPlantDescription"
name="Description"
rows="4"
- required
>
diff --git a/Scripts/Garden/AdminOverview.js b/Scripts/Garden/AdminOverview.js
index f2ef816..a2c2ed6 100644
--- a/Scripts/Garden/AdminOverview.js
+++ b/Scripts/Garden/AdminOverview.js
@@ -1,5 +1,6 @@
let AdminOverviewPlants = {};
let AdminOverviewMutations = {};
+let AdminOverviewToggleNumber = 0;
async function AdminOverviewRequest(
@@ -206,9 +207,231 @@ function CollectAdminOverviewCreatedPlants() {
}
+function GetAdminOverviewPatternPlantKeys(
+ Mutation
+) {
+ const PlantKeys = new Set();
+
+ for (const Row of Mutation.Pattern ?? []) {
+ if (!Array.isArray(Row)) {
+ continue;
+ }
+
+ for (const Cell of Row) {
+ if (
+ typeof Cell === "string" &&
+ AdminOverviewPlants[
+ Cell
+ ] !== undefined
+ ) {
+ PlantKeys.add(Cell);
+ continue;
+ }
+
+ if (
+ Cell !== null &&
+ typeof Cell === "object" &&
+ !Array.isArray(Cell) &&
+ typeof Cell.Plant === "string" &&
+ AdminOverviewPlants[
+ Cell.Plant
+ ] !== undefined
+ ) {
+ PlantKeys.add(
+ Cell.Plant
+ );
+ }
+ }
+ }
+
+ return PlantKeys;
+}
+
+
+function GetAdminOverviewResultPlantKeys(
+ Mutation
+) {
+ const PlantKeys = new Set();
+
+ for (const Row of Mutation.Success ?? []) {
+ if (!Array.isArray(Row)) {
+ continue;
+ }
+
+ for (const Cell of Row) {
+ let PlantKey = null;
+
+ if (typeof Cell === "string") {
+ PlantKey = Cell;
+ } else if (
+ Cell !== null &&
+ typeof Cell === "object" &&
+ !Array.isArray(Cell) &&
+ typeof Cell.Plant === "string"
+ ) {
+ PlantKey = Cell.Plant;
+ }
+
+ if (
+ PlantKey !== null &&
+ !PlantKey.startsWith("$") &&
+ AdminOverviewPlants[
+ PlantKey
+ ] !== undefined
+ ) {
+ PlantKeys.add(PlantKey);
+ }
+ }
+ }
+
+ return PlantKeys;
+}
+
+
+function HasAdminOverviewMissingRelations(
+ Mutation,
+ RelationName,
+ ExpectedPlantKeys
+) {
+ const RelationValues =
+ Mutation.Relations?.[
+ RelationName
+ ];
+
+ if (
+ !Array.isArray(RelationValues) ||
+ RelationValues.length === 0
+ ) {
+ return true;
+ }
+
+ const RelationIds =
+ new Set(
+ RelationValues.map(
+ Value => Number(Value)
+ )
+ );
+
+ for (const PlantKey of ExpectedPlantKeys) {
+ const PlantId = Number(
+ AdminOverviewPlants[
+ PlantKey
+ ]?.Id
+ );
+
+ if (
+ Number.isInteger(PlantId) &&
+ !RelationIds.has(PlantId)
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+function HasAdminOverviewSuccessChange(
+ Mutation
+) {
+ if (!Array.isArray(Mutation.Success)) {
+ return true;
+ }
+
+ for (const Row of Mutation.Success) {
+ if (!Array.isArray(Row)) {
+ return true;
+ }
+
+ for (const Cell of Row) {
+ if (
+ Cell !== null &&
+ Cell !== "Keep"
+ ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+function CreateAdminOverviewNestedToggle(
+ Label,
+ NestedList
+) {
+ AdminOverviewToggleNumber++;
+
+ const ContentId =
+ "AdminOverviewNested" +
+ AdminOverviewToggleNumber;
+
+ NestedList.id = ContentId;
+ NestedList.hidden = true;
+
+ const Toggle =
+ document.createElement("button");
+
+ Toggle.className =
+ "ShopSectionToggle";
+ Toggle.type = "button";
+ Toggle.setAttribute(
+ "aria-expanded",
+ "false"
+ );
+ Toggle.setAttribute(
+ "aria-controls",
+ ContentId
+ );
+
+ const Arrow =
+ document.createElement("span");
+
+ Arrow.className =
+ "ShopSectionArrow";
+ Arrow.setAttribute(
+ "aria-hidden",
+ "true"
+ );
+ Arrow.textContent = ">";
+
+ const Text =
+ document.createElement("span");
+
+ Text.textContent = Label;
+
+ Toggle.append(
+ Arrow,
+ Text
+ );
+
+ Toggle.addEventListener(
+ "click",
+ () => {
+ const IsExpanded =
+ Toggle.getAttribute(
+ "aria-expanded"
+ ) === "true";
+
+ Toggle.setAttribute(
+ "aria-expanded",
+ String(!IsExpanded)
+ );
+
+ NestedList.hidden =
+ IsExpanded;
+ }
+ );
+
+ return Toggle;
+}
+
+
function CreateAdminOverviewIssueList(
Items,
- NestedValues = null
+ NestedValues = null,
+ CollapseNested = false
) {
const List =
document.createElement("ul");
@@ -220,31 +443,52 @@ function CreateAdminOverviewIssueList(
const ListItem =
document.createElement("li");
- const Label =
- document.createElement("span");
-
- Label.textContent = Item.Label;
- ListItem.appendChild(Label);
-
const Nested =
NestedValues?.(Item) ?? [];
- if (Nested.length > 0) {
- const NestedList =
- document.createElement("ul");
-
- for (const Value of Nested) {
- const NestedItem =
- document.createElement("li");
-
- NestedItem.textContent =
- Value;
- NestedList.appendChild(
- NestedItem
+ if (Nested.length === 0) {
+ const Label =
+ document.createElement(
+ "span"
);
- }
- ListItem.appendChild(
+ Label.textContent = Item.Label;
+ ListItem.appendChild(Label);
+ List.appendChild(ListItem);
+ continue;
+ }
+
+ const NestedList =
+ document.createElement("ul");
+
+ for (const Value of Nested) {
+ const NestedItem =
+ document.createElement("li");
+
+ NestedItem.textContent =
+ Value;
+ NestedList.appendChild(
+ NestedItem
+ );
+ }
+
+ if (CollapseNested) {
+ ListItem.append(
+ CreateAdminOverviewNestedToggle(
+ Item.Label,
+ NestedList
+ ),
+ NestedList
+ );
+ } else {
+ const Label =
+ document.createElement(
+ "span"
+ );
+
+ Label.textContent = Item.Label;
+ ListItem.append(
+ Label,
NestedList
);
}
@@ -260,7 +504,8 @@ function RenderAdminOverviewSection(
ContainerId,
Title,
Items,
- NestedValues = null
+ NestedValues = null,
+ CollapseNested = false
) {
const Container =
document.getElementById(
@@ -289,13 +534,16 @@ function RenderAdminOverviewSection(
Container.appendChild(
CreateAdminOverviewIssueList(
Items,
- NestedValues
+ NestedValues,
+ CollapseNested
)
);
}
function RenderAdminOverview() {
+ AdminOverviewToggleNumber = 0;
+
const CreatedPlants =
CollectAdminOverviewCreatedPlants();
@@ -315,12 +563,18 @@ function RenderAdminOverview() {
);
const MissingSprites = [];
+ const PlantNoDescription = [];
+ const PlantNoTags = [];
const NoWaysToObtain = [];
for (
const [PlantKey, Plant]
of PlantEntries
) {
+ const Label =
+ Plant.Name?.trim() ||
+ PlantKey;
+
const Missing =
GetAdminOverviewMissingSprites(
PlantKey,
@@ -329,19 +583,36 @@ function RenderAdminOverview() {
if (Missing.length > 0) {
MissingSprites.push({
- Label:
- Plant.Name ?? PlantKey,
+ Label: Label,
Missing: Missing
});
}
+ if (
+ typeof Plant.Description !==
+ "string" ||
+ Plant.Description.trim() === ""
+ ) {
+ PlantNoDescription.push({
+ Label: Label
+ });
+ }
+
+ if (
+ !Array.isArray(Plant.Tags) ||
+ Plant.Tags.length === 0
+ ) {
+ PlantNoTags.push({
+ Label: Label
+ });
+ }
+
if (
Plant.Shop?.ShopPlant !== true &&
!CreatedPlants.has(PlantKey)
) {
NoWaysToObtain.push({
- Label:
- Plant.Name ?? PlantKey
+ Label: Label
});
}
}
@@ -363,6 +634,10 @@ function RenderAdminOverview() {
const NoDescription = [];
const NoHint = [];
+ const MissingParents = [];
+ const MissingChildren = [];
+ const NoChance = [];
+ const NoSuccessChange = [];
for (
const [MutationKey, Mutation]
@@ -391,13 +666,70 @@ function RenderAdminOverview() {
Label: Label
});
}
+
+ if (
+ HasAdminOverviewMissingRelations(
+ Mutation,
+ "PlantsUsed",
+ GetAdminOverviewPatternPlantKeys(
+ Mutation
+ )
+ )
+ ) {
+ MissingParents.push({
+ Label: Label
+ });
+ }
+
+ if (
+ HasAdminOverviewMissingRelations(
+ Mutation,
+ "PlantsCreated",
+ GetAdminOverviewResultPlantKeys(
+ Mutation
+ )
+ )
+ ) {
+ MissingChildren.push({
+ Label: Label
+ });
+ }
+
+ if (Number(Mutation.Chance) === 0) {
+ NoChance.push({
+ Label: Label
+ });
+ }
+
+ if (
+ !HasAdminOverviewSuccessChange(
+ Mutation
+ )
+ ) {
+ NoSuccessChange.push({
+ Label: Label
+ });
+ }
}
RenderAdminOverviewSection(
"AdminOverviewMissingSprites",
"Missing sprites",
MissingSprites,
- Item => Item.Missing
+ Item => Item.Missing,
+ true
+ );
+
+ RenderAdminOverviewSection(
+ "AdminOverviewPlantNoDescription",
+ "No description",
+ PlantNoDescription
+ );
+
+ RenderAdminOverviewSection(
+ "AdminOverviewPlantNoTags",
+ "No tags",
+ PlantNoTags
);
RenderAdminOverviewSection(
@@ -417,6 +749,30 @@ function RenderAdminOverview() {
"No hint",
NoHint
);
+
+ RenderAdminOverviewSection(
+ "AdminOverviewMissingParents",
+ "Missing parents",
+ MissingParents
+ );
+
+ RenderAdminOverviewSection(
+ "AdminOverviewMissingChildren",
+ "Missing children",
+ MissingChildren
+ );
+
+ RenderAdminOverviewSection(
+ "AdminOverviewNoChance",
+ "No chance",
+ NoChance
+ );
+
+ RenderAdminOverviewSection(
+ "AdminOverviewNoSuccessChange",
+ "No success change",
+ NoSuccessChange
+ );
}
diff --git a/Styles/Style.css b/Styles/Style.css
index 3ee8ebc..635e6ec 100644
--- a/Styles/Style.css
+++ b/Styles/Style.css
@@ -259,6 +259,14 @@ ul, ol {
color: var(--Text);
}
+ul > li::marker {
+ content: "▪ ";
+}
+
+ul ul > li::marker {
+ content: "▫ ";
+}
+
li {
margin-bottom: 0.5rem;
}
@@ -1716,6 +1724,10 @@ input, button, textarea, select {
list-style: none;
}
+.MutationHintList > li::marker {
+ content: "";
+}
+
.MutationHintList li + li {
margin-top: 0.35rem;
}