diff --git a/src/middleware/tool.js b/src/middleware/tool.js
index 7de66561..56fd3507 100644
--- a/src/middleware/tool.js
+++ b/src/middleware/tool.js
@@ -107,7 +107,7 @@ const getCheckedInTools = async (tenant) => {
try {
return await Tool.find()
.where("serviceAssignment.type")
- .equals("stockroom")
+ .equals("Stockroom")
.where("tenant")
.equals(tenant)
.where("archived")
@@ -229,14 +229,38 @@ const createTool = async (req, res, next) => {
throw new Error("Missing required fields");
}
- const existing = await Tool.findOne({
- $or: [{ serialNumber }, { barcode }, { toolID }],
- tenant,
- });
+ // Build query conditions for duplicate checking
+ const queryConditions = [];
+ if (serialNumber) {
+ queryConditions.push({ serialNumber: { $eq: serialNumber }, tenant: { $eq: tenant } });
+ }
+ if (barcode) {
+ queryConditions.push({ barcode: { $eq: barcode }, tenant: { $eq: tenant } });
+ }
+ if (toolID) {
+ queryConditions.push({ toolID: { $eq: toolID }, tenant: { $eq: tenant } });
+ }
+
+ const existingTools = await Tool.find({ $or: queryConditions });
- if (existing) {
- res.locals.tools = mutateToArray(existing);
- throw new Error("Tool already exists");
+ if (existingTools.length > 0) {
+ const errorList = existingTools.map((tool) => {
+ if (tool.serialNumber === serialNumber) {
+ return { field: "Serial Number", value: serialNumber, tool };
+ }
+ if (tool.barcode === barcode) {
+ return { field: "Barcode", value: barcode, tool };
+ }
+ return { field: "Tool ID", value: toolID, tool };
+ });
+
+ const error = new Error("Duplicate Tool(s) Found");
+ error.errorList = errorList.map((err) => ({
+ cause: err.field,
+ duplicateValue: err.value,
+ existingTool: err.tool._id,
+ }));
+ throw error;
}
const newTool = await Tool.create({
@@ -290,7 +314,10 @@ const createTool = async (req, res, next) => {
});
res.locals.message = error.message;
- res.status(500).redirect("back");
+ if (error.errorList) {
+ res.locals.errorList = error.errorList;
+ }
+ return res.render("results");
}
};
@@ -614,7 +641,7 @@ export const getRecentlyUpdatedTools = async (req, res, next) => {
metadata: { tenant: req.user.tenant, toolCount: tools.length },
});
- res.locals.recentlyUpdatedTools = tools;
+ res.locals.tools = tools;
return next();
} catch (error) {
req.logger.error({
diff --git a/src/public/js/toolkeeper.js b/src/public/js/toolkeeper.js
index 384bbf87..9168abb3 100644
--- a/src/public/js/toolkeeper.js
+++ b/src/public/js/toolkeeper.js
@@ -1,20 +1,32 @@
-function btnToSpinner() {
- const submitBtn = document.querySelector("#testButton");
+function openInNewTab() {
+ // Open new window with specific security features
+ const printWindow = window.open('', '_blank',
+ 'width=800,height=600,menubar=no,toolbar=no,location=no,status=no,noopener,noreferrer'
+ );
- submitBtn.addEventListener("click", () => {
- submitBtn.outerHTML =
- '';
- });
-}
+ if (printWindow) {
+ // Set security headers for the new window
+ printWindow.document.write('
');
+ printWindow.document.write('');
+ printWindow.document.write('Printer Friendly View');
+ // Get the content and create a sanitized copy
+ const content = document.getElementById("printerFriendlyTools");
+ if (content) {
+ // Create a deep clone to avoid manipulating the original DOM
+ const sanitizedContent = content.cloneNode(true);
+ // Remove any script tags from the cloned content
+ const scripts = sanitizedContent.getElementsByTagName('script');
+ while (scripts[0]) {
+ scripts[0].parentNode.removeChild(scripts[0]);
+ }
-function openInNewTab() {
- const x = globalThis.open();
- const newPage = x.document.createElement("div");
- newPage.width = "100%";
- newPage.height = "100%";
- newPage.innerHTML = document.getElementById("printerFriendlyTools").innerHTML;
- x.document.body.appendChild(newPage);
+ printWindow.document.body.appendChild(sanitizedContent);
+ }
+
+ printWindow.document.write('');
+ printWindow.document.close();
+ }
}
if (document.getElementsByClassName("fa-print").length > 0) {
console.log("Print button found.");
@@ -34,28 +46,58 @@ function populateDashboard(cachedData) {
// biome-ignore lint/complexity/noForEach:
cachedData.serviceAssignments.forEach((assignment) => {
if (assignment.toolCount > 0 && assignment.active) {
- const assignmentElement = document.createElement("div");
- assignmentElement.innerHTML = `
-
- | ${assignment.toolCount} | ${assignment.jobNumber} - ${assignment.jobName} |
-
- `;
-
- // Append each active assignment to the container
- serviceAssignmentsContainer.appendChild(assignmentElement);
+ const tr = document.createElement("tr");
+ const td = document.createElement("td");
+ const link = document.createElement("a");
+ link.href = `/tool/search?searchBy=serviceAssignment&searchTerm=${assignment._id}`;
+ link.textContent = `${assignment.toolCount} | ${assignment.jobNumber} - ${assignment.jobName}`;
+ td.appendChild(link);
+ tr.appendChild(td);
+ serviceAssignmentsContainer.appendChild(tr);
}
});
}
}
-document.addEventListener("DOMContentLoaded", () => {
+document.addEventListener("DOMContentLoaded", async () => {
const cachedData = JSON.parse(localStorage.getItem("serviceData"));
+ const cachedHash = localStorage.getItem("serviceDataHash");
+
+ // sourcery skip: avoid-function-declarations-in-blocks
+ async function fetchAndUpdateCache() {
+ console.log("Fetching fresh data from server...");
+ try {
+ const response = await fetch("/dashboard/cache");
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ const data = await response.json();
+
+ // If the hash is different, update the cache
+ if (data.hash !== cachedHash) {
+ console.log("Cache is stale, updating with new data...");
+ localStorage.setItem("serviceData", JSON.stringify(data.data));
+ localStorage.setItem("serviceDataHash", data.hash);
+ return data.data;
+ }
+ console.log("Cache is up to date");
+ return cachedData;
+ } catch (error) {
+ console.error("Error fetching cached data:", error);
+ return cachedData; // Fall back to cached data if fetch fails
+ }
+ }
- if (cachedData) {
- // Pass the cached data to a function to populate the dashboard
- populateDashboard(cachedData);
+ if (cachedData && cachedHash) {
+ console.log("Found cached data, checking if it's still valid...");
+ const data = await fetchAndUpdateCache();
+ populateDashboard(data);
} else {
- console.log("No cached data found.");
+ console.log("No cached data found, fetching from server...");
+ const data = await fetchAndUpdateCache();
+ if (data) {
+ populateDashboard(data);
+ }
}
});
diff --git a/src/routes/dashboard.routes.js b/src/routes/dashboard.routes.js
index b4aeb016..615292ff 100644
--- a/src/routes/dashboard.routes.js
+++ b/src/routes/dashboard.routes.js
@@ -3,7 +3,11 @@ import { generatePrinterFriendlyToolList, getRecentlyUpdatedTools } from '../mid
import {initCachedContent} from '../middleware/util.js'
import { hoistOnboarding, dashboardOnboardingComplete, skipStep } from '../middleware/onboarding.js'
import { Router } from 'express'
+
export const dashboardRouter = Router()
+
+// @desc Show dashboard
+// @route GET /dashboard
dashboardRouter.get(
'/',
initCachedContent,
@@ -12,6 +16,13 @@ dashboardRouter.get(
hoistOnboarding,
renderDashboard
)
+
+// @desc Get cached data
+// @route GET /dashboard/cache
+dashboardRouter.get("/cache", initCachedContent, (req, res) => {
+ res.json(res.locals.cachedContent);
+});
+
dashboardRouter.get('/skip-step/:step', skipStep, renderDashboard)
dashboardRouter.post('/onboarding-complete', dashboardOnboardingComplete)
// src\routes\dashboard.routes.js
diff --git a/src/routes/settings/users.routes.js b/src/routes/settings/users.routes.js
index 62f7e3b5..b83c1a36 100644
--- a/src/routes/settings/users.routes.js
+++ b/src/routes/settings/users.routes.js
@@ -23,11 +23,11 @@ userSettingsRouter.post('/resetPW/:id', resetPassword, renderSettingsUsers)
// @desc disable user
// @endpoint POST /settings/users/disableUser/:id
-userSettingsRouter.post('/disableUser/:id', disableUser, renderSettingsUsers)
+userSettingsRouter.post('/disableUser/:id', disableUser, getUsers, renderSettingsUsers)
// @desc delete user
// @endpoint GET /settings/users/:id/delete
-userSettingsRouter.get("/:id/delete", deleteUser, renderSettingsUsers);
+userSettingsRouter.get("/:id/delete", deleteUser, getUsers, renderSettingsUsers);
// @desc create new user
// @endpoint POST /settings/users/create
diff --git a/src/routes/tool.routes.js b/src/routes/tool.routes.js
index b0f40820..94544993 100644
--- a/src/routes/tool.routes.js
+++ b/src/routes/tool.routes.js
@@ -14,6 +14,7 @@ import {
import {
sanitizeReqBody,
hoistSearchParamsToBody,
+ initCachedContent,
} from "../middleware/util.js";
import { listAllSAs } from "../middleware/serviceAssignment.js";
import {
@@ -46,21 +47,21 @@ toolRouter.post(
);
// save the tool's new service assignment to the database.
-toolRouter.post("/submitCheckInOut", submitCheckInOut, renderResults);
+toolRouter.post("/submitCheckInOut", submitCheckInOut, initCachedContent, renderResults);
// create new tool
-toolRouter.post("/submit", sanitizeReqBody, createTool, redirectToDashboard);
+toolRouter.post("/submit", sanitizeReqBody, createTool, initCachedContent, redirectToDashboard);
// render batch creation page
toolRouter.get("/batchCreate", renderBatchCreationPage);
// validate and create a batch of submitted tools
-toolRouter.post("/batchCreate", sanitizeReqBody, batchCreateTools);
+toolRouter.post("/batchCreate", sanitizeReqBody, batchCreateTools, initCachedContent, redirectToDashboard);
// update tool
-toolRouter.post("/update", sanitizeReqBody, updateTool, renderResults);
+toolRouter.post("/update", sanitizeReqBody, updateTool, initCachedContent, renderResults);
// archive tool
-toolRouter.get("/archive/:id", archiveTool, getAllTools, renderResults);
+toolRouter.get("/archive/:id", archiveTool, initCachedContent, getAllTools, renderResults);
// archive tool
-toolRouter.get("/unarchive/:id", unarchiveTool, getAllTools, renderResults);
+toolRouter.get("/unarchive/:id", unarchiveTool, initCachedContent, getAllTools, renderResults);
// get tool by id
toolRouter.get("/:id", getToolByID, listAllSAs, renderEditTool);
diff --git a/src/views/dashboard.hbs b/src/views/dashboard.hbs
index cddf707b..827e6bdd 100644
--- a/src/views/dashboard.hbs
+++ b/src/views/dashboard.hbs
@@ -3,7 +3,7 @@
- {{#if recentlyUpdatedTools}}
+ {{#if tools}}
{{> dashboard/_recentlyUpdated}}
{{/if}}
diff --git a/src/views/layouts/main.hbs b/src/views/layouts/main.hbs
index 68bac94f..15b373d5 100644
--- a/src/views/layouts/main.hbs
+++ b/src/views/layouts/main.hbs
@@ -5,10 +5,10 @@
-
-
+
+
-
+
@@ -40,7 +40,7 @@
{{> _modals}}
-
+