diff --git a/packages/api/src/models/battery.ts b/packages/api/src/models/battery.ts index 637ee2b..0500fd4 100644 --- a/packages/api/src/models/battery.ts +++ b/packages/api/src/models/battery.ts @@ -54,7 +54,7 @@ const optionSchema = new Schema( const batteryStageSchema = new Schema({ type: { type: String, required: true }, - stageLabel: { type: String, required: true }, + stageLabel: String, options: [optionSchema], }); @@ -102,7 +102,7 @@ export const BatteryStage = model( export const batterySchema = new Schema({ name: { type: String, required: true }, - description: { type: String, required: true }, + description: String, imageUrl: { type: String, required: true }, stages: [{ type: Schema.Types.ObjectId, ref: "BatteryStage" }], }); diff --git a/packages/api/src/routes/admin.ts b/packages/api/src/routes/admin.ts index 1e6f4a7..7ca3e83 100644 --- a/packages/api/src/routes/admin.ts +++ b/packages/api/src/routes/admin.ts @@ -1,10 +1,86 @@ import { Router } from "express"; import isAdmin from "../middleware/admin"; +import { + Battery, + BatteryStage, + IBattery, + IBatteryStage, + IOption, +} from "../models/battery"; +import { HydratedDocument } from "mongoose"; const router = Router(); -router.get("/", isAdmin, (req, res) => { - res.status(200).send("You are an admin"); +router.get("/stages", isAdmin, (req, res, next) => { + BatteryStage.find() + .then((stages) => res.json(stages)) + .catch(next); }); +/* eslint-disable @typescript-eslint/no-explicit-any */ +router.post("/battery", isAdmin, async (req, res, next) => { + try { + const json = req.body as Record & { + Stages: Record[]; + }; + const name = json["Name"]; + const desc = json["Description"]; + const imageUrl = `https://picsum.photos/300/300?${crypto.randomUUID()}`; + const stages: IBatteryStage[] = json["Stages"].map((s: any) => { + const options: IOption[] = Object.entries(s).reduce( + (acc: IOption[], item: any) => { + const optionName = item[0]; + const optionValue = item[1]; + if ( + ["Type", "StageLabel"].includes(optionName) || + typeof optionValue === "object" + ) + return acc; + const option: IOption = { + name: optionName, + default: optionValue, + type: + typeof optionValue === "number" + ? "number" + : typeof optionValue === "boolean" + ? "checkbox" + : "text", + }; + acc.push(option); + return acc; + }, + [] + ); + + return { + stageLabel: s["StageLabel"], + type: s["Type"], + options, + }; + }); + + const newStages: HydratedDocument[] = []; + + for (const stage of stages) { + const existing = await BatteryStage.findOne({ type: stage.type }); + if (!existing) { + newStages.push(await BatteryStage.create(stage)); + } + } + + const bat: IBattery = { + name: name, + description: desc, + imageUrl: imageUrl, + stages: newStages.map((s) => s._id), + }; + + const data = await Battery.create(bat); + res.status(201).json(data); + } catch (e) { + next(e); + } +}); +/* eslint-enable @typescript-eslint/no-explicit-any */ + export default router; diff --git a/packages/ui/src/api/admin.ts b/packages/ui/src/api/admin.ts new file mode 100644 index 0000000..1e8c276 --- /dev/null +++ b/packages/ui/src/api/admin.ts @@ -0,0 +1,48 @@ +import axios from "axios"; + +const axiosInstance = axios.create({ + baseURL: import.meta.env.VITE_API_URL, + withCredentials: true, +}); + +interface IGenericOption { + name: string; + default: T; +} + +interface INumberOption extends IGenericOption { + type: "number"; + min?: number; + max?: number; + step?: number; +} + +interface ITextOption extends IGenericOption { + type: "text"; + maxLength?: number; +} + +interface IDropdownOption extends IGenericOption { + type: "dropdown"; + options: string[]; +} + +interface ICheckboxOption extends IGenericOption { + type: "checkbox"; +} + +type IOption = INumberOption | ITextOption | IDropdownOption | ICheckboxOption; + +interface GetStageResponse { + _id: string; + type: string; + stageLabel: string; + options: IOption[]; +} + +async function getStages() { + const result = await axiosInstance.get("admin/stages"); + return result.data; +} + +export default { getStages }; diff --git a/packages/ui/src/components/AppNavbar.vue b/packages/ui/src/components/AppNavbar.vue index 3fd88e4..281e860 100644 --- a/packages/ui/src/components/AppNavbar.vue +++ b/packages/ui/src/components/AppNavbar.vue @@ -25,7 +25,7 @@ function logOut() { Dashboard
Log Out, {{ authStore.currentUser.email }} diff --git a/packages/ui/src/pages/AdminPage.vue b/packages/ui/src/pages/AdminPage.vue index f848e41..d336fdc 100644 --- a/packages/ui/src/pages/AdminPage.vue +++ b/packages/ui/src/pages/AdminPage.vue @@ -1,6 +1,8 @@