diff --git a/src/components/SavedSearches.tsx b/src/components/SavedSearches.tsx index e3853b7..26c870b 100644 --- a/src/components/SavedSearches.tsx +++ b/src/components/SavedSearches.tsx @@ -1,21 +1,37 @@ import React, { useState } from "react"; import { RxCross1 } from "react-icons/rx"; +type ButtonClickHandler = (value:string) => void; interface ButtonComponentProps { content: string; clickStay: boolean; textSize: string; + clearSingleSavedItems: ButtonClickHandler; buttonClicked?: boolean; } -const SavedSearchBtn: React.FC = ({ content, clickStay, textSize, buttonClicked }) => { +const SavedSearchBtn: React.FC = ({ content, clickStay, textSize, clearSingleSavedItems, buttonClicked }) => { + const [displayx, setDisplayx] = useState(true); + const [greenBg, setGreenBg] = useState(true); + + const handleXClick = () => { + // setDisplayx(false); + // setGreenBg(false); + clearSingleSavedItems(content); + } + + const handleMainClick = () => { + setDisplayx(true); + setGreenBg(!greenBg); + } return ( ) diff --git a/src/components/Search.tsx b/src/components/Search.tsx index e931ded..46cfbfd 100644 --- a/src/components/Search.tsx +++ b/src/components/Search.tsx @@ -2,9 +2,8 @@ import React, { useEffect, useState } from "react"; // search input import { IoSearch } from "react-icons/io5"; import { RxCross1 } from "react-icons/rx"; -import { SearchCard } from "./SearchCard"; -import Fuse from 'fuse.js' -import DropInData from "../../backend/scraper/drop_in.json"; +// saved searches +import { saveArrayToLocalStorage, getArrayFromLocalStorage } from "../utils/localStorageUtil"; // for date picker import dayjs, { Dayjs } from 'dayjs'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; @@ -19,19 +18,12 @@ import Select, { SelectChangeEvent } from '@mui/material/Select'; import Checkbox from '@mui/material/Checkbox'; import { categoryListAcademics, categoryListClubs, categoryListCareer } from "../types"; // selectable dropdown -// import { Dropdown } from "./Dropdown"; import { Button } from "./Button"; import "react-dropdown/style.css"; import "./Search.css"; import { SavedSearchBtn } from "./SavedSearches"; -// import useSearch from "../../utils/hooks/useSearch"; -import DITData from "../../backend/scraper/drop_in.json"; -import PTData from "../../backend/scraper/peer_tutoring.json"; -import SIData from "../../backend/scraper/si.json"; -import ClubsData from "../../backend/scraper/tartanconnect.json"; -import CareerData from "../../backend/scraper/handshake.json"; - +import SearchContent from "./SearchContent"; // https://plainenglish.io/blog/how-to-implement-a-search-bar-in-react-js @@ -44,6 +36,7 @@ const Search: React.FC = ({ page }) => { // Chang name to search bar const [searchInput, setSearchInput] = useState(""); const [categoryName, setCategoryName] = useState([]); + const [savedItems, setSavedItems] = useState([]); // search results // const [data, setData] = useState(null) // date picker @@ -52,16 +45,36 @@ const Search: React.FC = ({ page }) => { const showDatePicker = true; - useEffect(()=> { - // const [data, setData] = useState([]); + useEffect(() => { + // Load the array from local storage when the component mounts + const storedItems = getArrayFromLocalStorage('savedSearches'); + setSavedItems(storedItems); + // clearSavedItems(); + console.log(savedItems); + }, [searchInput]); + + const clearSavedItems = () => { + setSavedItems([]); + localStorage.removeItem('savedSearches'); + }; - // const fuse = new Fuse(SIData, { - // keys: ["course_id", "course_name"], - // }); + const clearSingleSavedItems = (value:string) => { + const index:number = savedItems.indexOf(value); + const newArray = savedItems.slice(0,index).concat(savedItems.slice(index+1,-1)); + setSavedItems(newArray); + saveArrayToLocalStorage('savedSearches', newArray); + // localStorage.removeItem('savedSearches'); + }; + + const addSavedItem = () => { + if (!savedItems.includes(searchInput)) { + const newItems = [...savedItems, searchInput]; + setSavedItems(newItems); + saveArrayToLocalStorage('savedSearches', newItems); + } + console.log(savedItems); + }; - // setData([fuse.search(searchInput)]); - getCategoryData(page); - },[categoryName]) // search @@ -82,68 +95,6 @@ const Search: React.FC = ({ page }) => { ); }; - const inDateRange = (date: string) => { - const startDateObj = startDate.toDate(); - const endDateObj = endDate.toDate(); - const eventDate = new Date(date); - return eventDate >= startDateObj && eventDate <= endDateObj; - } - - const getCategoryData = (currPage:string) => { - // console.log("running category data"); - switch(currPage) { - case "academics": - const optionsAcademics = { keys: ['resource_type', 'course_id', 'course_name'] } - let combinedData = [...DITData, ...SIData, ...PTData]; - const fuseAcademics = new Fuse(combinedData, optionsAcademics); - // const fuse = new Fuse(SIData, options); - if (categoryName.length == 0 || categoryName.length==4) { - return fuseAcademics.search(searchInput); - } else { - if (!categoryName.includes("Supplemental Instructions")) { - fuseAcademics.remove((doc) => { - return doc.resource_type === 'SI'; - }) - } - if (!categoryName.includes("Drop-in Tutoring")) { - fuseAcademics.remove((doc) => { - return doc.resource_type === 'DIT'; - }) - } - if (!categoryName.includes("Peer Tutoring")) { - fuseAcademics.remove((doc) => { - return doc.resource_type === 'PT' - }) - } - return fuseAcademics.search(searchInput); - } - break; - case "clubs": - const optionsClubs = { keys: ['resource_type', 'event_name', 'event_host', 'categories'] } - const fuseClubs = new Fuse(ClubsData, optionsClubs); - return fuseClubs.search(searchInput); - break; - case "career": - const optionsCareer = { keys: ['resource_type', 'event_name', 'event_host', 'categories'] } - const fuseCareer = new Fuse(CareerData, optionsCareer); - return fuseCareer.search(searchInput); - break; - } - } - - const getWeekday = (weekday: number) => { - const today = new Date(); - const dayOfWeekToday = today.getDay(); // 0 (Sunday) to 6 (Saturday) - const offSet = dayOfWeekToday - weekday; - if (offSet === 0 || offSet === 7) { - return today.toDateString(); - } else { - const targetDay = new Date(today); - targetDay.setDate(today.getDate() - offSet); - return targetDay.toDateString(); - } - } - const getNumCategories = () => { if (page==="academics") { return categoryListAcademics.length; @@ -254,26 +205,17 @@ const Search: React.FC = ({ page }) => { ); } - // bogus values - const eventName1 = "Office Hours"; - const orgName1 = "15-122 Course Staff"; - const startDate1 = "06/17" - const startTime1 = "3PM" - const endDate1 = "06/17" - const endTime1 = "5PM" - const location1 = "POS 146"; - // const eventCategory1 = "academic"; - // const eventSubcategory1 = "OfficeHour"; return (
- - - - - - + {savedItems && savedItems.map((item, index) => { + return ( + + ) + })} + {/* + */}
= ({ page }) => { value={searchInput} className="bg-gray-200 flex-grow px-4 focus:outline-none" /> - {/* Search icon. Add onClick function in the future */} + {/* Search icon */} {searchInput? setSearchInput("")} className="h-6 w-6 text-gray-500 mr-2" /> : @@ -291,15 +233,16 @@ const Search: React.FC = ({ page }) => {
+ {/* drop down filter */}
{dateContent}
- {/* drop down filter */} {(page === "academics" || page === "clubs" || page==="career") ? (
{categoryContent}
) : ( <> )}
+ {/*
{actionsMenuComp}
*/}
{/* */} @@ -308,45 +251,8 @@ const Search: React.FC = ({ page }) => {
- {searchInput && page==="academics" && getCategoryData(page)?.map( (result) => { - for (let i = 0; i - ) - } - } - })} - {searchInput && (page==="clubs" || page==="career") && getCategoryData(page)?.map( (result) => { - for (let i = 0; i - ) - } - } - })} + { searchInput && }
- -
diff --git a/src/components/SearchCard.tsx b/src/components/SearchCard.tsx index 3fd0e85..03c5cf1 100644 --- a/src/components/SearchCard.tsx +++ b/src/components/SearchCard.tsx @@ -1,6 +1,9 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { IoLocationSharp, IoAdd } from "react-icons/io5"; import { FaRegClock, FaCheck } from "react-icons/fa"; +import { saveArrayToLocalStorage, getArrayFromLocalStorage } from "../utils/localStorageUtil"; + +type ButtonClickHandler = () => void; interface SearchCardProps { eventName: string; @@ -10,6 +13,7 @@ interface SearchCardProps { endDate: string; endTime: string; location: string; + addToSavedItems: ButtonClickHandler; // eventCategory: string; // eventSubcategory: string; } @@ -22,15 +26,20 @@ const SearchCard: React.FC = ({ endDate, endTime, location, + addToSavedItems, // eventCategory, // eventSubcategory, }) => { const [buttonClicked, setButtonClicked] = useState(false); const [cardClicked, setCardClicked] = useState(false); + // const [savedItems, setSavedItems] = useState([]); + + const handleAddToCalendar = () => { setButtonClicked((prevClicked) => !prevClicked); // Logic for calling GCal + addToSavedItems(); }; const handleCardClicked = () => { diff --git a/src/components/SearchContent.tsx b/src/components/SearchContent.tsx new file mode 100644 index 0000000..8b0bf35 --- /dev/null +++ b/src/components/SearchContent.tsx @@ -0,0 +1,147 @@ +import React, { useEffect, useState } from "react"; +import { SearchCard } from "./SearchCard"; +import Fuse from 'fuse.js'; +import dayjs, { Dayjs } from 'dayjs'; + +import DITData from "../../backend/scraper/drop_in.json"; +import PTData from "../../backend/scraper/peer_tutoring.json"; +import SIData from "../../backend/scraper/si.json"; +import ClubsData from "../../backend/scraper/tartanconnect.json"; +import CareerData from "../../backend/scraper/handshake.json"; +import { getArrayFromLocalStorage, saveArrayToLocalStorage } from "../utils/localStorageUtil"; + + +type ButtonClickHandler = () => void; + +interface SearchContentProps { + searchInput: string; + page: string; + categoryName: string[] | string; + startDate: Dayjs; + endDate: Dayjs; + addToSavedItems: ButtonClickHandler; + +} + +const SearchContent: React.FC = ({ searchInput, page, categoryName, startDate, endDate, addToSavedItems }) => { + const props = { searchInput, page, categoryName, startDate, endDate, addToSavedItems }; + + const getCategoryData = (currPage:string) => { + // console.log("running category data"); + switch(currPage) { + case "academics": + const optionsAcademics = { keys: ['resource_type', 'course_id', 'course_name'] } + let combinedData = [...DITData, ...SIData, ...PTData]; + const fuseAcademics = new Fuse(combinedData, optionsAcademics); + // const fuse = new Fuse(SIData, options); + if (categoryName.length == 0 || categoryName.length==4) { + return fuseAcademics.search(searchInput); + } else { + if (!categoryName.includes("Supplemental Instructions")) { + fuseAcademics.remove((doc) => { + return doc.resource_type === 'SI'; + }) + } + if (!categoryName.includes("Drop-in Tutoring")) { + fuseAcademics.remove((doc) => { + return doc.resource_type === 'DIT'; + }) + } + if (!categoryName.includes("Peer Tutoring")) { + fuseAcademics.remove((doc) => { + return doc.resource_type === 'PT' + }) + } + return fuseAcademics.search(searchInput); + } + break; + case "clubs": + const optionsClubs = { keys: ['resource_type', 'event_name', 'event_host', 'categories'] } + const fuseClubs = new Fuse(ClubsData, optionsClubs); + return fuseClubs.search(searchInput); + break; + case "career": + const optionsCareer = { keys: ['resource_type', 'event_name', 'event_host', 'categories'] } + const fuseCareer = new Fuse(CareerData, optionsCareer); + return fuseCareer.search(searchInput); + break; + } + } + + const inDateRange = (date: string) => { + const startDateObj = startDate.toDate(); + const endDateObj = endDate.toDate(); + const eventDate = new Date(date); + return eventDate >= startDateObj && eventDate <= endDateObj; + } + + const getWeekday = (weekday: number) => { + const today = new Date(); + const dayOfWeekToday = today.getDay(); // 0 (Sunday) to 6 (Saturday) + const offSet = dayOfWeekToday - weekday; + if (offSet === 0 || offSet === 7) { + return today.toDateString(); + } else { + const targetDay = new Date(today); + targetDay.setDate(today.getDate() - offSet); + return targetDay.toDateString(); + } + } + + const renderEvents = () => { + if (props.page=="academics") { + return getCategoryData(props.page)?.map( (result, index) => { + for (let i = 0; i < result.item.events.length; i++) { + if (endDate == null || inDateRange(getWeekday(result.item.events[i].weekday))) { + return ( + + ) + } + return null; + } + }) + } + else if (props.page==="clubs" || props.page==="career") { + return getCategoryData(props.page)?.map( (result, index) => { + for (let i = 0; i + ) + } + } + }) + } + return null; + } + + + // else page==="clubs" || page==="career" + + return ( +
+ {renderEvents()} +
+ ) +} + +export default SearchContent; \ No newline at end of file diff --git a/src/utils/localStorageUtil.ts b/src/utils/localStorageUtil.ts new file mode 100644 index 0000000..b0aca62 --- /dev/null +++ b/src/utils/localStorageUtil.ts @@ -0,0 +1,9 @@ +// localStorageUtil.ts +export const saveArrayToLocalStorage = (key: string, array: T[]): void => { + localStorage.setItem(key, JSON.stringify(array)); +}; + +export const getArrayFromLocalStorage = (key: string): T[] => { + const storedArray = localStorage.getItem(key); + return storedArray ? JSON.parse(storedArray) : []; +}; \ No newline at end of file