feat(webui): more equipment #826

Merged
AMelonInsideLemon merged 3 commits from webui-more-equipment into main 2025-01-19 06:03:34 -08:00
6 changed files with 360 additions and 217 deletions

View File

@ -51,6 +51,41 @@ export const sellController: RequestHandler = async (req, res) => {
inventory.Melee.pull({ _id: sellItem.String }); inventory.Melee.pull({ _id: sellItem.String });
}); });
} }
if (payload.Items.SpaceSuits) {
payload.Items.SpaceSuits.forEach(sellItem => {
inventory.SpaceSuits.pull({ _id: sellItem.String });
});
}
if (payload.Items.SpaceGuns) {
payload.Items.SpaceGuns.forEach(sellItem => {
inventory.SpaceGuns.pull({ _id: sellItem.String });
});
}
if (payload.Items.SpaceMelee) {
payload.Items.SpaceMelee.forEach(sellItem => {
inventory.SpaceMelee.pull({ _id: sellItem.String });
});
}
if (payload.Items.Sentinels) {
payload.Items.Sentinels.forEach(sellItem => {
inventory.Sentinels.pull({ _id: sellItem.String });
});
}
if (payload.Items.SentinelWeapons) {
payload.Items.SentinelWeapons.forEach(sellItem => {
inventory.SentinelWeapons.pull({ _id: sellItem.String });
});
}
if (payload.Items.OperatorAmps) {
payload.Items.OperatorAmps.forEach(sellItem => {
inventory.OperatorAmps.pull({ _id: sellItem.String });
});
}
if (payload.Items.Hoverboards) {
payload.Items.Hoverboards.forEach(sellItem => {
inventory.Hoverboards.pull({ _id: sellItem.String });
});
}
coderabbitai[bot] commented 2025-01-19 05:39:28 -08:00 (Migrated from github.com)
Review

🛠️ Refactor suggestion

Reduce code duplication in item removal logic.

The item removal logic is repeated for each item type. Consider extracting this into a reusable function.

+const removeItems = (
+    inventory: any,
+    items: ISellItem[] | undefined,
+    category: keyof typeof inventory
+) => {
+    if (items) {
+        items.forEach(sellItem => {
+            inventory[category].pull({ _id: sellItem.String });
+        });
+    }
+};

-if (payload.Items.SpaceSuits) {
-    payload.Items.SpaceSuits.forEach(sellItem => {
-        inventory.SpaceSuits.pull({ _id: sellItem.String });
-    });
-}
+removeItems(inventory, payload.Items.SpaceSuits, 'SpaceSuits');
// Apply similar changes to other item types
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    const removeItems = (
        inventory: any,
        items: ISellItem[] | undefined,
        category: keyof typeof inventory
    ) => {
        if (items) {
            items.forEach(sellItem => {
                inventory[category].pull({ _id: sellItem.String });
            });
        }
    };

    removeItems(inventory, payload.Items.SpaceSuits, 'SpaceSuits');
    removeItems(inventory, payload.Items.SpaceGuns, 'SpaceGuns');
    removeItems(inventory, payload.Items.SpaceMelee, 'SpaceMelee');
    removeItems(inventory, payload.Items.Sentinels, 'Sentinels');
    removeItems(inventory, payload.Items.SentinelWeapons, 'SentinelWeapons');
    removeItems(inventory, payload.Items.OperatorAmps, 'OperatorAmps');
    removeItems(inventory, payload.Items.Hoverboards, 'Hoverboards');
