mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
788 lines
17 KiB
JavaScript
788 lines
17 KiB
JavaScript
let AdminMassMutationCatalogue = {};
|
|
let AdminMassMutationPlantCatalogue = {};
|
|
let AdminMassMutationSavePending = false;
|
|
|
|
|
|
async function AdminMassMutationRequest(
|
|
Endpoint,
|
|
Action,
|
|
Data = {}
|
|
) {
|
|
const SaveKey =
|
|
localStorage.getItem(
|
|
SaveKeyName
|
|
);
|
|
|
|
if (
|
|
typeof SaveKey !== "string" ||
|
|
!/^[0-9A-Fa-f]{64}$/.test(
|
|
SaveKey
|
|
)
|
|
) {
|
|
throw new Error(
|
|
"A valid Account Key is required."
|
|
);
|
|
}
|
|
|
|
const Response = await fetch(
|
|
ApiUrl + "/" + Endpoint,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type":
|
|
"application/json"
|
|
},
|
|
body: JSON.stringify({
|
|
SaveKey: SaveKey,
|
|
Action: Action,
|
|
...Data
|
|
})
|
|
}
|
|
);
|
|
|
|
const Result =
|
|
await Response.json();
|
|
|
|
if (
|
|
!Response.ok ||
|
|
Result.Success !== true
|
|
) {
|
|
throw new Error(
|
|
Result.Error ??
|
|
"Mutation admin request failed."
|
|
);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
function GetSortedAdminMassMutationKeys() {
|
|
return Object.keys(
|
|
AdminMassMutationCatalogue
|
|
).sort(
|
|
(A, B) =>
|
|
Number(
|
|
AdminMassMutationCatalogue[A]
|
|
?.Id ?? 0
|
|
) -
|
|
Number(
|
|
AdminMassMutationCatalogue[B]
|
|
?.Id ?? 0
|
|
) ||
|
|
A.localeCompare(B)
|
|
);
|
|
}
|
|
|
|
|
|
function RenderAdminMassMutationSelection() {
|
|
const Container =
|
|
document.getElementById(
|
|
"AdminMassMutationSelection"
|
|
);
|
|
|
|
Container.replaceChildren();
|
|
|
|
for (
|
|
const MutationKey
|
|
of GetSortedAdminMassMutationKeys()
|
|
) {
|
|
const Mutation =
|
|
AdminMassMutationCatalogue[
|
|
MutationKey
|
|
];
|
|
|
|
const Label =
|
|
document.createElement("label");
|
|
Label.className =
|
|
"AdminPlantChecklistItem";
|
|
|
|
const Input =
|
|
document.createElement("input");
|
|
Input.type = "checkbox";
|
|
Input.value = MutationKey;
|
|
Input.dataset.adminMassTarget =
|
|
"mutation";
|
|
|
|
const Text =
|
|
document.createElement("span");
|
|
Text.textContent =
|
|
String(Mutation.Id).padStart(
|
|
3,
|
|
"0"
|
|
) +
|
|
" — " +
|
|
(Mutation.Name ?? MutationKey) +
|
|
(
|
|
Mutation.Archived === true
|
|
? " (archived)"
|
|
: ""
|
|
);
|
|
|
|
Label.append(Input, Text);
|
|
Container.appendChild(Label);
|
|
}
|
|
}
|
|
|
|
|
|
function SetAdminMassMutationSelection(
|
|
Checked
|
|
) {
|
|
for (
|
|
const Input
|
|
of document.querySelectorAll(
|
|
'[data-admin-mass-target="mutation"]'
|
|
)
|
|
) {
|
|
Input.checked = Checked;
|
|
}
|
|
}
|
|
|
|
|
|
function UpdateAdminMassMutationFieldStates() {
|
|
const FieldMap = {
|
|
Name: ["AdminMassMutationName"],
|
|
Description: [
|
|
"AdminMassMutationDescription"
|
|
],
|
|
Hint: ["AdminMassMutationHint"],
|
|
Priority: [
|
|
"AdminMassMutationPriority"
|
|
],
|
|
Chance: ["AdminMassMutationChance"],
|
|
Cooldown: [
|
|
"AdminMassMutationCooldownHours",
|
|
"AdminMassMutationCooldownMinutes",
|
|
"AdminMassMutationCooldownSeconds"
|
|
],
|
|
Rotation: [
|
|
"AdminMassMutationRotation"
|
|
],
|
|
AllowImmature: [
|
|
"AdminMassMutationAllowImmature"
|
|
],
|
|
Pattern: [
|
|
"AdminMassMutationPattern"
|
|
],
|
|
Success: [
|
|
"AdminMassMutationSuccess"
|
|
],
|
|
Failure: [
|
|
"AdminMassMutationFailure"
|
|
],
|
|
PlantsUsed: [
|
|
"AdminMassMutationPlantsUsed"
|
|
],
|
|
PlantsCreated: [
|
|
"AdminMassMutationPlantsCreated"
|
|
]
|
|
};
|
|
|
|
for (
|
|
const Apply
|
|
of document.querySelectorAll(
|
|
"[data-admin-mass-apply]"
|
|
)
|
|
) {
|
|
const FieldName =
|
|
Apply.dataset.adminMassApply;
|
|
|
|
for (
|
|
const ElementId
|
|
of FieldMap[FieldName] ?? []
|
|
) {
|
|
document.getElementById(
|
|
ElementId
|
|
).disabled = !Apply.checked;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function AdminMassMutationFieldEnabled(
|
|
FieldName
|
|
) {
|
|
return document.querySelector(
|
|
'[data-admin-mass-apply="' +
|
|
FieldName +
|
|
'"]'
|
|
)?.checked === true;
|
|
}
|
|
|
|
|
|
function GetAdminMassMutationSelectedKeys() {
|
|
return [
|
|
...document.querySelectorAll(
|
|
'[data-admin-mass-target="mutation"]:checked'
|
|
)
|
|
].map(Input => Input.value);
|
|
}
|
|
|
|
|
|
function GetAdminMassMutationCooldown() {
|
|
const Hours = Number(
|
|
document.getElementById(
|
|
"AdminMassMutationCooldownHours"
|
|
).value
|
|
);
|
|
const Minutes = Number(
|
|
document.getElementById(
|
|
"AdminMassMutationCooldownMinutes"
|
|
).value
|
|
);
|
|
const Seconds = Number(
|
|
document.getElementById(
|
|
"AdminMassMutationCooldownSeconds"
|
|
).value
|
|
);
|
|
|
|
if (
|
|
!Number.isInteger(Hours) ||
|
|
!Number.isInteger(Minutes) ||
|
|
!Number.isInteger(Seconds) ||
|
|
Hours < 0 ||
|
|
Minutes < 0 ||
|
|
Minutes > 59 ||
|
|
Seconds < 0 ||
|
|
Seconds > 59
|
|
) {
|
|
throw new Error(
|
|
"Cooldown is invalid."
|
|
);
|
|
}
|
|
|
|
return (
|
|
Hours * 3600000 +
|
|
Minutes * 60000 +
|
|
Seconds * 1000
|
|
);
|
|
}
|
|
|
|
|
|
function ParseAdminMassMutationJson(
|
|
ElementId,
|
|
Name
|
|
) {
|
|
const Raw =
|
|
document.getElementById(
|
|
ElementId
|
|
).value.trim();
|
|
|
|
try {
|
|
return JSON.parse(Raw);
|
|
} catch (Error) {
|
|
throw new Error(
|
|
Name + " is not valid JSON."
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function ParseAdminMassMutationPlantKeys(
|
|
ElementId,
|
|
Name
|
|
) {
|
|
const Keys = [
|
|
...new Set(
|
|
document.getElementById(
|
|
ElementId
|
|
).value
|
|
.split(",")
|
|
.map(Value => Value.trim())
|
|
.filter(Value => Value !== "")
|
|
)
|
|
];
|
|
|
|
return Keys.map(
|
|
PlantKey => {
|
|
const Plant =
|
|
AdminMassMutationPlantCatalogue[
|
|
PlantKey
|
|
];
|
|
|
|
if (Plant === undefined) {
|
|
throw new Error(
|
|
Name +
|
|
" references unknown Plant Key: " +
|
|
PlantKey
|
|
);
|
|
}
|
|
|
|
return Number(Plant.Id);
|
|
}
|
|
).sort((A, B) => A - B);
|
|
}
|
|
|
|
|
|
function GetAdminMassMutationPatch() {
|
|
const Patch = {};
|
|
|
|
if (AdminMassMutationFieldEnabled("Name")) {
|
|
Patch.Name =
|
|
document.getElementById(
|
|
"AdminMassMutationName"
|
|
).value.trim();
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"Description"
|
|
)
|
|
) {
|
|
Patch.Description =
|
|
document.getElementById(
|
|
"AdminMassMutationDescription"
|
|
).value.trim();
|
|
}
|
|
|
|
if (AdminMassMutationFieldEnabled("Hint")) {
|
|
Patch.Hint =
|
|
document.getElementById(
|
|
"AdminMassMutationHint"
|
|
).value.trim();
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"Priority"
|
|
)
|
|
) {
|
|
Patch.Priority =
|
|
Number(
|
|
document.getElementById(
|
|
"AdminMassMutationPriority"
|
|
).value
|
|
);
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"Chance"
|
|
)
|
|
) {
|
|
Patch.Chance =
|
|
Number(
|
|
document.getElementById(
|
|
"AdminMassMutationChance"
|
|
).value
|
|
) / 100;
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"Cooldown"
|
|
)
|
|
) {
|
|
Patch.Cooldown =
|
|
GetAdminMassMutationCooldown();
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"Rotation"
|
|
)
|
|
) {
|
|
Patch.Rotation =
|
|
document.getElementById(
|
|
"AdminMassMutationRotation"
|
|
).value;
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"AllowImmature"
|
|
)
|
|
) {
|
|
Patch.AllowImmature =
|
|
document.getElementById(
|
|
"AdminMassMutationAllowImmature"
|
|
).value === "true";
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"Pattern"
|
|
)
|
|
) {
|
|
Patch.Pattern =
|
|
ParseAdminMassMutationJson(
|
|
"AdminMassMutationPattern",
|
|
"Pattern JSON"
|
|
);
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"Success"
|
|
)
|
|
) {
|
|
Patch.Success =
|
|
ParseAdminMassMutationJson(
|
|
"AdminMassMutationSuccess",
|
|
"Success result JSON"
|
|
);
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"Failure"
|
|
)
|
|
) {
|
|
Patch.Failure =
|
|
ParseAdminMassMutationJson(
|
|
"AdminMassMutationFailure",
|
|
"Failure result JSON"
|
|
);
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"PlantsUsed"
|
|
)
|
|
) {
|
|
Patch.PlantsUsed =
|
|
ParseAdminMassMutationPlantKeys(
|
|
"AdminMassMutationPlantsUsed",
|
|
"Plants used"
|
|
);
|
|
}
|
|
|
|
if (
|
|
AdminMassMutationFieldEnabled(
|
|
"PlantsCreated"
|
|
)
|
|
) {
|
|
Patch.PlantsCreated =
|
|
ParseAdminMassMutationPlantKeys(
|
|
"AdminMassMutationPlantsCreated",
|
|
"Plants created"
|
|
);
|
|
}
|
|
|
|
return Patch;
|
|
}
|
|
|
|
|
|
function BuildAdminMassMutationPayload(
|
|
MutationKey,
|
|
Patch
|
|
) {
|
|
const Source =
|
|
AdminMassMutationCatalogue[
|
|
MutationKey
|
|
];
|
|
|
|
const Mutation = {
|
|
Id: Number(Source.Id),
|
|
MutationKey: MutationKey,
|
|
Name: Source.Name ?? "",
|
|
Description:
|
|
Source.Description ?? "",
|
|
Hint: Source.Hint ?? "",
|
|
Priority:
|
|
Number(Source.Priority ?? 0),
|
|
Chance:
|
|
Number(Source.Chance ?? 0),
|
|
Cooldown:
|
|
Number(Source.Cooldown ?? 0),
|
|
Rotation:
|
|
Source.Rotation ?? "None",
|
|
AllowImmature:
|
|
Source.AllowImmature === true,
|
|
Pattern:
|
|
JSON.parse(
|
|
JSON.stringify(
|
|
Source.Pattern ?? []
|
|
)
|
|
),
|
|
Success:
|
|
JSON.parse(
|
|
JSON.stringify(
|
|
Source.Success ?? []
|
|
)
|
|
),
|
|
Failure:
|
|
JSON.parse(
|
|
JSON.stringify(
|
|
Source.Failure ?? "Keep"
|
|
)
|
|
),
|
|
Relations: {
|
|
PlantsUsed: [
|
|
...(Source.Relations
|
|
?.PlantsUsed ?? [])
|
|
],
|
|
PlantsCreated: [
|
|
...(Source.Relations
|
|
?.PlantsCreated ?? [])
|
|
]
|
|
},
|
|
IsNew: false
|
|
};
|
|
|
|
for (
|
|
const [Key, Value]
|
|
of Object.entries(Patch)
|
|
) {
|
|
if (
|
|
Key === "PlantsUsed" ||
|
|
Key === "PlantsCreated"
|
|
) {
|
|
Mutation.Relations[Key] = [
|
|
...Value
|
|
];
|
|
continue;
|
|
}
|
|
|
|
Mutation[Key] =
|
|
Value !== null &&
|
|
typeof Value === "object"
|
|
? JSON.parse(
|
|
JSON.stringify(Value)
|
|
)
|
|
: Value;
|
|
}
|
|
|
|
return Mutation;
|
|
}
|
|
|
|
|
|
async function SaveAdminMassMutations(
|
|
Event
|
|
) {
|
|
Event.preventDefault();
|
|
|
|
if (AdminMassMutationSavePending) {
|
|
return;
|
|
}
|
|
|
|
const SelectedKeys =
|
|
GetAdminMassMutationSelectedKeys();
|
|
|
|
if (SelectedKeys.length === 0) {
|
|
SetAdminMassMutationMessage(
|
|
"Mark at least one mutation first."
|
|
);
|
|
return;
|
|
}
|
|
|
|
let Patch;
|
|
|
|
try {
|
|
Patch =
|
|
GetAdminMassMutationPatch();
|
|
} catch (Error) {
|
|
SetAdminMassMutationMessage(
|
|
Error.message
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (Object.keys(Patch).length === 0) {
|
|
SetAdminMassMutationMessage(
|
|
"Enable at least one field to update."
|
|
);
|
|
return;
|
|
}
|
|
|
|
const Payloads = [];
|
|
|
|
try {
|
|
for (
|
|
const MutationKey
|
|
of SelectedKeys
|
|
) {
|
|
const Mutation =
|
|
BuildAdminMassMutationPayload(
|
|
MutationKey,
|
|
Patch
|
|
);
|
|
|
|
ValidateAdminMutation(
|
|
Mutation,
|
|
AdminMassMutationCatalogue,
|
|
AdminMassMutationPlantCatalogue,
|
|
MutationKey
|
|
);
|
|
|
|
Payloads.push(Mutation);
|
|
}
|
|
} catch (Error) {
|
|
SetAdminMassMutationMessage(
|
|
Error.message
|
|
);
|
|
return;
|
|
}
|
|
|
|
AdminMassMutationSavePending = true;
|
|
|
|
const SaveButton =
|
|
document.getElementById(
|
|
"AdminMassMutationSaveButton"
|
|
);
|
|
|
|
SaveButton.disabled = true;
|
|
|
|
let SavedCount = 0;
|
|
|
|
try {
|
|
for (const Mutation of Payloads) {
|
|
SetAdminMassMutationMessage(
|
|
"Saving " +
|
|
(SavedCount + 1) +
|
|
" of " +
|
|
Payloads.length +
|
|
"..."
|
|
);
|
|
|
|
await AdminMassMutationRequest(
|
|
"AdminMutations.php",
|
|
"Save",
|
|
{Mutation: Mutation}
|
|
);
|
|
|
|
SavedCount++;
|
|
}
|
|
|
|
localStorage.removeItem(
|
|
"SarahtoninGardenContent"
|
|
);
|
|
|
|
await LoadAdminMassMutationData();
|
|
|
|
SetAdminMassMutationMessage(
|
|
SavedCount === 1
|
|
? "1 mutation updated."
|
|
: SavedCount +
|
|
" mutations updated."
|
|
);
|
|
} catch (Error) {
|
|
SetAdminMassMutationMessage(
|
|
"Saved " +
|
|
SavedCount +
|
|
" before stopping: " +
|
|
(Error.message ??
|
|
"Couldn't save mutations.")
|
|
);
|
|
} finally {
|
|
AdminMassMutationSavePending = false;
|
|
SaveButton.disabled = false;
|
|
}
|
|
}
|
|
|
|
|
|
function BindAdminMassMutationEditor() {
|
|
document.getElementById(
|
|
"AdminMassMutationSelectAll"
|
|
).addEventListener(
|
|
"click",
|
|
() =>
|
|
SetAdminMassMutationSelection(
|
|
true
|
|
)
|
|
);
|
|
|
|
document.getElementById(
|
|
"AdminMassMutationClear"
|
|
).addEventListener(
|
|
"click",
|
|
() =>
|
|
SetAdminMassMutationSelection(
|
|
false
|
|
)
|
|
);
|
|
|
|
document.getElementById(
|
|
"AdminMassMutationForm"
|
|
).addEventListener(
|
|
"submit",
|
|
SaveAdminMassMutations
|
|
);
|
|
|
|
for (
|
|
const Apply
|
|
of document.querySelectorAll(
|
|
"[data-admin-mass-apply]"
|
|
)
|
|
) {
|
|
Apply.addEventListener(
|
|
"change",
|
|
UpdateAdminMassMutationFieldStates
|
|
);
|
|
}
|
|
|
|
UpdateAdminMassMutationFieldStates();
|
|
}
|
|
|
|
|
|
async function LoadAdminMassMutationData() {
|
|
const [MutationResult, PlantResult] =
|
|
await Promise.all([
|
|
AdminMassMutationRequest(
|
|
"AdminMutations.php",
|
|
"List"
|
|
),
|
|
AdminMassMutationRequest(
|
|
"AdminPlants.php",
|
|
"List"
|
|
)
|
|
]);
|
|
|
|
AdminMassMutationCatalogue =
|
|
MutationResult.Mutations ?? {};
|
|
AdminMassMutationPlantCatalogue =
|
|
PlantResult.Plants ?? {};
|
|
|
|
RenderAdminMassMutationSelection();
|
|
}
|
|
|
|
|
|
function SetAdminMassMutationMessage(
|
|
Message
|
|
) {
|
|
document.getElementById(
|
|
"AdminMassMutationMessage"
|
|
).textContent = Message;
|
|
}
|
|
|
|
|
|
async function StartAdminMassMutationEditor() {
|
|
try {
|
|
const Status =
|
|
await GetAdminStatus();
|
|
|
|
if (
|
|
Status.Success !== true ||
|
|
Status.IsAdmin !== true
|
|
) {
|
|
SetAdminMassMutationMessage(
|
|
"Admin access required."
|
|
);
|
|
return;
|
|
}
|
|
|
|
BindAdminMassMutationEditor();
|
|
await LoadAdminMassMutationData();
|
|
|
|
SetAdminMassMutationMessage(
|
|
"Mark mutations and enable the fields you want to change."
|
|
);
|
|
} catch (Error) {
|
|
console.error(
|
|
"Couldn't start Mass mutation editor:",
|
|
Error
|
|
);
|
|
|
|
SetAdminMassMutationMessage(
|
|
Error.message ??
|
|
"Couldn't load the Mass mutation editor."
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
document.addEventListener(
|
|
"DOMContentLoaded",
|
|
StartAdminMassMutationEditor
|
|
);
|