From 7bcb5f21ce64ba73ce9f0488eab698b33b283ca3 Mon Sep 17 00:00:00 2001
From: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Date: Thu, 25 Sep 2025 10:21:04 -0700
Subject: [PATCH] chore(webui): unify Invigoration code (#2809)
Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2809
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
---
 .../editSuitInvigorationUpgradeController.ts  | 36 --------
 .../custom/setInvigorationController.ts       | 27 ++++++
 src/routes/custom.ts                          |  4 +-
 static/webui/index.html                       | 21 ++---
 static/webui/script.js                        | 89 +++++--------------
 static/webui/translations/de.js               | 10 +--
 static/webui/translations/en.js               | 10 +--
 static/webui/translations/es.js               | 10 +--
 static/webui/translations/fr.js               | 10 +--
 static/webui/translations/ru.js               |  8 +-
 static/webui/translations/uk.js               |  8 +-
 static/webui/translations/zh.js               | 10 +--
 12 files changed, 95 insertions(+), 148 deletions(-)
 delete mode 100644 src/controllers/custom/editSuitInvigorationUpgradeController.ts
 create mode 100644 src/controllers/custom/setInvigorationController.ts
diff --git a/src/controllers/custom/editSuitInvigorationUpgradeController.ts b/src/controllers/custom/editSuitInvigorationUpgradeController.ts
deleted file mode 100644
index 713b1c0e..00000000
--- a/src/controllers/custom/editSuitInvigorationUpgradeController.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { getAccountIdForRequest } from "../../services/loginService.ts";
-import { getInventory } from "../../services/inventoryService.ts";
-import type { RequestHandler } from "express";
-import { broadcastInventoryUpdate } from "../../services/wsService.ts";
-
-const DEFAULT_UPGRADE_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
-
-export const editSuitInvigorationUpgradeController: RequestHandler = async (req, res) => {
-    const accountId = await getAccountIdForRequest(req);
-    const { oid, data } = req.body as {
-        oid: string;
-        data?: {
-            DefensiveUpgrade: string;
-            OffensiveUpgrade: string;
-            UpgradesExpiry?: number;
-        };
-    };
-    const inventory = await getInventory(accountId);
-    const suit = inventory.Suits.id(oid)!;
-    if (data) {
-        suit.DefensiveUpgrade = data.DefensiveUpgrade;
-        suit.OffensiveUpgrade = data.OffensiveUpgrade;
-        if (data.UpgradesExpiry) {
-            suit.UpgradesExpiry = new Date(data.UpgradesExpiry);
-        } else {
-            suit.UpgradesExpiry = new Date(Date.now() + DEFAULT_UPGRADE_EXPIRY_MS);
-        }
-    } else {
-        suit.DefensiveUpgrade = undefined;
-        suit.OffensiveUpgrade = undefined;
-        suit.UpgradesExpiry = undefined;
-    }
-    await inventory.save();
-    res.end();
-    broadcastInventoryUpdate(req);
-};
diff --git a/src/controllers/custom/setInvigorationController.ts b/src/controllers/custom/setInvigorationController.ts
new file mode 100644
index 00000000..99c28765
--- /dev/null
+++ b/src/controllers/custom/setInvigorationController.ts
@@ -0,0 +1,27 @@
+import { getAccountIdForRequest } from "../../services/loginService.ts";
+import { getInventory } from "../../services/inventoryService.ts";
+import type { RequestHandler } from "express";
+import { broadcastInventoryUpdate } from "../../services/wsService.ts";
+
+export const setInvigorationController: RequestHandler = async (req, res) => {
+    const accountId = await getAccountIdForRequest(req);
+    const request = req.body as ISetInvigorationRequest;
+    const inventory = await getInventory(accountId, "Suits");
+    const suit = inventory.Suits.id(request.oid);
+    if (suit) {
+        const hasUpgrades = request.DefensiveUpgrade && request.OffensiveUpgrade && request.UpgradesExpiry;
+        suit.DefensiveUpgrade = hasUpgrades ? request.DefensiveUpgrade : undefined;
+        suit.OffensiveUpgrade = hasUpgrades ? request.OffensiveUpgrade : undefined;
+        suit.UpgradesExpiry = hasUpgrades ? new Date(request.UpgradesExpiry) : undefined;
+        await inventory.save();
+        broadcastInventoryUpdate(req);
+    }
+    res.end();
+};
+
+interface ISetInvigorationRequest {
+    oid: string;
+    DefensiveUpgrade: string;
+    OffensiveUpgrade: string;
+    UpgradesExpiry: number;
+}
diff --git a/src/routes/custom.ts b/src/routes/custom.ts
index 7eb8fafa..628aff6b 100644
--- a/src/routes/custom.ts
+++ b/src/routes/custom.ts
@@ -43,7 +43,7 @@ import { setBoosterController } from "../controllers/custom/setBoosterController
 import { updateFingerprintController } from "../controllers/custom/updateFingerprintController.ts";
 import { unlockLevelCapController } from "../controllers/custom/unlockLevelCapController.ts";
 import { changeModularPartsController } from "../controllers/custom/changeModularPartsController.ts";
-import { editSuitInvigorationUpgradeController } from "../controllers/custom/editSuitInvigorationUpgradeController.ts";
+import { setInvigorationController } from "../controllers/custom/setInvigorationController.ts";
 import { setAccountCheatController } from "../controllers/custom/setAccountCheatController.ts";
 import { setGuildCheatController } from "../controllers/custom/setGuildCheatController.ts";
 
@@ -92,7 +92,7 @@ customRouter.post("/setBooster", setBoosterController);
 customRouter.post("/updateFingerprint", updateFingerprintController);
 customRouter.post("/unlockLevelCap", unlockLevelCapController);
 customRouter.post("/changeModularParts", changeModularPartsController);
-customRouter.post("/editSuitInvigorationUpgrade", editSuitInvigorationUpgradeController);
+customRouter.post("/setInvigoration", setInvigorationController);
 customRouter.post("/setAccountCheat", setAccountCheatController);
 customRouter.post("/setGuildCheat", setGuildCheatController);
 
diff --git a/static/webui/index.html b/static/webui/index.html
index cfe5afd1..82990f15 100644
--- a/static/webui/index.html
+++ b/static/webui/index.html
@@ -719,12 +719,12 @@
                     
                 
                 
-                    
+                    
                     
diff --git a/static/webui/script.js b/static/webui/script.js
index a061c36d..593c7a52 100644
--- a/static/webui/script.js
+++ b/static/webui/script.js
@@ -1513,12 +1513,13 @@ function updateInventory() {
                             document.getElementById("crystals-list").appendChild(tr);
                         });
 
