diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 565b789f..76bc67ed 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -28,6 +28,7 @@ import PrivateRoute from "./components/auth/PrivateRoute"; import LoginPage from "./components/auth/LoginPage"; import SignupPage from "./components/auth/SignupPage"; import ResetPasswordPage from "./components/auth/ResetPasswordPage"; +import HomePage from "./components/pages/home/HomePage"; import AnnouncementsPage from "./components/pages/announcements/AnnouncementsPage"; import TasksPage from "./components/pages/tasks/TasksPage"; import SchedulePage from "./components/pages/schedule/SchedulePage"; @@ -83,7 +84,15 @@ const App = (): React.ReactElement => { path={Routes.HOME_PAGE} element={ - + + + } + /> + + } /> diff --git a/frontend/src/components/common/SideBar.tsx b/frontend/src/components/common/SideBar.tsx index 1d6630ca..37615f18 100644 --- a/frontend/src/components/common/SideBar.tsx +++ b/frontend/src/components/common/SideBar.tsx @@ -74,7 +74,9 @@ const SideBar: React.FC = () => { }; const pages = [ - { label: "Home", route: Routes.HOME_PAGE }, // NEED NEW HOME PAGE + { label: "Home", route: Routes.HOME_PAGE }, + { label: "Tasks", route: Routes.TASKS_PAGE }, + { label: "Approvals", route: Routes.APPROVALS_PAGE }, { label: "Schedule", route: Routes.SCHEDULE_PAGE }, { label: "Announcements", route: Routes.HOME_PAGE }, // NEED NEW NAME { label: "Participants", route: Routes.RESIDENTS_PAGE }, // RESIDENTS/PARTICIPANTS diff --git a/frontend/src/components/pages/announcements/AnnouncementsPage.tsx b/frontend/src/components/pages/announcements/AnnouncementsPage.tsx index b3de8324..9df5f401 100644 --- a/frontend/src/components/pages/announcements/AnnouncementsPage.tsx +++ b/frontend/src/components/pages/announcements/AnnouncementsPage.tsx @@ -1,43 +1,54 @@ -import React, { useEffect, useState } from "react"; -import { Flex } from "@chakra-ui/react"; - -import { GroupAnnouncements } from "../../../types/NotificationTypes"; -import AnnouncementsGroups from "./AnnouncementsGroups"; -import AnnouncementsView from "./AnnouncementsView"; -import { announcementsMockData } from "../../../mocks/notifications"; - -const AnnouncementsPage = (): React.ReactElement => { - const [announcements, setAnnouncements] = useState({}); - const [selectedGroup, setSelectedGroup] = useState(""); - const [addingNewRoom, setAddingNewRoom] = useState(false); - const [selectedRooms, setSelectedRooms] = useState([]); - - useEffect(() => { - // TODO: Fetch announcements from API - setAnnouncements(announcementsMockData); - }, []); - - return ( - - - - - - - ); -}; - -export default AnnouncementsPage; +import React, { useEffect, useState } from "react"; +import { Flex } from "@chakra-ui/react"; + +import { + GroupAnnouncements, + Announcement, +} from "../../../types/NotificationTypes"; +import AnnouncementsGroups from "./AnnouncementsGroups"; +import AnnouncementsView from "./AnnouncementsView"; +import { announcementsMockData } from "../../../mocks/notifications"; + +const AnnouncementsPage = (): React.ReactElement => { + const [announcements, setAnnouncements] = useState({}); + const [selectedGroup, setSelectedGroup] = useState(""); + + useEffect(() => { + // TODO: Fetch announcements from API + const combinedAnnouncements: GroupAnnouncements = {}; + Object.entries(announcementsMockData).forEach(([key, value]) => { + for (let i = 0; i < value.length; i += 1) { + const newAnnouncement: Announcement = { + room: key, + author: value[i].author, + message: value[i].message, + createdAt: value[i].createdAt, + }; + // check if alr exists, if not create new + if (!combinedAnnouncements[key]) { + combinedAnnouncements[key] = []; + } + combinedAnnouncements[key].push(newAnnouncement); + } + }); + + setAnnouncements(combinedAnnouncements); + }, []); + + return ( + + + + + + + ); +}; + +export default AnnouncementsPage; \ No newline at end of file diff --git a/frontend/src/components/pages/home/AnnouncementNotification.tsx b/frontend/src/components/pages/home/AnnouncementNotification.tsx new file mode 100644 index 00000000..7ce02db5 --- /dev/null +++ b/frontend/src/components/pages/home/AnnouncementNotification.tsx @@ -0,0 +1,70 @@ +import React, { useState } from "react"; +import moment from "moment"; +import { Box, Text, Flex, Icon, IconButton } from "@chakra-ui/react"; +import PersonOutlineOutlinedIcon from "@mui/icons-material/PersonOutlineOutlined"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import ExpandLessIcon from "@mui/icons-material/ExpandLess"; + +import { Announcement } from "../../../types/NotificationTypes"; + +const AnnouncementNotification = ({ + room, + message, + createdAt, +}: Announcement): React.ReactElement => { + const [showFullMessage, setShowFullMessage] = useState(false); + + return ( + + + + + + + + + {"Admin to Room ".concat(room)} + + + posted at {moment(createdAt).format("h:mm a")} + + + + {showFullMessage ? ( + {message} + ) : ( + + {message} + + )} + + + setShowFullMessage(!showFullMessage)} + icon={showFullMessage ? : } + size="md" + /> + + + ); +}; + +export default AnnouncementNotification; diff --git a/frontend/src/components/pages/home/HomePage.tsx b/frontend/src/components/pages/home/HomePage.tsx new file mode 100644 index 00000000..3e54b74a --- /dev/null +++ b/frontend/src/components/pages/home/HomePage.tsx @@ -0,0 +1,89 @@ +import React, { useState, useEffect } from "react"; +import { Flex, Box, Heading, Text } from "@chakra-ui/react"; +import { announcementsMockData } from "../../../mocks/notifications"; +import AnnouncementNotification from "./AnnouncementNotification"; +import { Announcement } from "../../../types/NotificationTypes"; + +const HomePage = (): React.ReactElement => { + const [numberPosts, setNumberPosts] = useState(0); + const [viewAll, setViewAll] = useState(false); + const [announcements, setAnnouncements] = useState([]); + const [recentAnnouncements, setRecentAnnouncements] = useState< + Announcement[] + >([]); + + useEffect(() => { + // Combine all announcements into a single array + const combinedAnnouncements: Announcement[] = []; + Object.entries(announcementsMockData).forEach(([key, value]) => { + for (let i = 0; i < value.length; i += 1) { + const newAnnouncement: Announcement = { + room: key, + author: value[i].author, + message: value[i].message, + createdAt: value[i].createdAt, + }; + combinedAnnouncements.push(newAnnouncement); + } + }); + + const sortedAnnouncements = combinedAnnouncements.sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ); + // Filter announcements from the current week + const oneWeekAgo = new Date(); + oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); + const thisWeeksAnnouncements = sortedAnnouncements.filter( + (announcement) => new Date(announcement.createdAt) >= oneWeekAgo, + ); + setAnnouncements(combinedAnnouncements); + setRecentAnnouncements(thisWeeksAnnouncements.slice(0, 3)); + setNumberPosts(thisWeeksAnnouncements.length); + }, []); + + return ( + + + Home Page + + + + + + Announcements + + + {numberPosts === 0 + ? "You're all caught up!" + : `${numberPosts} new posts today`} + + + + setViewAll(!viewAll)} cursor="pointer"> + {viewAll ? "View less" : "View all"} + + + {(viewAll ? announcements : recentAnnouncements).map( + (announcement, index) => ( + + ), + )} + + + ); +}; + +export default HomePage; diff --git a/frontend/src/constants/Routes.ts b/frontend/src/constants/Routes.ts index 90c8ccad..a7b3a65b 100644 --- a/frontend/src/constants/Routes.ts +++ b/frontend/src/constants/Routes.ts @@ -1,5 +1,7 @@ export const HOME_PAGE = "/"; +export const ANNOUNCEMENTS_PAGE = "/announcements"; + export const LOGIN_PAGE = "/login"; export const SIGNUP_PAGE = "/signup"; diff --git a/frontend/src/types/NotificationTypes.ts b/frontend/src/types/NotificationTypes.ts index 56c6e850..c55955c9 100644 --- a/frontend/src/types/NotificationTypes.ts +++ b/frontend/src/types/NotificationTypes.ts @@ -1,5 +1,6 @@ export interface Announcement { author: string; + room: string; message: string; createdAt: string; }