diff --git a/backend/src/controllers/vsr.ts b/backend/src/controllers/vsr.ts index 7143073..6b9a0f7 100644 --- a/backend/src/controllers/vsr.ts +++ b/backend/src/controllers/vsr.ts @@ -1,13 +1,15 @@ import { RequestHandler } from "express"; import { validationResult } from "express-validator"; import createHttpError from "http-errors"; -import VSRModel from "src/models/vsr"; +import FurnitureItemModel, { FurnitureItem } from "src/models/furnitureItem"; +import VSRModel, { FurnitureInput, VSR } from "src/models/vsr"; import { sendVSRConfirmationEmailToVeteran, sendVSRNotificationEmailToStaff, } from "src/services/emails"; import validationErrorParser from "src/util/validationErrorParser"; import ExcelJS from "exceljs"; +import { ObjectId } from "mongodb"; /** * Gets all VSRs in the database. Requires the user to be signed in and have @@ -158,11 +160,56 @@ export const deleteVSR: RequestHandler = async (req, res, next) => { } }; +/** + * Converts an entry in a VSR to a formatted string to write to the Excel spreadsheet + */ +const stringifyEntry = ( + entry: string | number | string[] | number[] | boolean | Date | undefined, +) => { + if (!entry) { + return ""; + } + + if (Array.isArray(entry)) { + return entry.join(", "); + } else { + return entry.toString(); + } +}; + +type FurnitureItemEntry = FurnitureItem & { _id: ObjectId }; + +/** + * Formats a VSR's selected furniture items as a string + */ +const stringifySelectedFurnitureItems = ( + selectedItems: FurnitureInput[] | undefined, + allFurnitureItems: FurnitureItemEntry[], +) => { + if (!selectedItems) { + return ""; + } + + const itemIdsToItems: Record = {}; + + for (const furnitureItem of allFurnitureItems) { + itemIdsToItems[furnitureItem._id.toString()] = furnitureItem; + } + + return selectedItems + .map((selectedItem) => { + const furnitureItem = itemIdsToItems[selectedItem.furnitureItemId]; + return furnitureItem ? `${furnitureItem.name}: ${selectedItem.quantity}` : "[unknown]"; + }) + .join(", "); +}; + const writeSpreadsheet = async (filename: string) => { const workbook = new ExcelJS.Workbook(); workbook.creator = "PAP Inventory System"; workbook.lastModifiedBy = "Bot"; + //current date workbook.created = new Date(); workbook.modified = new Date(); @@ -173,49 +220,76 @@ const writeSpreadsheet = async (filename: string) => { const vsrs = await VSRModel.find(); const plainVsrs = vsrs.map((doc) => doc.toObject()); - // Setup columns headers based on keys from the first object in the plainVsrs array - if (plainVsrs.length > 0) { - worksheet.columns = Object.keys(plainVsrs[0]).map((key) => ({ - header: key, - key: key, - width: 20, - })); - - // Add data rows to the worksheet - plainVsrs.forEach((item) => { - worksheet.addRow(item); - - //cast each element in the row to a string - - //for the row that was just added in the spreadsheet, if there are brackets surrounding any of the entries, remove them - if (worksheet.lastRow) { - worksheet.lastRow.eachCell((cell) => { - if (cell.value && !(typeof cell.value === "number")) { - cell.value = cell.value.toString(); - } - }); - - worksheet.lastRow.eachCell((cell) => { - if ( - cell.value && - typeof cell.value == "string" && - cell.value[0] === "[" && - cell.value[cell.value.length - 1] === "]" - ) { - cell.value = cell.value.slice(1, -1); - } - }); - } - }); + // Fields that we want to write to the spreadsheet. First is field name, second is display name. + const fieldsToWrite: [keyof VSR, string][] = [ + ["name", "Name"], + ["gender", "Gender"], + ["age", "Age"], + ["maritalStatus", "Marital Status"], + ["spouseName", "Spouse Name"], + ["agesOfBoys", "Ages of boys"], + ["agesOfGirls", "Ages of girls"], + ["ethnicity", "Ethnicity"], + ["employmentStatus", "Employment Status"], + ["incomeLevel", "Income Level"], + ["sizeOfHome", "Size of Home"], + + ["streetAddress", "Street Address"], + ["city", "City"], + ["state", "State"], + ["zipCode", "Zip Code"], + ["phoneNumber", "Phone Number"], + ["email", "Email Address"], + ["branch", "Branch"], + ["conflicts", "Conflicts"], + ["dischargeStatus", "Discharge Status"], + ["serviceConnected", "Service Connected"], + ["lastRank", "Last Rank"], + ["militaryID", "Military ID"], + ["petCompanion", "Pet Companion Desired"], + ["hearFrom", "Referral Source"], + + ["selectedFurnitureItems", "Selected Furniture Items"], + ["additionalItems", "Additional Items"], + + ["dateReceived", "Date Received"], + ["lastUpdated", "Last Updated"], + ["status", "Status"], + ]; + + worksheet.columns = fieldsToWrite.map((field) => ({ + header: field[1], + key: field[0], + width: 20, + })); + + const allFurnitureItems = await FurnitureItemModel.find(); + + // Add data rows to the worksheet + plainVsrs.forEach((vsr) => { + worksheet.addRow( + fieldsToWrite.reduce( + (prev, field) => ({ + ...prev, + [field[0]]: + field[0] === "selectedFurnitureItems" + ? stringifySelectedFurnitureItems( + vsr[field[0]] as FurnitureInput[] | undefined, + allFurnitureItems, + ) + : stringifyEntry(vsr[field[0]]), + }), + {}, + ), + ); + }); - // Write to file - await workbook.xlsx.writeFile(filename); - } + // Write to file + await workbook.xlsx.writeFile(filename); }; export const bulkExportVSRS: RequestHandler = async (req, res, next) => { try { - console.log("inside bulk export vsrs"); const filename = "vsrs.xlsx"; await writeSpreadsheet(filename); res.download(filename); diff --git a/backend/src/models/furnitureItem.ts b/backend/src/models/furnitureItem.ts index f5998bc..2c4f6d2 100644 --- a/backend/src/models/furnitureItem.ts +++ b/backend/src/models/furnitureItem.ts @@ -17,6 +17,6 @@ const furnitureItemSchema = new Schema({ categoryIndex: { type: Number, required: true }, }); -type FurnitureItem = InferSchemaType; +export type FurnitureItem = InferSchemaType; export default model("FurnitureItem", furnitureItemSchema); diff --git a/backend/src/models/vsr.ts b/backend/src/models/vsr.ts index 556cddf..cd46f87 100644 --- a/backend/src/models/vsr.ts +++ b/backend/src/models/vsr.ts @@ -3,7 +3,7 @@ import { InferSchemaType, Schema, model } from "mongoose"; /** * A schema for a single furniture item that a veteran can request */ -const furntitureInputSchema = new Schema({ +export const furntitureInputSchema = new Schema({ // ID of the furniture item being required (Object ID for an instance of the furniture item model) furnitureItemId: { type: String, required: true }, @@ -55,6 +55,7 @@ const vsrSchema = new Schema({ status: { type: String, required: true }, }); -type VSR = InferSchemaType; +export type FurnitureInput = InferSchemaType; +export type VSR = InferSchemaType; export default model("VSR", vsrSchema); diff --git a/backend/vsrs.xlsx b/backend/vsrs.xlsx deleted file mode 100644 index a8b0ef6..0000000 Binary files a/backend/vsrs.xlsx and /dev/null differ