Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test method #50

Merged
merged 25 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
30b1efa
testMethod filter works (but is messy)
toddnief Jul 11, 2024
7dc8d6b
move onSummaryClick to utils
toddnief Jul 11, 2024
b7a427b
use tailwind styling
toddnief Jul 11, 2024
df09e1c
add display controls component
toddnief Jul 11, 2024
97efff1
convert dropdowns to checkboxes
toddnief Jul 11, 2024
82b8230
commit all the new components
toddnief Jul 11, 2024
749b116
switch to radio buttons
toddnief Jul 11, 2024
c82b87c
commit radio button component
toddnief Jul 11, 2024
9e1b458
set y-axis to always display 100%
toddnief Jul 11, 2024
c2c5687
fix data loading bug
toddnief Jul 11, 2024
5f56af3
move checkbox to front
toddnief Jul 11, 2024
c025acb
remove plotly mode bar
toddnief Jul 11, 2024
dcd0608
add item brand to items table and display options
toddnief Jul 12, 2024
a17f3eb
differentiate no selection from not enough data
toddnief Jul 13, 2024
70f460d
add styled alert messages
toddnief Jul 14, 2024
d89842e
actually commit the Alert component
toddnief Jul 14, 2024
7815b4b
add margin to alert component
toddnief Jul 14, 2024
eb2b836
format axis labels
toddnief Jul 15, 2024
bfc3774
add some styling to filter menus
toddnief Jul 15, 2024
a998dcd
fix data capping bug
toddnief Jul 15, 2024
17a0e35
more styling and actually fix bug with ymax calculation
toddnief Jul 15, 2024
083cc6d
switch to default xaxis label handling
toddnief Jul 15, 2024
f07a42f
make x labels vertical when there's lots
toddnief Jul 20, 2024
5f1aae5
checkbox menus collapse
toddnief Jul 20, 2024
95d8c7b
add padding to controls
toddnief Jul 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions dashboard-react/app/api/data/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,13 @@ const getIntersectingTrialIDs = (...sets) => {
const prepareData = async (searchParams) => {
console.log("searchParams");
console.log(searchParams);
// TODO: Clean up the handling of defaults
// Display params
const aggCol = searchParams.get("aggcol") || "Material Class I";
const displayCol = searchParams.get("displaycol") || "% Residuals (Mass)";
const uncapResults = searchParams.get("uncapresults") === "true" || false;
const displayResiduals = searchParams.get("displayresiduals") === "true";
// Trial and item filters
const testMethods = searchParams.get("testmethods")
? searchParams.get("testmethods").split(",")
: [];
const testMethod = searchParams.get("testmethod") || "Mesh Bag";
const technologies = searchParams.get("technologies")
? searchParams.get("technologies").split(",")
: [];
Expand All @@ -161,6 +158,20 @@ const prepareData = async (searchParams) => {
? searchParams.get("trialdurations").split(",")
: [];

const noFiltersSelected =
technologies.length === 0 ||
materials.length === 0 ||
temperatureFilter.length === 0 ||
moistureFilter.length === 0 ||
trialDurations.length === 0;

if (noFiltersSelected) {
return {
message:
"None selected for some filtering criteria. Please make sure you have at least one filter selected.",
};
}

let trialData;
let operatingConditions;

Expand All @@ -180,8 +191,7 @@ const prepareData = async (searchParams) => {
var filteredData = [...trialData];

// filter data based on selected filters
// TODO: This is wrong!
filteredData = filterData(filteredData, "Test Method", testMethods);
filteredData = filterData(filteredData, "Test Method", [testMethod]);
console.log("filteredData.length after test methods");
console.log(filteredData.length);
filteredData = filterData(filteredData, "Technology", technologies);
Expand Down Expand Up @@ -257,7 +267,10 @@ const prepareData = async (searchParams) => {

// Not enough data - return empty object
if (filteredData.length < 5) {
return {};
return {
message:
"Not enough data for the selected criteria. Please select more options.",
};
}

const uniqueTrialIDs = new Set(filteredData.map((d) => d["Trial ID"]));
Expand Down
16 changes: 5 additions & 11 deletions dashboard-react/app/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import React, { useEffect } from "react";
import state from "@/lib/state";
import dynamic from "next/dynamic";
import FilterControls from "@/components/FilterControls";
import DashboardControls from "@/components/DashboardControls";

const Dashboard = dynamic(() => import("@/components/Dashboard"), {
ssr: false,
Expand All @@ -23,17 +23,11 @@ const Home = () => {
}, []);

return (
<main
style={{
display: "flex",
flexDirection: "row",
alignItems: "flex-start",
}}
>
<div style={{ marginRight: "20px" }}>
<FilterControls />
<main className="flex flex-row items-start h-screen">
<div className="max-w-[600px]">
<DashboardControls />
</div>
<div style={{ minWidth: "1000px" }}>
<div className="min-w-[1000px] h-full">
<Dashboard />
</div>
</main>
Expand Down
14 changes: 14 additions & 0 deletions dashboard-react/components/Alert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use client";
import React from "react";

const AlertMessage = ({ message }) => {
return (
<div
className={`border-l-4 p-4 bg-yellow-100 border-yellow-400 text-yellow-700 rounded mt-5`}
>
<p className="font-bold">{message}</p>
</div>
);
};

export default AlertMessage;
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
"use client";
import React, { useRef } from "react";
import React, { useState } from "react";
import { useSnapshot } from "valtio";
import state from "@/lib/state";
import { closeOpenedDetails } from "@/lib/utils";

const DropdownCheckbox = React.memo(function DropdownCheckbox({
export default function CheckboxMenu({
options,
selectedOptions,
filterKey,
title,
}) {
const snap = useSnapshot(state);
const divRef = useRef(null);

const onSummaryClick = () => {
closeOpenedDetails(`summary-${filterKey}`);
};
// Note: Use local state for the expanded state of the menu
const [expanded, setExpanded] = useState(false);

const handleCheckboxChange = (key, value) => (event) => {
const checked = event.target.checked;
console.log(`checked for ${key} ${value}`);
console.log(checked);
if (checked) {
state.setFilterValue(key, [...snap.filters[key], value]);
} else {
console.log("removing");
state.setFilterValue(
key,
snap.filters[key].filter((item) => item !== value)
Expand All @@ -40,26 +32,23 @@ const DropdownCheckbox = React.memo(function DropdownCheckbox({
state.setFilterValue(filterKey, []);
};

const isAllSelected = selectedOptions.length === options.length;

return (
<>
<h2>{title}</h2>
<div className="divider m-0"></div>
<details className="dropdown">
<summary
className="btn m-1"
onClick={onSummaryClick}
ref={divRef}
id={`summary-${filterKey}`}
<div className="my-4">
<h2 className="text-center">{title}</h2>
<div className="flex justify-center mt-3">
<button
className="btn btn-sm normal-case"
onClick={() => setExpanded((e) => !e)}
>
{isAllSelected
? "All Selected"
: selectedOptions.length > 0
? selectedOptions.join(", ")
: "None Selected"}
</summary>
<ul className="menu dropdown-content bg-base-100 rounded-box z-[1] w-52 p-2 shadow">
{expanded ? `Collapse Menu` : `Show Menu`}
</button>
</div>
<div
className={`overflow-auto flex-grow px-4 ${
expanded ? "max-h-[200px]" : "h-0"
}`}
>
<ul className="menu dropdown-content bg-base-100 rounded-box z-[1] w-52 p-2 mx-auto shadow">
{options?.map((option) => (
<li key={option}>
<label className="label cursor-pointer">
Expand All @@ -75,8 +64,8 @@ const DropdownCheckbox = React.memo(function DropdownCheckbox({
</li>
))}
</ul>
</details>
<div className="mt-2 flex join justify-left">
</div>
<div className="mt-2 flex join justify-center">
<button
className="btn join-item btn-sm normal-case"
onClick={selectAll}
Expand All @@ -91,8 +80,6 @@ const DropdownCheckbox = React.memo(function DropdownCheckbox({
</button>
</div>
<div className="divider m-0"></div>
</>
</div>
);
});

export default DropdownCheckbox;
}
48 changes: 39 additions & 9 deletions dashboard-react/components/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ import React from "react";
import Plot from "react-plotly.js";
import { useSnapshot } from "valtio";
import state from "@/lib/state";
import { col2material } from "@/lib/constants";
import Alert from "@/components/Alert";

export default function Dashboard() {
const snap = useSnapshot(state);

if (!snap.dataLoaded) {
return <p>Loading data...</p>;
}

const class2color = {
"Positive Control": "#70AD47",
"Mixed Materials": "#48646A",
Expand All @@ -20,9 +26,13 @@ export default function Dashboard() {
console.log(d);
const materialClass = d["Material Class I"];
const color = class2color[materialClass] || "#000";
const countDisplay =
snap.filters["testMethod"] === "Mesh Bag"
? ` (n=${d["count"]})`
: "";
return {
type: "box",
name: `${d["aggCol"]} (n=${d["count"]})`,
name: `${d["aggCol"]}${countDisplay}`,
y: [d.min, d.q1, d.median, d.q3, d.max],
marker: { color },
boxmean: true,
Expand All @@ -43,7 +53,7 @@ export default function Dashboard() {
);

function generateTitle(displayCol, aggCol, num_trials) {
return `${displayCol} by ${aggCol} - ${num_trials} Trial(s)`;
return `${displayCol} by ${col2material[aggCol]} - ${num_trials} Trial(s)`;
}

const title = generateTitle(
Expand All @@ -52,31 +62,51 @@ export default function Dashboard() {
snap.data.numTrials
);

const yMax =
snap.data.data && snap.data.data.length > 0
? Math.max(...snap.data.data.map((d) => d.max), 1)
: 1;

const xTickAngle = plotData.length > 6 ? 90 : 0;

return (
<div style={{ minWidth: "1000px" }}>
{plotData.length > 0 ? (
<div>
{snap.errorMessage ? (
<p>
<Alert message={snap.errorMessage} />
</p>
) : (
<Plot
data={plotData}
layout={{
width: 1000,
height: 700,
height: 800,
title: {
text: title,
text: `<b>${title}</b>`,
x: 0.5,
xanchor: "center",
yanchor: "top",
},
showlegend: false,
yaxis: {
title: {
text: yAxisTitle,
text: `<b>${yAxisTitle}</b>`,
},
tickformat: ".0%",
range: [0, yMax],
},
xaxis: {
tickangle: xTickAngle,
ticklen: 10,
},
margin: {
b: 300,
},
}}
config={{
displayModeBar: false,
}}
/>
) : (
<p>Not enough data for the selected criteria</p>
)}
</div>
);
Expand Down
59 changes: 59 additions & 0 deletions dashboard-react/components/DashboardControls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"use client";
import React, { useEffect, useRef } from "react";
import { useSnapshot } from "valtio";
import state from "@/lib/state";
import DashboardDisplayControls from "@/components/DashboardDisplayControls";
import DashboardFilterControls from "@/components/DashboardFilterControls";

export default function DashboardControls() {
const snap = useSnapshot(state);

useEffect(() => {
if (
snap.options["Test Method"] &&
snap.options["Material Class II"] &&
snap.options["Technology"] &&
state.filters.initialized === false
) {
state.setFilterValue("selectedTestMethods", snap.options["Test Method"]);
state.setFilterValue(
"selectedMaterialTypes",
snap.options["Material Class II"]
);
state.setFilterValue("selectedTechnologies", snap.options["Technology"]);
state.setFilterValue("initialized", true);
}
}, [snap.options]);

useEffect(() => {
const summaries = document.querySelectorAll("summary");

summaries.forEach((summary) => {
summary.addEventListener("click", closeOpenedDetails);
});

function closeOpenedDetails() {
summaries.forEach((summary) => {
let detail = summary.parentNode;
if (detail != this.parentNode) {
detail.removeAttribute("open");
}
});
}
}, [snap.options]);

if (!snap.filters.initialized) {
return <div>Loading...</div>;
}

return (
<div className="flex flex-col h-[100vh] overflow-y-auto px-2">
<div className="m-2">
<DashboardDisplayControls />
</div>
<div className="m-2">
<DashboardFilterControls />
</div>
</div>
);
}
Loading
Loading