Commit cad817bb authored by Webb, Jake's avatar Webb, Jake
Browse files

Merge branch 'simulation-list' into 'main'

Simulation list

See merge request !5
parents 2c6e8991 0e871eba
Loading
Loading
Loading
Loading
+113 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
        "@tanstack/router-devtools": "^1.16.4",
        "axios": "^1.6.7",
        "date-fns": "^3.3.1",
        "framer-motion": "^11.0.20",
        "keycloak-js": "^23.0.7",
        "lodash": "^4.17.21",
        "plotly.js": "^2.29.1",
@@ -24,7 +25,8 @@
        "react-dom": "^18.2.0",
        "react-loader-spinner": "^6.1.6",
        "react-plotly.js": "^2.6.0",
        "react-tooltip": "^5.26.3"
        "react-tooltip": "^5.26.3",
        "use-debounce": "^10.0.0"
      },
      "devDependencies": {
        "@tanstack/eslint-plugin-query": "^5.20.1",
@@ -46,6 +48,7 @@
        "eslint-plugin-react-refresh": "^0.4.5",
        "postcss": "^8.4.35",
        "prettier": "^3.2.5",
        "prettier-plugin-tailwindcss": "^0.5.12",
        "tailwindcss": "^3.4.1",
        "typescript": "^5.2.2",
        "vite": "^5.1.0",
@@ -4265,6 +4268,30 @@
        "url": "https://github.com/sponsors/rawify"
      }
    },
    "node_modules/framer-motion": {
      "version": "11.0.20",
      "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.0.20.tgz",
      "integrity": "sha512-YSDmWznt3hpdERosbE0UAPYWoYhTnmQ0J1qWPsgpCia9NgY8Xsz5IpOiUEGGj/nzCAW29fSrWugeLRkdp5de7g==",
      "dependencies": {
        "tslib": "^2.4.0"
      },
      "peerDependencies": {
        "@emotion/is-prop-valid": "*",
        "react": "^18.0.0",
        "react-dom": "^18.0.0"
      },
      "peerDependenciesMeta": {
        "@emotion/is-prop-valid": {
          "optional": true
        },
        "react": {
          "optional": true
        },
        "react-dom": {
          "optional": true
        }
      }
    },
    "node_modules/from2": {
      "version": "2.3.0",
      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
@@ -6446,6 +6473,79 @@
        "node": ">=6.0.0"
      }
    },
    "node_modules/prettier-plugin-tailwindcss": {
      "version": "0.5.12",
      "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.12.tgz",
      "integrity": "sha512-o74kiDBVE73oHW+pdkFSluHBL3cYEvru5YgEqNkBMFF7Cjv+w1vI565lTlfoJT4VLWDe0FMtZ7FkE/7a4pMXSQ==",
      "dev": true,
      "engines": {
        "node": ">=14.21.3"
      },
      "peerDependencies": {
        "@ianvs/prettier-plugin-sort-imports": "*",
        "@prettier/plugin-pug": "*",
        "@shopify/prettier-plugin-liquid": "*",
        "@trivago/prettier-plugin-sort-imports": "*",
        "prettier": "^3.0",
        "prettier-plugin-astro": "*",
        "prettier-plugin-css-order": "*",
        "prettier-plugin-import-sort": "*",
        "prettier-plugin-jsdoc": "*",
        "prettier-plugin-marko": "*",
        "prettier-plugin-organize-attributes": "*",
        "prettier-plugin-organize-imports": "*",
        "prettier-plugin-sort-imports": "*",
        "prettier-plugin-style-order": "*",
        "prettier-plugin-svelte": "*"
      },
      "peerDependenciesMeta": {
        "@ianvs/prettier-plugin-sort-imports": {
          "optional": true
        },
        "@prettier/plugin-pug": {
          "optional": true
        },
        "@shopify/prettier-plugin-liquid": {
          "optional": true
        },
        "@trivago/prettier-plugin-sort-imports": {
          "optional": true
        },
        "prettier-plugin-astro": {
          "optional": true
        },
        "prettier-plugin-css-order": {
          "optional": true
        },
        "prettier-plugin-import-sort": {
          "optional": true
        },
        "prettier-plugin-jsdoc": {
          "optional": true
        },
        "prettier-plugin-marko": {
          "optional": true
        },
        "prettier-plugin-organize-attributes": {
          "optional": true
        },
        "prettier-plugin-organize-imports": {
          "optional": true
        },
        "prettier-plugin-sort-imports": {
          "optional": true
        },
        "prettier-plugin-style-order": {
          "optional": true
        },
        "prettier-plugin-svelte": {
          "optional": true
        },
        "prettier-plugin-twig-melody": {
          "optional": true
        }
      }
    },
    "node_modules/probe-image-size": {
      "version": "7.2.3",
      "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.2.3.tgz",
@@ -7757,8 +7857,7 @@
    "node_modules/tslib": {
      "version": "2.6.2",
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
      "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
      "dev": true
      "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
    },
    "node_modules/type": {
      "version": "1.2.0",
@@ -7945,6 +8044,17 @@
        "punycode": "^2.1.0"
      }
    },
    "node_modules/use-debounce": {
      "version": "10.0.0",
      "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.0.tgz",
      "integrity": "sha512-XRjvlvCB46bah9IBXVnq/ACP2lxqXyZj0D9hj4K5OzNroMDpTEBg8Anuh1/UfRTRs7pLhQ+RiNxxwZu9+MVl1A==",
      "engines": {
        "node": ">= 16.0.0"
      },
      "peerDependencies": {
        "react": ">=16.8.0"
      }
    },
    "node_modules/use-sync-external-store": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+4 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
    "@tanstack/router-devtools": "^1.16.4",
    "axios": "^1.6.7",
    "date-fns": "^3.3.1",
    "framer-motion": "^11.0.20",
    "keycloak-js": "^23.0.7",
    "lodash": "^4.17.21",
    "plotly.js": "^2.29.1",
