Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config/scouting/2026/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,13 @@ export function layout() {
options: [
{
name: "Auto",
html: '<svg viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><rect fill="none" height="256" width="256"/><rect fill="none" height="160" rx="24" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" width="192" x="32" y="56"/><rect fill="none" height="40" rx="20" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" width="112" x="72" y="144"/><line fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" x1="148" x2="148" y1="144" y2="184"/><line fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" x1="108" x2="108" y1="144" y2="184"/><line fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" x1="128" x2="128" y1="56" y2="16"/><circle cx="84" cy="108" r="12"/><circle cx="172" cy="108" r="12"/></svg>',
html: '<svg viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><rect fill="none" height="256" width="256"/><rect fill="none" height="160" rx="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" width="192" x="32" y="56"/><rect fill="none" height="40" rx="20" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" width="112" x="72" y="144"/><line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" x1="148" x2="148" y1="144" y2="184"/><line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" x1="108" x2="108" y1="144" y2="184"/><line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" x1="128" x2="128" y1="56" y2="16"/><circle cx="84" cy="108" r="12" fill="currentColor"/><circle cx="172" cy="108" r="12" fill="currentColor"/></svg>',
refers: "auto",
active: true
},
{
name: "Teleop",
html: '<svg id="Layer_1" style="enable-background:new 0 0 30 30;" version="1.1" viewBox="0 0 30 30" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><circle cx="15" cy="12" r="2"/><line style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;" x1="15" x2="15" y1="13" y2="25"/><path d="M19.316,15.7 C20.342,14.79,21,13.48,21,12s-0.658-2.79-1.684-3.7" style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;"/><path d="M22.91,18.78 C24.801,17.13,26,14.707,26,12s-1.199-5.13-3.09-6.78" style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;"/><path d="M10.684,15.7 C9.658,14.79,9,13.48,9,12s0.658-2.79,1.684-3.7" style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;"/><path d="M7.09,18.78 C5.199,17.13,4,14.707,4,12s1.199-5.13,3.09-6.78" style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;"/></svg>',
html: '<svg id="Layer_1" style="enable-background:new 0 0 30 30;" version="1.1" viewBox="0 0 30 30" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><circle cx="15" cy="12" r="2" fill="currentColor"/><line style="fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;" x1="15" x2="15" y1="13" y2="25"/><path d="M19.316,15.7 C20.342,14.79,21,13.48,21,12s-0.658-2.79-1.684-3.7" style="fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;"/><path d="M22.91,18.78 C24.801,17.13,26,14.707,26,12s-1.199-5.13-3.09-6.78" style="fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;"/><path d="M10.684,15.7 C9.658,14.79,9,13.48,9,12s0.658-2.79,1.684-3.7" style="fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;"/><path d="M7.09,18.78 C5.199,17.13,4,14.707,4,12s1.199-5.13,3.09-6.78" style="fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;"/></svg>',
refers: "teleop",
active: false
},
Expand Down
31 changes: 26 additions & 5 deletions helpers/scouting.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ScoutingEntry from "../models/scoutingEntry";
import PurchaseEntry from "../models/purchaseEntry";
import ScoutingCategory from "../models/scoutingCategory";
import Team from "../models/team";
import { getTeamByNumber } from "./teams";
Expand Down Expand Up @@ -334,13 +335,33 @@ export async function getTotalIncentives(contributingTeam, contributingUser) {
})
.select({ xp: 1, nuts: 1, bolts: 1 })
.lean();

let purchases = await PurchaseEntry.find({
"contributor.team": (await getTeamByNumber(contributingTeam))._id,
"contributor.username": contributingUser
})
.select({ xp: 1, nuts: 1, bolts: 1 })
.lean();

let nuts =
Number(
entries.reduce((total, current: any) => total + current.nuts, 0)
) -
Number(
purchases.reduce((total, current: any) => total + current.nuts, 0)
);
let bolts =
Number(
entries.reduce((total, current: any) => total + current.bolts, 0)
) -
Number(
purchases.reduce((total, current: any) => total + current.bolts, 0)
);

return {
xp: entries.reduce((total, current: any) => total + current.xp, 0),
nuts: entries.reduce((total, current: any) => total + current.nuts, 0),
bolts: entries.reduce(
(total, current: any) => total + current.bolts,
0
),
nuts: nuts,
bolts: bolts,
...getLevelAndProgress(
entries.reduce((total, current: any) => total + current.xp, 0)
)
Expand Down
28 changes: 18 additions & 10 deletions helpers/shop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import mongoose from "mongoose";
import ShopItem from "../models/shopItem";
import UserInventory from "../models/userInventory";
import { getTotalIncentives } from "./scouting";
import PurchaseEntry from "../models/purchaseEntry";
import { getTeamByNumber } from "./teams";

export interface ShopItem {
id: string;
Expand Down Expand Up @@ -53,9 +55,6 @@ export async function purchaseShopItem(
item?: ShopItem;
newBalance?: { nuts: number; bolts: number };
}> {
const session = await mongoose.startSession();
session.startTransaction();

try {
const item = await ShopItem.findById(itemId);
if (!item || !item.enabled) {
Expand All @@ -73,6 +72,15 @@ export async function purchaseShopItem(
throw new Error("Insufficient funds");
}

const existingInventory = await UserInventory.findOne({
"user.team": teamNumber,
"user.username": username,
"items.itemId": item._id
});
if (existingInventory) {
throw new Error("You already own this item");
}

await UserInventory.findOneAndUpdate(
{
"user.team": teamNumber,
Expand All @@ -86,12 +94,15 @@ export async function purchaseShopItem(
}
}
},
{ upsert: true, session }
{ upsert: true }
);

// await deductCurrency(teamNumber, username, item.price);

await session.commitTransaction();
await PurchaseEntry.create({
"contributor.team": (await getTeamByNumber(teamNumber))._id,
"contributor.username": username,
nuts: item.price.nuts,
bolts: item.price.bolts
});

const newBalance = (await getTotalIncentives(
teamNumber,
Expand All @@ -115,13 +126,10 @@ export async function purchaseShopItem(
}
};
} catch (error) {
await session.abortTransaction();
return {
success: false,
error: error.message
};
} finally {
session.endSession();
}
}

Expand Down
26 changes: 26 additions & 0 deletions models/purchaseEntry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import mongoose from "../db";

let ScoutingContributor = new mongoose.Schema({
team: {
ref: "Team",
type: mongoose.Schema.Types.ObjectId
},
username: String
});

export default mongoose.model(
"PurchaseEntry",
new mongoose.Schema({
contributor: ScoutingContributor,
nuts: {
type: Number,
required: false,
default: 0
},
bolts: {
type: Number,
required: false,
default: 0
}
})
);
Loading