From 536b81cc6fee9a22f1b74e50d42a5f5d06c4c38b Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Fri, 12 Dec 2025 13:13:44 -0500 Subject: [PATCH 01/18] Add prettier settings for TS/JS --- .prettierrc | 6 ++++++ benefit-decision-toolkit.code-workspace | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..654f09a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "tabWidth": 2 +} diff --git a/benefit-decision-toolkit.code-workspace b/benefit-decision-toolkit.code-workspace index 722c497..14a8e03 100644 --- a/benefit-decision-toolkit.code-workspace +++ b/benefit-decision-toolkit.code-workspace @@ -22,7 +22,16 @@ "java.import.maven.enabled": true, "java.compile.nullAnalysis.mode": "automatic", + // Prettier + "prettier.requireConfig": true, + // Node.js settings for frontend modules + "[javascript, typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "typescript.preferences.importModuleSpecifier": "non-relative", + "javascript.preferences.importModuleSpecifier": "non-relative" + }, "typescript.preferences.includePackageJsonAutoImports": "auto", "typescript.suggest.autoImports": true, @@ -108,4 +117,4 @@ } ] } -} \ No newline at end of file +} From 3f7c2c3031a88361b2a7708f57a5488395abc34c Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Fri, 12 Dec 2025 13:26:59 -0500 Subject: [PATCH 02/18] organized tsconfig and added strict checking --- builder-frontend/tsconfig.json | 36 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/builder-frontend/tsconfig.json b/builder-frontend/tsconfig.json index d5e8cbe..3c78901 100644 --- a/builder-frontend/tsconfig.json +++ b/builder-frontend/tsconfig.json @@ -1,24 +1,30 @@ { "compilerOptions": { - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "outDir": "dist", + /* Language and Environment */ "target": "es5", - "module": "esnext", - "types": ["vite/client"], + "lib": ["dom", "dom.iterable", "esnext"], "jsx": "preserve", "jsxImportSource": "solid-js", - "allowJs": true, - "allowSyntheticDefaultImports": true, + + /* Modules */ + "module": "esnext", "moduleResolution": "node", "paths": { - "@/*": ["./src/*"], - } + "@/*": ["./src/*"] + }, + "types": ["vite/client"], + + /* JavaScript Support */ + "allowJs": true, + + /* Emit */ + "outDir": "dist", + + /* Interop Constraints */ + "allowSyntheticDefaultImports": true, + + /* Type Checking */ + "strict": true }, - "include": [ - "src" - ] + "include": ["src/**/*"] } From af828edb6e21fa1f21e23b8986683f312ae1024b Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Fri, 12 Dec 2025 13:58:02 -0500 Subject: [PATCH 03/18] dev environment tools --- builder-frontend/package.json | 2 ++ .../src/context/{AuthContext.jsx => AuthContext.tsx} | 0 2 files changed, 2 insertions(+) rename builder-frontend/src/context/{AuthContext.jsx => AuthContext.tsx} (100%) diff --git a/builder-frontend/package.json b/builder-frontend/package.json index a9db017..1c56d8b 100644 --- a/builder-frontend/package.json +++ b/builder-frontend/package.json @@ -30,8 +30,10 @@ "@tailwindcss/postcss": "^4.1.8", "autoprefixer": "^10.4.21", "postcss": "^8.5.4", + "prettier": "^3.7.4", "sass-embedded": "^1.89.2", "tailwindcss": "^4.1.8", + "typescript": "^5.9.3", "vite": "^6.3.5", "vite-plugin-solid": "^2.11.6", "vite-tsconfig-paths": "^5.1.4" diff --git a/builder-frontend/src/context/AuthContext.jsx b/builder-frontend/src/context/AuthContext.tsx similarity index 100% rename from builder-frontend/src/context/AuthContext.jsx rename to builder-frontend/src/context/AuthContext.tsx From 97479c88f5f0234103cc1b0dbd05586c24129fb6 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Fri, 12 Dec 2025 13:58:26 -0500 Subject: [PATCH 04/18] dev environment tools --- builder-frontend/package-lock.json | 39 +++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/builder-frontend/package-lock.json b/builder-frontend/package-lock.json index 1fa01e4..93a03bd 100644 --- a/builder-frontend/package-lock.json +++ b/builder-frontend/package-lock.json @@ -29,8 +29,10 @@ "@tailwindcss/postcss": "^4.1.8", "autoprefixer": "^10.4.21", "postcss": "^8.5.4", + "prettier": "^3.7.4", "sass-embedded": "^1.89.2", "tailwindcss": "^4.1.8", + "typescript": "^5.9.3", "vite": "^6.3.5", "vite-plugin-solid": "^2.11.6", "vite-tsconfig-paths": "^5.1.4" @@ -3155,9 +3157,10 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" }, "node_modules/debug": { "version": "4.4.1", @@ -4845,6 +4848,22 @@ "url": "https://opencollective.com/preact" } }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5724,6 +5743,20 @@ "node": ">=4" } }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undici-types": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", From 2b0ae0089333ff7e47c74e91c6f79ae3e9626a84 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:01:35 -0500 Subject: [PATCH 05/18] added types to and fixed undefined returns --- builder-frontend/src/context/AuthContext.tsx | 49 ++++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/builder-frontend/src/context/AuthContext.tsx b/builder-frontend/src/context/AuthContext.tsx index 3bca567..65f3153 100644 --- a/builder-frontend/src/context/AuthContext.tsx +++ b/builder-frontend/src/context/AuthContext.tsx @@ -4,6 +4,8 @@ import { useContext, onCleanup, onMount, + Accessor, + ParentProps, } from "solid-js"; import { onAuthStateChanged, @@ -12,17 +14,28 @@ import { createUserWithEmailAndPassword, signInWithPopup, GoogleAuthProvider, + User, + UserCredential, + Unsubscribe, } from "firebase/auth"; import { auth } from "../firebase/firebase"; -const AuthContext = createContext(); +type AuthContextValue = { + user: Accessor; + isAuthLoading: Accessor; + login: (email: string, password: string) => Promise; + loginWithGoogle: () => Promise; + register: (email: string, password: string) => Promise; + logout: () => Promise; +}; +const AuthContext = createContext(); const googleProvider = new GoogleAuthProvider(); -export function AuthProvider(props) { - const [user, setUser] = createSignal("loading"); +export function AuthProvider(props: ParentProps) { + const [user, setUser] = createSignal(null); const [isAuthLoading, setIsAuthLoading] = createSignal(true); - let unsubscribe; + let unsubscribe: Unsubscribe | undefined; onMount(() => { unsubscribe = onAuthStateChanged(auth, (firebaseUser) => { @@ -36,25 +49,27 @@ export function AuthProvider(props) { if (unsubscribe) unsubscribe(); }); - const login = async (email, password) => { + const login = (email: string, password: string) => { return signInWithEmailAndPassword(auth, email, password); }; - const register = async (email, password) => { + const register = (email: string, password: string) => { return createUserWithEmailAndPassword(auth, email, password); }; const loginWithGoogle = async () => { - try { - return signInWithPopup(auth, googleProvider); - } catch (error) { + return signInWithPopup(auth, googleProvider).catch((error) => { console.error("Google sign-in error:", error.message); - } + return null; + }); + // try { + // return signInWithPopup(auth, googleProvider); + // } catch (error) { + // console.error("Google sign-in error:", error.message); + // } }; - const logout = async () => { - await signOut(auth); - }; + const logout = () => signOut(auth); return ( "); + } + + return ctx; } From 13d49dafd947895a26a75a486a5ba0b30d15e87c Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:38:44 -0500 Subject: [PATCH 06/18] fixed 's useAuth type, simplified routing with a layout () and nested routing --- benefit-decision-toolkit.code-workspace | 6 +-- builder-frontend/src/App.tsx | 47 +++++++++++------ builder-frontend/src/components/Header.tsx | 22 +++++--- .../src/components/auth/AuthForm.jsx | 12 ++--- .../src/components/homeScreen/HomeScreen.tsx | 31 +++++++---- .../EligibilityCheckDetail.tsx | 52 ++++++++++++++----- .../src/components/project/Project.tsx | 36 ++++++++----- 7 files changed, 139 insertions(+), 67 deletions(-) diff --git a/benefit-decision-toolkit.code-workspace b/benefit-decision-toolkit.code-workspace index 14a8e03..841db03 100644 --- a/benefit-decision-toolkit.code-workspace +++ b/benefit-decision-toolkit.code-workspace @@ -28,10 +28,10 @@ // Node.js settings for frontend modules "[javascript, typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "typescript.preferences.importModuleSpecifier": "non-relative", - "javascript.preferences.importModuleSpecifier": "non-relative" + "editor.formatOnSave": true }, + "typescript.preferences.importModuleSpecifier": "non-relative", + "javascript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.includePackageJsonAutoImports": "auto", "typescript.suggest.autoImports": true, diff --git a/builder-frontend/src/App.tsx b/builder-frontend/src/App.tsx index edd7ffb..0081d3a 100644 --- a/builder-frontend/src/App.tsx +++ b/builder-frontend/src/App.tsx @@ -1,29 +1,31 @@ +import { Match, ParentProps, Switch } from "solid-js"; import { Navigate, Route } from "@solidjs/router"; import Project from "./components/project/Project"; import AuthForm from "./components/auth/AuthForm"; import { useAuth } from "./context/AuthContext"; import HomeScreen from "./components/homeScreen/HomeScreen"; -import EligibilityCheckDetail from "./components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail"; import Screener from "./components/screener/Screener"; import Loading from "./components/Loading"; -import { Match, Switch } from "solid-js"; - +import ProjectsList from "@/components/homeScreen/ProjectsList"; +import EligibilityChecksList from "@/components/homeScreen/eligibilityCheckList/EligibilityChecksList"; +import EligibilityCheckDetail from "./components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail"; +import Header from "@/components/Header"; -const ProtectedRoute = (props) => { +const ProtectedRoute = (props: ParentProps) => { const { user, isAuthLoading } = useAuth(); - - // If user is logged in, render the requested component, otherwise redirect to login + return ( - - + + - - + +
+ {props.children} ); @@ -32,13 +34,24 @@ const ProtectedRoute = (props) => { function App() { return ( <> - - - } /> - } /> - } /> - -
404 - Page Not Found
} /> + + + + + + + + + + + + + + +
404 - Page Not Found
} + /> ); } diff --git a/builder-frontend/src/components/Header.tsx b/builder-frontend/src/components/Header.tsx index bb42198..709f55d 100644 --- a/builder-frontend/src/components/Header.tsx +++ b/builder-frontend/src/components/Header.tsx @@ -2,10 +2,15 @@ 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"; +import { createMemo, Show } from "solid-js"; - -const HeaderButton = ({ buttonText, onClick }: { buttonText: string; onClick: () => void }) => { +const HeaderButton = ({ + buttonText, + onClick, +}: { + buttonText: string; + onClick: () => void; +}) => { return (
); -} +}; export default function Header() { const { logout } = useAuth(); const navigate = useNavigate(); const location = useLocation(); - const isNotRoot = location.pathname !== "/"; + const isNotRoot = createMemo(() => location.pathname !== "/"); const handleLogout = () => { logout(); @@ -37,8 +42,11 @@ export default function Header() {
- - navigate("/")} /> + + navigate("/")} + />
diff --git a/builder-frontend/src/components/auth/AuthForm.jsx b/builder-frontend/src/components/auth/AuthForm.jsx index 744537a..76d1074 100644 --- a/builder-frontend/src/components/auth/AuthForm.jsx +++ b/builder-frontend/src/components/auth/AuthForm.jsx @@ -12,10 +12,10 @@ export default function AuthForm() { const location = useLocation(); const toggleMode = () => { - if (location.pathname === "/login") { - navigate("/signup"); + if (location.pathname === "/signup") { + navigate("/"); } else { - navigate("/login"); + navigate("/signup"); } }; @@ -38,10 +38,10 @@ export default function AuthForm() {
Benefits Decision Tookit
- {location.pathname === "/login" ? ( - - ) : ( + {location.pathname === "/signup" ? ( + ) : ( + )}

diff --git a/builder-frontend/src/components/homeScreen/HomeScreen.tsx b/builder-frontend/src/components/homeScreen/HomeScreen.tsx index f6e24b3..a1577eb 100644 --- a/builder-frontend/src/components/homeScreen/HomeScreen.tsx +++ b/builder-frontend/src/components/homeScreen/HomeScreen.tsx @@ -1,28 +1,39 @@ import { Accessor, createSignal, Match, Switch } from "solid-js"; import EligibilityChecksList from "./eligibilityCheckList/EligibilityChecksList"; -import ProjectsList from "./ProjectsList" +import ProjectsList from "./ProjectsList"; import Header from "../Header"; -import BdtNavbar, { NavbarProps } from "@/components/shared/BdtNavbar";0 +import BdtNavbar, { NavbarProps } from "@/components/shared/BdtNavbar"; +0; const HomeScreen = () => { - const [screenMode, setScreenMode] = createSignal<"screeners" | "checks">("screeners"); + const [screenMode, setScreenMode] = createSignal<"screeners" | "checks">( + "screeners", + ); const navbarDefs: Accessor = () => { return { tabDefs: [ - { key: "screeners", label: "Screeners", onClick: () => setScreenMode("screeners") }, - { key: "checks", label: "Eligibility checks", onClick: () => setScreenMode("checks") }, + { + key: "screeners", + label: "Screeners", + onClick: () => setScreenMode("screeners"), + }, + { + key: "checks", + label: "Eligibility checks", + onClick: () => setScreenMode("checks"), + }, ], activeTabKey: () => screenMode(), titleDef: null, - } + }; }; return (
-
+ {/*
*/} @@ -33,6 +44,6 @@ const HomeScreen = () => {
- ) -} -export default HomeScreen; \ No newline at end of file + ); +}; +export default HomeScreen; diff --git a/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail.tsx b/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail.tsx index 786b640..6bf907c 100644 --- a/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail.tsx +++ b/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail.tsx @@ -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 }, @@ -63,10 +84,13 @@ 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 5fb2886..fabf595 100644 --- a/builder-frontend/src/components/project/Project.tsx +++ b/builder-frontend/src/components/project/Project.tsx @@ -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,8 @@ function Project() { return (
-
{project.loading ? ( - + ) : ( <> @@ -64,11 +78,9 @@ function Project() { setFormSchema={setFormSchema} /> )} - {activeTab() == "manageBenefits" && ( - - )} + {activeTab() == "manageBenefits" && } {activeTab() == "preview" && ( - + )} {activeTab() == "publish" && ( Date: Fri, 12 Dec 2025 21:36:17 -0500 Subject: [PATCH 07/18] add dynamic to each page, moved logos to static resources and simplified header links, used solidjs's <A> component for navbar links + simplified navbar generation --- builder-frontend/index.html | 3 +- builder-frontend/package-lock.json | 581 +++++++++++++++++- builder-frontend/package.json | 2 + .../logos/bdt-logo-large-mono-dark.svg | 0 .../logos/bdt-logo-large-mono-light.svg | 0 builder-frontend/src/App.tsx | 13 +- builder-frontend/src/components/Header.css | 13 + builder-frontend/src/components/Header.tsx | 56 +- .../src/components/auth/AuthForm.jsx | 11 +- .../src/components/homeScreen/HomeScreen.tsx | 29 +- .../src/components/shared/ANavBar.css | 11 + .../src/components/shared/ANavBar.tsx | 21 + builder-frontend/src/index.tsx | 20 +- builder-frontend/vite.config.js | 6 +- 14 files changed, 674 insertions(+), 92 deletions(-) rename builder-frontend/{src/assets => public}/logos/bdt-logo-large-mono-dark.svg (100%) rename builder-frontend/{src/assets => public}/logos/bdt-logo-large-mono-light.svg (100%) create mode 100644 builder-frontend/src/components/Header.css create mode 100644 builder-frontend/src/components/shared/ANavBar.css create mode 100644 builder-frontend/src/components/shared/ANavBar.tsx diff --git a/builder-frontend/index.html b/builder-frontend/index.html index 7ca8c16..a9fdfc7 100644 --- a/builder-frontend/index.html +++ b/builder-frontend/index.html @@ -1,4 +1,4 @@ -<!DOCTYPE html> +<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> @@ -10,7 +10,6 @@ integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" /> - <title>Benefit Decision Toolkit
diff --git a/builder-frontend/package-lock.json b/builder-frontend/package-lock.json index 93a03bd..9e0c555 100644 --- a/builder-frontend/package-lock.json +++ b/builder-frontend/package-lock.json @@ -14,6 +14,7 @@ "@kogito-tooling/kie-editors-standalone": "^0.16.0", "@solid-primitives/deep": "^0.3.3", "@solid-primitives/resource": "^0.4.2", + "@solidjs/meta": "^0.29.4", "@solidjs/router": "^0.15.3", "dmn-js": "^17.2.1", "fast-xml-parser": "^5.2.5", @@ -27,6 +28,7 @@ "devDependencies": { "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.8", + "@tailwindcss/vite": "^4.1.18", "autoprefixer": "^10.4.21", "postcss": "^8.5.4", "prettier": "^3.7.4", @@ -1660,9 +1662,10 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -2365,6 +2368,15 @@ "solid-js": "^1.6.12" } }, + "node_modules/@solidjs/meta": { + "version": "0.29.4", + "resolved": "https://registry.npmjs.org/@solidjs/meta/-/meta-0.29.4.tgz", + "integrity": "sha512-zdIWBGpR9zGx1p1bzIPqF5Gs+Ks/BH8R6fWhmUa/dcK1L2rUC8BAcZJzNRYBQv74kScf1TSOs0EY//Vd/I0V8g==", + "license": "MIT", + "peerDependencies": { + "solid-js": ">=1.8.4" + } + }, "node_modules/@solidjs/router": { "version": "0.15.3", "resolved": "https://registry.npmjs.org/@solidjs/router/-/router-0.15.3.tgz", @@ -2662,6 +2674,525 @@ "tailwindcss": "4.1.8" } }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", + "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.18", + "@tailwindcss/oxide": "4.1.18", + "tailwindcss": "4.1.18" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/node": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", + "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.18" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", + "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-x64": "4.1.18", + "@tailwindcss/oxide-freebsd-x64": "4.1.18", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-x64-musl": "4.1.18", + "@tailwindcss/oxide-wasm32-wasi": "4.1.18", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", + "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", + "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", + "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", + "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", + "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", + "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", + "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", + "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", + "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", + "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.0", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", + "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", + "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/vite/node_modules/tailwindcss": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3424,10 +3955,11 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -4051,10 +4583,11 @@ } }, "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, + "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -4205,6 +4738,27 @@ "lightningcss-win32-x64-msvc": "1.30.1" } }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lightningcss-darwin-arm64": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", @@ -4473,11 +5027,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/marked": { diff --git a/builder-frontend/package.json b/builder-frontend/package.json index 1c56d8b..0dc7b24 100644 --- a/builder-frontend/package.json +++ b/builder-frontend/package.json @@ -15,6 +15,7 @@ "@kogito-tooling/kie-editors-standalone": "^0.16.0", "@solid-primitives/deep": "^0.3.3", "@solid-primitives/resource": "^0.4.2", + "@solidjs/meta": "^0.29.4", "@solidjs/router": "^0.15.3", "dmn-js": "^17.2.1", "fast-xml-parser": "^5.2.5", @@ -28,6 +29,7 @@ "devDependencies": { "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.8", + "@tailwindcss/vite": "^4.1.18", "autoprefixer": "^10.4.21", "postcss": "^8.5.4", "prettier": "^3.7.4", diff --git a/builder-frontend/src/assets/logos/bdt-logo-large-mono-dark.svg b/builder-frontend/public/logos/bdt-logo-large-mono-dark.svg similarity index 100% rename from builder-frontend/src/assets/logos/bdt-logo-large-mono-dark.svg rename to builder-frontend/public/logos/bdt-logo-large-mono-dark.svg diff --git a/builder-frontend/src/assets/logos/bdt-logo-large-mono-light.svg b/builder-frontend/public/logos/bdt-logo-large-mono-light.svg similarity index 100% rename from builder-frontend/src/assets/logos/bdt-logo-large-mono-light.svg rename to builder-frontend/public/logos/bdt-logo-large-mono-light.svg diff --git a/builder-frontend/src/App.tsx b/builder-frontend/src/App.tsx index 0081d3a..60f2694 100644 --- a/builder-frontend/src/App.tsx +++ b/builder-frontend/src/App.tsx @@ -1,5 +1,5 @@ import { Match, ParentProps, Switch } from "solid-js"; -import { Navigate, Route } from "@solidjs/router"; +import { Route } from "@solidjs/router"; import Project from "./components/project/Project"; import AuthForm from "./components/auth/AuthForm"; @@ -11,10 +11,16 @@ import ProjectsList from "@/components/homeScreen/ProjectsList"; import EligibilityChecksList from "@/components/homeScreen/eligibilityCheckList/EligibilityChecksList"; import EligibilityCheckDetail from "./components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/EligibilityCheckDetail"; import Header from "@/components/Header"; +import ANavBar from "@/components/shared/ANavBar"; const ProtectedRoute = (props: ParentProps) => { const { user, isAuthLoading } = useAuth(); + const navbarItems = [ + { label: "Projects", href: "/project" }, + { label: "Eligibility checks", href: "/check" }, + ]; + return ( @@ -25,6 +31,7 @@ const ProtectedRoute = (props: ParentProps) => {
+ {props.children} @@ -39,11 +46,11 @@ function App() { - + - + diff --git a/builder-frontend/src/components/Header.css b/builder-frontend/src/components/Header.css new file mode 100644 index 0000000..4d78934 --- /dev/null +++ b/builder-frontend/src/components/Header.css @@ -0,0 +1,13 @@ +@import "tailwindcss"; + +.header { + @apply bg-gray-200 min-h-24 h-24 px-4 flex items-center justify-between border-b-2 border-gray-300; +} +.logout-btn, +.back-to-home { + @apply px-4 py-3 font-bold text-gray-700 rounded-md flex items-center cursor-pointer select-none no-underline; +} +.logout-btn:hover, +.back-to-home:hover { + @apply bg-gray-300 text-gray-700; +} diff --git a/builder-frontend/src/components/Header.tsx b/builder-frontend/src/components/Header.tsx index 709f55d..65e4f32 100644 --- a/builder-frontend/src/components/Header.tsx +++ b/builder-frontend/src/components/Header.tsx @@ -1,28 +1,8 @@ -import { useAuth } from "../context/AuthContext"; +import { createMemo } from "solid-js"; import { useLocation, useNavigate } from "@solidjs/router"; -import bdtLogo from "../assets/logos/bdt-logo-large-mono-light.svg"; -import { createMemo, Show } from "solid-js"; - -const HeaderButton = ({ - buttonText, - onClick, -}: { - buttonText: string; - onClick: () => void; -}) => { - return ( -
- {buttonText} -
- ); -}; +import { useAuth } from "../context/AuthContext"; +import "./Header.css"; export default function Header() { const { logout } = useAuth(); @@ -37,18 +17,24 @@ export default function Header() { }; return ( -
-
- -
-
- - navigate("/")} - /> - - +
+ + BDT logo + + +
+ {isNotRoot() && ( + + ← Back to home + + )} +
); diff --git a/builder-frontend/src/components/auth/AuthForm.jsx b/builder-frontend/src/components/auth/AuthForm.jsx index 76d1074..cf74dd8 100644 --- a/builder-frontend/src/components/auth/AuthForm.jsx +++ b/builder-frontend/src/components/auth/AuthForm.jsx @@ -4,6 +4,7 @@ import { useAuth } from "../../context/AuthContext"; import Login from "./Login"; import Signup from "./Signup"; import { useLocation, useNavigate } from "@solidjs/router"; +import { Title } from "@solidjs/meta"; export default function AuthForm() { const [isSigningIn, setIsSigningIn] = createSignal(false); @@ -39,9 +40,15 @@ export default function AuthForm() { Benefits Decision Tookit
{location.pathname === "/signup" ? ( - + <> + Sign up to Builder Decision Toolkit + + ) : ( - + <> + Log in to Builder Decision Toolkit + + )}

diff --git a/builder-frontend/src/components/homeScreen/HomeScreen.tsx b/builder-frontend/src/components/homeScreen/HomeScreen.tsx index a1577eb..619a102 100644 --- a/builder-frontend/src/components/homeScreen/HomeScreen.tsx +++ b/builder-frontend/src/components/homeScreen/HomeScreen.tsx @@ -1,40 +1,17 @@ -import { Accessor, createSignal, Match, Switch } from "solid-js"; +import { createSignal, Match, Switch } from "solid-js"; +import { Title } from "@solidjs/meta"; import EligibilityChecksList from "./eligibilityCheckList/EligibilityChecksList"; import ProjectsList from "./ProjectsList"; -import Header from "../Header"; - -import BdtNavbar, { NavbarProps } from "@/components/shared/BdtNavbar"; -0; const HomeScreen = () => { const [screenMode, setScreenMode] = createSignal<"screeners" | "checks">( "screeners", ); - const navbarDefs: Accessor = () => { - return { - tabDefs: [ - { - key: "screeners", - label: "Screeners", - onClick: () => setScreenMode("screeners"), - }, - { - key: "checks", - label: "Eligibility checks", - onClick: () => setScreenMode("checks"), - }, - ], - activeTabKey: () => screenMode(), - titleDef: null, - }; - }; - return (
- {/*
*/} - + BDT Home diff --git a/builder-frontend/src/components/shared/ANavBar.css b/builder-frontend/src/components/shared/ANavBar.css new file mode 100644 index 0000000..1e83aec --- /dev/null +++ b/builder-frontend/src/components/shared/ANavBar.css @@ -0,0 +1,11 @@ +@import "tailwindcss"; + +.navbarlink { + @apply px-4 py-2 no-underline border-b-2 hover:bg-gray-200 transition-colors; +} +.navbarlink.inactive { + @apply border-transparent text-gray-500 hover:text-gray-700; +} +.navbarlink.active { + @apply border-b border-gray-700 text-gray-700; +} diff --git a/builder-frontend/src/components/shared/ANavBar.tsx b/builder-frontend/src/components/shared/ANavBar.tsx new file mode 100644 index 0000000..97a3d92 --- /dev/null +++ b/builder-frontend/src/components/shared/ANavBar.tsx @@ -0,0 +1,21 @@ +import type { Component } from "solid-js"; +import { A } from "@solidjs/router"; + +import "./ANavBar.css"; + +interface Props { + items: { label: string; href: string }[]; +} +const ANavBar: Component = (props) => { + return ( +
+ {props.items.map(({ label, href }) => ( + + {label} + + ))} +
+ ); +}; + +export default ANavBar; diff --git a/builder-frontend/src/index.tsx b/builder-frontend/src/index.tsx index 2b5e226..6b1968d 100644 --- a/builder-frontend/src/index.tsx +++ b/builder-frontend/src/index.tsx @@ -2,23 +2,27 @@ import { render } from "solid-js/web"; import { Router } from "@solidjs/router"; -import { Toaster } from 'solid-toast'; +import { Toaster } from "solid-toast"; import { AuthProvider } from "./context/AuthContext.jsx"; import App from "./App"; import "./index.css"; +import { MetaProvider, Title } from "@solidjs/meta"; const root = document.getElementById("root"); render( () => ( - - - - - - + + Benefit Decision Toolkit + + + + + + + ), - root + root, ); diff --git a/builder-frontend/vite.config.js b/builder-frontend/vite.config.js index ed31482..33f2570 100644 --- a/builder-frontend/vite.config.js +++ b/builder-frontend/vite.config.js @@ -1,10 +1,10 @@ import { defineConfig } from "vite"; import solid from "vite-plugin-solid"; -import tsconfigPaths from 'vite-tsconfig-paths' +import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ plugins: [solid(), tsconfigPaths()], server: { - port: process.env.DEV_SERVER_PORT || 5173 - } + port: process.env.DEV_SERVER_PORT || 5173, + }, }); From 388c5f973bf6d0c7e25faa1542ccf6c0f887d7e2 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Fri, 12 Dec 2025 23:20:31 -0500 Subject: [PATCH 08/18] added server proxy in Vite config to connect to backend /api/* urls --- builder-frontend/.env.example | 2 +- .../ProjectDetails}/FormEditorView.tsx | 0 .../ProjectDetails}/Project.tsx | 0 .../ProjectDetails}/Publish.tsx | 0 .../FilterFormComponentsModule.tsx | 0 .../customFormFields/YesNoQuestion.ts | 0 .../customFormFields/index.js | 0 .../manageBenefits/ManageBenefits.tsx | 0 .../benefitList/BenefitList.tsx | 0 .../benefitList/modals/AddNewBenefitModal.tsx | 0 .../modals/SelectExistingBenefitModal.tsx | 0 .../benefitList/screenerBenefitsResource.ts | 0 .../configureBenefit/ConfigureBenefit.tsx | 0 .../EligibilityCheckListView.tsx | 0 .../SelectedEligibilityCheck.tsx | 0 .../configureBenefit/benefitResource.ts | 0 .../modals/ConfigureCheckModal.tsx | 0 .../ProjectDetails}/preview/FormRenderer.tsx | 0 .../ProjectDetails}/preview/Preview.tsx | 0 .../ProjectDetails}/preview/Results.tsx | 0 .../ProjectDetails}/preview/types.ts | 0 .../{homeScreen => Project}/ProjectsList.tsx | 0 builder-frontend/vite.config.js | 23 ++++++++++++++----- 23 files changed, 18 insertions(+), 7 deletions(-) rename builder-frontend/src/components/{project => Project/ProjectDetails}/FormEditorView.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/Project.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/Publish.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/formJsExtensions/FilterFormComponentsModule.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/formJsExtensions/customFormFields/YesNoQuestion.ts (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/formJsExtensions/customFormFields/index.js (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/ManageBenefits.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/benefitList/BenefitList.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/benefitList/modals/AddNewBenefitModal.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/benefitList/modals/SelectExistingBenefitModal.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/benefitList/screenerBenefitsResource.ts (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/configureBenefit/ConfigureBenefit.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/configureBenefit/EligibilityCheckListView.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/configureBenefit/SelectedEligibilityCheck.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/configureBenefit/benefitResource.ts (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/manageBenefits/configureBenefit/modals/ConfigureCheckModal.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/preview/FormRenderer.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/preview/Preview.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/preview/Results.tsx (100%) rename builder-frontend/src/components/{project => Project/ProjectDetails}/preview/types.ts (100%) rename builder-frontend/src/components/{homeScreen => Project}/ProjectsList.tsx (100%) diff --git a/builder-frontend/.env.example b/builder-frontend/.env.example index 78624a7..1bf4241 100644 --- a/builder-frontend/.env.example +++ b/builder-frontend/.env.example @@ -1,5 +1,5 @@ VITE_API_KEY=AIzaSyDummyKeyForEmulator -VITE_API_URL=http://localhost:8081/api +VITE_API_URL=http://localhost:8081 VITE_APP_ID=1:123456789:web:abcdef VITE_AUTH_DOMAIN=localhost:9099 VITE_MEASUREMENT_ID=G-XXXXXXXXXX diff --git a/builder-frontend/src/components/project/FormEditorView.tsx b/builder-frontend/src/components/Project/ProjectDetails/FormEditorView.tsx similarity index 100% rename from builder-frontend/src/components/project/FormEditorView.tsx rename to builder-frontend/src/components/Project/ProjectDetails/FormEditorView.tsx diff --git a/builder-frontend/src/components/project/Project.tsx b/builder-frontend/src/components/Project/ProjectDetails/Project.tsx similarity index 100% rename from builder-frontend/src/components/project/Project.tsx rename to builder-frontend/src/components/Project/ProjectDetails/Project.tsx diff --git a/builder-frontend/src/components/project/Publish.tsx b/builder-frontend/src/components/Project/ProjectDetails/Publish.tsx similarity index 100% rename from builder-frontend/src/components/project/Publish.tsx rename to builder-frontend/src/components/Project/ProjectDetails/Publish.tsx diff --git a/builder-frontend/src/components/project/formJsExtensions/FilterFormComponentsModule.tsx b/builder-frontend/src/components/Project/ProjectDetails/formJsExtensions/FilterFormComponentsModule.tsx similarity index 100% rename from builder-frontend/src/components/project/formJsExtensions/FilterFormComponentsModule.tsx rename to builder-frontend/src/components/Project/ProjectDetails/formJsExtensions/FilterFormComponentsModule.tsx diff --git a/builder-frontend/src/components/project/formJsExtensions/customFormFields/YesNoQuestion.ts b/builder-frontend/src/components/Project/ProjectDetails/formJsExtensions/customFormFields/YesNoQuestion.ts similarity index 100% rename from builder-frontend/src/components/project/formJsExtensions/customFormFields/YesNoQuestion.ts rename to builder-frontend/src/components/Project/ProjectDetails/formJsExtensions/customFormFields/YesNoQuestion.ts diff --git a/builder-frontend/src/components/project/formJsExtensions/customFormFields/index.js b/builder-frontend/src/components/Project/ProjectDetails/formJsExtensions/customFormFields/index.js similarity index 100% rename from builder-frontend/src/components/project/formJsExtensions/customFormFields/index.js rename to builder-frontend/src/components/Project/ProjectDetails/formJsExtensions/customFormFields/index.js diff --git a/builder-frontend/src/components/project/manageBenefits/ManageBenefits.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/ManageBenefits.tsx similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/ManageBenefits.tsx rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/ManageBenefits.tsx diff --git a/builder-frontend/src/components/project/manageBenefits/benefitList/BenefitList.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/BenefitList.tsx similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/benefitList/BenefitList.tsx rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/BenefitList.tsx diff --git a/builder-frontend/src/components/project/manageBenefits/benefitList/modals/AddNewBenefitModal.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/modals/AddNewBenefitModal.tsx similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/benefitList/modals/AddNewBenefitModal.tsx rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/modals/AddNewBenefitModal.tsx diff --git a/builder-frontend/src/components/project/manageBenefits/benefitList/modals/SelectExistingBenefitModal.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/modals/SelectExistingBenefitModal.tsx similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/benefitList/modals/SelectExistingBenefitModal.tsx rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/modals/SelectExistingBenefitModal.tsx diff --git a/builder-frontend/src/components/project/manageBenefits/benefitList/screenerBenefitsResource.ts b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/screenerBenefitsResource.ts similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/benefitList/screenerBenefitsResource.ts rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/screenerBenefitsResource.ts diff --git a/builder-frontend/src/components/project/manageBenefits/configureBenefit/ConfigureBenefit.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/ConfigureBenefit.tsx similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/configureBenefit/ConfigureBenefit.tsx rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/ConfigureBenefit.tsx diff --git a/builder-frontend/src/components/project/manageBenefits/configureBenefit/EligibilityCheckListView.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/EligibilityCheckListView.tsx similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/configureBenefit/EligibilityCheckListView.tsx rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/EligibilityCheckListView.tsx diff --git a/builder-frontend/src/components/project/manageBenefits/configureBenefit/SelectedEligibilityCheck.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/SelectedEligibilityCheck.tsx similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/configureBenefit/SelectedEligibilityCheck.tsx rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/SelectedEligibilityCheck.tsx diff --git a/builder-frontend/src/components/project/manageBenefits/configureBenefit/benefitResource.ts b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/benefitResource.ts similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/configureBenefit/benefitResource.ts rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/benefitResource.ts diff --git a/builder-frontend/src/components/project/manageBenefits/configureBenefit/modals/ConfigureCheckModal.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/modals/ConfigureCheckModal.tsx similarity index 100% rename from builder-frontend/src/components/project/manageBenefits/configureBenefit/modals/ConfigureCheckModal.tsx rename to builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/modals/ConfigureCheckModal.tsx diff --git a/builder-frontend/src/components/project/preview/FormRenderer.tsx b/builder-frontend/src/components/Project/ProjectDetails/preview/FormRenderer.tsx similarity index 100% rename from builder-frontend/src/components/project/preview/FormRenderer.tsx rename to builder-frontend/src/components/Project/ProjectDetails/preview/FormRenderer.tsx diff --git a/builder-frontend/src/components/project/preview/Preview.tsx b/builder-frontend/src/components/Project/ProjectDetails/preview/Preview.tsx similarity index 100% rename from builder-frontend/src/components/project/preview/Preview.tsx rename to builder-frontend/src/components/Project/ProjectDetails/preview/Preview.tsx diff --git a/builder-frontend/src/components/project/preview/Results.tsx b/builder-frontend/src/components/Project/ProjectDetails/preview/Results.tsx similarity index 100% rename from builder-frontend/src/components/project/preview/Results.tsx rename to builder-frontend/src/components/Project/ProjectDetails/preview/Results.tsx diff --git a/builder-frontend/src/components/project/preview/types.ts b/builder-frontend/src/components/Project/ProjectDetails/preview/types.ts similarity index 100% rename from builder-frontend/src/components/project/preview/types.ts rename to builder-frontend/src/components/Project/ProjectDetails/preview/types.ts diff --git a/builder-frontend/src/components/homeScreen/ProjectsList.tsx b/builder-frontend/src/components/Project/ProjectsList.tsx similarity index 100% rename from builder-frontend/src/components/homeScreen/ProjectsList.tsx rename to builder-frontend/src/components/Project/ProjectsList.tsx diff --git a/builder-frontend/vite.config.js b/builder-frontend/vite.config.js index 33f2570..7322ee6 100644 --- a/builder-frontend/vite.config.js +++ b/builder-frontend/vite.config.js @@ -1,10 +1,21 @@ -import { defineConfig } from "vite"; +import { defineConfig, loadEnv } from "vite"; import solid from "vite-plugin-solid"; import tsconfigPaths from "vite-tsconfig-paths"; -export default defineConfig({ - plugins: [solid(), tsconfigPaths()], - server: { - port: process.env.DEV_SERVER_PORT || 5173, - }, +export default defineConfig(({ mode }) => { + // Load variables from .env before vite.config.js finishes running + const env = loadEnv(mode, process.cwd(), ""); + return { + plugins: [solid(), tsconfigPaths()], + server: { + port: process.env.DEV_SERVER_PORT || 5173, + // Proxy to connect to backend + // https://vite.dev/config/server-options#server-proxy + proxy: { + "/api": { + target: env.VITE_API_URL || "http://localhost:8081", + }, + }, + }, + }; }); From 884cf88f98a0da63e72d45178bbbf041dbcf7822 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Sun, 14 Dec 2025 01:01:10 -0500 Subject: [PATCH 09/18] converted JSX to TSX --- builder-frontend/src/api/{auth.js => auth.ts} | 0 .../homeScreen/{DeleteConfirmation.jsx => DeleteConfirmation.tsx} | 0 .../homeScreen/{EditScreenerForm.jsx => EditScreenerForm.tsx} | 0 .../homeScreen/{NewScreenerForm.jsx => NewScreenerForm.tsx} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename builder-frontend/src/api/{auth.js => auth.ts} (100%) rename builder-frontend/src/components/homeScreen/{DeleteConfirmation.jsx => DeleteConfirmation.tsx} (100%) rename builder-frontend/src/components/homeScreen/{EditScreenerForm.jsx => EditScreenerForm.tsx} (100%) rename builder-frontend/src/components/homeScreen/{NewScreenerForm.jsx => NewScreenerForm.tsx} (100%) diff --git a/builder-frontend/src/api/auth.js b/builder-frontend/src/api/auth.ts similarity index 100% rename from builder-frontend/src/api/auth.js rename to builder-frontend/src/api/auth.ts diff --git a/builder-frontend/src/components/homeScreen/DeleteConfirmation.jsx b/builder-frontend/src/components/homeScreen/DeleteConfirmation.tsx similarity index 100% rename from builder-frontend/src/components/homeScreen/DeleteConfirmation.jsx rename to builder-frontend/src/components/homeScreen/DeleteConfirmation.tsx diff --git a/builder-frontend/src/components/homeScreen/EditScreenerForm.jsx b/builder-frontend/src/components/homeScreen/EditScreenerForm.tsx similarity index 100% rename from builder-frontend/src/components/homeScreen/EditScreenerForm.jsx rename to builder-frontend/src/components/homeScreen/EditScreenerForm.tsx diff --git a/builder-frontend/src/components/homeScreen/NewScreenerForm.jsx b/builder-frontend/src/components/homeScreen/NewScreenerForm.tsx similarity index 100% rename from builder-frontend/src/components/homeScreen/NewScreenerForm.jsx rename to builder-frontend/src/components/homeScreen/NewScreenerForm.tsx From a917e06b925f40fb7a8a2bb1134b891346fae832 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Sun, 14 Dec 2025 01:16:34 -0500 Subject: [PATCH 10/18] updated api/screener with simplified fetch url (vite server proxy fix), started using authGet and authPost, added minimal projects type --- builder-frontend/src/api/auth.ts | 32 ++++++++---- builder-frontend/src/api/screener.ts | 77 +++++++++++++--------------- builder-frontend/src/types.ts | 11 ++++ 3 files changed, 69 insertions(+), 51 deletions(-) diff --git a/builder-frontend/src/api/auth.ts b/builder-frontend/src/api/auth.ts index f44572f..1e0c385 100644 --- a/builder-frontend/src/api/auth.ts +++ b/builder-frontend/src/api/auth.ts @@ -1,17 +1,27 @@ import { auth } from "../firebase/firebase.js"; +type RestMethod = "GET" | "POST"; +type FetchInit = { + headers: Record; + body: string; +}; -export async function authFetch(input, init = {}) { - const user = auth.currentUser; +export const authFetch = + (method: RestMethod) => async (url: string, init?: FetchInit) => { + const user = auth.currentUser; - // If no user is logged in, you can handle it accordingly - if (!user) { - throw new Error("User not authenticated"); - } + // If no user is logged in, you can handle it accordingly + if (!user) { + throw new Error("User not authenticated"); + } - const token = await user.getIdToken(); - const headers = new Headers(init.headers || {}); + const token = await user.getIdToken(); + const basicHeaders = { Accept: "application/json" }; + const headers = new Headers({ ...basicHeaders, ...init?.headers }); + headers.set("Authorization", `Bearer ${token}`); - headers.set("Authorization", `Bearer ${token}`); - return fetch(input, { ...init, headers }); -} + return fetch(url, { method, ...init, headers }); + }; + +export const authGet = authFetch("GET"); +export const authPost = authFetch("POST"); diff --git a/builder-frontend/src/api/screener.ts b/builder-frontend/src/api/screener.ts index 8c3fb8f..891bd04 100644 --- a/builder-frontend/src/api/screener.ts +++ b/builder-frontend/src/api/screener.ts @@ -1,18 +1,11 @@ -import { authFetch } from "@/api/auth"; +import { authFetch, authGet, authPost } from "@/api/auth"; -import type { BenefitDetail, ScreenerResult } from "@/types"; +import type { BenefitDetail, Project, ScreenerResult } from "@/types"; -const apiUrl = import.meta.env.VITE_API_URL; - -export const fetchProjects = async () => { - const url = apiUrl + "/screeners"; +export const fetchProjects = async (): Promise => { + const url = "/api/screeners"; try { - const response = await authFetch(url, { - method: "GET", - headers: { - Accept: "application/json", - }, - }); + const response = await authGet(url); if (!response.ok) { throw new Error(`Fetch failed with status: ${response.status}`); @@ -25,15 +18,10 @@ export const fetchProjects = async () => { } }; -export const fetchProject = async (screenerId) => { - const url = apiUrl + "/screener/" + screenerId; +export const fetchProject = async (screenerId: string) => { + const url = `/api/screener/${screenerId}`; try { - const response = await authFetch(url, { - method: "GET", - headers: { - Accept: "application/json", - }, - }); + const response = await authGet(url); if (!response.ok) { throw new Error(`Fetch failed with status: ${response.status}`); @@ -46,15 +34,13 @@ export const fetchProject = async (screenerId) => { } }; -export const createNewScreener = async (screenerData) => { - const url = apiUrl + "/screener"; +export const createNewScreener = async (screenerData: { + screenerName: string; +}): Promise<{ id: string }> => { + const url = "/api/screener"; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify(screenerData), }); @@ -70,7 +56,7 @@ export const createNewScreener = async (screenerData) => { }; export const updateScreener = async (screenerData) => { - const url = apiUrl + "/screener"; + const url = "/api/screener"; try { const response = await authFetch(url, { method: "PUT", @@ -91,7 +77,7 @@ export const updateScreener = async (screenerData) => { }; export const deleteScreener = async (screenerData) => { - const url = apiUrl + "/screener/delete?screenerId=" + screenerData.id; + const url = `/api/screener/delete?screenerId=${screenerData.id}`; try { const response = await authFetch(url, { method: "DELETE", @@ -114,7 +100,7 @@ export const saveFormSchema = async (screenerId, schema) => { const requestData: any = {}; requestData.screenerId = screenerId; requestData.schema = schema; - const url = apiUrl + "/save-form-schema"; + const url = "/api/save-form-schema"; try { const response = await authFetch(url, { method: "POST", @@ -135,7 +121,7 @@ export const saveFormSchema = async (screenerId, schema) => { }; export const publishScreener = async (screenerId: string): Promise => { - const url = apiUrl + "/publish"; + const url = "/api/publish"; try { const response = await authFetch(url, { method: "POST", @@ -155,8 +141,11 @@ export const publishScreener = async (screenerId: string): Promise => { } }; -export const addCustomBenefit = async (screenerId: string, benefit: BenefitDetail) => { - const url = apiUrl + "/screener/" + screenerId + "/benefit"; +export const addCustomBenefit = async ( + screenerId: string, + benefit: BenefitDetail, +) => { + const url = `/api/screener/${screenerId}/benefit`; try { const response = await authFetch(url, { method: "POST", @@ -176,8 +165,11 @@ export const addCustomBenefit = async (screenerId: string, benefit: BenefitDetai } }; -export const removeCustomBenefit = async (screenerId: string, benefitId: string) => { - const url = apiUrl + "/screener/" + screenerId + "/benefit/" + benefitId; +export const removeCustomBenefit = async ( + screenerId: string, + benefitId: string, +) => { + const url = `/api/screener/${screenerId}/benefit/${benefitId}`; try { const response = await authFetch(url, { method: "DELETE", @@ -188,7 +180,9 @@ export const removeCustomBenefit = async (screenerId: string, benefitId: string) }); if (!response.ok) { - throw new Error(`Delete of benefit failed with status: ${response.status}`); + throw new Error( + `Delete of benefit failed with status: ${response.status}`, + ); } } catch (error) { console.error("Error deleting custom benefit:", error); @@ -196,8 +190,11 @@ export const removeCustomBenefit = async (screenerId: string, benefitId: string) } }; -export const evaluateScreener = async (screenerId: string, inputData: any): Promise => { - const url = apiUrl + "/decision/v2?screenerId=" + screenerId; +export const evaluateScreener = async ( + screenerId: string, + inputData: any, +): Promise => { + const url = `/api/decision/v2?screenerId=${screenerId}`; try { const response = await authFetch(url, { method: "POST", @@ -211,7 +208,7 @@ export const evaluateScreener = async (screenerId: string, inputData: any): Prom if (!response.ok) { throw new Error(`Evaluation failed with status: ${response.status}`); } - + const data = await response.json(); return data; } catch (error) { diff --git a/builder-frontend/src/types.ts b/builder-frontend/src/types.ts index b4a6a93..44db959 100644 --- a/builder-frontend/src/types.ts +++ b/builder-frontend/src/types.ts @@ -1,3 +1,14 @@ +export type Project = { + id: string; + screenerName: string; + owner: string; + organizationName: null; + benefits: BenefitDetail[]; + formSchema: null; + resultsSchema: null; + publishedScreenerId: string; + lastPublishedDate: Date; +}; /* Types for managing benefits in a project */ export interface ScreenerBenefits { benefits: BenefitDetail[]; From 360723056336f3a98a9dcc93df7835bdb8cd3edd Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Sun, 14 Dec 2025 01:22:30 -0500 Subject: [PATCH 11/18] updated imports after file move --- .../Project/ProjectDetails/FormEditorView.tsx | 8 +- .../Project/ProjectDetails/Project.tsx | 3 +- .../Project/ProjectDetails/Publish.tsx | 22 ++++- .../benefitList/BenefitList.tsx | 94 +++++++++++-------- .../configureBenefit/ConfigureBenefit.tsx | 8 +- .../ProjectDetails/preview/Preview.tsx | 18 ++-- .../ProjectDetails/preview/Results.tsx | 64 +++++++++---- 7 files changed, 133 insertions(+), 84 deletions(-) diff --git a/builder-frontend/src/components/Project/ProjectDetails/FormEditorView.tsx b/builder-frontend/src/components/Project/ProjectDetails/FormEditorView.tsx index 320e729..5294173 100644 --- a/builder-frontend/src/components/Project/ProjectDetails/FormEditorView.tsx +++ b/builder-frontend/src/components/Project/ProjectDetails/FormEditorView.tsx @@ -6,12 +6,11 @@ import { FormEditor } from "@bpmn-io/form-js-editor"; import FilterFormComponentsModule from "./formJsExtensions/FilterFormComponentsModule"; import CustomFormFieldsModule from "./formJsExtensions/customFormFields"; -import { saveFormSchema } from "../../api/screener"; +import { saveFormSchema } from "@/api/screener"; import "@bpmn-io/form-js/dist/assets/form-js.css"; import "@bpmn-io/form-js-editor/dist/assets/form-js-editor.css"; - function FormEditorView({ formSchema, setFormSchema }) { const [isUnsaved, setIsUnsaved] = createSignal(false); const [isSaving, setIsSaving] = createSignal(false); @@ -31,10 +30,7 @@ function FormEditorView({ formSchema, setFormSchema }) { onMount(() => { formEditor = new FormEditor({ container, - additionalModules: [ - FilterFormComponentsModule, - CustomFormFieldsModule - ], + additionalModules: [FilterFormComponentsModule, CustomFormFieldsModule], }); if (formSchema()) { diff --git a/builder-frontend/src/components/Project/ProjectDetails/Project.tsx b/builder-frontend/src/components/Project/ProjectDetails/Project.tsx index fabf595..dada30c 100644 --- a/builder-frontend/src/components/Project/ProjectDetails/Project.tsx +++ b/builder-frontend/src/components/Project/ProjectDetails/Project.tsx @@ -2,8 +2,7 @@ import { createSignal, createResource, Accessor } from "solid-js"; import { useParams } from "@solidjs/router"; import FormEditorView from "./FormEditorView"; -import Header from "../Header"; -import Loading from "../Loading"; +import Loading from "@/components/Loading"; import ManageBenefits from "./manageBenefits/ManageBenefits"; import Preview from "./preview/Preview"; import Publish from "./Publish"; diff --git a/builder-frontend/src/components/Project/ProjectDetails/Publish.tsx b/builder-frontend/src/components/Project/ProjectDetails/Publish.tsx index 593fa56..0c16211 100644 --- a/builder-frontend/src/components/Project/ProjectDetails/Publish.tsx +++ b/builder-frontend/src/components/Project/ProjectDetails/Publish.tsx @@ -1,15 +1,27 @@ import { createSignal, onMount } from "solid-js"; import { useParams } from "@solidjs/router"; -import { publishScreener } from "../../api/screener"; +import { publishScreener } from "@/api/screener"; export default function Publish({ project, refetchProject }) { const [isLoading, setIsLoading] = createSignal(false); - const screenerName = () => { return project()?.screenerName }; - const isPublished = () => { return project()?.publishedScreenerId !== null }; - const lastPublishDate = () => { return project()?.lastPublishDate }; + const screenerName = () => { + return project()?.screenerName; + }; + const isPublished = () => { + return project()?.publishedScreenerId !== null; + }; + const lastPublishDate = () => { + return project()?.lastPublishDate; + }; const screenerUrl = () => { - return window.location.protocol + "//" +window.location.host + "/screener/" + project()?.publishedScreenerId; + return ( + window.location.protocol + + "//" + + window.location.host + + "/screener/" + + project()?.publishedScreenerId + ); }; const { projectId } = useParams(); diff --git a/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/BenefitList.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/BenefitList.tsx index 9b10d10..eca15b1 100644 --- a/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/BenefitList.tsx +++ b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/benefitList/BenefitList.tsx @@ -1,37 +1,43 @@ import { Accessor, createSignal, For, Setter, Show } from "solid-js"; import AddNewBenefitModal from "./modals/AddNewBenefitModal"; -import ConfirmationModal from "../../../shared/ConfirmationModal"; +import ConfirmationModal from "@/components/shared/ConfirmationModal"; import SelectExistingBenefitModal from "./modals/SelectExistingBenefitModal"; import screenerBenefitResource from "./screenerBenefitsResource"; -import Loading from "../../../Loading"; +import Loading from "@/components/Loading"; import type { BenefitDetail } from "@/types"; - -const BenefitList = ( - { screenerId, setBenefitIdToConfigure }: - { screenerId: Accessor; setBenefitIdToConfigure: Setter } -) => { - const { screenerBenefits, actions, actionInProgress, initialLoadStatus } = screenerBenefitResource(screenerId); +const BenefitList = ({ + screenerId, + setBenefitIdToConfigure, +}: { + screenerId: Accessor; + setBenefitIdToConfigure: Setter; +}) => { + const { screenerBenefits, actions, actionInProgress, initialLoadStatus } = + screenerBenefitResource(screenerId); const [addingNewBenefit, setAddingNewBenefit] = createSignal(false); - const [selectExistingBenefitModal, setSelectExistingBenefitModal] = createSignal(false); - const [benefitIdToRemove, setBenefitIdToRemove] = createSignal(null); + const [selectExistingBenefitModal, setSelectExistingBenefitModal] = + createSignal(false); + const [benefitIdToRemove, setBenefitIdToRemove] = createSignal( + null, + ); return (
-
- Manage Benefits -
+
Manage Benefits
- Define and organize the benefits available in your screener. - Each benefit can have associated eligibility checks. + Define and organize the benefits available in your screener. Each + benefit can have associated eligibility checks.
{setAddingNewBenefit(true)}} + onClick={() => { + setAddingNewBenefit(true); + }} > Create New Benefit
@@ -47,9 +53,14 @@ const BenefitList = ( grid-cols-1 md:grid-cols-2 xl:grid-cols-3" > - + - +
No benefits found. Please add a new benefit.
@@ -66,13 +77,12 @@ const BenefitList = ( }}
- { - addingNewBenefit() && + {addingNewBenefit() && ( setAddingNewBenefit(false)} addNewBenefit={actions.addNewBenefit} /> - } + )} {/* { selectExistingBenefitModal() && } */} - { - benefitIdToRemove() !== null && + {benefitIdToRemove() !== null && ( actions.removeBenefit(benefitIdToRemove()) } - closeModal={() => setBenefitIdToRemove(null) } + callback={() => actions.removeBenefit(benefitIdToRemove())} + closeModal={() => setBenefitIdToRemove(null)} /> - } + )}
- ) + ); }; -const BenefitCard = ( - { benefit, setBenefitIdToConfigure, setBenefitIdToRemove }: - { - benefit: BenefitDetail, - setBenefitIdToConfigure: Setter, - setBenefitIdToRemove: Setter - } -) => { +const BenefitCard = ({ + benefit, + setBenefitIdToConfigure, + setBenefitIdToRemove, +}: { + benefit: BenefitDetail; + setBenefitIdToConfigure: Setter; + setBenefitIdToRemove: Setter; +}) => { return (
-
- {benefit.name} -
+
{benefit.name}
Description: {benefit.description}
@@ -125,13 +133,17 @@ const BenefitCard = ( >
{ setBenefitIdToConfigure(benefit.id); } } + onClick={() => { + setBenefitIdToConfigure(benefit.id); + }} > Edit
{ setBenefitIdToRemove(benefit.id); } } + onClick={() => { + setBenefitIdToRemove(benefit.id); + }} > Remove
@@ -139,6 +151,6 @@ const BenefitCard = (
); -} +}; export default BenefitList; diff --git a/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/ConfigureBenefit.tsx b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/ConfigureBenefit.tsx index b3cc645..77e5414 100644 --- a/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/ConfigureBenefit.tsx +++ b/builder-frontend/src/components/Project/ProjectDetails/manageBenefits/configureBenefit/ConfigureBenefit.tsx @@ -2,7 +2,7 @@ import { Accessor, createResource, createSignal, For, Show } from "solid-js"; import SelectedEligibilityCheck from "./SelectedEligibilityCheck"; import EligibilityCheckListView from "./EligibilityCheckListView"; -import Loading from "../../../Loading"; +import Loading from "@/components/Loading"; import BenefitResource from "./benefitResource"; import { fetchPublicChecks, fetchUserDefinedChecks } from "@/api/check"; @@ -26,7 +26,7 @@ const ConfigureBenefit = ({ createSignal("public"); const [publicChecks] = createResource(fetchPublicChecks); const [userDefinedChecks] = createResource(() => - fetchUserDefinedChecks(false) + fetchUserDefinedChecks(false), ); const onRemoveEligibilityCheck = (checkIndexToRemove: number) => { @@ -93,11 +93,11 @@ const ConfigureBenefit = ({ onRemoveEligibilityCheck(checkIndex()) } updateCheckConfigParams={( - newCheckData: ParameterValues + newCheckData: ParameterValues, ) => { actions.updateCheckConfigParams( checkIndex(), - newCheckData + newCheckData, ); }} /> diff --git a/builder-frontend/src/components/Project/ProjectDetails/preview/Preview.tsx b/builder-frontend/src/components/Project/ProjectDetails/preview/Preview.tsx index e704958..e0a9ebb 100644 --- a/builder-frontend/src/components/Project/ProjectDetails/preview/Preview.tsx +++ b/builder-frontend/src/components/Project/ProjectDetails/preview/Preview.tsx @@ -3,13 +3,13 @@ import { Accessor, createSignal } from "solid-js"; import FormRenderer from "./FormRenderer"; import Results from "./Results"; -import { evaluateScreener } from "../../../api/screener"; +import { evaluateScreener } from "@/api/screener"; import { PreviewFormData, ScreenerResult } from "./types"; - const Preview = ({ project, formSchema }) => { - const [lastInputDataSent, setLastInputDataSent] = createSignal({}); + const [lastInputDataSent, setLastInputDataSent] = + createSignal({}); const [results, setResults] = createSignal(); const [resultsLoading, setResultsLoading] = createSignal(false); @@ -24,7 +24,7 @@ const Preview = ({ project, formSchema }) => { schemaVersion: 18, type: "default", }; - } + }; const handleSubmitForm = async (data: PreviewFormData) => { setLastInputDataSent(data); @@ -39,13 +39,17 @@ const Preview = ({ project, formSchema }) => {
Form
- +
Results
- +
); -} +}; export default Preview; diff --git a/builder-frontend/src/components/Project/ProjectDetails/preview/Results.tsx b/builder-frontend/src/components/Project/ProjectDetails/preview/Results.tsx index 26b306c..9b965bc 100644 --- a/builder-frontend/src/components/Project/ProjectDetails/preview/Results.tsx +++ b/builder-frontend/src/components/Project/ProjectDetails/preview/Results.tsx @@ -2,19 +2,19 @@ import { Accessor, For, Match, Show, Switch } from "solid-js"; import { PreviewFormData, ScreenerResult } from "./types"; -import checkIcon from "../../../assets/images/checkIcon.svg"; -import questionIcon from "../../../assets/images/questionIcon.svg"; -import xIcon from "../../../assets/images/xIcon.svg"; +import checkIcon from "@/assets/images/checkIcon.svg"; +import questionIcon from "@/assets/images/questionIcon.svg"; +import xIcon from "@/assets/images/xIcon.svg"; - -export default function Results( - { inputData, results, resultsLoading }: - { - inputData: Accessor, - results: Accessor, - resultsLoading: Accessor - } -) { +export default function Results({ + inputData, + results, + resultsLoading, +}: { + inputData: Accessor; + results: Accessor; + resultsLoading: Accessor; +}) { return (
@@ -27,7 +27,9 @@ export default function Results( {([key, value]) => ( {key}: - {value?.toString() || "--"} + + {value?.toString() || "--"} + )} @@ -39,7 +41,13 @@ export default function Results(
Loading results...
- 0}> + 0 + } + >
Benefits
@@ -59,7 +67,9 @@ export default function Results( Ineligible - + Need more information @@ -74,13 +84,29 @@ export default function Results( {check.name}:{" "} - + - + - - + +
From 45d0f09bf45fa148c5624df5eb6e78953c84dfcf Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Sun, 14 Dec 2025 01:25:11 -0500 Subject: [PATCH 12/18] updated imports after moving project details --- .../src/components/homeScreen/HomeScreen.tsx | 2 +- .../checkTesting/EligibilityCheckTest.tsx | 6 +++--- .../src/components/screener/FormRenderer.tsx | 18 ++++++++++++------ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/builder-frontend/src/components/homeScreen/HomeScreen.tsx b/builder-frontend/src/components/homeScreen/HomeScreen.tsx index 619a102..22e3075 100644 --- a/builder-frontend/src/components/homeScreen/HomeScreen.tsx +++ b/builder-frontend/src/components/homeScreen/HomeScreen.tsx @@ -2,7 +2,7 @@ import { createSignal, Match, Switch } from "solid-js"; import { Title } from "@solidjs/meta"; import EligibilityChecksList from "./eligibilityCheckList/EligibilityChecksList"; -import ProjectsList from "./ProjectsList"; +import ProjectsList from "@/components/Project/ProjectsList"; const HomeScreen = () => { const [screenMode, setScreenMode] = createSignal<"screeners" | "checks">( diff --git a/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/checkTesting/EligibilityCheckTest.tsx b/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/checkTesting/EligibilityCheckTest.tsx index 5d05733..0e1704e 100644 --- a/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/checkTesting/EligibilityCheckTest.tsx +++ b/builder-frontend/src/components/homeScreen/eligibilityCheckList/eligibilityCheckDetail/checkTesting/EligibilityCheckTest.tsx @@ -6,7 +6,7 @@ import { OptionalBoolean, ParameterValues, } from "@/types"; -import SelectedEligibilityCheck from "@/components/project/manageBenefits/configureBenefit/SelectedEligibilityCheck"; +import SelectedEligibilityCheck from "@/components/Project/ProjectDetails/manageBenefits/configureBenefit/SelectedEligibilityCheck"; import CheckJsonEditor from "./CheckJsonEditor"; import { JSONContent } from "vanilla-jsoneditor"; @@ -22,7 +22,7 @@ const EligibilityCheckTest = ({ eligibilityCheck: Accessor; testEligibility: ( checkConfg: CheckConfig, - inputData: Record + inputData: Record, ) => Promise; }) => { const [checkConfig, setCheckConfig] = createSignal({ @@ -56,7 +56,7 @@ const EligibilityCheckTest = ({ onClick={async () => { const result = await testEligibility( checkConfig(), - currentJsonContent().json + currentJsonContent().json, ); if (!result) { return; diff --git a/builder-frontend/src/components/screener/FormRenderer.tsx b/builder-frontend/src/components/screener/FormRenderer.tsx index 9bc6944..f04abb5 100644 --- a/builder-frontend/src/components/screener/FormRenderer.tsx +++ b/builder-frontend/src/components/screener/FormRenderer.tsx @@ -4,18 +4,24 @@ import debounce from "lodash.debounce"; import { Form } from "@bpmn-io/form-js-viewer"; import { State } from "@bpmn-io/form-js-viewer/dist/types/Form"; -import CustomFormFieldsModule from "../project/formJsExtensions/customFormFields"; +import CustomFormFieldsModule from "@/components/Project/ProjectDetails/formJsExtensions/customFormFields"; import "@bpmn-io/form-js/dist/assets/form-js.css"; -function FormRenderer( - { schema, submitForm }: - { schema: { [key: string]: any; }, submitForm: (data: any) => void } -) { +function FormRenderer({ + schema, + submitForm, +}: { + schema: { [key: string]: any }; + submitForm: (data: any) => void; +}) { let container: Element | null = null; onMount(() => { - const form: Form = new Form({ container, additionalModules: [ CustomFormFieldsModule ] }); + const form: Form = new Form({ + container, + additionalModules: [CustomFormFieldsModule], + }); const debouncedSubmit = debounce((data) => { submitForm(data); From 8cd0ae213a65c722aefd178487f3f778b5c5fe7b Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Sun, 14 Dec 2025 01:35:27 -0500 Subject: [PATCH 13/18] moved project related components to @/components/Project --- .../src/components/{homeScreen => Project}/DeleteConfirmation.tsx | 0 .../src/components/{homeScreen => Project}/EditScreenerForm.tsx | 0 .../src/components/{homeScreen => Project}/NewScreenerForm.tsx | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename builder-frontend/src/components/{homeScreen => Project}/DeleteConfirmation.tsx (100%) rename builder-frontend/src/components/{homeScreen => Project}/EditScreenerForm.tsx (100%) rename builder-frontend/src/components/{homeScreen => Project}/NewScreenerForm.tsx (100%) diff --git a/builder-frontend/src/components/homeScreen/DeleteConfirmation.tsx b/builder-frontend/src/components/Project/DeleteConfirmation.tsx similarity index 100% rename from builder-frontend/src/components/homeScreen/DeleteConfirmation.tsx rename to builder-frontend/src/components/Project/DeleteConfirmation.tsx diff --git a/builder-frontend/src/components/homeScreen/EditScreenerForm.tsx b/builder-frontend/src/components/Project/EditScreenerForm.tsx similarity index 100% rename from builder-frontend/src/components/homeScreen/EditScreenerForm.tsx rename to builder-frontend/src/components/Project/EditScreenerForm.tsx diff --git a/builder-frontend/src/components/homeScreen/NewScreenerForm.tsx b/builder-frontend/src/components/Project/NewScreenerForm.tsx similarity index 100% rename from builder-frontend/src/components/homeScreen/NewScreenerForm.tsx rename to builder-frontend/src/components/Project/NewScreenerForm.tsx From 4d6635303b74007233420b6349d479f5cfa4d221 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:29:25 -0500 Subject: [PATCH 14/18] fixed api url and authFetch --- builder-frontend/src/api/auth.ts | 4 +- builder-frontend/src/api/benefit.ts | 38 ++---- builder-frontend/src/api/check.ts | 120 ++++++------------ builder-frontend/src/api/publishedScreener.ts | 20 +-- builder-frontend/src/api/screener.ts | 72 ++++------- 5 files changed, 81 insertions(+), 173 deletions(-) diff --git a/builder-frontend/src/api/auth.ts b/builder-frontend/src/api/auth.ts index 1e0c385..50a0ea2 100644 --- a/builder-frontend/src/api/auth.ts +++ b/builder-frontend/src/api/auth.ts @@ -1,6 +1,6 @@ import { auth } from "../firebase/firebase.js"; -type RestMethod = "GET" | "POST"; +type RestMethod = "GET" | "POST" | "PUT" | "DELETE"; type FetchInit = { headers: Record; body: string; @@ -25,3 +25,5 @@ export const authFetch = export const authGet = authFetch("GET"); export const authPost = authFetch("POST"); +export const authPut = authFetch("PUT"); +export const authDelete = authFetch("DELETE"); diff --git a/builder-frontend/src/api/benefit.ts b/builder-frontend/src/api/benefit.ts index 37e33c2..32f7f58 100644 --- a/builder-frontend/src/api/benefit.ts +++ b/builder-frontend/src/api/benefit.ts @@ -1,22 +1,13 @@ -import { authFetch } from "@/api/auth"; -import BenefitList from "@/components/project/manageBenefits/benefitList/BenefitList"; - import { Benefit } from "@/types"; - -const apiUrl = import.meta.env.VITE_API_URL; +import { authGet, authPut } from "@/api/auth"; export const fetchScreenerBenefit = async ( srceenerId: string, - benefitId: string + benefitId: string, ): Promise => { - const url = apiUrl + "/screener/" + srceenerId + "/benefit/" + benefitId; + const url = `/api/screener/${srceenerId}/benefit/${benefitId}`; try { - const response = await authFetch(url, { - method: "GET", - headers: { - Accept: "application/json", - }, - }); + const response = await authGet(url); if (!response.ok) { throw new Error(`Fetch failed with status: ${response.status}`); @@ -31,16 +22,12 @@ export const fetchScreenerBenefit = async ( export const updateScreenerBenefit = async ( screenerId: string, - benefitData: Benefit + benefitData: Benefit, ): Promise => { - const url = apiUrl + "/screener/" + screenerId + "/benefit"; + const url = `/api/screener/${screenerId}/benefit`; try { - const response = await authFetch(url, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPut(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify(benefitData), }); @@ -56,14 +43,9 @@ export const updateScreenerBenefit = async ( }; export const fetchPublicBenefits = async (): Promise => { - const url = apiUrl + "/benefit"; + const url = "/api/benefit"; try { - const response = await authFetch(url, { - method: "GET", - headers: { - Accept: "application/json", - }, - }); + const response = await authGet(url); if (!response.ok) { throw new Error(`Fetch failed with status: ${response.status}`); diff --git a/builder-frontend/src/api/check.ts b/builder-frontend/src/api/check.ts index d9f0d56..f739a11 100644 --- a/builder-frontend/src/api/check.ts +++ b/builder-frontend/src/api/check.ts @@ -1,18 +1,10 @@ -import { authFetch } from "@/api/auth"; - import type { EligibilityCheck, OptionalBoolean } from "@/types"; - -const apiUrl = import.meta.env.VITE_API_URL; +import { authGet, authPost, authPut } from "@/api/auth"; export const fetchPublicChecks = async (): Promise => { - const url = apiUrl + "/library-checks"; + const url = "/api/library-checks"; try { - const response = await authFetch(url, { - method: "GET", - headers: { - Accept: "application/json", - }, - }); + const response = await authGet(url); if (!response.ok) { throw new Error(`Fetch failed with status: ${response.status}`); @@ -27,20 +19,13 @@ export const fetchPublicChecks = async (): Promise => { }; export const fetchCheck = async ( - checkId: string + checkId: string, ): Promise => { - let url = apiUrl + `/custom-checks/${checkId}`; - if (checkId.charAt(0) === "L") { - url = apiUrl + `/library-checks/${checkId}`; - } + const checkResource = checkId[0] === "L" ? "library-checks" : "custom-checks"; + const url = `/api/${checkResource}/${checkId}`; try { - const response = await authFetch(url, { - method: "GET", - headers: { - Accept: "application/json", - }, - }); + const response = await authGet(url); if (!response.ok) { throw new Error(`Fetch failed with status: ${response.status}`); @@ -55,14 +40,10 @@ export const fetchCheck = async ( }; export const addCheck = async (check: EligibilityCheck) => { - const url = apiUrl + "/custom-checks"; + const url = "/api/custom-checks"; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify(check), }); @@ -78,14 +59,10 @@ export const addCheck = async (check: EligibilityCheck) => { }; export const updateCheck = async (check: EligibilityCheck) => { - const url = apiUrl + "/custom-checks"; + const url = "/api/custom-checks"; try { - const response = await authFetch(url, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPut(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify(check), }); @@ -101,14 +78,10 @@ export const updateCheck = async (check: EligibilityCheck) => { }; export const saveCheckDmn = async (checkId: string, dmnModel: string) => { - const url = apiUrl + "/save-check-dmn"; + const url = "/api/save-check-dmn"; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: checkId, dmnModel: dmnModel }), }); @@ -123,16 +96,12 @@ export const saveCheckDmn = async (checkId: string, dmnModel: string) => { export const validateCheckDmn = async ( checkId: string, - dmnModel: string + dmnModel: string, ): Promise => { - const url = apiUrl + "/validate-check-dmn"; + const url = "/api/validate-check-dmn"; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: checkId, dmnModel: dmnModel }), }); @@ -149,18 +118,12 @@ export const validateCheckDmn = async ( }; export const fetchUserDefinedChecks = async ( - working: boolean + working: boolean, ): Promise => { - const workingQueryParam = working ? "true" : "false"; - let url: string = apiUrl + `/custom-checks?working=${workingQueryParam}`; + const url = `/api/custom-checks?working=${working}`; try { - const response = await authFetch(url, { - method: "GET", - headers: { - Accept: "application/json", - }, - }); + const response = await authGet(url); if (!response.ok) { throw new Error(`Fetch failed with status: ${response.status}`); @@ -176,17 +139,13 @@ export const fetchUserDefinedChecks = async ( export const evaluateWorkingCheck = async ( checkId: string, checkConfig: any, - inputData: Record + inputData: Record, ): Promise => { - const url = apiUrl + `/decision/working-check?checkId=${checkId}`; + const url = `/api/decision/working-check?checkId=${checkId}`; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify({ checkConfig: checkConfig, inputData: inputData }), + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ checkConfig, inputData }), }); if (!response.ok) { @@ -201,16 +160,11 @@ export const evaluateWorkingCheck = async ( }; export const getRelatedPublishedChecks = async ( - checkId: string + checkId: string, ): Promise => { - const url = apiUrl + `/custom-checks/${checkId}/published-check-versions`; + const url = `/api/custom-checks/${checkId}/published-check-versions`; try { - const response = await authFetch(url, { - method: "GET", - headers: { - Accept: "application/json", - }, - }); + const response = await authGet(url); if (!response.ok) { throw new Error(`Fetch failed with status: ${response.status}`); @@ -224,15 +178,13 @@ export const getRelatedPublishedChecks = async ( }; export const publishCheck = async ( - checkId: string + checkId: string, ): Promise => { - const url = apiUrl + `/publish-check/${checkId}`; + const url = `/api/publish-check/${checkId}`; try { - const response = await authFetch(url, { - method: "POST", - headers: { - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({}), }); if (!response.ok) { diff --git a/builder-frontend/src/api/publishedScreener.ts b/builder-frontend/src/api/publishedScreener.ts index 0fc97db..17982fa 100644 --- a/builder-frontend/src/api/publishedScreener.ts +++ b/builder-frontend/src/api/publishedScreener.ts @@ -1,15 +1,13 @@ import type { PublishedScreener, ScreenerResult } from "@/types"; -const apiUrl = import.meta.env.VITE_API_URL; - -export const fetchPublishedScreener = async (publishedScreenerId: string): Promise => { - const url = apiUrl + "/published/screener/" + publishedScreenerId; +export const fetchPublishedScreener = async ( + publishedScreenerId: string, +): Promise => { + const url = `/api/published/screener/${publishedScreenerId}`; try { const response = await fetch(url, { method: "GET", - headers: { - Accept: "application/json", - }, + headers: { Accept: "application/json" }, }); if (!response.ok) { @@ -23,9 +21,11 @@ export const fetchPublishedScreener = async (publishedScreenerId: string): Promi } }; - -export const evaluatePublishedScreener = async (publishedScreenerId: string, inputData: any): Promise => { - const url = apiUrl + "/published/" + publishedScreenerId + "/evaluate"; +export const evaluatePublishedScreener = async ( + publishedScreenerId: string, + inputData: any, +): Promise => { + const url = `/api/published/${publishedScreenerId}/evaluate`; try { const response = await fetch(url, { method: "POST", diff --git a/builder-frontend/src/api/screener.ts b/builder-frontend/src/api/screener.ts index 891bd04..83ee691 100644 --- a/builder-frontend/src/api/screener.ts +++ b/builder-frontend/src/api/screener.ts @@ -1,6 +1,5 @@ -import { authFetch, authGet, authPost } from "@/api/auth"; - import type { BenefitDetail, Project, ScreenerResult } from "@/types"; +import { authDelete, authGet, authPost, authPut } from "@/api/auth"; export const fetchProjects = async (): Promise => { const url = "/api/screeners"; @@ -14,6 +13,7 @@ export const fetchProjects = async (): Promise => { return data; } catch (error) { console.error("Error fetching projects:", error); + return []; throw error; // rethrow so you can handle it in your component if needed } }; @@ -55,15 +55,14 @@ export const createNewScreener = async (screenerData: { } }; -export const updateScreener = async (screenerData) => { +export const updateScreener = async (screenerData: { + screenerName: string; + id: string; +}) => { const url = "/api/screener"; try { - const response = await authFetch(url, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPut(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify(screenerData), }); @@ -76,16 +75,10 @@ export const updateScreener = async (screenerData) => { } }; -export const deleteScreener = async (screenerData) => { - const url = `/api/screener/delete?screenerId=${screenerData.id}`; +export const deleteScreener = async (screenerId: string) => { + const url = `/api/screener/delete?screenerId=${screenerId}`; try { - const response = await authFetch(url, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }); + const response = await authDelete(url); if (!response.ok) { throw new Error(`Update failed with status: ${response.status}`); @@ -96,18 +89,15 @@ export const deleteScreener = async (screenerData) => { } }; -export const saveFormSchema = async (screenerId, schema) => { +// What is schema's type? +export const saveFormSchema = async (screenerId: string, schema) => { const requestData: any = {}; requestData.screenerId = screenerId; requestData.schema = schema; const url = "/api/save-form-schema"; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify(requestData), }); @@ -123,12 +113,8 @@ export const saveFormSchema = async (screenerId, schema) => { export const publishScreener = async (screenerId: string): Promise => { const url = "/api/publish"; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify({ screenerId: screenerId }), }); @@ -147,12 +133,8 @@ export const addCustomBenefit = async ( ) => { const url = `/api/screener/${screenerId}/benefit`; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify(benefit), }); @@ -171,13 +153,7 @@ export const removeCustomBenefit = async ( ) => { const url = `/api/screener/${screenerId}/benefit/${benefitId}`; try { - const response = await authFetch(url, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }); + const response = await authDelete(url); if (!response.ok) { throw new Error( @@ -196,12 +172,8 @@ export const evaluateScreener = async ( ): Promise => { const url = `/api/decision/v2?screenerId=${screenerId}`; try { - const response = await authFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, + const response = await authPost(url, { + headers: { "Content-Type": "application/json" }, body: JSON.stringify(inputData), }); From 68459f14ee1adaf640d9e655eb99efcba1724735 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:56:41 -0500 Subject: [PATCH 15/18] restructured main into modular components, added new
, , , , and - -
+
+

+ Are you sure you would like to delete {props.screenerName}? +

+
+ Once deleted, all associated data will be deleted and cant be recovered. +
+
+ +
); diff --git a/builder-frontend/src/components/Project/EditScreenerForm.tsx b/builder-frontend/src/components/Project/EditScreenerForm.tsx index 71256ca..802010c 100644 --- a/builder-frontend/src/components/Project/EditScreenerForm.tsx +++ b/builder-frontend/src/components/Project/EditScreenerForm.tsx @@ -1,125 +1,99 @@ -import { createSignal, onCleanup, onMount } from "solid-js"; -import TrashIcon from "../icon/TrashIcon"; -import DeleteConfirmation from "./DeleteConfirmation"; +import { + createSignal, + JSX, + ParentProps, + ResourceActions, + Show, +} from "solid-js"; +import { Trash2 } from "lucide-solid"; -export default function EditScreenerForm({ - setIsEditModalVisible, - screenerData, - handleEditScreener, - handleDeleteScreener, -}) { - const [isLoading, setIsLoading] = createSignal(false); - const [isConfirmationVisible, setIsConfirmationVisible] = createSignal(false); - const [screenerName, setScreenerName] = createSignal(); - let isActive = true; +import type { Project } from "@/types"; +import { deleteScreener, updateScreener } from "@/api/screener"; +import Form from "@/components/shared/Form"; +import { Button } from "@/components/shared/Button"; +import { Modal } from "@/components/shared/Modal"; +import DeleteConfirmation from "./DeleteConfirmation"; - onMount(() => { - if (screenerData?.screenerName) { - setScreenerName(screenerData.screenerName); - } - }); +interface Props { + id: string; + screenerName: string; + refetchProjects: ResourceActions["refetch"]; +} - onCleanup(() => { - isActive = false; - }); +export default function EditScreenerForm(props: ParentProps) { + const [showDelete, setShowDelete] = createSignal(false); + const [error, setError] = createSignal(null); + const [isLoading, setIsLoading] = createSignal(false); + const [screenerName, setScreenerName] = createSignal(props.screenerName); - const handleSubmit = async (e) => { + const handleSubmit: JSX.EventHandler = (e) => { e.preventDefault(); - try { - setIsLoading(true); - const data = { - screenerName: screenerName(), - id: screenerData.id, - }; - await handleEditScreener(data); - if (isActive) setIsLoading(false); - } catch (e) { - if (setIsLoading()) { - setIsLoading(false); - } - } - }; + setIsLoading(true); + const fd = new FormData(e.currentTarget); + const screenerName = fd.get("screenerName")?.toString() || ""; - const handleDelete = async () => { - try { - setIsLoading(true); - const data = { - id: screenerData.id, - }; - await handleDeleteScreener(data); - if (isActive) setIsLoading(false); - } catch (e) { - if (setIsLoading()) { - setIsLoading(false); - } + if (screenerName.length > 0) { + setError(null); + updateScreener({ screenerName, id: props.id }) + .then(() => { + props.refetchProjects(); + setIsLoading(false); + }) + .catch((e) => { + setIsLoading(false); + }); + } else { + setError("Please enter a name."); + setIsLoading(false); } }; - const handleDeleteClicked = (e) => { - e.preventDefault(); - setIsConfirmationVisible(true); + const handleDelete = () => { + setIsLoading(true); + deleteScreener(props.id) + .then(() => { + props.refetchProjects(); + setIsLoading(false); + }) + .catch((e) => { + setIsLoading(false); + }); }; return ( - <> -
setIsEditModalVisible(false)} - className="fixed inset-0 bg-black/10 backdrop-blur-sm flex items-center justify-center z-50" - > - e.stopPropagation()} - className="bg-white px-12 py-8 rounded-xl max-w-140 w-1/2 min-w-80 h-96" - > -
-
Edit screener
-
setIsEditModalVisible(false)} - className="text-2xl hover:font-bold hover:cursor-pointer" - > - X -
-
+
+

Edit screener

-
-
- - setScreenerName(e.currentTarget.value)} - className="p-1 border-1 border-gray-400 w-90" - > -
- + + + setScreenerName(e.target.value)} + /> + + +
{error()}
+
+ + -
-
-
+
- + - {isLoading() &&
Loading ...
} -
- -
- {isConfirmationVisible() && ( + setShowDelete(false)}> - )} - + id={props.id} + screenerName={props.screenerName} + onCancel={() => setShowDelete(false)} + onDelete={handleDelete} + /> + + {isLoading() &&
Loading ...
} +
); } diff --git a/builder-frontend/src/components/Project/NewProjectCard.tsx b/builder-frontend/src/components/Project/NewProjectCard.tsx new file mode 100644 index 0000000..5c535b0 --- /dev/null +++ b/builder-frontend/src/components/Project/NewProjectCard.tsx @@ -0,0 +1,19 @@ +import { createSignal } from "solid-js"; + +import { Modal } from "@/components/shared/Modal"; +import NewScreenerForm from "@/components/Project/NewScreenerForm"; +import { Button } from "@/components/shared/Button"; + +export const NewProjectCard = () => { + const [showModal, setShowModal] = createSignal(false); + return ( +
setShowModal(true)}> +
+ Create new screener +
+ setShowModal(false)}> + + +
+ ); +}; diff --git a/builder-frontend/src/components/Project/NewScreenerForm.tsx b/builder-frontend/src/components/Project/NewScreenerForm.tsx index f781a3e..e52770f 100644 --- a/builder-frontend/src/components/Project/NewScreenerForm.tsx +++ b/builder-frontend/src/components/Project/NewScreenerForm.tsx @@ -1,67 +1,55 @@ -import { createSignal, onCleanup } from "solid-js"; +import { createSignal, type JSX, Show } from "solid-js"; +import { useNavigate } from "@solidjs/router"; -export default function NewScreenerForm({ - setIsModalVisible, - handleCreateNewScreener, -}) { - const [isLoading, setIsLoading] = createSignal(false); - let nameInput; - let isActive = true; +import { createNewScreener } from "@/api/screener"; +import Form from "@/components/shared/Form"; - onCleanup(() => { - isActive = false; - }); +export default function NewScreenerForm() { + const navigate = useNavigate(); + const [isLoading, setIsLoading] = createSignal(false); + const [screenerName, setScreenerName] = createSignal(""); + const [error, setError] = createSignal(); - const handleSubmit = async (e) => { + const handleSubmit: JSX.EventHandler = async ( + e, + ) => { e.preventDefault(); setIsLoading(true); - const data = { - screenerName: nameInput.value, - }; - await handleCreateNewScreener(data); - if (isActive) setIsLoading(false); + const fd = new FormData(e.currentTarget); + const screenerName = fd.get("screenerName")?.toString() || ""; + + if (screenerName.length > 0) { + setError(null); + const newScreener = await createNewScreener({ screenerName }); + navigate(`/projects/${newScreener.id}`); + } else { + setError("Please enter a name."); + setIsLoading(false); + } }; return ( -
setIsModalVisible(false)} - className="fixed inset-0 bg-black/10 backdrop-blur-sm flex items-center justify-center z-50" - > -
e.stopPropagation()} - className="bg-white px-12 py-8 rounded-xl max-w-140 w-1/2 min-w-80 h-96" - > -
-
Create a screener
-
setIsModalVisible(false)} - className="text-2xl hover:font-bold hover:cursor-pointer" - > - X -
-
-
- What is the name of your screener? -
-
-
- - (nameInput = el)} - className="p-1 border-1 border-gray-400 w-90" - > -
- - {isLoading() &&
Loading ...
} -
-
+
+

Create a screener

+
+ + setScreenerName(e.target.value)} + /> + + +
{error()}
+
+ + {
Loading ...
}
+
); } diff --git a/builder-frontend/src/components/Project/ProjectCard.tsx b/builder-frontend/src/components/Project/ProjectCard.tsx new file mode 100644 index 0000000..6c44a87 --- /dev/null +++ b/builder-frontend/src/components/Project/ProjectCard.tsx @@ -0,0 +1,39 @@ +import { + Component, + createSignal, + ParentProps, + ResourceActions, +} from "solid-js"; +import { Ellipsis } from "lucide-solid"; + +import { Modal } from "@/components/shared/Modal"; +import EditScreenerForm from "@/components/Project/EditScreenerForm"; +import { Project } from "@/types"; + +interface Props { + id: string; + screenerName: string; + refetchProjects: ResourceActions["refetch"]; +} + +export const ProjectCard: Component> = (props) => { + const [showMenu, setShowMenu] = createSignal(false); + + return ( +
+
setShowMenu(true)}> + + setShowMenu(false)}> + + +
+ +
{props.screenerName}
+
+
+ ); +}; diff --git a/builder-frontend/src/components/Project/Projects.css b/builder-frontend/src/components/Project/Projects.css new file mode 100644 index 0000000..ace6e61 --- /dev/null +++ b/builder-frontend/src/components/Project/Projects.css @@ -0,0 +1,20 @@ +@reference "tailwindcss"; + +.new-project-card { + @apply p-4 w-80 h-60 flex justify-center cursor-pointer border-4 border-gray-300 rounded-lg shadow-md hover:shadow-lg hover:bg-gray-200; +} +.project-card { + @apply w-80 h-60 relative cursor-pointer border-2 border-gray-300 rounded-lg shadow-md hover:shadow-lg hover:bg-gray-200; + color: var(--text-strong); +} +.project-card-menu { + @apply absolute px-2 top-2 right-2 hover:bg-gray-300 rounded-xl; +} + +.project-card-link { + @apply h-60 p-4 flex flex-col justify-center items-center no-underline; + color: var(--text-strong); +} +.project-card-link:hover { + color: var(--text-strong); +} diff --git a/builder-frontend/src/components/Project/Projects.tsx b/builder-frontend/src/components/Project/Projects.tsx new file mode 100644 index 0000000..ced0b73 --- /dev/null +++ b/builder-frontend/src/components/Project/Projects.tsx @@ -0,0 +1,39 @@ +import { Show, createResource, onMount } from "solid-js"; +import { useNavigate } from "@solidjs/router"; + +import type { Project } from "@/types"; +import { fetchProjects } from "@/api/screener"; +import { useAuth } from "@/context/AuthContext"; +import { NewProjectCard } from "@/components/Project/NewProjectCard"; +import ProjectsList from "@/components/Project/ProjectsList"; + +import "./Projects.css"; + +export default function Projects() { + const [projectsList, { refetch }] = createResource(fetchProjects, { + initialValue: [], + }); + const navigate = useNavigate(); + const { user } = useAuth(); + + onMount(() => { + // Change to and redirect user to /projects after sign in? + if (user() === null) { + navigate("/login", { replace: true }); + } + }); + + return ( +
+ + +
+
Loading screeners...
+
+
+ + + +
+ ); +} diff --git a/builder-frontend/src/components/Project/ProjectsLayout.tsx b/builder-frontend/src/components/Project/ProjectsLayout.tsx new file mode 100644 index 0000000..b3f2cc2 --- /dev/null +++ b/builder-frontend/src/components/Project/ProjectsLayout.tsx @@ -0,0 +1,5 @@ +import { Component, createSignal, JSX, ParentProps } from "solid-js"; + +export const ProjectsLayout: Component = (props) => { + return
{props.children}
; +}; diff --git a/builder-frontend/src/components/Project/ProjectsList.tsx b/builder-frontend/src/components/Project/ProjectsList.tsx index af91db7..4650024 100644 --- a/builder-frontend/src/components/Project/ProjectsList.tsx +++ b/builder-frontend/src/components/Project/ProjectsList.tsx @@ -1,136 +1,23 @@ -import { For, Show, createResource, createSignal, onMount } from "solid-js"; -import { useNavigate } from "@solidjs/router"; +import { For, Resource, ResourceActions } from "solid-js"; -import EditScreenerForm from "./EditScreenerForm"; -import NewScreenerForm from "./NewScreenerForm"; -import MenuIcon from "../icon/MenuIcon"; +import { Project } from "@/types"; +import { ProjectCard } from "@/components/Project/ProjectCard"; -import { - fetchProjects, updateScreener, deleteScreener, createNewScreener, -} from "@/api/screener"; -import { useAuth } from "@/context/AuthContext"; - - -export default function ProjectsList() { - const [projectList, { refetch: refetchProjectList }] = createResource(fetchProjects); - const [isNewScreenerModalVisible, setIsNewScreenerModalVisible] = createSignal(false); - const [isEditModalVisible, setIsEditgModalVisible] = createSignal(false); - const [editModelData, setEditModalData] = createSignal(); - const navigate = useNavigate(); - const { user } = useAuth(); - - onMount(() => { - if (user() === null) { - navigate("/login", { replace: true }); - } - }); - - const navigateToProject = (project) => { - navigate("/project/" + project.id); - }; - - const handleCreateNewScreener = async (screenerData) => { - try { - const newScreener = await createNewScreener(screenerData); - navigate(`/project/${newScreener.id}`); - } catch (e) { - console.log("Error creating screener", e); - } - }; - - const handleProjectMenuClicked = (e, screenerData) => { - e.stopPropagation(); - setEditModalData(screenerData); - setIsEditgModalVisible(true); - }; - - const handleUpdateScreener = async (screenerData) => { - try { - await updateScreener(screenerData); - refetchProjectList(); - setIsEditgModalVisible(false); - } catch (e) { - console.log("Error editing screener", e); - } - }; - - const handleDeleteScreener = async (screenerData) => { - try { - await deleteScreener(screenerData); - refetchProjectList(); - setIsEditgModalVisible(false); - } catch (e) { - console.log("Error deleting screener", e); - } - }; +interface Props { + projects: Resource; + refetchProjects: ResourceActions["refetch"]; +} +export default function ProjectsList(props: Props) { return ( - <> -
- Loading...
}> -
-
setIsNewScreenerModalVisible(true)} - class=" - p-4 w-80 h-60 flex justify-center cursor-pointer - border-4 border-gray-300 rounded-lg - shadow-md hover:shadow-lg hover:bg-gray-200" - > -
- Create new screener -
-
- -
-
- Loading screeners... -
-
-
- - {(item) => - item && ( -
-
handleProjectMenuClicked(e, item)} - > - -
-
navigateToProject(item)} - class="h-60 p-4 flex flex-col justify-center items-center" - > -
- {item.screenerName} -
-
-
- ) - } -
-
- -
- {isNewScreenerModalVisible() && ( - - )} - {isEditModalVisible() && ( - + + {(item) => ( + )} - + ); } diff --git a/builder-frontend/src/components/homeScreen/HomeScreen.tsx b/builder-frontend/src/components/homeScreen/HomeScreen.tsx index 22e3075..e6cd9e0 100644 --- a/builder-frontend/src/components/homeScreen/HomeScreen.tsx +++ b/builder-frontend/src/components/homeScreen/HomeScreen.tsx @@ -12,14 +12,14 @@ const HomeScreen = () => { return (
BDT Home - + {/* - + */}
); }; diff --git a/builder-frontend/src/components/shared/ANavBar.css b/builder-frontend/src/components/shared/ANavBar.css index 1e83aec..17197ec 100644 --- a/builder-frontend/src/components/shared/ANavBar.css +++ b/builder-frontend/src/components/shared/ANavBar.css @@ -1,4 +1,4 @@ -@import "tailwindcss"; +@reference "tailwindcss"; .navbarlink { @apply px-4 py-2 no-underline border-b-2 hover:bg-gray-200 transition-colors; diff --git a/builder-frontend/src/components/shared/Button.module.css b/builder-frontend/src/components/shared/Button.module.css new file mode 100644 index 0000000..f3bd219 --- /dev/null +++ b/builder-frontend/src/components/shared/Button.module.css @@ -0,0 +1,45 @@ +@reference "tailwindcss"; + +.button { + @apply inline-flex py-1.5 px-4 rounded border-2 disabled:opacity-50; +} + +.primary { + @apply border-(--brand) bg-(--brand) text-white; +} +.primary:not([disabled]):hover { + @apply brightness-110; +} +.outline-primary { + @apply border-(--brand) bg-white text-(--brand); +} +.outline-primary:not([disabled]):hover { + @apply border-(--brand)/70 bg-(--brand)/30; +} + +.secondary { + @apply border-(--text-strong) bg-(--text-strong) text-white; +} +.secondary:not([disabled]):hover { + @apply brightness-130; +} +.outline-secondary { + @apply border-(--text-strong) bg-white text-(--text-strong); +} +.outline-secondary:not([disabled]):hover { + @apply border-(--text-strong)/70 bg-(--text-strong)/30; +} + +.tertiary { + @apply border-(--text-strong) bg-(--text-strong) text-white; +} + +.danger { + @apply border-red-400 bg-red-400 text-white; +} +.outline-danger { + @apply border-red-400 bg-white text-red-400; +} +.outline-danger:not([disabled]):hover { + @apply bg-red-100; +} diff --git a/builder-frontend/src/components/shared/Button.tsx b/builder-frontend/src/components/shared/Button.tsx new file mode 100644 index 0000000..654f1a8 --- /dev/null +++ b/builder-frontend/src/components/shared/Button.tsx @@ -0,0 +1,28 @@ +import { Component, JSX, ParentProps } from "solid-js"; + +import styles from "./Button.module.css"; + +interface Props extends JSX.ButtonHTMLAttributes { + variant?: + | "primary" + | "secondary" + | "tertiary" + | "danger" + | "outline-primary" + | "outline-secondary" + | "outline-tertiary" + | "outline-danger"; +} + +export const Button: Component> = (props) => { + const { type, variant, children, ...rest } = props; + return ( + + ); +}; diff --git a/builder-frontend/src/components/shared/Form.css b/builder-frontend/src/components/shared/Form.css new file mode 100644 index 0000000..8286ffd --- /dev/null +++ b/builder-frontend/src/components/shared/Form.css @@ -0,0 +1,24 @@ +@reference "tailwindcss"; + +.textfield { + @apply relative border-2 border-slate-400 rounded-sm; + flex-direction: column; + margin-bottom: 1rem; +} +.textfield-label { + @apply absolute top-0 left-0 p-4 text-slate-500 pointer-events-none; +} +.textfield:hover { + @apply border-slate-500; +} +.textfield:has(.textfield-input:focus) { + @apply border-slate-800; +} +.textfield > .textfield-input:focus ~ .textfield-label, +.textfield > .textfield-input:not(:placeholder-shown) ~ .textfield-label { + transform: scale(0.8) translateY(-1.5rem); + transform-origin: 1rem; +} +.textfield-input { + @apply w-full p-4 focus:outline-0 text-(--text-strong); +} diff --git a/builder-frontend/src/components/shared/Form.tsx b/builder-frontend/src/components/shared/Form.tsx new file mode 100644 index 0000000..41dacb5 --- /dev/null +++ b/builder-frontend/src/components/shared/Form.tsx @@ -0,0 +1,76 @@ +import { + Component, + createContext, + JSX, + splitProps, + useContext, +} from "solid-js"; + +import "./Form.css"; + +interface FormProps extends JSX.FormHTMLAttributes {} +interface LabelWrapperProps extends JSX.HTMLAttributes { + htmlFor: string; + placeholder: string; +} +interface TextInputProps extends JSX.InputHTMLAttributes { + value: string; + onChange: JSX.ChangeEventHandlerUnion; +} +interface LabelProps extends JSX.LabelHTMLAttributes { + htmlFor: string; + label: string; +} + +const FormContext = createContext<{ htmlFor?: string }>({}); + +const TextInput = (props: TextInputProps) => { + const [local, rest] = splitProps(props, ["value", "onChange", "placeholder"]); + const hasUserInput = local.value.length > 0; + const ctx = useContext(FormContext); + return ( + + ); +}; +const Label = (props: LabelProps) => { + const [local, rest] = splitProps(props, ["htmlFor", "label"]); + return ( + + ); +}; +const LabelAbove = (props: LabelWrapperProps) => { + const [local, rest] = splitProps(props, [ + "htmlFor", + "placeholder", + "children", + ]); + return ( + +
+ {local.children} +
+
+ ); +}; + +const Form: Component = (props) => { + return
; +}; + +export default Object.assign(Form, { + Label, + TextInput, + LabelAbove, +}); diff --git a/builder-frontend/src/components/shared/Modal.module.css b/builder-frontend/src/components/shared/Modal.module.css new file mode 100644 index 0000000..4f58426 --- /dev/null +++ b/builder-frontend/src/components/shared/Modal.module.css @@ -0,0 +1,27 @@ +@reference "tailwindcss"; + +.modal-wrapper { + @apply fixed top-0 left-0 h-full w-full bg-black/10 backdrop-blur-sm; + z-index: 10; + overflow-y: auto; +} +.modal-content { + @apply bg-white rounded-xl mx-auto my-24 w-1/2; + position: relative; + padding: 2rem; + width: fit-content; + min-width: 50%; +} +.modal-header { + display: flex; + justify-content: flex-end; +} +.modal-close { + @apply w-fit text-gray-500; +} +.modal-close:hover { + @apply text-gray-800 cursor-pointer; +} +body:has(.modal-wrapper) { + overflow-y: hidden; +} diff --git a/builder-frontend/src/components/shared/Modal.tsx b/builder-frontend/src/components/shared/Modal.tsx new file mode 100644 index 0000000..96dff16 --- /dev/null +++ b/builder-frontend/src/components/shared/Modal.tsx @@ -0,0 +1,36 @@ +import { Accessor, Component, createEffect, ParentProps, Show } from "solid-js"; +import { Portal } from "solid-js/web"; + +import styles from "./Modal.module.css"; +import { CircleX } from "lucide-solid"; + +interface Props { + show: Accessor; + onClose: () => void; +} +export const Modal: Component> = (props) => { + return ( + + +
props.onClose()}> +
e.stopPropagation()} + > +
+
props.onClose()} + > + +
+
+
{props.children}
+
+
+
+
+ ); +}; diff --git a/builder-frontend/src/index.css b/builder-frontend/src/index.css index 3baf52d..76f87a7 100644 --- a/builder-frontend/src/index.css +++ b/builder-frontend/src/index.css @@ -3,12 +3,20 @@ strategy: "class"; /* only generate form-* classes */ } +:root { + --brand: #0daf00; + --text-strong: #2d2d2d; + --text-weak: #6d6d6d; + --bg-green: #f5fef6; + --bg-yellow: #fff1c2; + --bg-red: #fff6f5; +} + @layer components { .btn-default { - @apply - inline-block px-6 py-1 + @apply inline-block px-6 py-1 border-gray-300 border-2 - cursor-pointer select-none rounded-md + cursor-pointer select-none rounded-md; } .btn-default.btn-gray { @apply bg-white hover:bg-gray-200; @@ -21,7 +29,7 @@ @apply bg-yellow-700 hover:bg-yellow-800 text-white; } .btn-default.btn-blue { - @apply bg-sky-600 hover:bg-sky-700 text-white + @apply bg-sky-600 hover:bg-sky-700 text-white; } .eligibility-check-table-header { diff --git a/builder-frontend/tailwind.config.js b/builder-frontend/tailwind.config.js index 72006dc..1d0aeb6 100644 --- a/builder-frontend/tailwind.config.js +++ b/builder-frontend/tailwind.config.js @@ -5,8 +5,8 @@ export default { extend: {}, }, plugins: [ - require('@tailwindcss/forms')({ - strategy: 'class', // only apply styles when using form-* classes + require("@tailwindcss/forms")({ + strategy: "class", // only apply styles when using form-* classes }), ], -} +}; From 8da8bcc4fd2e8c08d2c2c4a6484452034399ab43 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:10:36 -0500 Subject: [PATCH 16/18] use prettier for css files --- benefit-decision-toolkit.code-workspace | 1 + 1 file changed, 1 insertion(+) diff --git a/benefit-decision-toolkit.code-workspace b/benefit-decision-toolkit.code-workspace index 841db03..5b68da1 100644 --- a/benefit-decision-toolkit.code-workspace +++ b/benefit-decision-toolkit.code-workspace @@ -30,6 +30,7 @@ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true }, + "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "typescript.preferences.importModuleSpecifier": "non-relative", "javascript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.includePackageJsonAutoImports": "auto", From e12b3f8625372e50c641c5896804ca843bcdd79d Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:04:48 -0500 Subject: [PATCH 17/18] `npm audit fix` for security vulnerabilities --- builder-frontend/package-lock.json | 44 ++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/builder-frontend/package-lock.json b/builder-frontend/package-lock.json index e5e3851..52ffae7 100644 --- a/builder-frontend/package-lock.json +++ b/builder-frontend/package-lock.json @@ -3350,7 +3350,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/atoa": { "version": "1.0.0", @@ -3406,12 +3407,13 @@ } }, "node_modules/axios": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, @@ -3533,6 +3535,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -3647,6 +3650,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3732,6 +3736,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -3953,6 +3958,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -4003,6 +4009,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -4011,6 +4018,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -4019,6 +4027,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -4030,6 +4039,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -4330,9 +4340,10 @@ } }, "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4375,6 +4386,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4400,6 +4412,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -4423,6 +4436,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -4451,6 +4465,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4476,6 +4491,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4487,6 +4503,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -4501,6 +4518,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -5078,6 +5096,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -5107,6 +5126,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5115,6 +5135,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -6431,10 +6452,11 @@ "dev": true }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", From 6e9740cc5b52515ca4e9d73021908fc8ad456929 Mon Sep 17 00:00:00 2001 From: joshwanf <17016446+joshwanf@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:06:10 -0500 Subject: [PATCH 18/18] expanded
api to handle or plain and adding additional classes --- .../components/Project/EditScreenerForm.tsx | 6 +- .../components/Project/NewScreenerForm.tsx | 6 +- .../src/components/shared/Form.css | 25 +++++--- .../src/components/shared/Form.tsx | 60 ++++++++++++------- 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/builder-frontend/src/components/Project/EditScreenerForm.tsx b/builder-frontend/src/components/Project/EditScreenerForm.tsx index 802010c..74c572b 100644 --- a/builder-frontend/src/components/Project/EditScreenerForm.tsx +++ b/builder-frontend/src/components/Project/EditScreenerForm.tsx @@ -24,7 +24,6 @@ export default function EditScreenerForm(props: ParentProps) { const [showDelete, setShowDelete] = createSignal(false); const [error, setError] = createSignal(null); const [isLoading, setIsLoading] = createSignal(false); - const [screenerName, setScreenerName] = createSignal(props.screenerName); const handleSubmit: JSX.EventHandler = (e) => { e.preventDefault(); @@ -66,10 +65,7 @@ export default function EditScreenerForm(props: ParentProps) { - setScreenerName(e.target.value)} - /> +
{error()}
diff --git a/builder-frontend/src/components/Project/NewScreenerForm.tsx b/builder-frontend/src/components/Project/NewScreenerForm.tsx index e52770f..f19623a 100644 --- a/builder-frontend/src/components/Project/NewScreenerForm.tsx +++ b/builder-frontend/src/components/Project/NewScreenerForm.tsx @@ -7,7 +7,6 @@ import Form from "@/components/shared/Form"; export default function NewScreenerForm() { const navigate = useNavigate(); const [isLoading, setIsLoading] = createSignal(false); - const [screenerName, setScreenerName] = createSignal(""); const [error, setError] = createSignal(); const handleSubmit: JSX.EventHandler = async ( @@ -33,10 +32,7 @@ export default function NewScreenerForm() {

Create a screener

- setScreenerName(e.target.value)} - /> +
{error()}
diff --git a/builder-frontend/src/components/shared/Form.css b/builder-frontend/src/components/shared/Form.css index 8286ffd..dde3f53 100644 --- a/builder-frontend/src/components/shared/Form.css +++ b/builder-frontend/src/components/shared/Form.css @@ -1,21 +1,32 @@ @reference "tailwindcss"; .textfield { - @apply relative border-2 border-slate-400 rounded-sm; + @apply relative; +} +.textfield .textfield-input, +.textfield-above { + @apply border-2 border-slate-400 rounded-sm; +} +.textfield:hover .textfield-input, +.textfield-above:hover { + @apply border-slate-500; +} +.textfield-above { + @apply relative; flex-direction: column; margin-bottom: 1rem; } .textfield-label { - @apply absolute top-0 left-0 p-4 text-slate-500 pointer-events-none; + @apply py-2; } -.textfield:hover { - @apply border-slate-500; +.textfield-above > .textfield-label { + @apply absolute top-0 left-0 p-4 text-slate-500 pointer-events-none; } -.textfield:has(.textfield-input:focus) { +.textfield-above:has(.textfield-input:focus) { @apply border-slate-800; } -.textfield > .textfield-input:focus ~ .textfield-label, -.textfield > .textfield-input:not(:placeholder-shown) ~ .textfield-label { +.textfield-above > .textfield-input:focus ~ .textfield-label, +.textfield-above > .textfield-input:not(:placeholder-shown) ~ .textfield-label { transform: scale(0.8) translateY(-1.5rem); transform-origin: 1rem; } diff --git a/builder-frontend/src/components/shared/Form.tsx b/builder-frontend/src/components/shared/Form.tsx index 41dacb5..452470a 100644 --- a/builder-frontend/src/components/shared/Form.tsx +++ b/builder-frontend/src/components/shared/Form.tsx @@ -1,6 +1,7 @@ import { Component, createContext, + createSignal, JSX, splitProps, useContext, @@ -9,58 +10,74 @@ import { import "./Form.css"; interface FormProps extends JSX.FormHTMLAttributes {} +interface FormWrapperProps extends JSX.HTMLAttributes { + htmlFor: string; +} interface LabelWrapperProps extends JSX.HTMLAttributes { htmlFor: string; placeholder: string; } interface TextInputProps extends JSX.InputHTMLAttributes { - value: string; - onChange: JSX.ChangeEventHandlerUnion; -} -interface LabelProps extends JSX.LabelHTMLAttributes { - htmlFor: string; - label: string; + value?: string; + placeholder?: string; } +interface LabelProps extends JSX.LabelHTMLAttributes {} +const mergeClasses = (...classes: (string | undefined)[]): string => { + return classes.filter((c) => c !== undefined && c.length > 0).join(" "); +}; const FormContext = createContext<{ htmlFor?: string }>({}); const TextInput = (props: TextInputProps) => { - const [local, rest] = splitProps(props, ["value", "onChange", "placeholder"]); - const hasUserInput = local.value.length > 0; + const [value, setValue] = createSignal(props.value || ""); + const [local, rest] = splitProps(props, ["class", "placeholder"]); const ctx = useContext(FormContext); + const showPlaceholder = + value().length < 1 && (local.placeholder || "").length > 0; + return ( setValue(e.target.value)} + placeholder={showPlaceholder ? local.placeholder : ""} {...rest} /> ); }; const Label = (props: LabelProps) => { - const [local, rest] = splitProps(props, ["htmlFor", "label"]); + const [local, rest] = splitProps(props, ["class"]); + const ctx = useContext(FormContext); + return ( - +