From 8c85cdcd1d0f296bce3962ea401b589ecf7d08fa Mon Sep 17 00:00:00 2001
From: Sainan <63328889+Sainan@users.noreply.github.com>
Date: Wed, 2 Jul 2025 14:18:24 -0700
Subject: [PATCH] feat(webui): "add maxed" for mods (#2387)
Closes #2382
Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2387
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
---
 src/controllers/custom/addItemsController.ts |  3 ++-
 src/services/inventoryService.ts             | 10 ++++++++++
 static/webui/index.html                      |  1 +
 static/webui/script.js                       | 12 ++++++++++--
 static/webui/translations/de.js              |  1 +
 static/webui/translations/en.js              |  1 +
 static/webui/translations/es.js              |  1 +
 static/webui/translations/fr.js              |  1 +
 static/webui/translations/ru.js              |  1 +
 static/webui/translations/zh.js              |  1 +
 10 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/src/controllers/custom/addItemsController.ts b/src/controllers/custom/addItemsController.ts
index dc39ef64..30edd917 100644
--- a/src/controllers/custom/addItemsController.ts
+++ b/src/controllers/custom/addItemsController.ts
@@ -7,7 +7,7 @@ export const addItemsController: RequestHandler = async (req, res) => {
     const requests = req.body as IAddItemRequest[];
     const inventory = await getInventory(accountId);
     for (const request of requests) {
-        await addItem(inventory, request.ItemType, request.ItemCount, true, undefined, undefined, true);
+        await addItem(inventory, request.ItemType, request.ItemCount, true, undefined, request.Fingerprint, true);
     }
     await inventory.save();
     res.end();
@@ -16,4 +16,5 @@ export const addItemsController: RequestHandler = async (req, res) => {
 interface IAddItemRequest {
     ItemType: string;
     ItemCount: number;
+    Fingerprint?: string;
 }
diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts
index 3bfb3244..69299ec8 100644
--- a/src/services/inventoryService.ts
+++ b/src/services/inventoryService.ts
@@ -483,6 +483,16 @@ export const addItem = async (
         return addCustomization(inventory, typeName);
     }
     if (typeName in ExportUpgrades || typeName in ExportArcanes) {
+        if (targetFingerprint) {
+            if (quantity != 1) {
+                logger.warn(`adding 1 of ${typeName} ${targetFingerprint} even tho quantity ${quantity} was requested`);
+            }
+            inventory.Upgrades.push({
+                ItemType: typeName,
+                UpgradeFingerprint: targetFingerprint
+            });
+            return {}; // there's not exactly a common "InventoryChanges" format for these
+        }
         const changes = [
             {
                 ItemType: typeName,
diff --git a/static/webui/index.html b/static/webui/index.html
index 66309c9d..dd5479a1 100644
--- a/static/webui/index.html
+++ b/static/webui/index.html
@@ -527,6 +527,7 @@
                                 
                                 
diff --git a/static/webui/script.js b/static/webui/script.js
index 9755f08e..4d726036 100644
--- a/static/webui/script.js
+++ b/static/webui/script.js
@@ -1873,6 +1873,8 @@ function setFingerprint(ItemType, ItemId, fingerprint) {
 }
 
 function doAcquireMod() {
+    const maxed = !!window.maxed;
+    window.maxed = false;
     const uniqueName = getKey(document.getElementById("mod-to-acquire"));
     if (!uniqueName) {
         $("#mod-to-acquire").addClass("is-invalid").focus();
@@ -1880,14 +1882,15 @@ function doAcquireMod() {
     }
     const count = parseInt($("#mod-count").val());
     if (count != 0) {
-        revalidateAuthz().then(() => {
+        Promise.all([window.itemListPromise, revalidateAuthz()]).then(([itemList]) => {
             $.post({
                 url: "/custom/addItems?" + window.authz,
                 contentType: "application/json",
                 data: JSON.stringify([
                     {
                         ItemType: uniqueName,
-                        ItemCount: count
+                        ItemCount: count,
+                        Fingerprint: maxed ? JSON.stringify({ lvl: itemList[uniqueName].fusionLimit ?? 5 }) : undefined
                     }
                 ])
             }).done(function () {
@@ -1902,6 +1905,11 @@ function doAcquireMod() {
     }
 }
 
+function doAcquireModMax() {
+    const uniqueName = getKey(document.getElementById("mod-to-acquire"));
+    alert("doAcquireModMax: " + uniqueName);
+}
+
 const uiConfigs = [...$(".config-form input[id], .config-form select[id]")].map(x => x.id);
 
 for (const id of uiConfigs) {
diff --git a/static/webui/translations/de.js b/static/webui/translations/de.js
index 2de9e90b..26d9fd19 100644
--- a/static/webui/translations/de.js
+++ b/static/webui/translations/de.js
@@ -127,6 +127,7 @@ dict = {
     mods_fingerprintHelp: `Benötigst du Hilfe mit dem Fingerabdruck?`,
     mods_rivens: `Rivens`,
     mods_mods: `Mods`,
+    mods_addMax: `[UNTRANSLATED] Add Maxed`,
     mods_addMissingUnrankedMods: `Fehlende Mods ohne Rang hinzufügen`,
     mods_removeUnranked: `Mods ohne Rang entfernen`,
     mods_addMissingMaxRankMods: `Fehlende Mods mit Max. Rang hinzufügen`,
diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js
index 54994a6c..e94c94bc 100644
--- a/static/webui/translations/en.js
+++ b/static/webui/translations/en.js
@@ -126,6 +126,7 @@ dict = {
     mods_fingerprintHelp: `Need help with the fingerprint?`,
     mods_rivens: `Rivens`,
     mods_mods: `Mods`,
+    mods_addMax: `Add Maxed`,
     mods_addMissingUnrankedMods: `Add Missing Unranked Mods`,
     mods_removeUnranked: `Remove Unranked Mods`,
     mods_addMissingMaxRankMods: `Add Missing Max Rank Mods`,
diff --git a/static/webui/translations/es.js b/static/webui/translations/es.js
index 7f515af7..6776aba6 100644
--- a/static/webui/translations/es.js
+++ b/static/webui/translations/es.js
@@ -127,6 +127,7 @@ dict = {
     mods_fingerprintHelp: `¿Necesitas ayuda con la huella digital?`,
     mods_rivens: `Agrietados`,
     mods_mods: `Mods`,
+    mods_addMax: `[UNTRANSLATED] Add Maxed`,
     mods_addMissingUnrankedMods: `Agregar mods sin rango faltantes`,
     mods_removeUnranked: `Quitar mods sin rango`,
     mods_addMissingMaxRankMods: `Agregar mods de rango máximo faltantes`,
diff --git a/static/webui/translations/fr.js b/static/webui/translations/fr.js
index 2578e9c9..28112086 100644
--- a/static/webui/translations/fr.js
+++ b/static/webui/translations/fr.js
@@ -127,6 +127,7 @@ dict = {
     mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`,
     mods_rivens: `Rivens`,
     mods_mods: `Mods`,
+    mods_addMax: `[UNTRANSLATED] Add Maxed`,
     mods_addMissingUnrankedMods: `Ajouter les mods sans rang manquants`,
     mods_removeUnranked: `Retirer les mods sans rang`,
     mods_addMissingMaxRankMods: `Ajouter les mods niveau max manquants`,
diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js
index e84437b5..c3ca845b 100644
--- a/static/webui/translations/ru.js
+++ b/static/webui/translations/ru.js
@@ -127,6 +127,7 @@ dict = {
     mods_fingerprintHelp: `Нужна помощь с отпечатком?`,
     mods_rivens: `Моды Разлома`,
     mods_mods: `Моды`,
+    mods_addMax: `[UNTRANSLATED] Add Maxed`,
     mods_addMissingUnrankedMods: `Добавить недостающие моды без ранга`,
     mods_removeUnranked: `Удалить моды без ранга`,
     mods_addMissingMaxRankMods: `Добавить недостающие моды максимального ранга`,
diff --git a/static/webui/translations/zh.js b/static/webui/translations/zh.js
index d1cb95bb..097c143c 100644
--- a/static/webui/translations/zh.js
+++ b/static/webui/translations/zh.js
@@ -127,6 +127,7 @@ dict = {
     mods_fingerprintHelp: `需要印记相关的帮助?`,
     mods_rivens: `裂罅MOD`,
     mods_mods: `Mods`,
+    mods_addMax: `[UNTRANSLATED] Add Maxed`,
     mods_addMissingUnrankedMods: `添加所有缺失的Mods`,
     mods_removeUnranked: `删除所有未升级的Mods`,
     mods_addMissingMaxRankMods: `添加所有缺失的满级Mods`,