diff --git a/builder-frontend/src/components/Header.tsx b/builder-frontend/src/components/Header.tsx deleted file mode 100644 index e04ad7e1..00000000 --- a/builder-frontend/src/components/Header.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useAuth } from "../context/AuthContext"; -import { useLocation, useNavigate } from "@solidjs/router"; - -import bdtLogo from "../assets/logos/bdt-logo-large-mono-light.svg"; -import { Show } from "solid-js"; - -const HeaderButton = ({ - buttonText, - onClick, -}: { - buttonText: string; - onClick: () => void; -}) => { - return ( -
- {buttonText} -
- ); -}; - -export default function Header() { - const { logout } = useAuth(); - const navigate = useNavigate(); - - const location = useLocation(); - const isNotRoot = location.pathname !== "/"; - - const handleLogout = () => { - logout(); - navigate("/login", { replace: true }); - }; - - return ( -
-
- BDT logo navigate("/")} - /> -
-
- - navigate("/")} - /> - - window.open("https://bdt-docs.web.app/", "_blank")} - /> - -
-
- ); -} diff --git a/builder-frontend/src/components/Header/Header.css b/builder-frontend/src/components/Header/Header.css new file mode 100644 index 00000000..f5033787 --- /dev/null +++ b/builder-frontend/src/components/Header/Header.css @@ -0,0 +1,18 @@ +@import "tailwindcss"; + +.header-user-email { + width: 75%; + overflow-wrap: break-word; + @apply mt-2; +} + +.header-menu ul { + @apply p-0; +} +.header-menu-item { + width: 100%; + @apply p-2 rounded-sm list-none; +} +.header-menu-item:hover { + @apply bg-slate-200 cursor-pointer; +} diff --git a/builder-frontend/src/components/Header/Header.tsx b/builder-frontend/src/components/Header/Header.tsx new file mode 100644 index 00000000..ef990bfb --- /dev/null +++ b/builder-frontend/src/components/Header/Header.tsx @@ -0,0 +1,107 @@ +import { useAuth } from "../../context/AuthContext"; +import { useLocation, useNavigate } from "@solidjs/router"; +import { Component, For, Show } from "solid-js"; + +import bdtLogo from "@/assets/logos/bdt-logo-large-mono-light.svg"; +import { HamburgerMenu } from "@/components/shared/HamburgerMenu"; + +import "./Header.css"; + +const HeaderButton = ({ + buttonText, + onClick, +}: { + buttonText: string; + onClick: () => void; +}) => { + return ( +
+ {buttonText} +
+ ); +}; + +interface MenuProps { + userEmail: string; + logout: () => void; +} + +const HeaderMenu: Component = (props) => { + const menuItems: { label: string; onClick: () => void }[] = [ + { + label: "User Guide", + onClick: () => window.open("https://bdt-docs.web.app/", "_blank"), + }, + { label: "Logout", onClick: props.logout }, + ]; + + return ( +
+
+ Welcome {props.userEmail} +
+
+ +
+ ); +}; + +export default function Header() { + const auth = useAuth(); + const userEmail = auth.user().email; + const { logout } = auth; + + const navigate = useNavigate(); + + const location = useLocation(); + const isNotRoot = location.pathname !== "/"; + + const handleLogout = () => { + logout(); + navigate("/login", { replace: true }); + }; + + return ( +
+
+ BDT logo navigate("/")} + /> +
+
+ + navigate("/")} + /> + + + + +
Open menu
+
+ + + +
+
+
+ ); +} diff --git a/builder-frontend/src/components/homeScreen/HomeScreen.tsx b/builder-frontend/src/components/homeScreen/HomeScreen.tsx index 039fd79d..86c35384 100644 --- a/builder-frontend/src/components/homeScreen/HomeScreen.tsx +++ b/builder-frontend/src/components/homeScreen/HomeScreen.tsx @@ -2,14 +2,14 @@ import { Accessor, createSignal, Match, Switch } from "solid-js"; import EligibilityChecksList from "./eligibilityCheckList/EligibilityChecksList"; import ProjectsList from "./ProjectsList"; -import Header from "../Header"; +import Header from "../Header/Header"; import BdtNavbar, { NavbarProps } from "@/components/shared/BdtNavbar"; 0; const HomeScreen = () => { const [screenMode, setScreenMode] = createSignal<"screeners" | "checks">( - "screeners" + "screeners", ); const navbarDefs: Accessor = () => { diff --git a/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail.tsx b/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail.tsx index 786b6405..546929b8 100644 --- a/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail.tsx +++ b/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail.tsx @@ -4,7 +4,7 @@ import { useParams } from "@solidjs/router"; import { clsx } from "clsx"; import toast from "solid-toast"; -import Header from "../../../Header"; +import Header from "../../../Header/Header"; import Loading from "../../../Loading"; import KogitoDmnEditorView from "./KogitoDmnEditorView"; import EligibilityCheckTest from "./checkTesting/EligibilityCheckTest"; @@ -16,17 +16,22 @@ import ParametersConfiguration from "./ParametersConfiguration"; import ErrorDisplayModal from "@/components/shared/ErrorModal"; import BdtNavbar, { NavbarProps } from "@/components/shared/BdtNavbar"; - -type CheckDetailScreenMode = "paramConfig" | "dmnDefinition" | "testing" | "publish"; +type CheckDetailScreenMode = + | "paramConfig" + | "dmnDefinition" + | "testing" + | "publish"; const EligibilityCheckDetail = () => { const { checkId } = useParams(); const [currentDmnModel, setCurrentDmnModel] = createSignal(""); - const [screenMode, setScreenMode] = createSignal("paramConfig"); + const [screenMode, setScreenMode] = + createSignal("paramConfig"); const [validationErrors, setValidationErrors] = createSignal([]); - const [showingErrorModal, setShowingErrorModal] = createSignal(false); + const [showingErrorModal, setShowingErrorModal] = + createSignal(false); const { eligibilityCheck, actions, actionInProgress, initialLoadStatus } = eligibilityCheckDetailResource(() => checkId); @@ -43,15 +48,31 @@ const EligibilityCheckDetail = () => { } else { toast.success("No validation errors found in DMN model."); } - } + }; const navbarDefs: Accessor = () => { return { tabDefs: [ - { key: "paramConfig", label: "Parameter Configuration", onClick: () => setScreenMode("paramConfig") }, - { key: "dmnDefinition", label: "DMN Definition", onClick: () => setScreenMode("dmnDefinition") }, - { key: "testing", label: "Testing", onClick: () => setScreenMode("testing") }, - { key: "publish", label: "Publish", onClick: () => setScreenMode("publish") }, + { + key: "paramConfig", + label: "Parameter Configuration", + onClick: () => setScreenMode("paramConfig"), + }, + { + key: "dmnDefinition", + label: "DMN Definition", + onClick: () => setScreenMode("dmnDefinition"), + }, + { + key: "testing", + label: "Testing", + onClick: () => setScreenMode("testing"), + }, + { + key: "publish", + label: "Publish", + onClick: () => setScreenMode("publish"), + }, ], activeTabKey: () => screenMode(), titleDef: { label: eligibilityCheck().name }, @@ -66,7 +87,11 @@ const EligibilityCheckDetail = () => {
- + { Validate Current DMN
actions.saveDmnModel(currentDmnModel())} > Save Changes diff --git a/builder-frontend/src/components/project/Project.tsx b/builder-frontend/src/components/project/Project.tsx index 5fb28868..a92d0b12 100644 --- a/builder-frontend/src/components/project/Project.tsx +++ b/builder-frontend/src/components/project/Project.tsx @@ -2,7 +2,7 @@ import { createSignal, createResource, Accessor } from "solid-js"; import { useParams } from "@solidjs/router"; import FormEditorView from "./FormEditorView"; -import Header from "../Header"; +import Header from "../Header/Header"; import Loading from "../Loading"; import ManageBenefits from "./manageBenefits/ManageBenefits"; import Preview from "./preview/Preview"; @@ -11,7 +11,6 @@ import Publish from "./Publish"; import { fetchProject } from "@/api/screener"; import BdtNavbar, { NavbarProps } from "@/components/shared/BdtNavbar"; - type TabOption = "manageBenefits" | "formEditor" | "preview" | "publish"; function Project() { @@ -34,16 +33,32 @@ function Project() { // including a dummy signal 'forceUpdate' that can be unique for // each call to the refetch () => [params.projectId, forceUpdate()], - fetchAndCacheProject + fetchAndCacheProject, ); const navbarDefs: Accessor = () => { return { tabDefs: [ - { key: "manageBenefits", label: "Manage Benefits", onClick: () => setActiveTab("manageBenefits") }, - { key: "formEditor", label: "Form Editor", onClick: () => setActiveTab("formEditor") }, - { key: "preview", label: "Preview", onClick: () => setActiveTab("preview") }, - { key: "publish", label: "Publish", onClick: () => setActiveTab("publish") }, + { + key: "manageBenefits", + label: "Manage Benefits", + onClick: () => setActiveTab("manageBenefits"), + }, + { + key: "formEditor", + label: "Form Editor", + onClick: () => setActiveTab("formEditor"), + }, + { + key: "preview", + label: "Preview", + onClick: () => setActiveTab("preview"), + }, + { + key: "publish", + label: "Publish", + onClick: () => setActiveTab("publish"), + }, ], activeTabKey: () => activeTab(), titleDef: { label: project().screenerName }, @@ -52,9 +67,9 @@ function Project() { return (
-
+
{project.loading ? ( - + ) : ( <> @@ -64,11 +79,9 @@ function Project() { setFormSchema={setFormSchema} /> )} - {activeTab() == "manageBenefits" && ( - - )} + {activeTab() == "manageBenefits" && } {activeTab() == "preview" && ( - + )} {activeTab() == "publish" && ( = (props) => { + const menuCtx = useHamburgerMenuContext(); + return ( + + ); +}; diff --git a/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuItem.tsx b/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuItem.tsx new file mode 100644 index 00000000..2ed37191 --- /dev/null +++ b/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuItem.tsx @@ -0,0 +1,13 @@ +import { Component } from "solid-js"; + +interface Props { + label: string; + onClick: () => void; +} +export const HamburgerMenuItem: Component = (props) => { + return ( + + ); +}; diff --git a/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuPanel.tsx b/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuPanel.tsx new file mode 100644 index 00000000..18a6c268 --- /dev/null +++ b/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuPanel.tsx @@ -0,0 +1,21 @@ +import { Component, JSX, Show } from "solid-js"; +import { useHamburgerMenuContext } from "./HamburgerMenuWrapper"; + +interface Props { + children: JSX.Element; +} +export const HamburgerMenuPanel: Component = (props) => { + const { showMenu, setShowMenu } = useHamburgerMenuContext(); + return ( + +
+ +
+
+ ); +}; diff --git a/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuWrapper.tsx b/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuWrapper.tsx new file mode 100644 index 00000000..336b4038 --- /dev/null +++ b/builder-frontend/src/components/shared/HamburgerMenu/HamburgerMenuWrapper.tsx @@ -0,0 +1,60 @@ +import { + createContext, + createSignal, + onCleanup, + onMount, + ParentComponent, + useContext, +} from "solid-js"; + +export type HamburgerMenuContextValue = { + showMenu: () => boolean; + setShowMenu: (show: boolean) => void; + toggle: () => void; +}; + +export const HamburgerMenuContext = createContext(); + +export const useHamburgerMenuContext = () => { + const ctx = useContext(HamburgerMenuContext); + if (!ctx) { + throw new Error( + "HamburgerMenu components must be used within ", + ); + } + return ctx; +}; + +export const HamburgerMenuWrapper: ParentComponent = (props) => { + const [root, setRoot] = createSignal(); + const [showMenu, setShowMenu] = createSignal(false); + + const handleClickOutside = (ev: MouseEvent) => { + const el = root(); + if (showMenu() && el && !el.contains(ev.target as Node)) { + setShowMenu(false); + } + }; + + const ctx: HamburgerMenuContextValue = { + showMenu, + setShowMenu, + toggle: () => setShowMenu(!showMenu()), + }; + + onMount(() => { + document.addEventListener("click", handleClickOutside); + }); + + onCleanup(() => { + document.removeEventListener("click", handleClickOutside); + }); + + return ( + + + + ); +}; diff --git a/builder-frontend/src/components/shared/HamburgerMenu/index.tsx b/builder-frontend/src/components/shared/HamburgerMenu/index.tsx new file mode 100644 index 00000000..3c81a233 --- /dev/null +++ b/builder-frontend/src/components/shared/HamburgerMenu/index.tsx @@ -0,0 +1,27 @@ +/** + * HamburgerMenu component + * + * Usage: + * - Wrap all sub-components in + * - Opens a panel on the left side (can be parameterized in the future) + * + * + * + * + * + * + * + * + * + */ + +import { HamburgerMenuWrapper } from "@/components/shared/HamburgerMenu/HamburgerMenuWrapper"; +import { HamburgerMenuButton } from "@/components/shared/HamburgerMenu/HamburgerMenuButton"; +import { HamburgerMenuPanel } from "@/components/shared/HamburgerMenu/HamburgerMenuPanel"; + +import "./HamburgerMenu.css"; + +export const HamburgerMenu = Object.assign(HamburgerMenuWrapper, { + Button: HamburgerMenuButton, + Panel: HamburgerMenuPanel, +});