Skip to content

Commit

Permalink
implemented add and remove event from CMUCal frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
qianxuege committed Aug 1, 2024
1 parent 418703f commit b84c8f1
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 42 deletions.
16 changes: 13 additions & 3 deletions src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { Button } from "./Button";
import SearchContent from "./SearchContent";
import CategoryDropdown from "./CategoryDropdown";
import DatePickerSearch from "./DatePickerSearch";
import { AddFCEventProps, AddFCEventType } from "../types";
import FullCalendar from "@fullcalendar/react";


// https://plainenglish.io/blog/how-to-implement-a-search-bar-in-react-js
Expand All @@ -26,9 +28,15 @@ interface SearchComponentProps {
page: string;
showSearchBar: boolean;
handleSearchBarClick: () => void;
handleAddFCEvent: AddFCEventType;
eventId: number;
setEventId: React.Dispatch<React.SetStateAction<number>>;
calendarRef: React.RefObject<FullCalendar>;
setEvents: React.Dispatch<React.SetStateAction<any[]>>;
}

const Search: React.FC<SearchComponentProps> = ({ page, showSearchBar, handleSearchBarClick }) => {
const Search: React.FC<SearchComponentProps> = ({ page, showSearchBar,
handleSearchBarClick, handleAddFCEvent, eventId, setEventId, calendarRef, setEvents }) => {
// Chang name to search bar
const [searchInput, setSearchInput] = useState("");

Expand All @@ -45,7 +53,7 @@ const Search: React.FC<SearchComponentProps> = ({ page, showSearchBar, handleSea
const storedItems = getArrayFromLocalStorage<string>('savedSearches');
setSavedItems(storedItems);
// clearSavedItems();
console.log(savedItems);
// console.log(savedItems);
}, [searchInput]);


Expand Down Expand Up @@ -200,7 +208,9 @@ const Search: React.FC<SearchComponentProps> = ({ page, showSearchBar, handleSea
</div>

<div className="overflow-scroll" style={{height: '70vh'}}>
{ searchInput && <SearchContent searchInput={searchInput} page={page} categoryName={categoryName} startDate={startDate} endDate={endDate} addToSavedItems={addSavedItem}/>}
{ searchInput && <SearchContent searchInput={searchInput} page={page} categoryName={categoryName} startDate={startDate}
endDate={endDate} addToSavedItems={addSavedItem} handleAddFCEvent={handleAddFCEvent} eventId={eventId}
setEventId={setEventId} calendarRef={calendarRef} setEvents={setEvents}/>}
</div>

</div>
Expand Down
52 changes: 47 additions & 5 deletions src/components/SearchCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useState, useContext } from "react";
import { IoLocationSharp, IoAdd } from "react-icons/io5";
import { FaRegClock, FaCheck } from "react-icons/fa";
import { saveArrayToLocalStorage, getArrayFromLocalStorage } from "../utils/localStorageUtil";
import { AddFCEventType } from "../types";
import { getFormattedDateStr } from "../utils/DateManipulations";
import { handleEventRemoveFC } from "./calendar/event-utils";
import FullCalendar from "@fullcalendar/react";


type ButtonClickHandler = () => void;

Expand All @@ -14,6 +18,12 @@ interface SearchCardProps {
endTime: string;
location: string;
addToSavedItems: ButtonClickHandler;
handleAddFCEvent: AddFCEventType;
eventId: number;
setEventId: React.Dispatch<React.SetStateAction<number>>;
calendarRef: React.RefObject<FullCalendar>;
// events: any[];
setEvents: React.Dispatch<React.SetStateAction<any[]>>;
// eventCategory: string;
// eventSubcategory: string;
}
Expand All @@ -27,19 +37,51 @@ const SearchCard: React.FC<SearchCardProps> = ({
endTime,
location,
addToSavedItems,
handleAddFCEvent,
eventId,
setEventId,
calendarRef,
// events,
setEvents
// eventCategory,
// eventSubcategory,
}) => {
const [buttonClicked, setButtonClicked] = useState(false);
const [cardClicked, setCardClicked] = useState(false);
const [currEventId, setCurrEventId] = useState<number>(-1);

// const [savedItems, setSavedItems] = useState<string[]>([]);


const handleAddToCalendar = () => {
setButtonClicked((prevClicked) => !prevClicked);
// Logic for calling GCal
addToSavedItems();
if (buttonClicked) {
setButtonClicked((prevClicked) => !prevClicked);
setEvents((prevItems) => prevItems.filter((item) => item.id !== `${currEventId}`));
handleEventRemoveFC(calendarRef, `${currEventId}`);
console.log('delete',currEventId);

} else {
setButtonClicked((prevClicked) => !prevClicked);
addToSavedItems();
const startDateTime = getFormattedDateStr(startDate, startTime);
// console.log(currEventId);

if (endDate) {
const endDateTime = getFormattedDateStr(endDate, endTime);
handleAddFCEvent({id:`${eventId}`, title:eventName, start:startDateTime, end: endDateTime, allDay: !endTime });
} else {
const endDateTime = getFormattedDateStr(startDate, endTime);
// console.log(endDateTime);
handleAddFCEvent({id:`${eventId}`, title:eventName, start:startDateTime, end: endDateTime, allDay: !endTime });
}

setCurrEventId(_=>eventId);
setEventId(prev => prev+1);
// console.log(eventId);


}

};

const handleCardClicked = () => {
Expand Down
22 changes: 19 additions & 3 deletions src/components/SearchContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import PTData from "../../backend_scraper/scraper/peer_tutoring.json";
import SIData from "../../backend_scraper/scraper/si.json";
import ClubsData from "../../backend_scraper/scraper/tartanconnect.json";
import CareerData from "../../backend_scraper/scraper/handshake.json";
import { categoryListAcademics, categoryShorthandAcademics, categoryListClubs, categoryListCareer, Clubs_type, Career_type } from "../types";
import { categoryListAcademics, categoryShorthandAcademics, categoryListClubs, categoryListCareer, Clubs_type, Career_type, AddFCEventProps, AddFCEventType } from "../types";
import FullCalendar from "@fullcalendar/react";


type ButtonClickHandler = () => void;
Expand All @@ -20,11 +21,16 @@ interface SearchContentProps {
startDate: Dayjs | null;
endDate: Dayjs | null;
addToSavedItems: ButtonClickHandler;
handleAddFCEvent: AddFCEventType;
eventId: number;
setEventId: React.Dispatch<React.SetStateAction<number>>;
calendarRef: React.RefObject<FullCalendar>;
setEvents: React.Dispatch<React.SetStateAction<any[]>>;

}

const SearchContent: React.FC<SearchContentProps> = ({ searchInput, page, categoryName, startDate, endDate, addToSavedItems }) => {
const props = { searchInput, page, categoryName, startDate, endDate, addToSavedItems };
const SearchContent: React.FC<SearchContentProps> = ({ searchInput, page, categoryName, startDate, endDate, addToSavedItems, handleAddFCEvent, eventId, setEventId, calendarRef, setEvents }) => {
const props = { searchInput, page, categoryName, startDate, endDate, addToSavedItems, handleAddFCEvent, eventId, setEventId, calendarRef, setEvents };

const getCategoryData = (currPage:string) => {
// this is where we apply the category filters
Expand Down Expand Up @@ -130,6 +136,11 @@ const SearchContent: React.FC<SearchContentProps> = ({ searchInput, page, catego
endTime={result.item.events[i].end_time}
location={result.item.events[i].location}
addToSavedItems={addToSavedItems}
handleAddFCEvent={handleAddFCEvent}
eventId={eventId}
setEventId={setEventId}
calendarRef={calendarRef}
setEvents={setEvents}
/>
)
}
Expand All @@ -152,6 +163,11 @@ const SearchContent: React.FC<SearchContentProps> = ({ searchInput, page, catego
endTime={result.item.events[i].end_time}
location={result.item.events[i].location}
addToSavedItems={addToSavedItems}
handleAddFCEvent={handleAddFCEvent}
eventId={eventId}
setEventId={setEventId}
calendarRef={calendarRef}
setEvents={setEvents}
/>
)
}
Expand Down
19 changes: 12 additions & 7 deletions src/components/calendar/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react";
import React, { RefObject, useRef, useState } from "react";
import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
Expand All @@ -15,7 +15,7 @@ import Select, { SelectChangeEvent } from '@mui/material/Select';

function renderEventContent(eventContent: EventContentArg) {
return (
<div className={`${eventContent.event.allDay ? 'bg-green': ''}`}>
<div className={`${eventContent.event.allDay ? 'bg-green': 'bg-green'}`}>

<b>{eventContent.timeText}</b>
<i>{eventContent.event.title}</i>
Expand All @@ -27,16 +27,18 @@ function renderEventContent(eventContent: EventContentArg) {

interface CalendarProps {
showSearchBar: boolean;
events: any[];
calendarRef: RefObject<FullCalendar>;
}

export default function Calendar({showSearchBar}:CalendarProps) {
const [events, setEvents] = useState<any[]>([]);
export default function Calendar({showSearchBar, events, calendarRef}:CalendarProps) {

const [weekendsVisible, setWeekendsVisible] = useState<boolean>(true);
const [currentEvents, setCurrentEvents] = useState<EventApi[]>();
const [currView, setCurrView] = useState<string>('dayGridMonth');
const [showGCal, setShowGCal] = useState<boolean>(false);

const calendarRef = useRef<FullCalendar>(null);
// console.log(events);

const handleDropdown = (event: SelectChangeEvent<string>) => {
const {target: { value }} = event;
Expand All @@ -51,6 +53,7 @@ export default function Calendar({showSearchBar}:CalendarProps) {
}
};


const handleDateSelect = (selectInfo: DateSelectArg) => {
let title = prompt('Please enter a new title for your event')
let calendarApi = selectInfo.view.calendar
Expand All @@ -77,6 +80,8 @@ export default function Calendar({showSearchBar}:CalendarProps) {
const handleEvents = (events: EventApi[]) => {
setCurrentEvents(events);
}

// console.log(events);


return (
Expand Down Expand Up @@ -107,7 +112,7 @@ export default function Calendar({showSearchBar}:CalendarProps) {
height={showSearchBar? 600: 580}
// dayGridMonth,timeGridWeek,timeGridDay
// today
// events={events}
events={events}
customButtons={{
customDropdown: {
text: 'month',
Expand All @@ -123,7 +128,7 @@ export default function Calendar({showSearchBar}:CalendarProps) {
selectMirror={true}
dayMaxEvents={true}
weekends={weekendsVisible}
initialEvents={INITIAL_EVENTS} // alternatively, use the `events` setting to fetch from a feed
//initialEvents={INITIAL_EVENTS} // alternatively, use the `events` setting to fetch from a feed
select={handleDateSelect}
eventContent={renderEventContent} // custom render function
eventClick={handleEventClick}
Expand Down
11 changes: 11 additions & 0 deletions src/components/calendar/event-utils.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EventInput } from '@fullcalendar/core'
import FullCalendar from '@fullcalendar/react'

let eventGuid = 0
let todayStr = new Date().toISOString().replace(/T.*$/, '') // YYYY-MM-DD of today
Expand All @@ -19,4 +20,14 @@ export const INITIAL_EVENTS: EventInput[] = [

export function createEventId() {
return String(eventGuid++)
}

export const handleEventRemoveFC = (calendarRef: React.RefObject<FullCalendar>, eventId: string) => {
if (calendarRef.current) {
const calendarApi = calendarRef.current.getApi();
const event = calendarApi.getEventById(eventId);
if (event) {
event.remove();
}
}
}
70 changes: 47 additions & 23 deletions src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,56 @@
import React, { useState } from "react";
import React, { useRef, useState } from "react";
import { SecondNav, Search, Footer } from "../components";
import Calendar from "../components/calendar/Calendar";
import { AddFCEventProps } from "../types";
import FullCalendar from "@fullcalendar/react";



const Home: React.FC = () => {
const [showSearchBar, setShowSearchBar] = useState(true);
const [page, setPage] = useState("academics");
const handleSearchBarClick = () => {
setShowSearchBar(prev => !prev);
}
return (
<div>
<div className="relative">
<div className="justify-between z-0">
<SecondNav page={page} setPage={setPage}/>
const [showSearchBar, setShowSearchBar] = useState(true);
const [page, setPage] = useState("academics");
// calendar events
const [events, setEvents] = useState<any[]>([]);
const [eventIdCount, setEventIdCount] = useState(0);

const calendarRef = useRef<FullCalendar>(null);

const handleSearchBarClick = () => {
setShowSearchBar(prev => !prev);
}


const handleAddFCEvent = ({id, title, start, end, allDay}: AddFCEventProps) => {
setEvents([...events, {
id: id,
title: title,
start: start,
end: end,
allDay: allDay
}])
}

return (
<div>
<div className="relative">
<div className="justify-between z-0">
<SecondNav page={page} setPage={setPage}/>
</div>
<div className="flex justify-between z-10 mt-2">
<div className={`${showSearchBar? 'w-2/5': 'w-1/12'}`}>
<Search page={page} showSearchBar={showSearchBar} handleAddFCEvent={handleAddFCEvent}
handleSearchBarClick={handleSearchBarClick} eventId={eventIdCount}
setEventId={setEventIdCount} calendarRef={calendarRef} setEvents={setEvents}/>
</div>

<div className={`${showSearchBar? 'Calendar': 'CalendarFull'}`}>
<Calendar showSearchBar={showSearchBar} events={events} calendarRef={calendarRef}/>
</div>
</div>
</div>
<div className="flex justify-between z-10 mt-2">
<div className={`${showSearchBar? 'w-2/5': 'w-1/12'}`}>
<Search page={page} showSearchBar={showSearchBar} handleSearchBarClick={handleSearchBarClick}/>
</div>

<div className={`${showSearchBar? 'Calendar': 'CalendarFull'}`}>
<Calendar showSearchBar={showSearchBar} />
</div>
<Footer/>
</div>
</div>
<Footer/>
</div>
);
);
};

export { Home };
12 changes: 11 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,14 @@ export type Career_type = {
categories: string[]
}

export type Merged_Data_type = DIT_type & PT_type & SI_type & Clubs_type & Career_type;
export type Merged_Data_type = DIT_type & PT_type & SI_type & Clubs_type & Career_type;

export interface AddFCEventProps {
id: string,
title: string,
start: string,
end?: string,
allDay?: boolean,
}

export type AddFCEventType = ({id, title, start, end, allDay}: AddFCEventProps) => void;
24 changes: 24 additions & 0 deletions src/utils/DateManipulations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";

export const getDateString = (date: string) => {
// return: 2024-08-02
return new Date(date).toISOString().slice(0, 10);
}

export const get24Hour = (time: string) => {
// time: 07:00PM
const suffix = time.slice(5); //default end is the lenght of string
if (suffix == 'PM') {
let hour = Number(time.slice(0,2));
hour = hour + 12;
const newH = `${hour}`;
return newH+time.slice(2,5);
}
return time.slice(0,5);
}

export const getFormattedDateStr = (date:string, time:string) => {
const formattedDate = getDateString(date);
const formattedTime = get24Hour(time);
return formattedDate + 'T' + formattedTime;
}
Loading

0 comments on commit b84c8f1

Please sign in to comment.