-                        document.getElementById("edit-suit-invigorations-card").classList.remove("d-none");
-                        const { OffensiveUpgrade, DefensiveUpgrade, UpgradesExpiry } =
-                            suitInvigorationUpgradeData(item);
-                        document.getElementById("dv-invigoration-offensive").value = OffensiveUpgrade;
-                        document.getElementById("dv-invigoration-defensive").value = DefensiveUpgrade;
-                        document.getElementById("dv-invigoration-expiry").value = UpgradesExpiry;
+                        {
+                            document.getElementById("edit-suit-invigorations-card").classList.remove("d-none");
+                            document.getElementById("invigoration-offensive").value = item.OffensiveUpgrade || "";
+                            document.getElementById("invigoration-defensive").value = item.DefensiveUpgrade || "";
+                            document.getElementById("invigoration-expiry").value =
+                                formatDatetime("%Y-%m-%d %H:%M", Number(item.UpgradesExpiry?.$date.$numberLong)) || "";
+                        }
 
                         {
                             document.getElementById("loadout-card").classList.remove("d-none");
@@ -3445,9 +3446,9 @@ function doBulkQuestUpdate(operation) {
     });
 }
 
-function toast(text) {
+function toast(text, type = "primary") {
     const toast = document.createElement("div");
-    toast.className = "toast align-items-center text-bg-primary border-0";
+    toast.className = `toast align-items-center text-bg-${type} border-0`;
     const div = document.createElement("div");
     div.className = "d-flex";
     const body = document.createElement("div");
@@ -3999,73 +4000,31 @@ function handleModularPartsChange(event) {
         });
     }
 }
