Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ app.get(
//Resource API for ENACT-Data-Apps
app.get("/api/v0/resources/", resourceController.getResources);
app.get("/api/v0/resources/all", resourceController.getAllResources);
app.get("/api/v0/resources/sets", resourceController.getResourceUnique);
app.get("/api/v0/resources/allstats", resourceController.getResourcesAndStats);
app.get("/api/v0/resources/stats", resourceController.getResourceStats);
app.get("/api/v0/resources/search", (req, res) => {
Expand Down
61 changes: 45 additions & 16 deletions controllers/resourceController.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,6 @@ const AuthorAlt = require('../models/AuthorAlternative')
const mongoose = require('mongoose');


// Function to retrieve all resources
// exports.getAllResources = async (req, res, next) => {
// try {
// // Retrieve all resource documents
// const allResources = await Resource.find({});
// // Optionally, modify or filter the documents as needed here

// // Return or send the array of all resources
// res.json(allResources);
// } catch (e) {
// // Handle any errors that occur during the process
// next(e);
// }
// };


// Function to retrieve all resources with expanded owner and course information
exports.getAllResources = async (req, res, next) => {
try {
Expand Down Expand Up @@ -358,6 +342,51 @@ exports.getResourceStats = async (req, res, next) => {
}
};

exports.getResourceUnique = async (req, res, next) => {
try {
// Aggregation pipelines for collecting unique sets
const contentTypesPipeline = [
{ $group: { _id: null, contentTypes: { $addToSet: "$contentType" } } },
{ $project: { _id: 0, contentTypes: 1 } }
];

const mediaTypesPipeline = [
{ $group: { _id: null, mediaTypes: { $addToSet: "$mediaType" } } },
{ $project: { _id: 0, mediaTypes: 1 } }
];

const institutionsPipeline = [
{ $group: { _id: null, institutions: { $addToSet: "$institution" } } },
{ $project: { _id: 0, institutions: 1 } }
];

const yearsPipeline = [
{ $group: { _id: null, years: { $addToSet: "$yearOfCreation" } } },
{ $project: { _id: 0, years: 1 } }
];

// Execute the aggregation pipelines
const [contentTypes] = await Resource.aggregate(contentTypesPipeline);
const [mediaTypes] = await Resource.aggregate(mediaTypesPipeline);
const [institutions] = await Resource.aggregate(institutionsPipeline);
const [years] = await Resource.aggregate(yearsPipeline);

// Construct the final sets response
const sets = {
contentTypes: contentTypes ? contentTypes.contentTypes : [],
mediaTypes: mediaTypes ? mediaTypes.mediaTypes : [],
institutions: institutions ? institutions.institutions : [],
years: years ? years.years : []
};

// Send back the sets of values
res.json(sets);
} catch (e) {
next(e);
}
};





Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"passport-local": "^1.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-loading-skeleton": "^3.4.0",
"session": "^0.1.0"
},
"devDependencies": {
Expand All @@ -58,10 +59,12 @@
"babel-plugin-transform-runtime": "^6.23.0",
"chai": "^5.1.0",
"concurrently": "^8.2.2",
"css-loader": "^7.1.2",
"cypress": "^13.8.1",
"dateformat": "^3.0.3",
"lodash.template": ">=4.5.0",
"mocha": "^10.4.0",
"style-loader": "^4.0.0",
"supertest": "^7.0.0",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
Expand Down
501 changes: 499 additions & 2 deletions public/js/bundle.js

Large diffs are not rendered by default.

212 changes: 167 additions & 45 deletions src/app/CategorySelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ function CategorySelector({
setSelectedCategory,
searchTerm,
setSearchTerm,
hits
hits,
resetFilters
}) {
const [inputValue, setInputValue] = useState(searchTerm);
const debouncedSearchTerm = useDebounce(inputValue, 250); // Debounce the input by 250ms
Expand All @@ -15,46 +16,144 @@ function CategorySelector({
setSearchTerm(debouncedSearchTerm);
}, [debouncedSearchTerm, setSearchTerm]);

// useEffect(() => {
// console.log("Selected Category:", selectedCategory);
// }, [selectedCategory]);

// Define categories with appropriate input types
const categories = {
"Types": ["Op-ed", "Research", "Tool", "Other"],
"Years": [2025, 2024, 2023, 2022, 2021, 2020],
"State": ["MA", "CA", "NY", "TX", "FL", "IL", "PA"],
"Topics": ["Health", "Education", "Environment", "Economy", "Social Justice", "Other"]
Years: [
2009, 1830, 2011, 2013, 2015, 2017, 2021, 2012, 2022, 2023, 2018, 2014,
2016, 2010, 2024, 2019, 2020, 2004
],
State: ["MA", "CA", "NY", "TX", "FL", "IL", "PA"],
Topics: [
"Health",
"Education",
"Environment",
"Economy",
"Social Justice",
"Other"
],
contentTypes: [
"Next Steps",
"Personal Reflection",
"Online",
"Flyer",
"Course Planning",
"ENACT Research",
"Elevator Speech",
"Op-Ed",
"Assignment Guidelines",
"Advocacy Video",
"Student Advice",
"Journal",
"Letter to Legislator",
"Research Report",
"Portfolio",
"empty",
"News and Articles",
"Syllabus",
"Presentations",
"Script"
],
mediaTypes: ["video", "document", "photo", "PowerPoint", "empty"],
institutions: [
"Arts & Democracy Project",
"The University of Maine",
"Boise State University",
"Randolph-macon",
"The Pennsylvania State University",
"University of New Hampshire",
"Seattle University",
"Brandeis University",
"Delaware State University",
"Bennington College",
"Randolph Macon College",
"Emory University",
"FrameWorks Institute",
"Penn State University",
"Florida Agricultural and Mechanical University"
]
};

// Calculate the number of selected filters
const selectedFilterCount = Object.values(selectedCategory).filter(
(value) => value
).length;

function renderInput(group, items) {
switch (group) {
case "State":
case "Years": // Using dropdowns for "State" and "Years"
case "Years":
case "Topics":
case "contentTypes":
case "mediaTypes":
case "institutions":
// Using dropdowns for "State" and "Years"
return (
<select
value={selectedCategory[group] || ''}
onChange={(e) => setSelectedCategory({ ...selectedCategory, [group]: e.target.value })}
style={{ marginBottom: "20px", padding: "5px", width: "100%" }}
>
{items.map((item, index) => <option key={index} value={item}>{item}</option>)}
value={selectedCategory[group] || ""}
onChange={(e) =>
setSelectedCategory({
...selectedCategory,
[group]: e.target.value
})
}
style={{ marginBottom: "20px", padding: "5px", width: "100%" }}>
<option value="">Select {group}</option>{" "}
{/* Default 'not selected' state */}
{items.map((item, index) => (
<option key={index} value={item}>
{item}
</option>
))}
</select>
);
case "Types":
case "Topic(s)/Issue(s)": // Using radio buttons for "Types" and "Topic(s)/Issue(s)"
return items.map((categoryName, index) => (
<label key={index} style={{ display: "block", marginTop: "5px", listStyle: "none" }}>
<input
type="radio"
name={group}
value={categoryName}
checked={selectedCategory[group] === categoryName}
onChange={() => setSelectedCategory({ ...selectedCategory, [group]: categoryName })}
style={{ marginRight: "5px" }}
/>
{categoryName}
</label>
));
case "Topics":
case "contentTypes":
case "mediaTypes":
// Using radio buttons for "Topics", "contentTypes", and "mediaTypes"
return (
<>
<label
style={{ display: "block", marginTop: "5px", listStyle: "none" }}>
<input
type="radio"
name={group}
value=""
checked={!selectedCategory[group]}
onChange={() =>
setSelectedCategory({
...selectedCategory,
[group]: ""
})
}
style={{ marginRight: "5px" }}
/>
None {/* Default 'not selected' radio option */}
</label>
{items.map((categoryName, index) => (
<label
key={index}
style={{
display: "block",
marginTop: "5px",
listStyle: "none"
}}>
<input
type="radio"
name={group}
value={categoryName}
checked={selectedCategory[group] === categoryName}
onChange={() =>
setSelectedCategory({
...selectedCategory,
[group]: categoryName
})
}
style={{ marginRight: "5px" }}
/>
{categoryName}
</label>
))}
</>
);
default:
return null; // Default case for unexpected categories
}
Expand All @@ -63,6 +162,7 @@ function CategorySelector({
return (
<div
style={{
marginTop: "-20px",
width: "200px",
height: "100%",
maxHeight: "80vh",
Expand All @@ -72,25 +172,47 @@ function CategorySelector({
padding: "1rem",
listStyle: "none",
position: "sticky",
top: 100,
top: 80,
zIndex: 1,
overflowY: "auto",
border: "0.1px solid lightgrey"
}}
>
<input
type="search"
placeholder="Search..."
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
style={{ marginBottom: "20px", padding: "5px", width: "100%" }}
/>
}}>
{/* Conditionally render the reset button if any filters are selected */}
{/* selectedFilterCount > 0 */}
{selectedFilterCount > 0 && (
<button
onClick={resetFilters}
style={{
borderRadius: "10px",
border: "0.5px solid whitesmoke",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignContent: "center",
alignItems: "center",
color: "white",
backgroundColor: "#0053A4",

}}>
<span
style={{
backgroundColor: "#3584d2",
borderRadius: "12px",
padding: "1px 5px",
fontSize: "0.7rem"
}}>
{selectedFilterCount}
</span>
Clear Filters
<span style={{ color: "white !important", fontWeight: "bold" }}>
</span>{" "}
{/* Red 'x' */}
</button>
)}

{Object.entries(categories).map(([group, items]) => (
<details key={group} open>
<summary>{group}</summary>
{renderInput(group, items)}
</details>
<div key={group}>{renderInput(group, items)}</div>
))}
</div>
);
Expand Down
Loading