_:hammer_and_wrench: Refactor suggestion_ **Reduce code duplication in item removal logic.** The item removal logic is repeated for each item type. Consider extracting this into a reusable function. ```diff +const removeItems = ( + inventory: any, + items: ISellItem[] | undefined, + category: keyof typeof inventory +) => { + if (items) { + items.forEach(sellItem => { + inventory[category].pull({ _id: sellItem.String }); + }); + } +}; -if (payload.Items.SpaceSuits) { - payload.Items.SpaceSuits.forEach(sellItem => { - inventory.SpaceSuits.pull({ _id: sellItem.String }); - }); -} +removeItems(inventory, payload.Items.SpaceSuits, 'SpaceSuits'); // Apply similar changes to other item types ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. `````suggestion const removeItems = ( inventory: any, items: ISellItem[] | undefined, category: keyof typeof inventory ) => { if (items) { items.forEach(sellItem => { inventory[category].pull({ _id: sellItem.String }); }); } }; removeItems(inventory, payload.Items.SpaceSuits, 'SpaceSuits'); removeItems(inventory, payload.Items.SpaceGuns, 'SpaceGuns'); removeItems(inventory, payload.Items.SpaceMelee, 'SpaceMelee'); removeItems(inventory, payload.Items.Sentinels, 'Sentinels'); removeItems(inventory, payload.Items.SentinelWeapons, 'SentinelWeapons'); removeItems(inventory, payload.Items.OperatorAmps, 'OperatorAmps'); removeItems(inventory, payload.Items.Hoverboards, 'Hoverboards'); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
if (payload.Items.Consumables) { if (payload.Items.Consumables) {
const consumablesChanges = []; const consumablesChanges = [];
for (const sellItem of payload.Items.Consumables) { for (const sellItem of payload.Items.Consumables) {
@ -110,6 +145,13 @@ interface ISellRequest {
Recipes?: ISellItem[]; Recipes?: ISellItem[];
Upgrades?: ISellItem[]; Upgrades?: ISellItem[];
MiscItems?: ISellItem[]; MiscItems?: ISellItem[];
SpaceSuits?: ISellItem[];
SpaceGuns?: ISellItem[];
SpaceMelee?: ISellItem[];
Sentinels?: ISellItem[];
SentinelWeapons?: ISellItem[];
OperatorAmps?: ISellItem[];
Hoverboards?: ISellItem[];
}; };
SellPrice: number; SellPrice: number;
SellCurrency: SellCurrency:

View File

@ -1,8 +1,8 @@
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getWeaponType } from "@/src/services/itemDataService"; import { addEquipment, addPowerSuit, getInventory, updateSlots } from "@/src/services/inventoryService";
import { addPowerSuit, addEquipment, getInventory, updateSlots } from "@/src/services/inventoryService"; import { SlotNames } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
export const addItemsController: RequestHandler = async (req, res) => { export const addItemsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -10,14 +10,14 @@ export const addItemsController: RequestHandler = async (req, res) => {
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
for (const request of requests) { for (const request of requests) {
switch (request.type) { switch (request.type) {
case ItemType.Powersuit: case ItemType.Suits:
updateSlots(inventory, InventorySlot.SUITS, 0, 1); updateSlots(inventory, productCategoryToSlotName[request.type], 0, 1);
addPowerSuit(inventory, request.internalName); addPowerSuit(inventory, request.internalName);
break; break;
case ItemType.Weapon: default:
updateSlots(inventory, InventorySlot.WEAPONS, 0, 1); updateSlots(inventory, productCategoryToSlotName[request.type], 0, 1);
addEquipment(inventory, getWeaponType(request.internalName), request.internalName); addEquipment(inventory, request.type, request.internalName);
break; break;
} }
} }
@ -25,9 +25,28 @@ export const addItemsController: RequestHandler = async (req, res) => {
res.end(); res.end();
}; };
const productCategoryToSlotName: Record<ItemType, SlotNames> = {
coderabbitai[bot] commented 2025-01-19 05:39:27 -08:00 (Migrated from github.com)
Review

⚠️ Potential issue

Add error handling for unknown item types.

The default case assumes the item type exists in productCategoryToSlotName. Add validation to prevent runtime errors.

 default:
+    if (!(request.type in productCategoryToSlotName)) {
+        throw new Error(`Unknown item type: ${request.type}`);
+    }
     updateSlots(inventory, productCategoryToSlotName[request.type], 0, 1);
     addEquipment(inventory, request.type, request.internalName);
     break;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            default:
                if (!(request.type in productCategoryToSlotName)) {
                    throw new Error(`Unknown item type: ${request.type}`);
                }
                updateSlots(inventory, productCategoryToSlotName[request.type], 0, 1);
                addEquipment(inventory, request.type, request.internalName);
                break;
_:warning: Potential issue_ **Add error handling for unknown item types.** The default case assumes the item type exists in productCategoryToSlotName. Add validation to prevent runtime errors. ```diff default: + if (!(request.type in productCategoryToSlotName)) { + throw new Error(`Unknown item type: ${request.type}`); + } updateSlots(inventory, productCategoryToSlotName[request.type], 0, 1); addEquipment(inventory, request.type, request.internalName); break; ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. `````suggestion default: if (!(request.type in productCategoryToSlotName)) { throw new Error(`Unknown item type: ${request.type}`); } updateSlots(inventory, productCategoryToSlotName[request.type], 0, 1); addEquipment(inventory, request.type, request.internalName); break; ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
Suits: InventorySlot.SUITS,
Pistols: InventorySlot.WEAPONS,
Melee: InventorySlot.WEAPONS,
LongGuns: InventorySlot.WEAPONS,
SpaceSuits: InventorySlot.SPACESUITS,
SpaceGuns: InventorySlot.SPACESUITS,
SpaceMelee: InventorySlot.SPACESUITS,
Sentinels: InventorySlot.SENTINELS,
SentinelWeapons: InventorySlot.SENTINELS
};
enum ItemType { enum ItemType {
Powersuit = "Powersuit", Suits = "Suits",
Weapon = "Weapon" SpaceSuits = "SpaceSuits",
LongGuns = "LongGuns",
Pistols = "Pistols",
Melee = "Melee",
SpaceGuns = "SpaceGuns",
SpaceMelee = "SpaceMelee",
SentinelWeapons = "SentinelWeapons",
Sentinels = "Sentinels"
} }
interface IAddItemRequest { interface IAddItemRequest {

View File

@ -5,6 +5,7 @@ import {
ExportGear, ExportGear,
ExportRecipes, ExportRecipes,
ExportResources, ExportResources,
ExportSentinels,
ExportUpgrades, ExportUpgrades,
ExportWarframes, ExportWarframes,
ExportWeapons ExportWeapons
@ -15,21 +16,66 @@ interface ListedItem {
uniqueName: string; uniqueName: string;
name: string; name: string;
fusionLimit?: number; fusionLimit?: number;
exalted?: string[];
} }
const getItemListsController: RequestHandler = (req, response) => { const getItemListsController: RequestHandler = (req, response) => {
const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en"); const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
const res: Record<string, ListedItem[]> = {}; const res: Record<string, ListedItem[]> = {};
res.LongGuns = []; res.LongGuns = [];
res.Pistols = [];
res.Melee = []; res.Melee = [];
res.ModularParts = [];
res.Pistols = [];
res.Sentinels = [];
res.SentinelWeapons = [];
res.SpaceGuns = [];
res.SpaceMelee = [];
res.SpaceSuits = [];
res.Suits = [];
res.miscitems = []; res.miscitems = [];
for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
if (item.productCategory == "Suits" || item.productCategory == "SpaceSuits") {
res[item.productCategory].push({
uniqueName,
name: getString(item.name, lang),
exalted: item.exalted
});
}
}
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
if (item.productCategory == "Sentinels") {
res[item.productCategory].push({
uniqueName,
name: getString(item.name, lang)
});
}
}
for (const [uniqueName, item] of Object.entries(ExportWeapons)) { for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
if (item.totalDamage !== 0) { if (
uniqueName.split("/")[4] == "OperatorAmplifiers" ||
uniqueName.split("/")[5] == "SUModularSecondarySet1" ||
uniqueName.split("/")[5] == "SUModularPrimarySet1" ||
uniqueName.split("/")[5] == "InfKitGun" ||
uniqueName.split("/")[5] == "HoverboardParts"
) {
res.ModularParts.push({
uniqueName,
name: getString(item.name, lang)
});
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
res.miscitems.push({
uniqueName: "MiscItems:" + uniqueName,
name: getString(item.name, lang)
});
}
} else if (item.totalDamage !== 0) {
if ( if (
item.productCategory == "LongGuns" || item.productCategory == "LongGuns" ||
item.productCategory == "Pistols" || item.productCategory == "Pistols" ||
item.productCategory == "Melee" item.productCategory == "Melee" ||
item.productCategory == "SpaceGuns" ||
item.productCategory == "SpaceMelee" ||
item.productCategory == "SentinelWeapons"
) { ) {
res[item.productCategory].push({ res[item.productCategory].push({
uniqueName, uniqueName,
coderabbitai[bot] commented 2025-01-19 05:42:54 -08:00 (Migrated from github.com)
Review

🛠️ Refactor suggestion

Refactor path-based checks for better maintainability.

The current implementation uses hard-coded path components which is fragile. Consider using an enum or constant for these paths:

// Add at the top of the file
const MODULAR_CATEGORIES = {
    OPERATOR_AMPLIFIERS: 'OperatorAmplifiers',
    MODULAR_SETS: {
        SECONDARY: 'SUModularSecondarySet1',
        PRIMARY: 'SUModularPrimarySet1'
    },
    KIT_GUN: 'InfKitGun',
    HOVERBOARD: 'HoverboardParts',
    SENTINEL_AMPLIFIER: 'SentTrainingAmplifier'
} as const;

// Then use in the condition
if (
    uniqueName.split("/")[4] === MODULAR_CATEGORIES.OPERATOR_AMPLIFIERS ||
    uniqueName.split("/")[5] === MODULAR_CATEGORIES.MODULAR_SETS.SECONDARY ||
    // ... etc
)
_:hammer_and_wrench: Refactor suggestion_ **Refactor path-based checks for better maintainability.** The current implementation uses hard-coded path components which is fragile. Consider using an enum or constant for these paths: ```typescript // Add at the top of the file const MODULAR_CATEGORIES = { OPERATOR_AMPLIFIERS: 'OperatorAmplifiers', MODULAR_SETS: { SECONDARY: 'SUModularSecondarySet1', PRIMARY: 'SUModularPrimarySet1' }, KIT_GUN: 'InfKitGun', HOVERBOARD: 'HoverboardParts', SENTINEL_AMPLIFIER: 'SentTrainingAmplifier' } as const; // Then use in the condition if ( uniqueName.split("/")[4] === MODULAR_CATEGORIES.OPERATOR_AMPLIFIERS || uniqueName.split("/")[5] === MODULAR_CATEGORIES.MODULAR_SETS.SECONDARY || // ... etc ) ``` <!-- This is an auto-generated comment by CodeRabbit -->
@ -102,15 +148,6 @@ const getItemListsController: RequestHandler = (req, response) => {
} }
response.json({ response.json({
warframes: Object.entries(ExportWarframes)
.filter(([_uniqueName, warframe]) => warframe.productCategory == "Suits")
.map(([uniqueName, warframe]) => {
return {
uniqueName,
name: getString(warframe.name, lang),
exalted: warframe.exalted
};
}),
badItems, badItems,
archonCrystalUpgrades, archonCrystalUpgrades,
...res ...res

View File

@ -365,7 +365,7 @@ export const addSentinelWeapon = (
typeName: string, typeName: string,
inventoryChanges: IInventoryChanges inventoryChanges: IInventoryChanges
): void => { ): void => {
const index = inventory.SentinelWeapons.push({ ItemType: typeName }) - 1; const index = inventory.SentinelWeapons.push({ ItemType: typeName, XP: 0 }) - 1;
inventoryChanges.SentinelWeapons ??= []; inventoryChanges.SentinelWeapons ??= [];
(inventoryChanges.SentinelWeapons as IEquipmentClient[]).push( (inventoryChanges.SentinelWeapons as IEquipmentClient[]).push(
inventory.SentinelWeapons[index].toJSON<IEquipmentClient>() inventory.SentinelWeapons[index].toJSON<IEquipmentClient>()

View File

@ -99,12 +99,12 @@
<div class="card mb-3" style="height: 400px;"> <div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Warframes</h5> <h5 class="card-header">Warframes</h5>
<div class="card-body overflow-auto"> <div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireWarframe();return false;"> <form class="input-group mb-3" onsubmit="doAcquireEquipment('Suits');return false;">
<input class="form-control" id="warframe-to-acquire" list="datalist-warframes" /> <input class="form-control" id="acquire-type-Suits" list="datalist-Suits" />
<button class="btn btn-primary" type="submit">Add</button> <button class="btn btn-primary" type="submit">Add</button>
</form> </form>
<table class="table table-hover w-100"> <table class="table table-hover w-100">
<tbody id="warframe-list"></tbody> <tbody id="Suits-list"></tbody>
</table> </table>
</div> </div>
</div> </div>
@ -113,7 +113,7 @@
<div class="card mb-3" style="height: 400px;"> <div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Primary Weapons</h5> <h5 class="card-header">Primary Weapons</h5>
<div class="card-body overflow-auto"> <div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireWeapon('LongGuns');return false;"> <form class="input-group mb-3" onsubmit="doAcquireEquipment('LongGuns');return false;">
<input class="form-control" id="acquire-type-LongGuns" list="datalist-LongGuns" /> <input class="form-control" id="acquire-type-LongGuns" list="datalist-LongGuns" />
<button class="btn btn-primary" type="submit">Add</button> <button class="btn btn-primary" type="submit">Add</button>
</form> </form>
@ -129,7 +129,7 @@
<div class="card mb-3" style="height: 400px;"> <div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Secondary Weapons</h5> <h5 class="card-header">Secondary Weapons</h5>
<div class="card-body overflow-auto"> <div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireWeapon('Pistols');return false;"> <form class="input-group mb-3" onsubmit="doAcquireEquipment('Pistols');return false;">
<input class="form-control" id="acquire-type-Pistols" list="datalist-Pistols" /> <input class="form-control" id="acquire-type-Pistols" list="datalist-Pistols" />
<button class="btn btn-primary" type="submit">Add</button> <button class="btn btn-primary" type="submit">Add</button>
</form> </form>
@ -143,7 +143,7 @@
<div class="card mb-3" style="height: 400px;"> <div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Melee Weapons</h5> <h5 class="card-header">Melee Weapons</h5>
<div class="card-body overflow-auto"> <div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireWeapon('Melee');return false;"> <form class="input-group mb-3" onsubmit="doAcquireEquipment('Melee');return false;">
<input class="form-control" id="acquire-type-Melee" list="datalist-Melee" /> <input class="form-control" id="acquire-type-Melee" list="datalist-Melee" />
<button class="btn btn-primary" type="submit">Add</button> <button class="btn btn-primary" type="submit">Add</button>
</form> </form>
@ -154,13 +154,123 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row g-3">
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Archwing</h5>
<div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceSuits');return false;">
<input class="form-control" id="acquire-type-SpaceSuits" list="datalist-SpaceSuits" />
<button class="btn btn-primary" type="submit">Add</button>
</form>
<table class="table table-hover w-100">
<tbody id="SpaceSuits-list"></tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Archwing Primary Weapons</h5>
<div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceGuns');return false;">
<input class="form-control" id="acquire-type-SpaceGuns" list="datalist-SpaceGuns" />
<button class="btn btn-primary" type="submit">Add</button>
</form>
<table class="table table-hover w-100">
<tbody id="SpaceGuns-list"></tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Archwing Melee Weapons</h5>
<div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceMelee');return false;">
<input class="form-control" id="acquire-type-SpaceMelee" list="datalist-SpaceMelee" />
<button class="btn btn-primary" type="submit">Add</button>
</form>
<table class="table table-hover w-100">
<tbody id="SpaceMelee-list"></tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Sentinel Weapons</h5>
<div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireEquipment('SentinelWeapons');return false;">
<input class="form-control" id="acquire-type-SentinelWeapons" list="datalist-SentinelWeapons" />
<button class="btn btn-primary" type="submit">Add</button>
</form>
<table class="table table-hover w-100">
<tbody id="SentinelWeapons-list"></tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Sentinels</h5>
<div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireEquipment('Sentinels');return false;">
<input class="form-control" id="acquire-type-Sentinels" list="datalist-Sentinels" />
<button class="btn btn-primary" type="submit">Add</button>
</form>
<table class="table table-hover w-100">
<tbody id="Sentinels-list"></tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header">Amps</h5>
<div class="card-body overflow-auto">
<table class="table table-hover w-100">
<tbody id="OperatorAmps-list"></tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header">K-Drives</h5>
<div class="card-body overflow-auto">
<table class="table table-hover w-100">
<tbody id="Hoverboards-list"></tbody>
</table>
</div>
</div>
</div>
</div>
<div class="card mb-3"> <div class="card mb-3">
<h5 class="card-header">Bulk Actions</h5> <h5 class="card-header">Bulk Actions</h5>
<div class="card-body d-flex flex-wrap gap-2"> <div class="card-body">
<button class="btn btn-primary" onclick="addMissingWarframes();">Add Missing Warframes</button> <div class="mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="addMissingWeapons();">Add Missing Weapons</button> <button class="btn btn-primary" onclick="addMissingEquipment(['Suits']);">Add Missing Warframes</button>
<button class="btn btn-success" onclick="maxRankAllWarframes()">Max Rank All Warframes</button> <button class="btn btn-primary" onclick="addMissingEquipment(['Melee', 'LongGuns', 'Pistols']);">Add Missing Weapons</button>
<button class="btn btn-success" onclick="maxRankAllWeapons()">Max Rank All Weapons</button> <button class="btn btn-primary" onclick="addMissingEquipment(['SpaceSuits']);">Add Missing Archwings</button>
<button class="btn btn-primary" onclick="addMissingEquipment(['SpaceGuns', 'SpaceMelee']);">Add Missing Archwing Weapons</button>
<button class="btn btn-primary" onclick="addMissingEquipment(['Sentinels']);">Add Missing Sentinels</button>
<button class="btn btn-primary" onclick="addMissingEquipment(['SentinelWeapons']);">Add Missing Sentinel Weapons</button>
</div>
<div class="mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-success" onclick="maxRankAllEquipment(['Suits']);">Max Rank All Warframes</button>
<button class="btn btn-success" onclick="maxRankAllEquipment(['Melee', 'LongGuns', 'Pistols']);">Max Rank All Weapons</button>
<button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceSuits']);">Max Rank All Archwings</button>
<button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceGuns', 'SpaceMelee']);">Max Rank All Archwing Weapons</button>
<button class="btn btn-success" onclick="maxRankAllEquipment(['Sentinels']);">Max Rank All Sentinels</button>
<button class="btn btn-success" onclick="maxRankAllEquipment(['SentinelWeapons']);">Max Rank All Sentinel Weapons</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -384,10 +494,16 @@
</div> </div>
</div> </div>
</div> </div>
<datalist id="datalist-warframes"></datalist> <datalist id="datalist-Suits"></datalist>
<datalist id="datalist-SpaceSuits"></datalist>
<datalist id="datalist-LongGuns"></datalist> <datalist id="datalist-LongGuns"></datalist>
<datalist id="datalist-Pistols"></datalist> <datalist id="datalist-Pistols"></datalist>
<datalist id="datalist-Melee"></datalist> <datalist id="datalist-Melee"></datalist>
<datalist id="datalist-SpaceGuns"></datalist>
<datalist id="datalist-SpaceMelee"></datalist>
<datalist id="datalist-SentinelWeapons"></datalist>
<datalist id="datalist-Sentinels"></datalist>
<datalist id="datalist-ModularParts"></datalist>
<datalist id="datalist-miscitems"></datalist> <datalist id="datalist-miscitems"></datalist>
<datalist id="datalist-mods"> <datalist id="datalist-mods">
<option data-key="/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser" value="Legendary Core"></option> <option data-key="/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser" value="Legendary Core"></option>

View File

@ -149,6 +149,12 @@ function fetchItemList() {
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: "Kitgun" }, "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: "Kitgun" },
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: "Kitgun" }, "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: "Kitgun" },
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" }, "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" },
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/OperatorTrainingAmpWeapon": {
name: "Mote Amp"
},
"/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon": { name: "Amp" },
"/Lotus/Weapons/Operator/Pistols/DrifterPistol/DrifterPistolPlayerWeapon": { name: "Sirocco" },
"/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit": { name: "K-Drive" },
// Missing in data sources // Missing in data sources
"/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser": { name: "Legendary Core" }, "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser": { name: "Legendary Core" },
"/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" } "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" }
@ -188,8 +194,21 @@ function updateInventory() {
window.didInitialInventoryUpdate = true; window.didInitialInventoryUpdate = true;
// Populate inventory route // Populate inventory route
document.getElementById("warframe-list").innerHTML = ""; [
data.Suits.forEach(item => { "Suits",
"SpaceSuits",
"Sentinels",
"LongGuns",
"Pistols",
"Melee",
"SpaceGuns",
"SpaceMelee",
"SentinelWeapons",
"Hoverboards",
"OperatorAmps"
].forEach(category => {
document.getElementById(category + "-list").innerHTML = "";
data[category].forEach(item => {
const tr = document.createElement("tr"); const tr = document.createElement("tr");
tr.setAttribute("data-item-type", item.ItemType); tr.setAttribute("data-item-type", item.ItemType);
{ {
@ -198,17 +217,32 @@ function updateInventory() {
if (item.ItemName) { if (item.ItemName) {
td.textContent = item.ItemName + " (" + td.textContent + ")"; td.textContent = item.ItemName + " (" + td.textContent + ")";
} }
if (item.ModularParts) {
td.textContent += " [";
item.ModularParts.forEach(part => {
td.textContent += " " + (itemMap[part]?.name ?? part) + ",";
});
td.textContent = td.textContent.slice(0, -1) + " ]";
}
tr.appendChild(td); tr.appendChild(td);
} }
{ {
const td = document.createElement("td"); const td = document.createElement("td");
td.classList = "text-end"; td.classList = "text-end";
if (item.XP < 1_600_000) { const maxXP =
category === "Suits" ||
category === "SpaceSuits" ||
category === "Sentinels" ||
category === "Hoverboards"
? 1_600_000
: 800_000;
if (item.XP < maxXP) {
const a = document.createElement("a"); const a = document.createElement("a");
a.href = "#"; a.href = "#";
a.onclick = function (event) { a.onclick = function (event) {
event.preventDefault(); event.preventDefault();
addGearExp("Suits", item.ItemId.$oid, 1_600_000 - item.XP); addGearExp(category, item.ItemId.$oid, maxXP - item.XP);
if ("exalted" in itemMap[item.ItemType]) { if ("exalted" in itemMap[item.ItemType]) {
for (const exaltedType of itemMap[item.ItemType].exalted) { for (const exaltedType of itemMap[item.ItemType].exalted) {
const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType); const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType);
@ -230,68 +264,12 @@ function updateInventory() {
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`; a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`;
td.appendChild(a); td.appendChild(a);
} }
{ if (category == "Suits") {
const a = document.createElement("a"); const a = document.createElement("a");
a.href = "/webui/powersuit/" + item.ItemId.$oid; a.href = "/webui/powersuit/" + item.ItemId.$oid;
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>`; a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>`;
td.appendChild(a); td.appendChild(a);
} }
{
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
const name = prompt("Enter new custom name:");
if (name !== null) {
renameGear("Suits", item.ItemId.$oid, name);
}
};
a.title = "Rename";
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 80V229.5c0 17 6.7 33.3 18.7 45.3l176 176c25 25 65.5 25 90.5 0L418.7 317.3c25-25 25-65.5 0-90.5l-176-176c-12-12-28.3-18.7-45.3-18.7H48C21.5 32 0 53.5 0 80zm112 32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>`;
td.appendChild(a);
}
{
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
disposeOfGear("Suits", item.ItemId.$oid);
};
a.title = "Remove";
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>`;
td.appendChild(a);
}
tr.appendChild(td);
}
document.getElementById("warframe-list").appendChild(tr);
});
["LongGuns", "Pistols", "Melee"].forEach(category => {
document.getElementById(category + "-list").innerHTML = "";
data[category].forEach(item => {
const tr = document.createElement("tr");
tr.setAttribute("data-item-type", item.ItemType);
{
const td = document.createElement("td");
td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
if (item.ItemName) {
td.textContent = item.ItemName + " (" + td.textContent + ")";
}
tr.appendChild(td);
}
{
const td = document.createElement("td");
td.classList = "text-end";
if (item.XP < 800_000) {
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
addGearExp(category, item.ItemId.$oid, 800_000 - item.XP);
};
a.title = "Make Rank 30";
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`;
td.appendChild(a);
}
{ {
const a = document.createElement("a"); const a = document.createElement("a");
a.href = "#"; a.href = "#";
@ -524,35 +502,7 @@ function getKey(input) {
?.getAttribute("data-key"); ?.getAttribute("data-key");
} }
function doAcquireWarframe() { function doAcquireEquipment(category) {
const uniqueName = getKey(document.getElementById("warframe-to-acquire"));
if (!uniqueName) {
$("#warframe-to-acquire").addClass("is-invalid").focus();
return;
}
revalidateAuthz(() => {
const req = $.post({
url: "/custom/addItems?" + window.authz,
contentType: "application/json",
data: JSON.stringify([
{
type: "Powersuit",
internalName: uniqueName
}
])
});
req.done(() => {
document.getElementById("warframe-to-acquire").value = "";
updateInventory();
});
});
}
$("input[list]").on("input", function () {
$(this).removeClass("is-invalid");
});
function doAcquireWeapon(category) {
const uniqueName = getKey(document.getElementById("acquire-type-" + category)); const uniqueName = getKey(document.getElementById("acquire-type-" + category));
if (!uniqueName) { if (!uniqueName) {
$("#acquire-type-" + category) $("#acquire-type-" + category)
@ -566,7 +516,7 @@ function doAcquireWeapon(category) {
contentType: "application/json", contentType: "application/json",
data: JSON.stringify([ data: JSON.stringify([
{ {
type: "Weapon", type: category,
internalName: uniqueName internalName: uniqueName
} }
]) ])
@ -578,6 +528,10 @@ function doAcquireWeapon(category) {
}); });
} }
$("input[list]").on("input", function () {
$(this).removeClass("is-invalid");
});
function dispatchAddItemsRequestsBatch(requests) { function dispatchAddItemsRequestsBatch(requests) {
revalidateAuthz(() => { revalidateAuthz(() => {
const req = $.post({ const req = $.post({
@ -591,13 +545,19 @@ function dispatchAddItemsRequestsBatch(requests) {
}); });
} }
function addMissingWarframes() { function addMissingEquipment(categories) {
const requests = []; const requests = [];
document.querySelectorAll("#datalist-warframes option").forEach(elm => { categories.forEach(category => {
if (!document.querySelector("#warframe-list [data-item-type='" + elm.getAttribute("data-key") + "']")) { document.querySelectorAll("#datalist-" + category + " option").forEach(elm => {
requests.push({ type: "Powersuit", internalName: elm.getAttribute("data-key") }); if (
!document.querySelector(
"#" + category + "-list [data-item-type='" + elm.getAttribute("data-key") + "']"
)
) {
requests.push({ type: category, internalName: elm.getAttribute("data-key") });
} }
}); });
});
if ( if (
requests.length != 0 && requests.length != 0 &&
window.confirm("Are you sure you want to add " + requests.length + " items to your account?") window.confirm("Are you sure you want to add " + requests.length + " items to your account?")
@ -606,28 +566,40 @@ function addMissingWarframes() {
} }
} }
function maxRankAllWarframes() { function maxRankAllEquipment(categories) {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1"); const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => { req.done(data => {
window.itemListPromise.then(itemMap => { window.itemListPromise.then(itemMap => {
const batchData = { Suits: [], SpecialItems: [] }; const batchData = {};
data.Suits.forEach(item => { categories.forEach(category => {
if (item.XP < 1_600_000) { data[category].forEach(item => {
batchData.Suits.push({ const maxXP =
category === "Suits" ||
category === "SpaceSuits" ||
category === "Sentinels" ||
category === "Hoverboards"
? 1_600_000
: 800_000;
if (item.XP < maxXP) {
if (!batchData[category]) {
batchData[category] = [];
}
batchData[category].push({
ItemId: { $oid: item.ItemId.$oid }, ItemId: { $oid: item.ItemId.$oid },
XP: 1_600_000 - item.XP XP: maxXP
}); });
} }
if (category === "Suits") {
if ("exalted" in itemMap[item.ItemType]) { if ("exalted" in itemMap[item.ItemType]) {
for (const exaltedType of itemMap[item.ItemType].exalted) { for (const exaltedType of itemMap[item.ItemType].exalted) {
const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType); const exaltedItem = data["SpecialItems"].find(x => x.ItemType == exaltedType);
if (exaltedItem) { if (exaltedItem) {
const exaltedCap = itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000; const exaltedCap = itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
if (exaltedItem.XP < exaltedCap) { if (exaltedItem.XP < exaltedCap) {
batchData.SpecialItems.push({ batchData["SpecialItems"].push({
ItemId: { $oid: exaltedItem.ItemId.$oid }, ItemId: { $oid: exaltedItem.ItemId.$oid },
XP: exaltedCap XP: exaltedCap
}); });
@ -635,50 +607,6 @@ function maxRankAllWarframes() {
} }
} }
} }
});
if (batchData.Suits.length > 0 || batchData.SpecialItems.length > 0) {
return sendBatchGearExp(batchData);
}
alert("No Warframes to rank up.");
});
});
}
function addMissingWeapons() {
const requests = [];
document
.querySelectorAll("#datalist-LongGuns option, #datalist-Pistols option, #datalist-Melee option")
.forEach(elm => {
if (!document.querySelector("#weapon-list [data-item-type='" + elm.getAttribute("data-key") + "']")) {
requests.push({ type: "Weapon", internalName: elm.getAttribute("data-key") });
}
});
if (
requests.length != 0 &&
window.confirm("Are you sure you want to add " + requests.length + " items to your account?")
) {
dispatchAddItemsRequestsBatch(requests);
}
}
function maxRankAllWeapons() {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => {
const batchData = {};
["LongGuns", "Pistols", "Melee"].forEach(category => {
data[category].forEach(item => {
if (item.XP < 800_000) {
if (!batchData[category]) {
batchData[category] = [];
}
batchData[category].push({
ItemId: { $oid: item.ItemId.$oid },
XP: 800_000 - item.XP
});
} }
}); });
}); });
@ -687,7 +615,8 @@ function maxRankAllWeapons() {
return sendBatchGearExp(batchData); return sendBatchGearExp(batchData);
} }
alert("No weapons to rank up."); alert("No equipment to rank up.");
});
}); });
} }