-function suitInvigorationUpgradeData(suitData) {
-    let expiryDate = "";
-    if (suitData.UpgradesExpiry) {
-        if (suitData.UpgradesExpiry.$date) {
-            expiryDate = new Date(parseInt(suitData.UpgradesExpiry.$date.$numberLong));
-        } else if (typeof suitData.UpgradesExpiry === "number") {
-            expiryDate = new Date(suitData.UpgradesExpiry);
-        } else if (suitData.UpgradesExpiry instanceof Date) {
-            expiryDate = suitData.UpgradesExpiry;
-        }
-        if (expiryDate && !isNaN(expiryDate.getTime())) {
-            const year = expiryDate.getFullYear();
-            const month = String(expiryDate.getMonth() + 1).padStart(2, "0");
-            const day = String(expiryDate.getDate()).padStart(2, "0");
-            const hours = String(expiryDate.getHours()).padStart(2, "0");
-            const minutes = String(expiryDate.getMinutes()).padStart(2, "0");
-            expiryDate = `${year}-${month}-${day}T${hours}:${minutes}`;
-        } else {
-            expiryDate = "";
-        }
-    }
-    return {
-        oid: suitData.ItemId.$oid,
-        OffensiveUpgrade: suitData.OffensiveUpgrade || "",
-        DefensiveUpgrade: suitData.DefensiveUpgrade || "",
-        UpgradesExpiry: expiryDate
-    };
-}
 
