diff --git a/client/src/components/cards/session-card.tsx b/client/src/components/cards/session-card.tsx new file mode 100644 index 0000000..556a987 --- /dev/null +++ b/client/src/components/cards/session-card.tsx @@ -0,0 +1,176 @@ +import { BellIcon, ChevronDownIcon } from "@radix-ui/react-icons"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +import { Badge } from "@/components/ui/badge"; +import type { Session } from "@/pages/sessions/sessions"; +import { Button } from "@/components/ui/button"; +import { Icons } from "../ui/icons"; +import { useAuth } from "@/contexts/AuthContext"; +import { useNavigate } from "react-router-dom"; +import { type Staff } from "@/contexts/AuthContext"; + +const SessionCard = ({ session }: { session: Session }) => { + const navigate = useNavigate(); + const { mongoUser, refresh, setRefresh } = useAuth(); + + // const getLoanStatus = () => { + // if (!beneficiary.loan) return "No Loan"; + + // return beneficiary.loan?.loanStatus; + // }; + + const formatDate = (date: Date) => { + const options = { year: "numeric", month: "long", day: "numeric" }; + return new Date(date).toLocaleDateString([], options); + }; + + const compareDate = (date: Date) => { + const now = new Date(); + const formattedDate = new Date(date); + + //This is a check for same day, because now.getTime() does not equal formattedDate.getTime() + if ( + now.getFullYear() === formattedDate.getFullYear() && + now.getMonth() === formattedDate.getMonth() && + now.getDay() === formattedDate.getDay() + ) + return "Today"; + if (Date.now() < formattedDate.getTime()) return "Upcoming"; + return "Complete"; + }; + + const handleGoToSession = (e: React.MouseEvent) => { + e.stopPropagation(); + navigate(`./${session._id}`); + }; + + const handleGoToSessionEdit = (e: React.MouseEvent) => { + e.stopPropagation(); + navigate(`./${session._id}?f=1`); + }; + + // const handleBookmarkBeneficiary = async (e: React.MouseEvent) => { + // e.stopPropagation(); + // console.log("bookmarking beneficiary"); + // let newBookmarks; + // if (mongoUser?.bookmarkedBeneficiaries?.includes(beneficiary._id ?? "")) { + // // We need to remove bookmark + // newBookmarks = mongoUser?.bookmarkedBeneficiaries?.filter( + // (bookmark) => bookmark !== beneficiary._id, + // ); + // } else { + // // We need to add bookmark + // newBookmarks = mongoUser?.bookmarkedBeneficiaries?.concat( + // beneficiary._id ?? "", + // ); + // } + // try { + // await fetch(`http://localhost:3001/user/edit?_id=${mongoUser?._id}`, { + // method: "POST", + // headers: { + // "Content-Type": "application/json", + // }, + // body: JSON.stringify({ + // bookmarkedBeneficiaries: newBookmarks, + // }), + // }).then((res) => res.json() as unknown as Staff); + // setRefresh(!refresh); + // } catch { + // console.log("error bookmarking beneficiary"); + // } + // }; + + return ( + navigate(`./${session._id}`)} + > + + + + {formatDate(session?.sessionDate)} + + + ({session?.region}) + + + + + + + + + + + Quick Actions + + {/* handleBookmarkBeneficiary(e)} + > + Bookmark User + */} + handleGoToSessionEdit(e)} + > + Edit Session + + View Session + + handleGoToSession(e)}> + View Profile + + + + + + + + + + {compareDate(session?.sessionDate)} + + + Expected: {session?.expectedAttendance.length} + + {/* {beneficiary?.phoneNumber?.length ?? 0 > 0 ? ( + #: {beneficiary?.phoneNumber} + ) : null} */} + + + + ); +}; + +export default SessionCard; diff --git a/client/src/components/toolbars/session-toolbar.tsx b/client/src/components/toolbars/session-toolbar.tsx index 3705887..7169a6d 100644 --- a/client/src/components/toolbars/session-toolbar.tsx +++ b/client/src/components/toolbars/session-toolbar.tsx @@ -2,36 +2,43 @@ import { Button } from "@/components/ui/button"; import { Combobox } from "@/components/combobox"; import { Icons } from "../ui/icons"; import { Input } from "@/components/ui/input"; +import { DoubleArrowUpIcon, DoubleArrowDownIcon } from "@radix-ui/react-icons"; + import React from "react"; +import { type SetURLSearchParams } from "react-router-dom"; const sessionStatus = [ { - value: "pending", + value: "1", label: "Completed", }, { - value: "good", + value: "2", label: "Coming Up", }, { - value: "bad", + value: "3", label: "Happening Soon", }, { - value: "s", + value: "4", label: "Has Missing Attendees", }, ]; const sortBy = [ { - value: "jd", + value: "1", label: "Meeting Date", }, { - value: "init-la", + value: "2", label: "Expected Attendence", }, + { + value: "3", + label: "Region", + }, ]; const BeneficiaryToolbar = ({ @@ -41,13 +48,17 @@ const BeneficiaryToolbar = ({ setStatus, sort, setSort, + sortDirection, + setSortDirection, }: { - query: string; - setQuery: React.Dispatch>; + query: string | null; + setQuery: SetURLSearchParams; status: string; setStatus: React.Dispatch>; sort: string; setSort: React.Dispatch>; + sortDirection: string; + setSortDirection: React.Dispatch>; }) => { return ( @@ -55,8 +66,8 @@ const BeneficiaryToolbar = ({ setQuery(e.target.value)} + value={query ?? ""} + onChange={(e) => setQuery({ f: e.target.value })} /> - {query !== "" || status !== "" || sort !== "" ? ( + {sort !== "" ? ( + sortDirection === "Ascending" ? ( + setSortDirection("Descending")} + > + + + ) : ( + setSortDirection("Ascending")} + > + + + ) + ) : ( + <>> + )} + {!!query || !!status || !!sort ? ( >; +}) { + const [location, setLocation] = useState(""); + const [date, setDate] = useState(""); + const [phoneNumber, setPhoneNumber] = useState(); + const [currentSavings, setCurrentSavings] = useState(""); + const [currentSpending, setCurrentSpending] = useState(""); + + const handleAddSession = async () => { + const data = { + location, + date, + phoneNumber, + currentSavings, + currentSpending, + }; + console.log(data); + try { + await fetch("http://localhost:3001/beneficiary/create", { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }); + setNotify(!notify); + } catch (err) { + console.log(err); + } + }; + + return ( + + + + + + + Add Session + + You can always edit this information later. + + + + + + Location + + setLocation(e.target.value)} + className="col-span-3" + /> + + + + Date + + setDate(e.target.value)} + className="col-span-3" + /> + + + + Phone Number + + setPhoneNumber(e.target.value)} + className="col-span-3" + /> + + + + Current Savings + + setCurrentSavings(e.target.value)} + className="col-span-3" + /> + + + + Current Spending + + setCurrentSpending(e.target.value)} + className="col-span-3" + /> + + + + + Save beneficiary + + + + + ); +} diff --git a/client/src/pages/sessions/sessions.tsx b/client/src/pages/sessions/sessions.tsx index ac471d1..99ef3e7 100644 --- a/client/src/pages/sessions/sessions.tsx +++ b/client/src/pages/sessions/sessions.tsx @@ -1,14 +1,148 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; +import { useSearchParams } from "react-router-dom"; import { DashboardHeader } from "@/components/header"; import { DashboardShell } from "@/components/shell"; import { ItemCreateButton } from "@/components/create-item-button"; +import SessionCard from "@/components/cards/session-card"; import SessionToolbar from "@/components/toolbars/session-toolbar"; +import { AddSession } from "./add-session"; -const Beneficiaries = () => { - const [query, setQuery] = useState(""); +export interface Session { + _id?: string; + sessionDate?: Date; + region?: string; + staff?: string[]; + archived?: boolean; + __V?: number; + actualAttendance?: string[]; + expectedAttendance?: string[]; +} + +const Sessions = () => { + const [query, setQuery] = useSearchParams(); const [status, setStatus] = useState(""); const [sort, setSort] = useState(""); + const [sortDirection, setSortDirection] = useState("Ascending"); + const [notifyNew, setNotifyNew] = useState(false); + const [sessions, setSessions] = useState([]); + + // I've been trying to have it sort by date by default, but I can't figure it out. + // There's also a bug where reseting when *only* the Sort By is changed will not work + // This bug (is it even a bug?) is also present in the beneficiaries page too + useEffect(() => { + if (!sort) return; + + // direction is connected to the double arrow button in the toolbar. + // it controls direction of the sort + const direction = sortDirection === "Ascending" ? 1 : -1; + switch (sort) { + case "1": + console.log("sort by Date"); + const dateSort = [...sessions]; + dateSort.sort((a, b) => { + if (a.sessionDate && b.sessionDate) { + return ( + direction * + (new Date(a.sessionDate).getTime() - + new Date(b.sessionDate).getTime()) + ); + } + return 0; + }); + setSessions(dateSort); + break; + case "2": + console.log("sort by attendance"); + const expectedSort = [...sessions]; + expectedSort.sort((a, b) => { + if (a.expectedAttendance && b.expectedAttendance) { + return ( + direction * + (a.expectedAttendance.length - b.expectedAttendance.length) + ); + } + return 0; + }); + setSessions(expectedSort); + break; + case "3": + const regionSort = [...sessions]; + regionSort.sort((a, b) => { + if (a.region && b.region) { + return direction * a.region.localeCompare(b.region); + } + return 0; + }); + setSessions(regionSort); + break; + case "4": + // TODO + } + }, [sort, sortDirection]); + + useEffect(() => { + const getSessions = async () => { + try { + const data: Session[] = await fetch( + "http://localhost:3001/session/sessions", + { + headers: { + "Content-Type": "application/json", + }, + }, + ).then((res: Response) => res.json() as unknown as Session[]); + //console.log(data); + // TODO: decide what the initial sort should be + setSessions(data); + } catch (error) { + console.error(error); + } + }; + void getSessions(); + }, [notifyNew]); + + // Status is checked via dates. + // There isn't a status parameter in the Session object. + const handleFilters = (s: Session) => { + if (!status) return true; + + switch (status) { + case "1": + if (s.sessionDate) return compareDate(s.sessionDate) === "Complete"; + case "2": + if (s.sessionDate) return compareDate(s.sessionDate) === "Upcoming"; + case "3": + if (s.sessionDate) return compareDate(s.sessionDate) === "Today"; + case "4": + if (s.expectedAttendance && s.actualAttendance) + return s.actualAttendance.length !== s.expectedAttendance.length; + + // TODO: add rest of cases + } + }; + + // I have this function defined twice: here and session-card.tsx + // Please fix if necessary + const compareDate = (date: Date) => { + const now = new Date(); + // formattedDate is necessary + const formattedDate = new Date(date); + //This is a check for same day, because now.getTime() does not equal formattedDate.getTime() + if ( + now.getFullYear() === formattedDate.getFullYear() && + now.getMonth() === formattedDate.getMonth() && + now.getDay() === formattedDate.getDay() + ) + return "Today"; + if (Date.now() < formattedDate.getTime()) return "Upcoming"; + return "Complete"; + }; + + // const formatDate = (date: Date) => { + // const options = { year: "numeric", month: "long", day: "numeric" }; + // return new Date(date).toLocaleDateString([], options); + // }; return ( @@ -16,19 +150,40 @@ const Beneficiaries = () => { heading="Sessions" text="View and manage your session data." > - + - sessions + {/* I don't get the get("f") stuff so please change if it is unnecessary. Also right now it only searches by location. */} + + {sessions + .filter((ses) => { + if (!query.get("f")) return true; + return ( + // Can't figure how to search for a date. That should be added. + ses.region + ?.toLowerCase() + .includes(query.get("f")?.toLowerCase() ?? "") ?? + ses._id + ?.toLowerCase() + .includes(query.get("f")?.toLowerCase() ?? "") + ); + }) + .filter((ses) => handleFilters(ses)) + .map((ses, i) => { + return ; + })} + ); }; -export default Beneficiaries; +export default Sessions;