diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bdc62ef..fc7b43f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ brew install yarn Then install the project dependencies in development mode: ```shell -yarn +yarn -D ``` Install Redis by following [these instructions](README.md#pulling-redis-docker). diff --git a/florist/app/assets/js/material-dashboard.js b/florist/app/assets/js/material-dashboard.js index 043b5be..91e2c9d 100644 --- a/florist/app/assets/js/material-dashboard.js +++ b/florist/app/assets/js/material-dashboard.js @@ -592,72 +592,119 @@ function getEventTarget(e) { // End tabs navigation function onWindowLoadFunction() { // Material Design Input function - var inputs = document.querySelectorAll("input"); + const textFields = document.querySelectorAll("input"); + const selects = document.querySelectorAll("select"); + const allResults = [textFields, selects]; - for (var i = 0; i < inputs.length; i++) { - inputs[i].addEventListener( - "focus", - function (e) { - this.parentElement.classList.add("is-focused"); - this.parentElement.classList.add("focused"); - }, - false, - ); + const inputs = []; + for (var i = 0; i < allResults.length; i++) { + for (var j = 0; j < allResults[i].length; j++) { + inputs.push(allResults[i][j]); + } + } - inputs[i].onkeyup = function (e) { - if (this.value != "") { - this.parentElement.classList.add("is-filled"); - } else { - this.parentElement.classList.remove("is-filled"); - } - }; + for (var i = 0; i < inputs.length; i++) { + if (inputs[i].getAttribute("hasFocusListener") !== "true") { + inputs[i].addEventListener( + "focus", + function (e) { + this.parentElement.classList.add("is-focused"); + this.parentElement.classList.add("focused"); + }, + false, + ); + inputs[i].setAttribute("hasFocusListener", "true"); + } - inputs[i].addEventListener( - "focusout", - function (e) { + if (inputs[i].getAttribute("hasOnKeyUp") !== "true") { + inputs[i].onkeyup = function (e) { if (this.value != "") { this.parentElement.classList.add("is-filled"); + } else { + this.parentElement.classList.remove("is-filled"); } - this.parentElement.classList.remove("is-focused"); - this.parentElement.classList.remove("focused"); - }, - false, - ); + }; + inputs[i].setAttribute("hasOnKeyUp", "true"); + } + + if (inputs[i].getAttribute("hasFocusOutListener") !== "true") { + inputs[i].addEventListener( + "focusout", + function (e) { + if (this.value != "") { + this.parentElement.classList.add("is-filled"); + } + this.parentElement.classList.remove("is-focused"); + this.parentElement.classList.remove("focused"); + }, + false, + ); + inputs[i].setAttribute("hasFocusOutListener", "true"); + } } // Ripple Effect var ripples = document.querySelectorAll(".btn"); for (var i = 0; i < ripples.length; i++) { - ripples[i].addEventListener( - "click", - function (e) { - var targetEl = e.target; - var rippleDiv = targetEl.querySelector(".ripple"); - - rippleDiv = document.createElement("span"); - rippleDiv.classList.add("ripple"); - rippleDiv.style.width = rippleDiv.style.height = - Math.max(targetEl.offsetWidth, targetEl.offsetHeight) + - "px"; - targetEl.appendChild(rippleDiv); - - rippleDiv.style.left = - e.offsetX - rippleDiv.offsetWidth / 2 + "px"; - rippleDiv.style.top = - e.offsetY - rippleDiv.offsetHeight / 2 + "px"; - rippleDiv.classList.add("ripple"); - setTimeout(function () { - rippleDiv.parentElement.removeChild(rippleDiv); - }, 600); - }, - false, - ); + if (ripples[i].getAttribute("hasClickListener") !== "true") { + ripples[i].addEventListener( + "click", + function (e) { + var targetEl = e.target; + var rippleDiv = targetEl.querySelector(".ripple"); + + rippleDiv = document.createElement("span"); + rippleDiv.classList.add("ripple"); + rippleDiv.style.width = rippleDiv.style.height = + Math.max(targetEl.offsetWidth, targetEl.offsetHeight) + + "px"; + targetEl.appendChild(rippleDiv); + + rippleDiv.style.left = + e.offsetX - rippleDiv.offsetWidth / 2 + "px"; + rippleDiv.style.top = + e.offsetY - rippleDiv.offsetHeight / 2 + "px"; + rippleDiv.classList.add("ripple"); + setTimeout(function () { + rippleDiv.parentElement.removeChild(rippleDiv); + }, 600); + }, + false, + ); + } + ripples[i].setAttribute("hasClickListener", "true"); } } onWindowLoadFunction(); +const observer = new MutationObserver((mutationList) => { + var hasTargetElementTypes = false; + + // Check if any select, input or .btn has been added to the DOM + for (var i = 0; i < mutationList.length; i++) { + if (mutationList[i].type === "childList") { + for (var j = 0; j < mutationList[i].addedNodes.length; j++) { + const addedNode = mutationList[i].addedNodes[j]; + const inputs = addedNode.querySelectorAll("select"); + const selects = addedNode.querySelectorAll("input"); + const btns = addedNode.querySelectorAll(".btn"); + + hasTargetElementTypes = + inputs.length > 0 || selects.length > 0 || btns.length > 0; + } + } + } + + // If so, execute the onWindowLoadFunction to attach listeners + // to those new elements + if (hasTargetElementTypes) { + onWindowLoadFunction(); + } +}); +observer.observe(document, { childList: true, subtree: true }); + // Toggle Sidenav const iconNavbarSidenav = document.getElementById("iconNavbarSidenav"); const iconSidenav = document.getElementById("iconSidenav"); diff --git a/florist/app/jobs/hooks.tsx b/florist/app/jobs/hooks.tsx index 7a204b7..b8c34d2 100644 --- a/florist/app/jobs/hooks.tsx +++ b/florist/app/jobs/hooks.tsx @@ -1,7 +1,7 @@ import useSWR from "swr"; import { fetcher } from "../client_imports"; -export default function useGetJobsByJobStatus(status: string) { +export function useGetJobsByJobStatus(status: string) { const endpoint = "/api/server/job/".concat(status); const { data, error, isLoading } = useSWR(endpoint, fetcher, { refresh_interval: 1000, diff --git a/florist/app/jobs/page.tsx b/florist/app/jobs/page.tsx index f0e6c31..cbd1ca0 100644 --- a/florist/app/jobs/page.tsx +++ b/florist/app/jobs/page.tsx @@ -1,7 +1,8 @@ "use client"; + import { ReactElement } from "react/React"; -import useGetJobsByStatus from "./hooks"; -import useGetJobsByJobStatus from "./hooks"; + +import { useGetJobsByJobStatus } from "./hooks"; export const validStatuses = { NOT_STARTED: "Not Started", @@ -38,7 +39,7 @@ export default function Page(): ReactElement { )); return (
-

Job Status

+

Jobs By Status

{statusComponents}
); diff --git a/florist/tests/unit/app/jobs/page.test.tsx b/florist/tests/unit/app/jobs/page.test.tsx index a5d44bb..5d39ce5 100644 --- a/florist/tests/unit/app/jobs/page.test.tsx +++ b/florist/tests/unit/app/jobs/page.test.tsx @@ -3,7 +3,7 @@ import { getByText, render, cleanup } from "@testing-library/react"; import { describe, afterEach, it, expect } from "@jest/globals"; import Page, { validStatuses } from "../../../../app/jobs/page"; -import useGetJobsByStatus from "../../../../app/jobs/hooks"; +import { useGetJobsByJobStatus } from "../../../../app/jobs/hooks"; jest.mock("../../../../app/jobs/hooks"); @@ -33,7 +33,7 @@ function setupMock( error: boolean, isLoading: boolean, ) { - useGetJobsByStatus.mockImplementation((status: string) => { + useGetJobsByJobStatus.mockImplementation((status: string) => { if (validStatuses.includes(status)) { return { data, @@ -56,7 +56,7 @@ describe("List Jobs Page", () => { const { container } = render(); const h1 = container.querySelector("h1"); expect(h1).toBeInTheDocument(); - expect(h1).toHaveTextContent("Job Status"); + expect(h1).toHaveTextContent("Jobs By Status"); }); it("Renders Status Components Headers", () => {