@@ -26,7 +27,8 @@
    "react-dom": "^18.2.0",
    "react-loader-spinner": "^6.1.6",
    "react-plotly.js": "^2.6.0",
    "react-tooltip": "^5.26.3"
    "react-tooltip": "^5.26.3",
    "use-debounce": "^10.0.0"
  },
  "devDependencies": {
    "@tanstack/eslint-plugin-query": "^5.20.1",
@@ -48,6 +50,7 @@
    "eslint-plugin-react-refresh": "^0.4.5",
    "postcss": "^8.4.35",
    "prettier": "^3.2.5",
    "prettier-plugin-tailwindcss": "^0.5.12",
    "tailwindcss": "^3.4.1",
    "typescript": "^5.2.2",
    "vite": "^5.1.0",

prettier.config.cjs

0 → 100644
+3 −0
Original line number Diff line number Diff line
module.exports = {
  plugins: ["prettier-plugin-tailwindcss"],
};
+40 −6
Original line number Diff line number Diff line
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import Keycloak, { KeycloakConfig } from "keycloak-js";
import { createContext } from "react";
import { createContext, useEffect, useState } from "react";

import { routeTree } from "./routeTree.gen";
import { LoadingSpinner } from "./components/shared/loadingSpinner";
@@ -9,7 +9,9 @@ import { LoadingSpinner } from "./components/shared/loadingSpinner";
import "react-datepicker/dist/react-datepicker.css";
import "react-tooltip/dist/react-tooltip.css";

const basepath = import.meta.env.VITE_BASE_PATH ? new URL(import.meta.env.VITE_BASE_PATH).pathname : "/"
const basepath = import.meta.env.VITE_BASE_PATH
  ? new URL(import.meta.env.VITE_BASE_PATH).pathname
  : "/";

const initOptions: KeycloakConfig = {
  url: "https://obsidian.ccs.ornl.gov/auth/",
@@ -32,7 +34,7 @@ kc.init({
  },
  () => {
    console.error("Authenticated Failed");
  }
  },
);

export interface RouterContext {
@@ -52,11 +54,43 @@ declare module "@tanstack/react-router" {
  }
}

export const AppContext = createContext({ AuthToken: kc.token });
export const AppContext = createContext<{
  AuthToken?: string;
  theme: string | null;
  setTheme: (value: "dark" | "light") => void;
}>({ AuthToken: kc.token, theme: null, setTheme: () => {} });

function App() {
  const theme = localStorage.getItem("theme");
  const [_theme, setTheme] = useState(theme);

  const onThemeSwitch = (theme: "dark" | "light") => {
    if (theme === "dark") {
      document.documentElement.classList.add("dark");
    } else {
      document.documentElement.classList.remove("dark");
    }

    localStorage.setItem("theme", theme);
    setTheme(theme);
  };

  useEffect(() => {
    if (
      localStorage.theme === "dark" ||
      (!("theme" in localStorage) &&
        window.matchMedia("(prefers-color-scheme: dark)").matches)
    ) {
      onThemeSwitch("dark");
    } else {
      onThemeSwitch("light");
    }
  }, []);

  return (
    <AppContext.Provider value={{ AuthToken: kc.token }}>
    <AppContext.Provider
      value={{ AuthToken: kc.token, theme: _theme, setTheme: onThemeSwitch }}
    >
      <QueryClientProvider client={queryClient}>
        <RouterProvider router={router} basepath={basepath} />
      </QueryClientProvider>
+100 −0
Original line number Diff line number Diff line
import { useNavigate, useParams, useSearch } from "@tanstack/react-router";
import { Select } from "../shared/dropdown";
import { BaseSyntheticEvent, useCallback, useState } from "react";
import { NumberInput } from "../shared/number";
import { Route as SimulationCoolingRoute } from "../../routes/simulations.$simulationId.cooling";
import { debounce } from "lodash";
import { Input } from "../shared/input";

const defaultResolution = 10;
const defaultGranularity = 1.0;

export function TimeStepBar() {
  const navigate = useNavigate();
  const { timestepType, resolution, granularity } = useSearch({
    from: SimulationCoolingRoute.fullPath,
  });
  const { simulationId } = useParams({ from: SimulationCoolingRoute.fullPath });
  const [timestepValue, setTimestepValue] = useState({
    resolution,
    granularity,
  });

  const onChange = ({ g, r }: { g?: number; r?: number }) => {
    setTimestepValue({ granularity: g, resolution: r });

    debouncedNavigate({ g, r });
  };

  const debouncedNavigate = useCallback(
    debounce(({ g, r }: { g?: number; r?: number }) => {
      navigate({
        from: SimulationCoolingRoute.fullPath,
        params: { simulationId },
        search: (prev) => ({
          ...prev,
          granularity: g,
          resolution: r,
        }),
      });
    }, 500),
    [],
  );

  return (
    <div className="my-4 flex items-center px-6">
      <span className="mr-2 dark:text-neutral-200">Timestep Type:</span>
      <Select
        value={timestepType}
        choices={[
          { label: "Resolution", value: "resolution" },
          { label: "Granularity", value: "granularity" },
        ]}
        onChange={(e) => {
          const value = e.target.value as "resolution" | "granularity";
          const g = value === "granularity" ? defaultGranularity : undefined;
          const r = value === "resolution" ? defaultResolution : undefined;
          navigate({
            from: SimulationCoolingRoute.fullPath,
            params: { simulationId },
            search: (prev) => ({
              ...prev,
              granularity: g,
              resolution: r,
              timestepType: value,
            }),
          });
          setTimestepValue({ granularity: g, resolution: r });
        }}
      />
      <div className="ml-4">
        {timestepType === "resolution" && (
          <NumberInput
            label="Resolution:"
            labelAlignment="horizontal"
            inputProps={{
              onChange: (e: BaseSyntheticEvent) => {
                onChange({ g: undefined, r: Number(e.target.value) });
              },
              value: timestepValue.resolution,
              min: 1,
            }}
          />
        )}
        {timestepType === "granularity" && (
          <Input
            label="Granularity:"
            labelAlignment="horizontal"
            value={timestepValue.granularity}
            onChange={(e: BaseSyntheticEvent) => {
              const value = e.target.value;
              if (!value || Number(value) > 0) {
                onChange({ g: value, r: undefined });
              }
            }}
          />
        )}
      </div>
    </div>
  );
}
Loading