-function submitSuitInvigorationUpgrade(event) {
+function submitInvigoration(event) {
     event.preventDefault();
-    const oid = new URLSearchParams(window.location.search).get("itemId");
-    const offensiveUpgrade = document.getElementById("dv-invigoration-offensive").value;
-    const defensiveUpgrade = document.getElementById("dv-invigoration-defensive").value;
-    const expiry = document.getElementById("dv-invigoration-expiry").value;
+    const OffensiveUpgrade = document.getElementById("invigoration-offensive").value;
+    const DefensiveUpgrade = document.getElementById("invigoration-defensive").value;
+    const expiry = document.getElementById("invigoration-expiry").value;
 
-    if (!offensiveUpgrade || !defensiveUpgrade) {
-        alert(loc("code_requiredInvigorationUpgrade"));
+    if (!OffensiveUpgrade || !DefensiveUpgrade) {
+        toast(loc("code_requiredInvigorationUpgrade"), "warning");
         return;
     }
 
-    const data = {
-        OffensiveUpgrade: offensiveUpgrade,
-        DefensiveUpgrade: defensiveUpgrade
-    };
-
-    if (expiry) {
-        data.UpgradesExpiry = new Date(expiry).getTime();
-    }
-
-    editSuitInvigorationUpgrade(oid, data);
+    setInvigoration({
+        OffensiveUpgrade,
+        DefensiveUpgrade,
+        UpgradesExpiry: expiry ? new Date(expiry).getTime() : Date.now() + 7 * 24 * 60 * 60 * 1000
+    });
 }
 
-function clearSuitInvigorationUpgrades() {
-    editSuitInvigorationUpgrade(new URLSearchParams(window.location.search).get("itemId"), null);
-}
-
-async function editSuitInvigorationUpgrade(oid, data) {
-    /* data?: {
-            DefensiveUpgrade: string;
-            OffensiveUpgrade: string;
-            UpgradesExpiry?: number;
-    }*/
+function setInvigoration(data) {
+    const oid = new URLSearchParams(window.location.search).get("itemId");
     $.post({
-        url: "/custom/editSuitInvigorationUpgrade?" + window.authz,
+        url: "/custom/setInvigoration?" + window.authz,
         contentType: "application/json",
-        data: JSON.stringify({ oid, data })
+        data: JSON.stringify({ oid, ...data })
     }).done(function () {
         updateInventory();
     });
diff --git a/static/webui/translations/de.js b/static/webui/translations/de.js
index 588f4df9..337aa158 100644
--- a/static/webui/translations/de.js
+++ b/static/webui/translations/de.js
@@ -76,7 +76,7 @@ dict = {
     code_replays: `[UNTRANSLATED] Replays`,
     code_stalker: `Stalker`,
     code_succChange: `Erfolgreich geändert.`,
-    code_requiredInvigorationUpgrade: `Du musst sowohl ein offensives & defensives Upgrade auswählen.`,
+    code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
     login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`,
     login_emailLabel: `E-Mail-Adresse`,
     login_passwordLabel: `Passwort`,
@@ -147,7 +147,7 @@ dict = {
     detailedView_valenceBonusLabel: `Valenz-Bonus`,
     detailedView_valenceBonusDescription: `Du kannst den Valenz-Bonus deiner Waffe festlegen oder entfernen.`,
     detailedView_modularPartsLabel: `Modulare Teile ändern`,
-    detailedView_suitInvigorationLabel: `Warframe-Kräftigung`,
+    detailedView_invigorationLabel: `Kräftigung`,
     detailedView_loadoutLabel: `Loadouts`,
 
     invigorations_offensive_AbilityStrength: `+200% Fähigkeitsstärke`,
@@ -172,9 +172,9 @@ dict = {
     invigorations_utility_Jumps: `+5 Sprung-Zurücksetzungen`,
     invigorations_utility_EnergyRegen: `+2 Energieregeneration pro Sekunde`,
 
-    invigorations_offensiveLabel: `Offensives Upgrade`,
-    invigorations_defensiveLabel: `Defensives Upgrade`,
-    invigorations_expiryLabel: `Upgrades Ablaufdatum (optional)`,
+    detailedView_invigorationOffensiveLabel: `Offensives Upgrade`,
+    detailedView_invigorationUtilityLabel: `[UNTRANSLATED] Utility Upgrade`,
+    detailedView_invigorationExpiryLabel: `[UNTRANSLATED] Invigoration Expiry (optional)`,
 
     abilityOverride_label: `Fähigkeitsüberschreibung`,
     abilityOverride_onSlot: `auf Slot`,
diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js
index 28af5370..4ca00cec 100644
--- a/static/webui/translations/en.js
+++ b/static/webui/translations/en.js
@@ -75,7 +75,7 @@ dict = {
     code_replays: `Replays`,
     code_stalker: `Stalker`,
     code_succChange: `Successfully changed.`,
-    code_requiredInvigorationUpgrade: `You must select both an offensive & defensive upgrade.`,
+    code_requiredInvigorationUpgrade: `You must select both an offensive & utility upgrade.`,
     login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`,
     login_emailLabel: `Email address`,
     login_passwordLabel: `Password`,
@@ -146,7 +146,7 @@ dict = {
     detailedView_valenceBonusLabel: `Valence Bonus`,
     detailedView_valenceBonusDescription: `You can set or remove the Valence Bonus from your weapon.`,
     detailedView_modularPartsLabel: `Change Modular Parts`,
-    detailedView_suitInvigorationLabel: `Warframe Invigoration`,
+    detailedView_invigorationLabel: `Invigoration`,
     detailedView_loadoutLabel: `Loadouts`,
 
     invigorations_offensive_AbilityStrength: `+200% Ability Strength`,
@@ -171,9 +171,9 @@ dict = {
     invigorations_utility_Jumps: `+5 Jump Resets`,
     invigorations_utility_EnergyRegen: `+2 Energy Regen/s`,
 
-    invigorations_offensiveLabel: `Offensive Upgrade`,
-    invigorations_defensiveLabel: `Defensive Upgrade`,
-    invigorations_expiryLabel: `Upgrades Expiry (optional)`,
+    detailedView_invigorationOffensiveLabel: `Offensive Upgrade`,
+    detailedView_invigorationUtilityLabel: `Utility Upgrade`,
+    detailedView_invigorationExpiryLabel: `Invigoration Expiry (optional)`,
 
     abilityOverride_label: `Ability Override`,
     abilityOverride_onSlot: `on slot`,
diff --git a/static/webui/translations/es.js b/static/webui/translations/es.js
index 07aafcc8..5c9b2369 100644
--- a/static/webui/translations/es.js
+++ b/static/webui/translations/es.js
@@ -76,7 +76,7 @@ dict = {
     code_replays: `Repeticiones`,
     code_stalker: `Stalker`,
     code_succChange: `Cambiado correctamente`,
-    code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una defensiva.`,
+    code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
     login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`,
     login_emailLabel: `Dirección de correo electrónico`,
     login_passwordLabel: `Contraseña`,
@@ -147,7 +147,7 @@ dict = {
     detailedView_valenceBonusLabel: `Bonus de Valéncia`,
     detailedView_valenceBonusDescription: `Puedes establecer o quitar el bonus de valencia de tu arma.`,
     detailedView_modularPartsLabel: `Cambiar partes modulares`,
-    detailedView_suitInvigorationLabel: `Vigorización de Warframe`,
+    detailedView_invigorationLabel: `Fortalecimiento`,
     detailedView_loadoutLabel: `Equipamientos`,
 
     invigorations_offensive_AbilityStrength: `+200% Fuerza de Habilidad`,
@@ -172,9 +172,9 @@ dict = {
     invigorations_utility_Jumps: `+5 Restablecimientos de Salto`,
     invigorations_utility_EnergyRegen: `+2 Regeneración de Energía/s`,
 
-    invigorations_offensiveLabel: `Mejora Ofensiva`,
-    invigorations_defensiveLabel: `Mejora Defensiva`,
-    invigorations_expiryLabel: `Caducidad de Mejoras (opcional)`,
+    detailedView_invigorationOffensiveLabel: `Mejora Ofensiva`,
+    detailedView_invigorationUtilityLabel: `[UNTRANSLATED] Utility Upgrade`,
+    detailedView_invigorationExpiryLabel: `[UNTRANSLATED] Invigoration Expiry (optional)`,
 
     abilityOverride_label: `Intercambio de Habilidad`,
     abilityOverride_onSlot: `en el espacio`,
diff --git a/static/webui/translations/fr.js b/static/webui/translations/fr.js
index f08248b9..996ae13c 100644
--- a/static/webui/translations/fr.js
+++ b/static/webui/translations/fr.js
@@ -76,7 +76,7 @@ dict = {
     code_replays: `[UNTRANSLATED] Replays`,
     code_stalker: `Stalker`,
     code_succChange: `Changement effectué.`,
-    code_requiredInvigorationUpgrade: `Augmentation offensive et défensive requises.`,
+    code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
     login_description: `Connexion avec les informations de connexion OpenWF.`,
     login_emailLabel: `Email`,
     login_passwordLabel: `Mot de passe`,
@@ -147,7 +147,7 @@ dict = {
     detailedView_valenceBonusLabel: `Bonus de Valence`,
     detailedView_valenceBonusDescription: `Définir le Bonus Valence de l'arme.`,
     detailedView_modularPartsLabel: `Changer l'équipement modulaire`,
-    detailedView_suitInvigorationLabel: `Invigoration de Warframe`,
+    detailedView_invigorationLabel: `Dynamisation`,
     detailedView_loadoutLabel: `Équipements`,
 
     invigorations_offensive_AbilityStrength: `+200% de puissance de pouvoir`,
@@ -172,9 +172,9 @@ dict = {
     invigorations_utility_Jumps: `+5 réinitialisations de saut`,
     invigorations_utility_EnergyRegen: `+2 d'énergie régénérés/s`,
 
-    invigorations_offensiveLabel: `Amélioration offensive`,
-    invigorations_defensiveLabel: `Amélioration défensive`,
-    invigorations_expiryLabel: `Expiration de l'invigoration (optionnel)`,
+    detailedView_invigorationOffensiveLabel: `Amélioration offensive`,
+    detailedView_invigorationUtilityLabel: `[UNTRANSLATED] Utility Upgrade`,
+    detailedView_invigorationExpiryLabel: `[UNTRANSLATED] Invigoration Expiry (optional)`,
 
     abilityOverride_label: `Remplacement de pouvoir`,
     abilityOverride_onSlot: `Sur l'emplacement`,
diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js
index 5e648379..b0e30e60 100644
--- a/static/webui/translations/ru.js
+++ b/static/webui/translations/ru.js
@@ -147,7 +147,7 @@ dict = {
     detailedView_valenceBonusLabel: `Бонус Валентности`,
     detailedView_valenceBonusDescription: `Вы можете установить или убрать бонус Валентности с вашего оружия.`,
     detailedView_modularPartsLabel: `Изменить модульные части`,
-    detailedView_suitInvigorationLabel: `Воодушевление Варфрейма`,
+    detailedView_invigorationLabel: `Воодушевление`,
     detailedView_loadoutLabel: `Конфигурации`,
 
     invigorations_offensive_AbilityStrength: `+200% к силе способностей.`,
@@ -172,9 +172,9 @@ dict = {
     invigorations_utility_Jumps: `+5 сбросов прыжка.`,
     invigorations_utility_EnergyRegen: `+2 к регенерации энергии в секунду.`,
 
-    invigorations_offensiveLabel: `Атакующее улучшение`,
-    invigorations_defensiveLabel: `Вспомогательное улучшение`,
-    invigorations_expiryLabel: `Срок действия Воодушевления (необязательно)`,
+    detailedView_invigorationOffensiveLabel: `Атакующее улучшение`,
+    detailedView_invigorationUtilityLabel: `Вспомогательное улучшение`,
+    detailedView_invigorationExpiryLabel: `Срок действия Воодушевления (необязательно)`,
 
     abilityOverride_label: `Переопределение способности`,
     abilityOverride_onSlot: `в ячейке`,
diff --git a/static/webui/translations/uk.js b/static/webui/translations/uk.js
index 0c6c7665..eaf9b06f 100644
--- a/static/webui/translations/uk.js
+++ b/static/webui/translations/uk.js
@@ -147,7 +147,7 @@ dict = {
     detailedView_valenceBonusLabel: `Ознака Валентності`,
     detailedView_valenceBonusDescription: `Ви можете встановити або прибрати ознаку Валентності з вашої зброї.`,
     detailedView_modularPartsLabel: `Змінити модульні частини`,
-    detailedView_suitInvigorationLabel: `Зміцнення Ворфрейма`,
+    detailedView_invigorationLabel: `Зміцнення`,
     detailedView_loadoutLabel: `Конфігурації`,
 
     invigorations_offensive_AbilityStrength: `+200% до потужності здібностей.`,
@@ -172,9 +172,9 @@ dict = {
     invigorations_utility_Jumps: `+5 Оновлень стрибків.`,
     invigorations_utility_EnergyRegen: `+2 до відновлення енергії на секунду.`,
 
-    invigorations_offensiveLabel: `Атакуюче вдосконалення`,
-    invigorations_defensiveLabel: `Допоміжне вдосконалення`,
-    invigorations_expiryLabel: `Термін дії Зміцнення (необов'язково)`,
+    detailedView_invigorationOffensiveLabel: `Атакуюче вдосконалення`,
+    detailedView_invigorationUtilityLabel: `Допоміжне вдосконалення`,
+    detailedView_invigorationExpiryLabel: `Термін дії Зміцнення (необов'язково)`,
 
     abilityOverride_label: `Перевизначення здібностей`,
     abilityOverride_onSlot: `у комірці`,
diff --git a/static/webui/translations/zh.js b/static/webui/translations/zh.js
index 29cb3e68..4f5c5808 100644
--- a/static/webui/translations/zh.js
+++ b/static/webui/translations/zh.js
@@ -76,7 +76,7 @@ dict = {
     code_replays: `[UNTRANSLATED] Replays`,
     code_stalker: `追猎者`,
     code_succChange: `更改成功`,
-    code_requiredInvigorationUpgrade: `您必须同时选择一个进攻型和一个功能型活化属性.`,
+    code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
     login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)`,
     login_emailLabel: `电子邮箱`,
     login_passwordLabel: `密码`,
@@ -147,7 +147,7 @@ dict = {
     detailedView_valenceBonusLabel: `效价加成`,
     detailedView_valenceBonusDescription: `您可以设置或移除武器上的效价加成.`,
     detailedView_modularPartsLabel: `更换部件`,
-    detailedView_suitInvigorationLabel: `编辑战甲活化属性`,
+    detailedView_invigorationLabel: `活化`,
     detailedView_loadoutLabel: `配置`,
 
     invigorations_offensive_AbilityStrength: `+200%技能强度`,
@@ -172,9 +172,9 @@ dict = {
     invigorations_utility_Jumps: `+5跳跃次数`,
     invigorations_utility_EnergyRegen: `+2/秒能量恢复`,
 
-    invigorations_offensiveLabel: `进攻型属性`,
-    invigorations_defensiveLabel: `功能型属性`,
-    invigorations_expiryLabel: `活化时效(可选)`,
+    detailedView_invigorationOffensiveLabel: `进攻型属性`,
+    detailedView_invigorationUtilityLabel: `[UNTRANSLATED] Utility Upgrade`,
+    detailedView_invigorationExpiryLabel: `活化时效(可选)`,
 
     abilityOverride_label: `技能替换`,
     abilityOverride_onSlot: `槽位`,