diff --git a/RAILWAY_DEPLOYMENT.md b/RAILWAY_DEPLOYMENT.md index 44c9c0f3..74c575c9 100644 --- a/RAILWAY_DEPLOYMENT.md +++ b/RAILWAY_DEPLOYMENT.md @@ -150,5 +150,3 @@ Railway respects the ENTRYPOINT, so both environments run `prismaInitAndRun` on ``` 4. **Monitor Railway logs** on deployment to verify seeding behavior 5. **Keep system badges in sync** between `prodData.ts` and production database - -TODO: Review & update doc \ No newline at end of file diff --git a/backend/gql/resolvers/assignedTaskResolver.ts b/backend/gql/resolvers/assignedTaskResolver.ts index 242876ca..31d6c7de 100644 --- a/backend/gql/resolvers/assignedTaskResolver.ts +++ b/backend/gql/resolvers/assignedTaskResolver.ts @@ -75,8 +75,8 @@ const assignedTaskResolver = { end_date: { gte: getStartOfDay(new Date()) }, }, orderBy: { - start_date: "asc" - } + start_date: "asc", + }, }); }, getAssignedTasksByWeek: async ( @@ -97,8 +97,8 @@ const assignedTaskResolver = { end_date: { gte: weekStart }, }, orderBy: { - start_date: "asc" - } + start_date: "asc", + }, }); }, hasCompletedAllRequiredTasks: async ( diff --git a/backend/gql/resolvers/badgeLevelProgressResolver.ts b/backend/gql/resolvers/badgeLevelProgressResolver.ts index 9186a9d7..98bb9a49 100644 --- a/backend/gql/resolvers/badgeLevelProgressResolver.ts +++ b/backend/gql/resolvers/badgeLevelProgressResolver.ts @@ -20,10 +20,7 @@ const badgeLevelProgressResolver = { }, }, }, - orderBy: [ - { name: "asc" }, - { badge_level: { value: "asc" } }, - ], + orderBy: [{ name: "asc" }, { badge_level: { value: "asc" } }], }); }, }, diff --git a/backend/gql/resolvers/participantResolver.ts b/backend/gql/resolvers/participantResolver.ts index 35cd44db..d6e20a6b 100644 --- a/backend/gql/resolvers/participantResolver.ts +++ b/backend/gql/resolvers/participantResolver.ts @@ -119,22 +119,8 @@ const participantResolver = { const isEmpty = Object.keys(updates).length === 0; if (isEmpty) throw new Error("no updates received"); - if (room !== undefined) { - if (room < 1 || room > 10) { - throw new Error("room must be between 1 and 10"); - } - - const occupiedRoom = await db.participant.findFirst({ - where: { - room, - pid: { not: pid }, - OR: [ - { departure: null }, - { departure: { gt: getEndOfDay(new Date()) } }, - ], - }, - }); - if (occupiedRoom) throw new Error("room is occupied"); + if (room !== undefined && (room < 1 || room > 10)) { + throw new Error("room must be between 1 and 10"); } return db.participant.update({ diff --git a/backend/gql/resolvers/transactionResolver.ts b/backend/gql/resolvers/transactionResolver.ts index b7ddfa14..b0b4bc94 100644 --- a/backend/gql/resolvers/transactionResolver.ts +++ b/backend/gql/resolvers/transactionResolver.ts @@ -1,6 +1,5 @@ import { Transaction, TransactionType, DayOfWeek } from "@prisma/client"; import db from "../../prisma"; -import { orderedDays } from "../../constants/days"; import { getEndOfWeek, getStartOfWeek, whichDay } from "../../utils/dateUtils"; type GetWeeklyEarningsResponse = Record; diff --git a/backend/prisma/seed/seed.ts b/backend/prisma/seed/seed.ts index 0c129357..13a28d37 100644 --- a/backend/prisma/seed/seed.ts +++ b/backend/prisma/seed/seed.ts @@ -1,5 +1,5 @@ import { createSeedClient } from "@snaplet/seed"; -import { Priority, TaskType } from "@prisma/client"; +import { TaskType } from "@prisma/client"; import { badgeLevels, systemBadges, tasks } from "./initialData"; import db from "../index"; import * as random from "./random"; diff --git a/backend/utils/dateUtils.ts b/backend/utils/dateUtils.ts index a3378623..8e0d5278 100644 --- a/backend/utils/dateUtils.ts +++ b/backend/utils/dateUtils.ts @@ -7,8 +7,8 @@ import { startOfWeek, } from "date-fns"; import { toZonedTime, fromZonedTime } from "date-fns-tz"; -import { orderedDays } from "../constants/days"; import { DayOfWeek } from "@prisma/client"; +import { orderedDays } from "../constants/days"; const timeZone = "America/Toronto"; diff --git a/backend/utils/taskUtils.ts b/backend/utils/taskUtils.ts index 9f66b045..07cfbb0e 100644 --- a/backend/utils/taskUtils.ts +++ b/backend/utils/taskUtils.ts @@ -77,14 +77,20 @@ function buildStartAndEndDates(task: Task): StartAndEndDates[] { return startAndEndDates; } -export async function assignTasksToParticipants(tasks: Task[], pids?: number[]) { +export async function assignTasksToParticipants( + tasks: Task[], + pids?: number[] +) { let participantPids: number[]; if (pids) { participantPids = pids; } else { const participants = await db.participant.findMany({ where: { - OR: [{ departure: null }, { departure: { gt: getEndOfDay(new Date()) } }], + OR: [ + { departure: null }, + { departure: { gt: getEndOfDay(new Date()) } }, + ], }, select: { pid: true }, }); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4365a227..fd20c643 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -35,7 +35,7 @@ import NotFoundScreen from "./ui/screens/NotFoundScreen"; import UI from "./ui/UI"; import colors from "./theme/colors"; import { Text, textStyles } from "./theme/typography"; -import { Progress } from "./theme/progress"; +import { Progress, Checkbox, Alert } from "./theme/components"; function initApolloClient() { const backendUrl = @@ -80,9 +80,11 @@ const App = (): React.ReactElement => { const theme = extendTheme({ colors, textStyles, - components: { + components: { Text, Progress, + Checkbox, + Alert, }, }); const apolloClient = initApolloClient(); diff --git a/frontend/src/admin/AdminContext.tsx b/frontend/src/admin/AdminContext.tsx index 00017c6a..9b3a8cac 100644 --- a/frontend/src/admin/AdminContext.tsx +++ b/frontend/src/admin/AdminContext.tsx @@ -20,7 +20,9 @@ interface AdminProviderProps { export const AdminProvider: React.FC = ({ children }) => { const [role, setRole] = useState(""); - const [roomToParticipant, setRoomToParticipant] = useState>({}); + const [roomToParticipant, setRoomToParticipant] = useState< + Record + >({}); return ( {label} @@ -40,6 +42,7 @@ type SignOutPopUpProps = { function SignOutPopUp({ cancel }: SignOutPopUpProps) { const navigate = useNavigate(); + const handleSignOut = () => { localStorage.removeItem("token"); return navigate(ROUTES.ADMIN_LOGIN_PAGE); @@ -52,7 +55,7 @@ function SignOutPopUp({ cancel }: SignOutPopUpProps) { submit_action={handleSignOut} cancel_action={cancel} > - + Are you sure you want to sign out? @@ -62,6 +65,7 @@ function SignOutPopUp({ cancel }: SignOutPopUpProps) { export default function AdminMenu() { const navigate = useNavigate(); const [signOut, setSignOut] = useState(false); + const { role } = useContext(AdminContext); const pages = [ { label: "Home", route: ROUTES.ADMIN_HOME_PAGE }, @@ -70,7 +74,10 @@ export default function AdminMenu() { { label: "Participants", route: ROUTES.ADMIN_PARTICIPANTS_PAGE }, { label: "Task Library", route: ROUTES.ADMIN_TASKS_PAGE }, { label: "Badge Library", route: ROUTES.ADMIN_BADGES_PAGE }, - { label: "Reports", route: ROUTES.ADMIN_REPORTS_PAGE }, + + ...(role === ADMIN + ? [{ label: "Reports", route: ROUTES.ADMIN_REPORTS_PAGE }] + : []), ]; const currentPage = pages.findIndex( @@ -85,8 +92,8 @@ export default function AdminMenu() { top={0} left={0} borderRight="1px" - borderRightColor="neutral.300" - bg="neutral.0" + borderRightColor="background.border" + bg="background.admin" padding="25px 20px" display="flex" flexDirection="column" @@ -118,7 +125,7 @@ export default function AdminMenu() { label="Sign Out" action={() => setSignOut(true)} is_active={signOut} - text_color="danger.900" + text_color="indicate.brightRed" /> {signOut && setSignOut(false)} />} diff --git a/frontend/src/admin/AdminRoute.tsx b/frontend/src/admin/AdminRoute.tsx index f1d7f28b..098b2ef9 100644 --- a/frontend/src/admin/AdminRoute.tsx +++ b/frontend/src/admin/AdminRoute.tsx @@ -11,6 +11,7 @@ import { AdminContext } from "./AdminContext"; import ErrorScreen from "../ui/screens/ErrorScreen"; import AdminMenu from "./AdminMenu"; import { Participant } from "../types/models"; +import useNotification from "../hooks/useNotification"; type AdminRouteProps = { children: React.ReactElement; @@ -64,7 +65,7 @@ export default function AdminRoute({ children }: AdminRouteProps) { adminContext.setRole(role); } setPopulatingContext(false); - } + }; populateContext(); } }, [authorizing, authorized]); @@ -103,9 +104,9 @@ export default function AdminRoute({ children }: AdminRouteProps) { left="0px" width="100%" height="55px" - bg="primary.100" + bg="brand.primaryLight" borderBottom="1px solid" - borderColor="neutral.300" + borderColor="background.border" zIndex={5} /> ; return ( - + - - + + Announcements - + Expires in 7 days } label="Create Announcement" action={() => setCreate(true)} is_active={create} @@ -98,10 +100,10 @@ export default function AdminAnnouncementsPage() { - + Filters: - + {selectedButtons.map((isSelected: boolean, index: number) => ( - - Most Recent - - - + {getAllAnnouncementsData?.getAnnouncementsSentToParticipants.map( (announcement: any) => ( ) )} - + {create && ( (""); - + const { sendNotification } = useNotification(); const [deleteAnnouncement] = useMutation(DELETE_ANNOUNCEMENT); const handleDeleteAnnouncement = async (aid: number) => { setError(""); @@ -37,6 +38,7 @@ export default function AnnouncementCard({ }); refetch(); + sendNotification("Announcement deleted successfully"); } catch (err: any) { setError(err.message); } @@ -46,16 +48,14 @@ export default function AnnouncementCard({ - + - - {room} - + {room} {(priority === Priority.HIGH || priority === Priority.CRITICAL) && } @@ -63,22 +63,16 @@ export default function AnnouncementCard({ - + {message} - + {timestamp} - + setEdit(true)} cursor="pointer"> diff --git a/frontend/src/admin/pages/announcements/components/CreateAnnouncementModal.tsx b/frontend/src/admin/pages/announcements/components/CreateAnnouncementModal.tsx index 5f14c2e6..f3d69a3d 100644 --- a/frontend/src/admin/pages/announcements/components/CreateAnnouncementModal.tsx +++ b/frontend/src/admin/pages/announcements/components/CreateAnnouncementModal.tsx @@ -10,6 +10,7 @@ import TextAreaInput from "../../../../ui/inputs/TextAreaInput"; import { AdminContext } from "../../../AdminContext"; import { Priority } from "../../../../types/enums"; import TextInput from "../../../../ui/inputs/TextInput"; +import useNotification from "../../../../hooks/useNotification"; const CreateAnnouncementModal = ({ onClose, @@ -24,12 +25,19 @@ const CreateAnnouncementModal = ({ const [message, setMessage] = useState(""); const [error, setError] = useState(""); + const { sendNotification } = useNotification(); + const [createAnnouncement, { loading: createAnnouncementLoading }] = useMutation(CREATE_ANNOUNCEMENT); const { roomToParticipant } = useContext(AdminContext); const handleSend = async () => { - if (selectedRooms.length === 0 || priority === null || message === "" || topic === "") { + if ( + selectedRooms.length === 0 || + priority === null || + message === "" || + topic === "" + ) { setError("Missing fields."); return; } @@ -59,6 +67,7 @@ const CreateAnnouncementModal = ({ }); refetch(); + sendNotification("Announcement created successfully"); onClose(); } catch (err: any) { setError("Unable to create announcement"); @@ -93,7 +102,7 @@ const CreateAnnouncementModal = ({ error_message={error} > - + Send To: {[[0], ...ROOM_NUMBERS].flat().map((room: number) => { diff --git a/frontend/src/admin/pages/announcements/components/EditAnnouncementModal.tsx b/frontend/src/admin/pages/announcements/components/EditAnnouncementModal.tsx index c2d96d7d..88e8c4f8 100644 --- a/frontend/src/admin/pages/announcements/components/EditAnnouncementModal.tsx +++ b/frontend/src/admin/pages/announcements/components/EditAnnouncementModal.tsx @@ -8,6 +8,7 @@ import TextAreaInput from "../../../../ui/inputs/TextAreaInput"; import { Priority } from "../../../../types/enums"; import FixedInput from "../../../../ui/inputs/FixedInput"; import { toTitleCase } from "../../../../helpers/stringUtils"; +import useNotification from "../../../../hooks/useNotification"; type EditAnnouncementModalProps = { setIsOpen: React.Dispatch>; @@ -30,7 +31,7 @@ const EditAnnouncementModal = ({ const [message, setMessage] = useState(initialMessage); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const [editAnnouncement, { loading: editAnnouncementLoading }] = useMutation(UPDATE_ANNOUNCEMENT); @@ -52,6 +53,7 @@ const EditAnnouncementModal = ({ refetch(); setIsOpen(false); + sendNotification("Announcement updated successfully"); } catch (err: any) { setError("Unable to update announcement"); } diff --git a/frontend/src/admin/pages/badges/Main.tsx b/frontend/src/admin/pages/badges/Main.tsx index 5647d1d7..07b6f2c3 100644 --- a/frontend/src/admin/pages/badges/Main.tsx +++ b/frontend/src/admin/pages/badges/Main.tsx @@ -1,6 +1,6 @@ import { Flex, Text } from "@chakra-ui/react"; import { useQuery } from "@apollo/client"; -import React, { useState } from "react"; +import React, { useContext, useState } from "react"; import CreateCustomBadgeModal from "./components/CreateCustomBadgeModal"; import CustomBadgeTable from "./components/CustomBadgeTable"; import SystemBadgeTable from "./components/SystemBadgeTable"; @@ -9,9 +9,13 @@ import { GET_SYSTEM_BADGES } from "../../../gql/systemBadgeRequests"; import AssignCustomBadgeModal from "./components/AssignCustomBadgeModal"; import GreenOutlineButton from "../../../ui/buttons/GreenOutlineButton"; import OrangeButton from "../../../ui/buttons/OrangeButton"; +import { Plus } from "../../../ui/icons/ActionIcons"; +import { AdminContext } from "../../AdminContext"; +import { ADMIN } from "../../../constants/roles"; -// TODO: Relief staff cannot change the badges export default function AdminBadgesPage() { + const { role } = useContext(AdminContext); + const [create, setCreate] = useState(false); const [assign, setAssign] = useState(false); @@ -37,11 +41,11 @@ export default function AdminBadgesPage() { alignItems="center" justifyContent="space-between" > - - + + System Badges - + System badges will be granted to participants automatically. @@ -59,26 +63,29 @@ export default function AdminBadgesPage() { justifyContent="space-between" mt="10px" > - - + + Custom Badges - + You can create new and reward participants custom badges. - - setAssign(true)} - is_active={assign} - /> - setCreate(true)} - is_active={create} - /> - + {role === ADMIN && ( + + setAssign(true)} + is_active={assign} + /> + setCreate(true)} + is_active={create} + icon={} + /> + + )} void; @@ -21,7 +22,7 @@ const AssignCustomBadgeModal: React.FC = ({ customBadges, }) => { const { roomToParticipant } = useContext(AdminContext); - + const { sendNotification } = useNotification(); const [selectedBadgeId, setSelectedBadgeId] = useState(""); const [badgeValue, setBadgeValue] = useState(null); const [selectedParticipants, setSelectedParticipants] = useState( @@ -81,6 +82,7 @@ const AssignCustomBadgeModal: React.FC = ({ ) ); onClose(); + sendNotification("Custom badge assigned successfully"); } catch (err: any) { setError(err.message); } @@ -126,11 +128,9 @@ const AssignCustomBadgeModal: React.FC = ({ size="small" /> - + - - Choose Room(s) - + Choose Room(s) {ROOM_NUMBERS.map((num: number) => ( void; @@ -20,7 +21,7 @@ const CreateCustomBadgeModal = ({ const [criteria, setCriteria] = useState(""); const [selectedIcon, setSelectedIcon] = useState(null); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const availableIcons = [ Icon.PLANT, Icon.GROUP, @@ -48,6 +49,7 @@ const CreateCustomBadgeModal = ({ }); onClose(); await refetch(); + sendNotification("Custom badge created successfully"); } catch (err: any) { setError(err.message); } @@ -79,7 +81,7 @@ const CreateCustomBadgeModal = ({ size="large" /> - + Choose Badge Icon @@ -95,14 +97,19 @@ const CreateCustomBadgeModal = ({ py="12px" px="8px" border="1px solid" - borderColor={isSelected ? "primary.700" : "neutral.300"} + borderColor={ + isSelected ? "brand.primaryDark" : "background.border" + } borderRadius="8px" cursor="pointer" onClick={() => setSelectedIcon(icon)} + _hover={{ + bg: "background.highlight", + }} > ); diff --git a/frontend/src/admin/pages/badges/components/CustomBadgeTable.tsx b/frontend/src/admin/pages/badges/components/CustomBadgeTable.tsx index d2cf2b4f..016bf5fa 100644 --- a/frontend/src/admin/pages/badges/components/CustomBadgeTable.tsx +++ b/frontend/src/admin/pages/badges/components/CustomBadgeTable.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useContext, useState } from "react"; import { useMutation } from "@apollo/client"; import { DELETE_CUSTOM_BADGE } from "../../../../gql/customBadgeRequests"; import EditCustomBadgeModal from "./EditCustomBadgeModal"; @@ -6,6 +6,9 @@ import DataTable, { Column, Row } from "../../../../ui/misc/DataTable"; import { CustomBadge } from "../../../../types/models"; import { Marker, Trash } from "../../../../ui/icons/ActionIcons"; import { ICON_MAP } from "../../../../constants/icons"; +import { AdminContext } from "../../../AdminContext"; +import { ADMIN } from "../../../../constants/roles"; +import useNotification from "../../../../hooks/useNotification"; type CustomBadgeTableProps = { loading: boolean; @@ -20,6 +23,8 @@ const CustomBadgeTable = ({ badges, refetch, }: CustomBadgeTableProps) => { + const { role } = useContext(AdminContext); + const { sendNotification } = useNotification(); const [edit, setEdit] = useState(false); const [selected, setSelected] = useState(null); const [deleteError, setDeleteError] = useState(""); @@ -36,6 +41,7 @@ const CustomBadgeTable = ({ }, }); await refetch(); + sendNotification("Custom badge deleted successfully"); } catch (err: any) { setDeleteError(err.message); } @@ -45,8 +51,13 @@ const CustomBadgeTable = ({ { header: "Icon", width: "5%", center: true }, { header: "Badge Name", width: "25%" }, { header: "Description", width: "68%" }, - { header: "", width: "1%" }, - { header: "", width: "1%" }, + + ...(role === ADMIN + ? [ + { header: "", width: "1%" }, + { header: "", width: "1%" }, + ] + : []), ]; const rows: Row[][] = badges.length @@ -56,17 +67,22 @@ const CustomBadgeTable = ({ { element: }, { element: badge.name }, { element: badge.description }, - { - element: , - action: () => { - setSelected(badge); - setEdit(true); - }, - }, - { - element: , - action: async () => handleDelete(badge.cid), - }, + + ...(role === ADMIN + ? [ + { + element: , + action: () => { + setSelected(badge); + setEdit(true); + }, + }, + { + element: , + action: async () => handleDelete(badge.cid), + }, + ] + : []), ]; }) : []; diff --git a/frontend/src/admin/pages/badges/components/EditCustomBadgeModal.tsx b/frontend/src/admin/pages/badges/components/EditCustomBadgeModal.tsx index f1966c76..2e1ca4df 100644 --- a/frontend/src/admin/pages/badges/components/EditCustomBadgeModal.tsx +++ b/frontend/src/admin/pages/badges/components/EditCustomBadgeModal.tsx @@ -3,6 +3,7 @@ import { useMutation } from "@apollo/client"; import { UPDATE_CUSTOM_BADGE } from "../../../../gql/customBadgeRequests"; import PopupContainer from "../../../../ui/containers/PopupContainer"; import TextInput from "../../../../ui/inputs/TextInput"; +import useNotification from "../../../../hooks/useNotification"; interface EditCustomBadgeModalProps { onClose: () => void; @@ -18,7 +19,7 @@ const EditCustomBadgeModal: React.FC = ({ const [badgeName, setBadgeName] = useState(selected.name); const [badgeCriteria, setBadgeCriteria] = useState(selected.description); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const [updateCustomBadge, { loading: updateCustomBadgeLoading }] = useMutation(UPDATE_CUSTOM_BADGE); @@ -40,6 +41,7 @@ const EditCustomBadgeModal: React.FC = ({ }); await refetch(); onClose(); + sendNotification("Custom badge updated successfully"); } catch (err: any) { setError(err.message); } diff --git a/frontend/src/admin/pages/badges/components/EditSystemBadgeModal.tsx b/frontend/src/admin/pages/badges/components/EditSystemBadgeModal.tsx index e459a557..087fd960 100644 --- a/frontend/src/admin/pages/badges/components/EditSystemBadgeModal.tsx +++ b/frontend/src/admin/pages/badges/components/EditSystemBadgeModal.tsx @@ -11,6 +11,7 @@ import FixedInput from "../../../../ui/inputs/FixedInput"; import { LEVEL_ORDER } from "../../../../constants/levels"; import { toTitleCase } from "../../../../helpers/stringUtils"; import NumberInput from "../../../../ui/inputs/NumberInput"; +import useNotification from "../../../../hooks/useNotification"; interface EditSystemBadgeModalProps { isOpen: boolean; @@ -62,7 +63,7 @@ const EditSystemBadgeModal = ({ ) ); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const [updateBadgeLevel, { loading: updateBadgeLevelLoading }] = useMutation(UPDATE_BADGE_LEVEL); const [updateSystemBadge, { loading: updateSystemBadgeLoading }] = @@ -133,6 +134,7 @@ const EditSystemBadgeModal = ({ await Promise.all(requests); await refetch(); onClose(); + sendNotification("System badge updated successfully"); } catch (err: any) { setError("Failed to edit system badge"); } @@ -162,9 +164,7 @@ const EditSystemBadgeModal = ({ - - Set Badge Levels - + Set Badge Levels {LEVEL_ORDER.map((level: Level) => { if (!badgeLevels[level]) return null; const data = badgeLevels[level]; @@ -175,7 +175,7 @@ const EditSystemBadgeModal = ({ justifyContent="center" width="90%" > - + {toTitleCase(level)}: - times + times ); })} - + Marillac Bucks {LEVEL_ORDER.map((level: Level) => { diff --git a/frontend/src/admin/pages/badges/components/SystemBadgeTable.tsx b/frontend/src/admin/pages/badges/components/SystemBadgeTable.tsx index 5e542baf..4d39a169 100644 --- a/frontend/src/admin/pages/badges/components/SystemBadgeTable.tsx +++ b/frontend/src/admin/pages/badges/components/SystemBadgeTable.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useContext, useState } from "react"; import { useMutation } from "@apollo/client"; import { UPDATE_SYSTEM_BADGE } from "../../../../gql/systemBadgeRequests"; import EditSystemBadgeModal from "./EditSystemBadgeModal"; @@ -8,6 +8,9 @@ import { ICON_MAP } from "../../../../constants/icons"; import { Marker } from "../../../../ui/icons/ActionIcons"; import { LEVEL_ABBREVIATION, LEVEL_ORDER } from "../../../../constants/levels"; import ToggleButton from "../../../../ui/buttons/ToggleButton"; +import { AdminContext } from "../../../AdminContext"; +import { ADMIN } from "../../../../constants/roles"; +import useNotification from "../../../../hooks/useNotification"; type SystemBadgeTableProps = { loading: boolean; @@ -22,6 +25,8 @@ const SystemBadgeTable = ({ badges, refetch, }: SystemBadgeTableProps) => { + const { role } = useContext(AdminContext); + const { sendNotification } = useNotification(); const [edit, setEdit] = useState(false); const [selected, setSelected] = useState(null); const [updateError, setUpdateError] = useState(""); @@ -37,6 +42,7 @@ const SystemBadgeTable = ({ }, }); await refetch(); + sendNotification("System badge status updated successfully"); } catch (err: any) { setUpdateError(err.message); } @@ -46,8 +52,13 @@ const SystemBadgeTable = ({ { header: "Badge Name", width: "25%" }, { header: "Description", width: "50%" }, { header: "Offered Levels", width: "10%" }, - { header: "Status", width: "5%", center: true }, - { header: "", width: "5%" }, + + ...(role === ADMIN + ? [ + { header: "Status", width: "5%", center: true }, + { header: "", width: "5%" }, + ] + : []), ]; const rows: Row[][] = badges.length @@ -63,21 +74,29 @@ const SystemBadgeTable = ({ { element: badge.name }, { element: badge.description }, { element: offeredLevels }, - { - element: ( - {}} /> - ), - action: async () => { - changeActivityStatus(badge.name, !badge.is_active); - }, - }, - { - element: , - action: () => { - setSelected(badge); - setEdit(true); - }, - }, + + ...(role === ADMIN + ? [ + { + element: ( + {}} + /> + ), + action: async () => { + changeActivityStatus(badge.name, !badge.is_active); + }, + }, + { + element: , + action: () => { + setSelected(badge); + setEdit(true); + }, + }, + ] + : []), ]; }) : []; diff --git a/frontend/src/admin/pages/home/Main.tsx b/frontend/src/admin/pages/home/Main.tsx index 8265fde2..8d07d343 100644 --- a/frontend/src/admin/pages/home/Main.tsx +++ b/frontend/src/admin/pages/home/Main.tsx @@ -15,14 +15,14 @@ const AdminHomePage = (): React.ReactElement => { zIndex="100" direction="row" justifyContent="space-between" - alignItems="center" + alignItems="baseline" px="20px" width="100%" > - + Marillac Place Overview - + {formatDateV1(new Date())} diff --git a/frontend/src/admin/pages/home/components/AnnouncementSection.tsx b/frontend/src/admin/pages/home/components/AnnouncementSection.tsx index 7cbf86b5..c9e741d2 100644 --- a/frontend/src/admin/pages/home/components/AnnouncementSection.tsx +++ b/frontend/src/admin/pages/home/components/AnnouncementSection.tsx @@ -16,26 +16,22 @@ const AnnouncementCard: React.FC<{ announcement: Announcement }> = ({ }) => { return ( - + {getRoomString(announcement)} - + posted at {formatDateV2(new Date(announcement.date))} - + {announcement.message} @@ -74,10 +70,10 @@ const AnnouncementSection = () => { paddingBottom="10px" > - + Announcements - + {announcements.length} new post {announcements.length === 1 ? "" : "s"} today @@ -108,7 +104,7 @@ const AnnouncementSection = () => { /> )) ) : ( - + No Announcements Yet )} diff --git a/frontend/src/admin/pages/home/components/NoteSection.tsx b/frontend/src/admin/pages/home/components/NoteSection.tsx index 4c0bc201..2e6eb51c 100644 --- a/frontend/src/admin/pages/home/components/NoteSection.tsx +++ b/frontend/src/admin/pages/home/components/NoteSection.tsx @@ -77,10 +77,10 @@ const NoteSection = () => { gap="10px" paddingBottom="10px" > - + Internal Notes - + Expires in 48h @@ -100,7 +100,7 @@ const NoteSection = () => { }} > {getNotesData?.getNotes.length === 0 ? ( - + No Admin Notes Yet ) : ( @@ -108,9 +108,10 @@ const NoteSection = () => { return ( { flexWrap="wrap" overflow="hidden" > - + {note.message} @@ -126,10 +127,10 @@ const NoteSection = () => { - + {formatDateV3(new Date(note.date))} { bottom="0" left="0" paddingX="20px" - paddingTop="10px" - paddingBottom="12px" + paddingY="10px" > { height="100%" fontFamily="Nunito" fontWeight="400" - fontSize="14px" - color="#000000" + fontSize="12px" + color="text.dark" placeholder="Write a note" type="text" value={newNote} onChange={(e) => setNewNote(e.target.value)} border="1px solid" - borderColor="neutral.300" + borderColor="background.border" borderRadius="8px" _focus={{ - borderColor: "neutral.300", + borderColor: "background.border", boxShadow: "none", }} /> @@ -182,7 +182,7 @@ const NoteSection = () => { padding="0px" _hover={{ bg: "transparent" }} > - + diff --git a/frontend/src/admin/pages/home/components/RoomsOverview.tsx b/frontend/src/admin/pages/home/components/RoomsOverview.tsx index 6b55d674..d5df98c1 100644 --- a/frontend/src/admin/pages/home/components/RoomsOverview.tsx +++ b/frontend/src/admin/pages/home/components/RoomsOverview.tsx @@ -45,10 +45,10 @@ export default function RoomsOverview() { gap="10px" paddingBottom="10px" > - + Rooms - + Showing pending tasks for this week @@ -68,13 +68,13 @@ export default function RoomsOverview() { height="100%" > @@ -82,10 +82,10 @@ export default function RoomsOverview() { {room in roomToParticipant ? ( <> - + ID Number: #{roomToParticipant[room]} - + {data?.getNumberOfAssignedTasksByRoom[room - 1]} Assigned Tasks @@ -104,7 +104,7 @@ export default function RoomsOverview() { ) : ( <> - + Room Available. - Sign in - Please enter your login information. + Sign In + Please enter your login information. @@ -117,14 +117,14 @@ export default function AdminLoginPage() { /> {error && ( - + {error} )} diff --git a/frontend/src/admin/pages/participants/Main.tsx b/frontend/src/admin/pages/participants/Main.tsx index 62485f41..c32b4697 100644 --- a/frontend/src/admin/pages/participants/Main.tsx +++ b/frontend/src/admin/pages/participants/Main.tsx @@ -1,7 +1,10 @@ import { Flex, Spinner, Text, Grid } from "@chakra-ui/react"; import React from "react"; import { useQuery } from "@apollo/client"; -import { GET_CURRENT_PARTICIPANTS, GET_PAST_PARTICIPANTS } from "../../../gql/participantRequests"; +import { + GET_CURRENT_PARTICIPANTS, + GET_PAST_PARTICIPANTS, +} from "../../../gql/participantRequests"; import { ROOM_NUMBERS } from "../../../constants/rooms"; import OccupiedRoomCard from "./components/OccupiedRoomCard"; import EmptyRoomCard from "./components/EmptyRoomCard"; @@ -11,8 +14,18 @@ import ErrorScreen from "../../../ui/screens/ErrorScreen"; import { Participant } from "../../../types/models"; export default function AdminParticipantsPage() { - const { loading: loadingCurrent, error: errorCurrent, data: dataCurrent, refetch: refetchCurrent } = useQuery(GET_CURRENT_PARTICIPANTS); - const { loading: loadingPast, error: errorPast, data: dataPast, refetch: refetchPast } = useQuery(GET_PAST_PARTICIPANTS); + const { + loading: loadingCurrent, + error: errorCurrent, + data: dataCurrent, + refetch: refetchCurrent, + } = useQuery(GET_CURRENT_PARTICIPANTS); + const { + loading: loadingPast, + error: errorPast, + data: dataPast, + refetch: refetchPast, + } = useQuery(GET_PAST_PARTICIPANTS); const currentParticipants: Record = {}; if (dataCurrent && dataCurrent.getCurrentParticipants) { @@ -26,7 +39,7 @@ export default function AdminParticipantsPage() { return ( - + Current Participants @@ -41,15 +54,24 @@ export default function AdminParticipantsPage() { refetchPast={refetchPast} /> ) : ( - + ) )} - + Past Participants - + ); } diff --git a/frontend/src/admin/pages/participants/components/AddParticipantCard.tsx b/frontend/src/admin/pages/participants/components/AddParticipantCard.tsx index ad152955..fcd144c6 100644 --- a/frontend/src/admin/pages/participants/components/AddParticipantCard.tsx +++ b/frontend/src/admin/pages/participants/components/AddParticipantCard.tsx @@ -5,6 +5,7 @@ import ModalContainer from "../../../../ui/containers/PopupContainer"; import NumberInput from "../../../../ui/inputs/NumberInput"; import TextInput from "../../../../ui/inputs/TextInput"; import DateInput from "../../../../ui/inputs/DateInput"; +import PasswordInput from "../../../../ui/inputs/PasswordInput"; type AddParticipantCardProps = { roomNumber: number; @@ -77,7 +78,7 @@ const AddParticipantCard = ({ update_action={(value: Date) => setArrivalDate(value)} size="large" /> - setPassword(value)} @@ -87,4 +88,4 @@ const AddParticipantCard = ({ ); }; -export default AddParticipantCard; \ No newline at end of file +export default AddParticipantCard; diff --git a/frontend/src/admin/pages/participants/components/EditParticipantCard.tsx b/frontend/src/admin/pages/participants/components/EditParticipantCard.tsx index 5641a7d7..0f681b3f 100644 --- a/frontend/src/admin/pages/participants/components/EditParticipantCard.tsx +++ b/frontend/src/admin/pages/participants/components/EditParticipantCard.tsx @@ -10,6 +10,10 @@ import TextInput from "../../../../ui/inputs/TextInput"; import GreenButton from "../../../../ui/buttons/GreenOutlineButton"; import { Participant } from "../../../../types/models"; import FixedInput from "../../../../ui/inputs/FixedInput"; +import PasswordInput from "../../../../ui/inputs/PasswordInput"; +import { Swap } from "../../../../ui/icons/ActionIcons"; +import { ExitDoor } from "../../../../ui/icons/MiscIcons"; +import useNotification from "../../../../hooks/useNotification"; type EditParticipantCardProps = { roomNumber: number; @@ -29,12 +33,16 @@ export default function EditParticipantCard({ const participant: Participant = participants[roomNumber]; const id: number = participant.pid; const currentArrivalDate = new Date(participant.arrival); - const currentDepartureDate = participant.departure ? new Date(participant.departure) : null; + const currentDepartureDate = participant.departure + ? new Date(participant.departure) + : null; const currentPassword = participant.password; const [arrivalDate, setArrivalDate] = useState(currentArrivalDate); const [password, setPassword] = useState(currentPassword); - const [departureDate, setDepartureDate] = useState(currentDepartureDate); + const [departureDate, setDepartureDate] = useState( + currentDepartureDate + ); const [swapParticipant, setSwapParticipant] = useState(false); const [endStay, setEndStay] = useState(false); @@ -43,7 +51,7 @@ export default function EditParticipantCard({ const [selectedSwap, setSelectedSwap] = useState(-1); const [updateParticipant, { loading }] = useMutation(UPDATE_PARTICIPANT); - + const { sendNotification } = useNotification(); async function handleSubmit() { setError(""); const today = endOfDay(new Date()); @@ -91,9 +99,7 @@ export default function EditParticipantCard({ room: swapParticipant ? selectedSwap : undefined, arrival: arrivalDate.toISOString(), departure: - endStay && departureDate - ? departureDate.toISOString() - : undefined, + endStay && departureDate ? departureDate.toISOString() : undefined, password, }, }); @@ -110,6 +116,7 @@ export default function EditParticipantCard({ refetchCurrent(); refetchPast(); close(); + sendNotification("Participant updated successfully"); } catch (err: any) { setError(err.message); } @@ -141,7 +148,7 @@ export default function EditParticipantCard({ size="large" /> - { - setEndStay(false); - setDepartureDate(null); + if (!swapParticipant) { + setEndStay(false); + setDepartureDate(null); + } setError(""); - setSwapParticipant(true); + setSwapParticipant(!swapParticipant); }} is_active={swapParticipant} + icon={} /> {(endStay || swapParticipant) && ( - + )} {swapParticipant && ( - + Available Rooms @@ -216,18 +229,18 @@ export default function EditParticipantCard({ {selectedSwap !== -1 && (selectedSwap === roomNumber ? ( - + Participant #{participants[roomNumber].pid} is already in Room{" "} {roomNumber}. ) : ( - + Participant #{participants[roomNumber].pid} will be moved to Room{" "} {selectedSwap}. {selectedSwap in participants && ( - + Participant #{participants[selectedSwap].pid} will be moved to Room {roomNumber}. diff --git a/frontend/src/admin/pages/participants/components/EditPastParticipantCard.tsx b/frontend/src/admin/pages/participants/components/EditPastParticipantCard.tsx index 143089c6..e2015d2c 100644 --- a/frontend/src/admin/pages/participants/components/EditPastParticipantCard.tsx +++ b/frontend/src/admin/pages/participants/components/EditPastParticipantCard.tsx @@ -4,6 +4,7 @@ import { UPDATE_PARTICIPANT } from "../../../../gql/participantRequests"; import ModalContainer from "../../../../ui/containers/PopupContainer"; import DateInput from "../../../../ui/inputs/DateInput"; import FixedInput from "../../../../ui/inputs/FixedInput"; +import useNotification from "../../../../hooks/useNotification"; type EditPastParticipantCardProps = { id: number; @@ -23,7 +24,7 @@ export default function EditPastParticipantCard({ const [arrivalDate, setArrivalDate] = useState(arrival); const [departureDate, setDepartureDate] = useState(departure); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const [updateParticipant, { loading }] = useMutation(UPDATE_PARTICIPANT); async function handleSubmit() { @@ -54,6 +55,7 @@ export default function EditPastParticipantCard({ }); refetch(); close(); + sendNotification("Past participant updated successfully"); } catch (err: any) { setError(err.message); } diff --git a/frontend/src/admin/pages/participants/components/EmptyRoomCard.tsx b/frontend/src/admin/pages/participants/components/EmptyRoomCard.tsx index d75358a1..900069f0 100644 --- a/frontend/src/admin/pages/participants/components/EmptyRoomCard.tsx +++ b/frontend/src/admin/pages/participants/components/EmptyRoomCard.tsx @@ -8,13 +8,16 @@ type EmptyRoomCardProps = { refetch: () => void; }; -export default function EmptyRoomCard({ roomNumber, refetch }: EmptyRoomCardProps) { +export default function EmptyRoomCard({ + roomNumber, + refetch, +}: EmptyRoomCardProps) { const [addParticipant, setAddParticipant] = useState(false); return ( - Room {roomNumber} + Room {roomNumber} - This room is empty. + + This room is empty. + - Room {roomNumber} + Room {roomNumber} - + ID Number:  - + #{id} - - Arrival Date:  - + + Arrival:  + {formatDateV6(new Date(arrival))} diff --git a/frontend/src/admin/pages/participants/components/PastParticipantTable.tsx b/frontend/src/admin/pages/participants/components/PastParticipantTable.tsx index fd49f114..bcb3e87e 100644 --- a/frontend/src/admin/pages/participants/components/PastParticipantTable.tsx +++ b/frontend/src/admin/pages/participants/components/PastParticipantTable.tsx @@ -12,7 +12,12 @@ type PastParticipantTableProps = { loading: boolean; error: ApolloError | undefined; }; -const PastParticipantTable = ({ participants, refetch, loading, error }: PastParticipantTableProps) => { +const PastParticipantTable = ({ + participants, + refetch, + loading, + error, +}: PastParticipantTableProps) => { const [edit, setEdit] = useState(false); const [selectedId, setSelectedId] = useState(-1); const [selectedArrival, setSelectedArrival] = useState(null); @@ -36,27 +41,32 @@ const PastParticipantTable = ({ participants, refetch, loading, error }: PastPar const rows: Row[][] = participants.length ? participants.map((participant: Participant, index: number) => { - return [ - { - element: participant.pid, - }, - { - element: formatDateV1(new Date(participant.arrival)), - }, - { - element: participant.departure ? formatDateV1(new Date(participant.departure)) : "", - }, - { - element: , - action: () => { - setSelectedId(participant.pid); - setSelectedArrival(new Date(participant.arrival)); - setSelectedDeparture(participant.departure ? new Date(participant.departure) : null); - setEdit(true); - } - } - ]; - }): []; + return [ + { + element: participant.pid, + }, + { + element: formatDateV1(new Date(participant.arrival)), + }, + { + element: participant.departure + ? formatDateV1(new Date(participant.departure)) + : "", + }, + { + element: , + action: () => { + setSelectedId(participant.pid); + setSelectedArrival(new Date(participant.arrival)); + setSelectedDeparture( + participant.departure ? new Date(participant.departure) : null + ); + setEdit(true); + }, + }, + ]; + }) + : []; return ( <> @@ -66,14 +76,15 @@ const PastParticipantTable = ({ participants, refetch, loading, error }: PastPar loading={loading} error={error?.message} /> - {edit && selectedArrival && selectedDeparture && selectedId && - setEdit(false)} - refetch={refetch} - />} + {edit && selectedArrival && selectedDeparture && selectedId && ( + setEdit(false)} + refetch={refetch} + /> + )} ); }; diff --git a/frontend/src/admin/pages/reports/Main.tsx b/frontend/src/admin/pages/reports/Main.tsx index e0d6a30f..926edc5c 100644 --- a/frontend/src/admin/pages/reports/Main.tsx +++ b/frontend/src/admin/pages/reports/Main.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useContext, useState } from "react"; import { Flex, Text } from "@chakra-ui/react"; import { useQuery } from "@apollo/client"; import ReportsTable from "./components/ReportsTable"; @@ -7,6 +7,9 @@ import { GET_REPORT_RECIPIENTS } from "../../../gql/reportRecipientRequests"; import LoadingScreen from "../../../ui/screens/LoadingScreen"; import ErrorScreen from "../../../ui/screens/ErrorScreen"; import OrangeButton from "../../../ui/buttons/OrangeButton"; +import { Plus } from "../../../ui/icons/ActionIcons"; +import { AdminContext } from "../../AdminContext"; +import { ADMIN } from "../../../constants/roles"; type Report = { email: string; @@ -14,8 +17,8 @@ type Report = { monthly: boolean; }; -// TODO: Relief staff cannot access this page export default function AdminReportsPage() { + const { role } = useContext(AdminContext); const { loading, error, data, refetch } = useQuery(GET_REPORT_RECIPIENTS); const reports: Report[] = data?.getReportRecipients || []; @@ -23,6 +26,8 @@ export default function AdminReportsPage() { if (loading) return ; if (error) return ; + if (role !== ADMIN) + return ; return ( @@ -33,15 +38,11 @@ export default function AdminReportsPage() { justifyContent="space-between" > - - + + Reports - + Reports will be automatically generated and emailed. Edit frequency of reports below. @@ -50,6 +51,7 @@ export default function AdminReportsPage() { label="Add Email" action={() => setAddEmail(true)} is_active={addEmail} + icon={} /> diff --git a/frontend/src/admin/pages/reports/components/AddEmailModal.tsx b/frontend/src/admin/pages/reports/components/AddEmailModal.tsx index 906a552a..c88cb20a 100644 --- a/frontend/src/admin/pages/reports/components/AddEmailModal.tsx +++ b/frontend/src/admin/pages/reports/components/AddEmailModal.tsx @@ -5,6 +5,7 @@ import PopupContainer from "../../../../ui/containers/PopupContainer"; import TextInput from "../../../../ui/inputs/TextInput"; import { CREATE_REPORT_RECIPIENT } from "../../../../gql/reportRecipientRequests"; import ToggleButton from "../../../../ui/buttons/ToggleButton"; +import useNotification from "../../../../hooks/useNotification"; type AddEmailModalProps = { refetch: () => void; @@ -19,7 +20,7 @@ export default function AddEmailModal({ const [weekly, setWeekly] = useState(false); const [monthly, setMonthly] = useState(false); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const [createReportRecipient, { loading: createReportRecipientLoading }] = useMutation(CREATE_REPORT_RECIPIENT); const handleAddEmail = async ( @@ -42,6 +43,7 @@ export default function AddEmailModal({ }); onClose(); refetch(); + sendNotification("Email added successfully"); } catch (err: any) { setError(err.message); } @@ -62,17 +64,14 @@ export default function AddEmailModal({ update_action={setEmail} size="large" /> - - Report Frequency - - - + + Weekly Reports - + Monthly Reports diff --git a/frontend/src/admin/pages/reports/components/ReportsTable.tsx b/frontend/src/admin/pages/reports/components/ReportsTable.tsx index d230e23d..c6745138 100644 --- a/frontend/src/admin/pages/reports/components/ReportsTable.tsx +++ b/frontend/src/admin/pages/reports/components/ReportsTable.tsx @@ -7,6 +7,7 @@ import { DELETE_REPORT_RECIPIENT, UPDATE_REPORT_RECIPIENT, } from "../../../../gql/reportRecipientRequests"; +import useNotification from "../../../../hooks/useNotification"; type Report = { email: string; @@ -24,6 +25,7 @@ export default function ReportsTable({ reports, refetch }: ReportsTableProps) { useMutation(UPDATE_REPORT_RECIPIENT); const [deleteReportRecipient, { loading: deleteReportRecipientLoading }] = useMutation(DELETE_REPORT_RECIPIENT); + const { sendNotification } = useNotification(); const handleChangeWeekly = async (email: string, weekly: boolean) => { try { @@ -34,6 +36,7 @@ export default function ReportsTable({ reports, refetch }: ReportsTableProps) { }, }); refetch(); + sendNotification("Weekly report frequency updated successfully"); } catch (err) { console.error("Error updating report recipient:", err); } @@ -48,6 +51,7 @@ export default function ReportsTable({ reports, refetch }: ReportsTableProps) { }, }); refetch(); + sendNotification("Monthly report frequency updated successfully"); } catch (err) { console.error("Error updating report recipient:", err); } @@ -61,6 +65,7 @@ export default function ReportsTable({ reports, refetch }: ReportsTableProps) { }, }); refetch(); + sendNotification("Email deleted successfully"); } catch (err) { console.error("Error deleting report recipient:", err); } diff --git a/frontend/src/admin/pages/schedule/Main.tsx b/frontend/src/admin/pages/schedule/Main.tsx index 5fd50a54..1cc3c8bf 100644 --- a/frontend/src/admin/pages/schedule/Main.tsx +++ b/frontend/src/admin/pages/schedule/Main.tsx @@ -16,7 +16,7 @@ import OrangeButton from "../../../ui/buttons/OrangeButton"; import { ADMIN_PARTICIPANTS_PAGE } from "../../../constants/routes"; import GreenOutlineButton from "../../../ui/buttons/GreenOutlineButton"; import { Calendar, List } from "../../../ui/icons/MiscIcons"; -import { Marker } from "../../../ui/icons/ActionIcons"; +import { Marker, Plus } from "../../../ui/icons/ActionIcons"; import AnyDayTasksTable from "./components/AnyDayTasksTable"; import DailyTasksTable from "./components/DailyTasksTable"; import MarillacBalanceModal from "./components/MarillacBalanceModal"; @@ -102,7 +102,7 @@ export default function AdminSchedulePage() { {selectedRoom in roomToParticipant ? ( <> - + {formatDateV5(weekStart)} - + List @@ -157,18 +157,18 @@ export default function AdminSchedulePage() { borderRightRadius="8px" border="1px solid" borderColor="#E67D4F" - bg="#FFFFFF" - color="#E67D4F" + bg="white" + color="brand.secondaryDark" _hover={{ - bg: "#E67D4F", - color: "#FFFFFF", + bg: "brand.secondaryDark", + color: "white", }} _active={{ - bg: "#E67D4F", - color: "#FFFFFF", + bg: "brand.secondaryDark", + color: "white", }} > - + Calendar @@ -177,6 +177,7 @@ export default function AdminSchedulePage() { label="Assign Task" action={() => setAssignTask(true)} is_active={assignTask} + icon={} /> @@ -189,7 +190,7 @@ export default function AdminSchedulePage() { /> ) : ( <> - + Daily - + Any Day - + This room is empty void; @@ -105,7 +106,7 @@ export default function AssignTaskModal({ }: AssignTaskModalProps) { const [initialState, setInitialState] = useState(true); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const [taskId, setTaskId] = useState(null); const [taskName, setTaskName] = useState(""); const [taskType, setTaskType] = useState(TaskType.OPTIONAL); @@ -164,6 +165,7 @@ export default function AssignTaskModal({ await Promise.all(operations); refetchAssignedTasks(); onClose(); + sendNotification("Task assigned successfully"); } catch (err: any) { setError(err.message); } diff --git a/frontend/src/admin/pages/schedule/components/DailyTasksTable.tsx b/frontend/src/admin/pages/schedule/components/DailyTasksTable.tsx index e0024468..8f989366 100644 --- a/frontend/src/admin/pages/schedule/components/DailyTasksTable.tsx +++ b/frontend/src/admin/pages/schedule/components/DailyTasksTable.tsx @@ -79,7 +79,7 @@ export default function DailyTasksTable({ ]) ); }, [chosenDay, tasks]); - + return ( @@ -97,16 +97,17 @@ export default function DailyTasksTable({ width="14.1%" height="fit-content" paddingX="12px" - paddingY="6px" - bg="#FFFFFF" + paddingTop="8px" + paddingBottom="6px" + bg="white" _hover={{ - bg: "#F5F6F8", + bg: "background.highlight", }} _active={{ - bg: "#F5F6F8", + bg: "background.highlight", }} > - + {toTitleCase(day)} diff --git a/frontend/src/admin/pages/schedule/components/EditAssignedTaskModal.tsx b/frontend/src/admin/pages/schedule/components/EditAssignedTaskModal.tsx index e792c793..833a0949 100644 --- a/frontend/src/admin/pages/schedule/components/EditAssignedTaskModal.tsx +++ b/frontend/src/admin/pages/schedule/components/EditAssignedTaskModal.tsx @@ -7,6 +7,7 @@ import TextInput from "../../../../ui/inputs/TextInput"; import NumberInput from "../../../../ui/inputs/NumberInput"; import TextAreaInput from "../../../../ui/inputs/TextAreaInput"; import { UPDATE_ASSIGNED_TASK } from "../../../../gql/assignedTaskRequests"; +import useNotification from "../../../../hooks/useNotification"; type EditAssignedTaskModalProps = { task: AssignedTask; @@ -19,6 +20,7 @@ export default function EditAssignedTaskModal({ onClose, refetch, }: EditAssignedTaskModalProps) { + const { sendNotification } = useNotification(); const [taskName, setTaskName] = useState(task.name); const [addition, setAddition] = useState(task.value); const [deduction, setDeduction] = useState(task.penalty); @@ -50,6 +52,7 @@ export default function EditAssignedTaskModal({ }); refetch(); onClose(); + sendNotification("Assigned task updated successfully"); } catch (err: any) { setError(err.message); } diff --git a/frontend/src/admin/pages/schedule/components/MarillacBalanceModal.tsx b/frontend/src/admin/pages/schedule/components/MarillacBalanceModal.tsx index 879016a3..2e3c5f58 100644 --- a/frontend/src/admin/pages/schedule/components/MarillacBalanceModal.tsx +++ b/frontend/src/admin/pages/schedule/components/MarillacBalanceModal.tsx @@ -7,6 +7,7 @@ import FixedInput from "../../../../ui/inputs/FixedInput"; import NumberInput from "../../../../ui/inputs/NumberInput"; import SelectInput from "../../../../ui/inputs/SelectInput"; import TextAreaInput from "../../../../ui/inputs/TextAreaInput"; +import useNotification from "../../../../hooks/useNotification"; type MarillacBalanceModalProps = { currentBalance: number; @@ -25,7 +26,7 @@ export default function MarillacBalanceModal({ const [amount, setAmount] = useState(0); const [reason, setReason] = useState(""); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const [updateMarillacBucks, { loading: updateMarillacBucksLoading }] = useMutation(UPDATE_BALANCE); @@ -45,11 +46,12 @@ export default function MarillacBalanceModal({ reason, }, }); + refetchParticipant(); + close(); + sendNotification("Marillac balance updated successfully"); } catch (err: any) { setError(err.message); } - refetchParticipant(); - close(); } } diff --git a/frontend/src/admin/pages/schedule/components/RoomNavigation.tsx b/frontend/src/admin/pages/schedule/components/RoomNavigation.tsx index 4fe35353..c61e9bb1 100644 --- a/frontend/src/admin/pages/schedule/components/RoomNavigation.tsx +++ b/frontend/src/admin/pages/schedule/components/RoomNavigation.tsx @@ -17,7 +17,7 @@ export default function RoomNavigation({ justifyContent="space-between" alignItems="center" position="absolute" - top="18px" + top="21px" left="0px" zIndex={10} paddingX="20px" @@ -25,14 +25,15 @@ export default function RoomNavigation({ {ROOM_NUMBERS.map((num: number) => ( changeRoom(num)} borderBottom="3px solid" - borderColor={selectedRoom === num ? "primary.700" : "transparent"} - px="12px" + borderColor={ + selectedRoom === num ? "brand.primaryDark" : "transparent" + } + px="10px" pb="10px" > Room {num} diff --git a/frontend/src/admin/pages/schedule/components/TaskDetailsModal.tsx b/frontend/src/admin/pages/schedule/components/TaskDetailsModal.tsx index 1a7236d7..6f902530 100644 --- a/frontend/src/admin/pages/schedule/components/TaskDetailsModal.tsx +++ b/frontend/src/admin/pages/schedule/components/TaskDetailsModal.tsx @@ -20,6 +20,7 @@ import { Incomplete, } from "../../../../ui/icons/StatusIcons"; import EditAssignedTaskModal from "./EditAssignedTaskModal"; +import useNotification from "../../../../hooks/useNotification"; interface TaskDetailsModalProps { task: AssignedTask; @@ -35,7 +36,7 @@ export default function TaskDetailsModal({ const [selectedStatus, setSelectedStatus] = useState(task.status); const [editDetails, setEditDetails] = useState(false); const [error, setError] = useState(""); - + const { sendNotification } = useNotification(); const [deleteAssignedTask, { loading: deleteAssignedTaskLoading }] = useMutation(DELETE_ASSIGNED_TASK); const handleDelete = async () => { @@ -47,6 +48,7 @@ export default function TaskDetailsModal({ }); refetch(); onClose(); + sendNotification("Assigned task deleted successfully"); } catch (err: any) { setError(err.message); } @@ -74,6 +76,7 @@ export default function TaskDetailsModal({ }); refetch(); onClose(); + sendNotification("Task status updated successfully"); } catch (err: any) { setError(err.message); } @@ -124,8 +127,8 @@ export default function TaskDetailsModal({ orientation="horizontal" /> - - + + Status diff --git a/frontend/src/admin/pages/tasks/Main.tsx b/frontend/src/admin/pages/tasks/Main.tsx index d59ea422..5e38ef77 100644 --- a/frontend/src/admin/pages/tasks/Main.tsx +++ b/frontend/src/admin/pages/tasks/Main.tsx @@ -14,7 +14,7 @@ import OrangeButton from "../../../ui/buttons/OrangeButton"; import { TaskType } from "../../../types/enums"; import { Task } from "../../../types/models"; import { toTitleCase } from "../../../helpers/stringUtils"; -import { MagnifyingGlass } from "../../../ui/icons/ActionIcons"; +import { MagnifyingGlass, Plus } from "../../../ui/icons/ActionIcons"; import AddTaskModal from "./components/AddTaskModal"; export default function AdminTasksPage() { @@ -46,7 +46,7 @@ export default function AdminTasksPage() { justifyContent="flex-start" alignItems="center" position="absolute" - top="18px" + top="21px" left="0px" zIndex={10} paddingX="20px" @@ -55,14 +55,15 @@ export default function AdminTasksPage() { {[TaskType.REQUIRED, TaskType.OPTIONAL].map((type: TaskType) => ( setSelectedTaskType(type)} borderBottom="3px solid" borderColor={ - selectedTaskType === type ? "primary.700" : "transparent" + selectedTaskType === type ? "brand.primaryDark" : "transparent" } px="12px" pb="10px" @@ -76,7 +77,7 @@ export default function AdminTasksPage() { - + @@ -105,6 +106,7 @@ export default function AdminTasksPage() { label="Add Task" action={() => setAddTask(true)} is_active={addTask} + icon={} /> diff --git a/frontend/src/admin/pages/tasks/components/AddTaskModal.tsx b/frontend/src/admin/pages/tasks/components/AddTaskModal.tsx index 1f5fd3a2..f4f7efd2 100644 --- a/frontend/src/admin/pages/tasks/components/AddTaskModal.tsx +++ b/frontend/src/admin/pages/tasks/components/AddTaskModal.tsx @@ -16,6 +16,7 @@ import TextInput from "../../../../ui/inputs/TextInput"; import TextAreaInput from "../../../../ui/inputs/TextAreaInput"; import DateOptions from "../../../../ui/misc/DateOptions"; import { isValidTask } from "../../../../helpers/taskHelpers"; +import useNotification from "../../../../hooks/useNotification"; type AddTaskModalProps = { taskType: TaskType; @@ -45,6 +46,7 @@ export default function AddTaskModal({ const [error, setError] = useState(""); const [createTask, { loading: createTaskLoading }] = useMutation(CREATE_TASK); + const { sendNotification } = useNotification(); async function handleSubmit() { const newTask: any = { tid: 0, @@ -90,6 +92,7 @@ export default function AddTaskModal({ }); refetch(); close(); + sendNotification("Task created successfully"); } catch (err: any) { setError(err.message); } diff --git a/frontend/src/admin/pages/tasks/components/EditTaskModal.tsx b/frontend/src/admin/pages/tasks/components/EditTaskModal.tsx index ee92075b..b3a314c3 100644 --- a/frontend/src/admin/pages/tasks/components/EditTaskModal.tsx +++ b/frontend/src/admin/pages/tasks/components/EditTaskModal.tsx @@ -17,6 +17,7 @@ import TextInput from "../../../../ui/inputs/TextInput"; import DateOptions from "../../../../ui/misc/DateOptions"; import NumberInput from "../../../../ui/inputs/NumberInput"; import TextAreaInput from "../../../../ui/inputs/TextAreaInput"; +import useNotification from "../../../../hooks/useNotification"; type EditTaskModalProps = { selected: Task; @@ -67,7 +68,7 @@ export default function EditTaskModal({ const [error, setError] = useState(""); const [updateTask] = useMutation(UPDATE_TASK); - + const { sendNotification } = useNotification(); async function handleSubmit() { const updatedTask: any = { tid: selected.tid, @@ -114,6 +115,7 @@ export default function EditTaskModal({ }); refetch(); close(); + sendNotification("Task updated successfully"); } catch (err: any) { setError(err.message); } diff --git a/frontend/src/admin/pages/tasks/components/TasksTable.tsx b/frontend/src/admin/pages/tasks/components/TasksTable.tsx index f16405e7..e9933db3 100644 --- a/frontend/src/admin/pages/tasks/components/TasksTable.tsx +++ b/frontend/src/admin/pages/tasks/components/TasksTable.tsx @@ -14,6 +14,7 @@ import { formatDateV2 } from "../../../../helpers/formatDateTime"; import { Marker, Trash } from "../../../../ui/icons/ActionIcons"; import EditTaskModal from "./EditTaskModal"; import { formatCurrency } from "../../../../helpers/formatCurrency"; +import useNotification from "../../../../hooks/useNotification"; type TasksTableProps = { taskType: TaskType; @@ -32,16 +33,17 @@ const TasksTable = ({ }: TasksTableProps) => { const [editTask, setEditTask] = useState(null); const [deleteTask] = useMutation(DELETE_TASK); - + const { sendNotification } = useNotification(); async function handleDeleteTask(tid: number) { try { await deleteTask({ variables: { tid }, }); + refetch(); + sendNotification("Task deleted successfully"); } catch (err: any) { console.log(err); } - refetch(); } function getAssignedDaysString( diff --git a/frontend/src/gql/announcementRequests.ts b/frontend/src/gql/announcementRequests.ts index 82fb8986..d54b55b1 100644 --- a/frontend/src/gql/announcementRequests.ts +++ b/frontend/src/gql/announcementRequests.ts @@ -41,7 +41,12 @@ export const CREATE_ANNOUNCEMENT = gql` $message: String! $topic: String! ) { - createAnnouncement(priority: $priority, pids: $pids, message: $message, topic: $topic) { + createAnnouncement( + priority: $priority + pids: $pids + message: $message + topic: $topic + ) { aid date message diff --git a/frontend/src/helpers/formatDateTime.ts b/frontend/src/helpers/formatDateTime.ts index b4d50085..3414652f 100644 --- a/frontend/src/helpers/formatDateTime.ts +++ b/frontend/src/helpers/formatDateTime.ts @@ -3,58 +3,58 @@ import { format, parse } from "date-fns"; // e.g. January 1, 2026 export function formatDateV1(date: Date): string { return format(date, "MMMM d, yyyy"); -}; +} // e.g. 2:30 PM export function formatDateV2(date: Date): string { return format(date, "h:mm a"); -}; +} // e.g. Jan 1, 2:30 PM export function formatDateV3(date: Date): string { return format(date, "MMM d, h:mm a"); -}; +} // e.g. THURSDAY 1 export function formatDateV4(date: Date): string { return format(date, "EEEE d").toUpperCase(); -}; +} // e.g. JANUARY 2025 export function formatDateV5(date: Date): string { return format(date, "MMMM yyyy").toUpperCase(); -}; +} // e.g. Jan 1, 2026 export function formatDateV6(date: Date): string { return format(date, "MMM d, yyyy"); -}; +} // e.g. Thursday, Jan 1 export function formatDateV7(date: Date): string { return format(date, "EEEE, MMM d"); -}; +} // e.g. Thursday - January 1, 2026 export function formatDateV8(date: Date): string { return format(date, "EEEE - MMM d, yyyy"); -}; +} export function formatDateInputValue(date: Date): string { return format(date, "yyyy-MM-dd"); -}; +} export function parseDateInputValue(date: string): Date { return parse(date, "yyyy-MM-dd", new Date()); -}; +} export function formatTimeInputValue(date: Date): string { return format(date, "HH:mm"); -}; +} export function parseTimeInputValue(time: string): Date { return parse(time, "HH:mm", new Date()); -}; +} export function combineDayAndTime(day: Date, time: Date): Date { return new Date( @@ -66,4 +66,4 @@ export function combineDayAndTime(day: Date, time: Date): Date { time.getSeconds(), time.getMilliseconds() ); -}; +} diff --git a/frontend/src/helpers/stringUtils.ts b/frontend/src/helpers/stringUtils.ts index 0fa435c8..04a131e3 100644 --- a/frontend/src/helpers/stringUtils.ts +++ b/frontend/src/helpers/stringUtils.ts @@ -16,7 +16,7 @@ export const getRoomString = (announcement: Announcement) => { announcement.ReceivedAnnouncement?.map( (ra: ReceivedAnnouncement) => ra.participant?.room ).filter((room) => room !== undefined) ?? [] - ).sort(); + ).sort((a, b) => Number(a) - Number(b)); if (announcement.ReceivedAnnouncement?.length === 1) { return `Room ${rooms[0]}`; diff --git a/frontend/src/helpers/taskHelpers.ts b/frontend/src/helpers/taskHelpers.ts index d39bfbd0..fd8bc280 100644 --- a/frontend/src/helpers/taskHelpers.ts +++ b/frontend/src/helpers/taskHelpers.ts @@ -14,11 +14,10 @@ import { combineDayAndTime } from "./formatDateTime"; export function isAllDayTask(assignedTask: AssignedTask): boolean { const startDate = new Date(assignedTask.start_date); const endDate = new Date(assignedTask.end_date); - const allDay = ( + const allDay = !isSameDay(startDate, endDate) || (isEqual(startOfDay(startDate), startDate) && - isEqual(endOfDay(endDate), endDate)) - ); + isEqual(endOfDay(endDate), endDate)); return allDay; } diff --git a/frontend/src/hooks/useLocalStorage.tsx b/frontend/src/hooks/useLocalStorage.tsx deleted file mode 100644 index cc0e0dcd..00000000 --- a/frontend/src/hooks/useLocalStorage.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useState, useEffect } from "react"; - -// TODO: Apply this hook for all local storage operations in this project -export function useLocalStorage(key: string, initialValue: T) { - const [value, setValue] = useState(() => { - try { - const json = localStorage.getItem(key); - return json != null ? JSON.parse(json) : initialValue; - } catch { - return initialValue; - } - }); - - useEffect(() => { - localStorage.setItem(key, JSON.stringify(value)); - }, [key, value]); - - return [value, setValue] as const; -} diff --git a/frontend/src/hooks/useNotification.tsx b/frontend/src/hooks/useNotification.tsx index be80fdad..41daedd5 100644 --- a/frontend/src/hooks/useNotification.tsx +++ b/frontend/src/hooks/useNotification.tsx @@ -1,18 +1,15 @@ import { useToast } from "@chakra-ui/react"; -// TODO: Customize toast to match Figma design -// https://www.figma.com/design/Ts9QxCIXFe4l9h6GKOLOIq/Admin-Application?node-id=5530-38022&t=d0D0hBm1Lo6YUL70-4 -// Apply this hook for all successful create / update / delete operations in this project export default function useNotification() { const toast = useToast(); const sendNotification = (message: string) => { toast({ description: message, - status: "success", position: "top", duration: 3000, - variant: "left-accent", + variant: "subtle", + status: "success", }); }; diff --git a/frontend/src/index.css b/frontend/src/index.css index 896139f0..7b7cdfc7 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -31,6 +31,18 @@ flex: 1 !important; height: 100% !important; min-height: 0 !important; + background-color: transparent !important; +} + +.rbc-month-view, +.rbc-time-view, +.rbc-agenda-view, +.rbc-time-content, +.rbc-time-header, +.rbc-header, +.rbc-day-bg, +.rbc-today { + background-color: transparent !important; } .rbc-toolbar { @@ -39,7 +51,6 @@ .rbc-time-view { border: none !important; - background-color: white; display: flex !important; flex-direction: column !important; flex: 1 !important; @@ -48,7 +59,6 @@ } .rbc-header { - background-color: white; font-weight: 600; border: none !important; } @@ -58,7 +68,6 @@ } .rbc-time-header { - background-color: white; flex-shrink: 0; margin: 0 !important; padding: 0 !important; @@ -131,14 +140,13 @@ } .rbc-time-header-gutter { - background-color: white; border-bottom: 1px solid var(--border-color); flex-shrink: 0; border: none !important; } .rbc-allday-cell { - background-color: white; + background-color: transparent !important; border: none !important; padding: 2px 0; min-height: 24px; @@ -198,7 +206,7 @@ position: sticky !important; z-index: 9 !important; - background-color: white !important; + background-color: transparent !important; } .rbc-time-column { diff --git a/frontend/src/participant/ParticipantContext.tsx b/frontend/src/participant/ParticipantContext.tsx index d84a68c6..19a9e302 100644 --- a/frontend/src/participant/ParticipantContext.tsx +++ b/frontend/src/participant/ParticipantContext.tsx @@ -26,12 +26,14 @@ interface ParticipantProviderProps { children: ReactNode; } -export const ParticipantProvider: React.FC = ({ children }) => { +export const ParticipantProvider: React.FC = ({ + children, +}) => { const [pid, setPid] = useState(-1); const [room, setRoom] = useState(0); const [balance, setBalance] = useState(0); const [totalEarnings, setTotalEarnings] = useState(0); - + return ( - + Room {room} { navigate(page.route); collapseMenu(); }} _selected={{ - fontWeight: 700, - color: "neutral.0", - bg: "secondary.700", + fontWeight: 650, + color: "white", + bg: "brand.secondaryDark", }} > {page.label} @@ -83,13 +83,13 @@ function ExpandedParticipantMenu({ ))} - + handleSignOut()} - mt="10px" + mt="8px" > Sign Out @@ -102,7 +102,10 @@ type ParticipantMenuProps = { balance: number; }; -export default function ParticipantMenu({ room, balance }: ParticipantMenuProps) { +export default function ParticipantMenu({ + room, + balance, +}: ParticipantMenuProps) { const pages = [ { label: "Home", route: ROUTES.PARTICIPANTS_HOME_PAGE }, { label: "Schedule", route: ROUTES.PARTICIPANTS_SCHEDULE_PAGE }, @@ -119,7 +122,7 @@ export default function ParticipantMenu({ room, balance }: ParticipantMenuProps) {!expandMenu && ( - setExpandMenu(true)} cursor="pointer" position="absolute" left="20px" top="20px" zIndex={100}> - + setExpandMenu(true)} + cursor="pointer" + position="absolute" + left="20px" + top="24px" + zIndex={100} + > + )} {expandMenu && ( <> - setExpandMenu(false)} cursor="pointer" position="absolute" left="20px" top="20px" zIndex={100}> - + setExpandMenu(false)} + cursor="pointer" + position="absolute" + left="21px" + top="26px" + zIndex={100} + > + )} - {pages[currentPageIndex].label} + + {pages[currentPageIndex].label} + - - - {balance} + + + {balance} ); diff --git a/frontend/src/participant/ParticipantRoute.tsx b/frontend/src/participant/ParticipantRoute.tsx index 07d5cde6..d06042a7 100644 --- a/frontend/src/participant/ParticipantRoute.tsx +++ b/frontend/src/participant/ParticipantRoute.tsx @@ -26,13 +26,20 @@ export default function ParticipantRoute({ children }: ParticipantRouteProps) { const [getParticipantByPid] = useLazyQuery(GET_PARTICIPANT_BY_PID, { onCompleted: (data) => { - if (!data || !data.getParticipantByPid || !data.getParticipantByPid.room || !data.getParticipantByPid.balance) { + if ( + !data || + !data.getParticipantByPid || + !data.getParticipantByPid.room || + !data.getParticipantByPid.balance + ) { setError("participant data is missing"); return; } participantContext.setRoom(data.getParticipantByPid.room); participantContext.setBalance(data.getParticipantByPid.balance); - participantContext.setTotalEarnings(data.getParticipantByPid.total_earnings); + participantContext.setTotalEarnings( + data.getParticipantByPid.total_earnings + ); }, onError: (err: Error) => { setError(err.message); @@ -71,14 +78,18 @@ export default function ParticipantRoute({ children }: ParticipantRouteProps) { } return ( - - - + + + - {error ? : (authorizing || populatingContext) ? : children} + {error ? ( + + ) : authorizing || populatingContext ? ( + + ) : ( + children + )} diff --git a/frontend/src/participant/pages/announcements/Main.tsx b/frontend/src/participant/pages/announcements/Main.tsx index 61eabd1c..828054a8 100644 --- a/frontend/src/participant/pages/announcements/Main.tsx +++ b/frontend/src/participant/pages/announcements/Main.tsx @@ -19,7 +19,7 @@ export default function ParticipantsAnnouncementsPage() { const [filter, setFilter] = useState(0); const getFilterVariables = () => { - switch(filter){ + switch (filter) { case 0: // ALL return { pid: participantId }; case 1: // UNREAD @@ -33,33 +33,42 @@ export default function ParticipantsAnnouncementsPage() { } }; - const { data, loading, error, refetch } = useQuery(GET_RECEIVED_ANNOUNCEMENTS, { - variables : getFilterVariables(), - skip: !participantId, - fetchPolicy: "network-only", - nextFetchPolicy: "cache-first", - notifyOnNetworkStatusChange: true, - }); + const { data, loading, error, refetch } = useQuery( + GET_RECEIVED_ANNOUNCEMENTS, + { + variables: getFilterVariables(), + skip: !participantId, + fetchPolicy: "network-only", + nextFetchPolicy: "cache-first", + notifyOnNetworkStatusChange: true, + } + ); if (loading) return ; if (error) return ; if (expandedView && selected) { return ( - { refetch(); setExpandedView(false); setSelected(null); }} /> - ) + ); } return ( <> - + setFilter(0)} @@ -81,21 +90,23 @@ export default function ParticipantsAnnouncementsPage() { is_active={filter === 3} /> - - {data?.getReceivedAnnouncements.map((announcement: ReceivedAnnouncement, index: number) => { - return ( - { - setSelected(announcement); - setExpandedView(true); - }} - > - - - ); - })} + + {data?.getReceivedAnnouncements.map( + (announcement: ReceivedAnnouncement, index: number) => { + return ( + { + setSelected(announcement); + setExpandedView(true); + }} + > + + + ); + } + )} ); } diff --git a/frontend/src/participant/pages/announcements/components/AnnouncementsExpandedView.tsx b/frontend/src/participant/pages/announcements/components/AnnouncementsExpandedView.tsx index 26b3fa3c..0473b2d7 100644 --- a/frontend/src/participant/pages/announcements/components/AnnouncementsExpandedView.tsx +++ b/frontend/src/participant/pages/announcements/components/AnnouncementsExpandedView.tsx @@ -2,7 +2,11 @@ import { Divider, Flex, Text } from "@chakra-ui/react"; import React, { useContext, useEffect, useRef, useState } from "react"; import { useMutation } from "@apollo/client"; import { formatDateV3 } from "../../../../helpers/formatDateTime"; -import { ExclamationMark, Dot, Mail } from "../../../../ui/icons/NotificationIcons"; +import { + ExclamationMark, + Dot, + Mail, +} from "../../../../ui/icons/NotificationIcons"; import { Pin, Pinned } from "../../../../ui/icons/ActionIcons"; import GreenOutlineButton from "../../../../ui/buttons/GreenOutlineButton"; import { UPDATE_RECEIVED_ANNOUNCEMENT } from "../../../../gql/receivedAnnouncementRequests"; @@ -31,7 +35,7 @@ export default function AnnouncementsExpandedView({ const handleUpdatePin = async (pin: boolean) => { if (pid === -1) { - setError("Unable to update pin status, something went wrong.") + setError("Unable to update pin status, something went wrong."); } try { @@ -47,10 +51,10 @@ export default function AnnouncementsExpandedView({ setError(err.message); } }; - + const handleMarkAsUnread = async () => { if (pid === -1) { - setError("Unable to update read status, something went wrong.") + setError("Unable to update read status, something went wrong."); } try { @@ -64,7 +68,7 @@ export default function AnnouncementsExpandedView({ }); setRead(false); } - onBack(); + onBack(); } catch (err: any) { setError(err.message); } @@ -72,9 +76,9 @@ export default function AnnouncementsExpandedView({ const handleGoBack = async () => { if (pid === -1) { - setError("Unable to mark message as read, something went wrong.") + setError("Unable to mark message as read, something went wrong."); } - + try { if (!read) { await updatePinRead({ @@ -86,31 +90,37 @@ export default function AnnouncementsExpandedView({ }); setRead(true); } - onBack(); + onBack(); } catch (err: any) { setError(err.message); } - } + }; const details: Announcement | undefined = announcement.announcement; - if (!details || (details && (!details.topic || !details.date || !details.message || !details.priority))) { - return + if ( + !details || + (details && + (!details.topic || + !details.date || + !details.message || + !details.priority)) + ) { + return ( + + ); } return ( <> - + {toTitleCase(details.topic)} - + - + {formatDateV3(new Date(details.date))} @@ -118,7 +128,7 @@ export default function AnnouncementsExpandedView({ {details.priority !== Priority.NORMAL && ( - + Priority @@ -127,7 +137,7 @@ export default function AnnouncementsExpandedView({ {pinned && ( - + Pinned @@ -135,12 +145,14 @@ export default function AnnouncementsExpandedView({ - + - {details.message} + + {details.message} + {error && ( - + this is an error message )} @@ -156,9 +168,15 @@ export default function AnnouncementsExpandedView({ label={pinned ? "Unpin" : "Pin"} action={() => handleUpdatePin(!pinned)} is_active={false} - icon={pinned ? : } + icon={ + pinned ? ( + + ) : ( + + ) + } /> ); -} \ No newline at end of file +} diff --git a/frontend/src/participant/pages/announcements/components/ParticipantAnnouncementCard.tsx b/frontend/src/participant/pages/announcements/components/ParticipantAnnouncementCard.tsx index b374ae85..a46af20f 100644 --- a/frontend/src/participant/pages/announcements/components/ParticipantAnnouncementCard.tsx +++ b/frontend/src/participant/pages/announcements/components/ParticipantAnnouncementCard.tsx @@ -11,53 +11,62 @@ type ParticipantAnnouncementCardProps = { announcement: ReceivedAnnouncement; }; -export default function ParticipantAnnouncementCard({ announcement }: ParticipantAnnouncementCardProps) { +export default function ParticipantAnnouncementCard({ + announcement, +}: ParticipantAnnouncementCardProps) { const details: Announcement | undefined = announcement.announcement; - if (!details || (details && (!details.topic || !details.date || !details.message || !details.priority))) { + if ( + !details || + (details && + (!details.topic || + !details.date || + !details.message || + !details.priority)) + ) { return ( - + Unable to load announcement details. - ) + ); } return ( - {!announcement.read && } - {toTitleCase(details.topic)} - + {!announcement.read && } + {toTitleCase(details.topic)} + {formatDateV3(new Date(details.date))} - {(details.priority !== Priority.NORMAL) && ( - + {details.priority !== Priority.NORMAL && ( + )} - {announcement.pinned && } + {announcement.pinned && } ); -} \ No newline at end of file +} diff --git a/frontend/src/participant/pages/home/Main.tsx b/frontend/src/participant/pages/home/Main.tsx index 4edbcddd..bdb01ea1 100644 --- a/frontend/src/participant/pages/home/Main.tsx +++ b/frontend/src/participant/pages/home/Main.tsx @@ -10,20 +10,23 @@ import AnnouncementWidget from "./components/AnnouncementWidget"; export default function ParticipantsHomePage() { const { pid } = useContext(ParticipantContext); - if (pid === -1) return ; + if (pid === -1) + return ( + + ); return ( <> - - + + Welcome to Marillac Place - + {formatDateV8(new Date())} - - + + diff --git a/frontend/src/participant/pages/home/components/AnnouncementWidget.tsx b/frontend/src/participant/pages/home/components/AnnouncementWidget.tsx index 95cc40d4..9ffaccc5 100644 --- a/frontend/src/participant/pages/home/components/AnnouncementWidget.tsx +++ b/frontend/src/participant/pages/home/components/AnnouncementWidget.tsx @@ -12,7 +12,7 @@ import { ReceivedAnnouncement } from "../../../../types/models"; type AnnouncementWidgetProps = { pid: number; -} +}; export default function AnnouncementWidget({ pid }: AnnouncementWidgetProps) { const navigate = useNavigate(); @@ -28,29 +28,45 @@ export default function AnnouncementWidget({ pid }: AnnouncementWidgetProps) { const announcements = announcementData?.getReceivedAnnouncements ?? []; return ( - - 0 ? "8px" : "0px"}> - Recent Announcements + 0 ? "8px" : "0px"} + > + Recent Announcements navigate(PARTICIPANTS_ANNOUNCEMENTS_PAGE)} /> - {announcements.slice(0, 5).map((announcement: ReceivedAnnouncement, index: number) => ( - - {index > 0 && } - {announcement.announcement?.message} - {formatDateV3(new Date(announcement.announcement?.date ?? ""))} - - ))} + {announcements + .slice(0, 5) + .map((announcement: ReceivedAnnouncement, index: number) => ( + + {index > 0 && ( + + )} + + {announcement.announcement?.message} + + + {formatDateV3(new Date(announcement.announcement?.date ?? ""))} + + + ))} ); } diff --git a/frontend/src/participant/pages/home/components/BadgeRow.tsx b/frontend/src/participant/pages/home/components/BadgeRow.tsx deleted file mode 100644 index b4ec878c..00000000 --- a/frontend/src/participant/pages/home/components/BadgeRow.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React from "react"; -import { Box, Flex, Text, Button } from "@chakra-ui/react"; -import { Level, Icon } from "../../../../types/enums"; -import Badge from "../../../../ui/misc/BadgeProgress"; - -interface BadgeRowProps { - messageText: string; - title: string; - subtitle: string; - badge: { - icon: Icon; - level: Level; - percentageComplete?: number; - }; - isLast?: boolean; - showButton?: boolean; - onProgressClick?: () => void; -} - -const BadgeRow: React.FC = ({ - messageText, - title, - subtitle, - badge, - isLast = false, - showButton = false, - onProgressClick, -}) => { - return ( - - {/* Message text with button */} - - - {messageText} - - {showButton && onProgressClick && ( - - )} - - - {/* Badge and content row */} - - - - - {title} - - - {subtitle} - - - - - ); -}; - -export default BadgeRow; diff --git a/frontend/src/participant/pages/home/components/NewAchievedBadgesWidget.tsx b/frontend/src/participant/pages/home/components/NewAchievedBadgesWidget.tsx index 0b20bef7..4e0d5bd5 100644 --- a/frontend/src/participant/pages/home/components/NewAchievedBadgesWidget.tsx +++ b/frontend/src/participant/pages/home/components/NewAchievedBadgesWidget.tsx @@ -4,43 +4,68 @@ import { useMutation } from "@apollo/client"; import { Divider, Flex, Text } from "@chakra-ui/react"; import WidgetContainer from "../../../../ui/containers/WidgetContainer"; import { FETCH_NEW_ACHIEVED_BADGE_LEVELS } from "../../../../gql/achievedBadgeLevelRequests"; -import { AchievedBadgeLevel, EarnedCustomBadge } from "../../../../types/models"; -import BadgeLevelRow from "../../../../ui/misc/BadgeLevelRow"; +import { + AchievedBadgeLevel, + EarnedCustomBadge, +} from "../../../../types/models"; +import BadgeLevelRow from "../../../../ui/badge/BadgeLevelRow"; import { PARTICIPANTS_PROGRESS_PAGE } from "../../../../constants/routes"; import UnderlineButton from "../../../../ui/buttons/UnderlineButton"; import { FETCH_NEW_EARNED_CUSTOM_BADGES } from "../../../../gql/earnedCustomBadgeRequests"; -import CustomBadgeRow from "../../../../ui/misc/CustomBadgeRow"; +import CustomBadgeRow from "../../../../ui/badge/CustomBadgeRow"; type NewAchievedBadgesWidgetProps = { pid: number; -} +}; -export default function NewAchievedBadgesWidget({ pid }: NewAchievedBadgesWidgetProps) { +export default function NewAchievedBadgesWidget({ + pid, +}: NewAchievedBadgesWidgetProps) { const navigate = useNavigate(); - const [fetchNewEarnedCustomBadges, { data: earnedCustomBadgeData, loading: earnedCustomBadgeLoading, error: earnedCustomBadgeError}] = useMutation(FETCH_NEW_EARNED_CUSTOM_BADGES); - const [fetchNewAchievedBadgeLevels, { data: achievedBadgeData, loading: achievedBadgeLoading, error: achievedBadgeError}] = useMutation(FETCH_NEW_ACHIEVED_BADGE_LEVELS); + const [ + fetchNewEarnedCustomBadges, + { + data: earnedCustomBadgeData, + loading: earnedCustomBadgeLoading, + error: earnedCustomBadgeError, + }, + ] = useMutation(FETCH_NEW_EARNED_CUSTOM_BADGES); + const [ + fetchNewAchievedBadgeLevels, + { + data: achievedBadgeData, + loading: achievedBadgeLoading, + error: achievedBadgeError, + }, + ] = useMutation(FETCH_NEW_ACHIEVED_BADGE_LEVELS); useEffect(() => { fetchNewEarnedCustomBadges({ variables: { pid } }); fetchNewAchievedBadgeLevels({ variables: { pid } }); }, [pid]); - - const newEarnedCustomBadges = earnedCustomBadgeData?.fetchNewEarnedCustomBadges || []; - const newAchievedBadges = achievedBadgeData?.fetchNewAchievedBadgeLevels || []; - if (newAchievedBadges.length + newEarnedCustomBadges.length === 0) return null; + const newEarnedCustomBadges = + earnedCustomBadgeData?.fetchNewEarnedCustomBadges || []; + const newAchievedBadges = + achievedBadgeData?.fetchNewAchievedBadgeLevels || []; + if (newAchievedBadges.length + newEarnedCustomBadges.length === 0) + return null; return ( - - - New Badges Achieved! + + New Badges Achieved! navigate(PARTICIPANTS_PROGRESS_PAGE)} @@ -49,10 +74,12 @@ export default function NewAchievedBadgesWidget({ pid }: NewAchievedBadgesWidget {newEarnedCustomBadges.map((badge: EarnedCustomBadge, index: number) => ( ))} - {newEarnedCustomBadges.length > 0 && newAchievedBadges.length > 0 && } + {newEarnedCustomBadges.length > 0 && newAchievedBadges.length > 0 && ( + + )} {newAchievedBadges.map((badge: AchievedBadgeLevel, index: number) => ( ))} ); -} \ No newline at end of file +} diff --git a/frontend/src/participant/pages/home/components/TasksCompletedWidget.tsx b/frontend/src/participant/pages/home/components/TasksCompletedWidget.tsx index a017ad4c..a7886535 100644 --- a/frontend/src/participant/pages/home/components/TasksCompletedWidget.tsx +++ b/frontend/src/participant/pages/home/components/TasksCompletedWidget.tsx @@ -10,36 +10,47 @@ import { HAS_COMPLETED_ALL_REQUIRED_TASKS } from "../../../../gql/assignedTaskRe type TasksCompletedWidgetProps = { pid: number; -} +}; -export default function TasksCompletedWidget({ pid }: TasksCompletedWidgetProps) { +export default function TasksCompletedWidget({ + pid, +}: TasksCompletedWidgetProps) { const navigate = useNavigate(); - const { data: completedAllTasksData, loading: completedAllTasksLoading, error: completedAllTasksError} = useQuery(HAS_COMPLETED_ALL_REQUIRED_TASKS, { + const { + data: completedAllTasksData, + loading: completedAllTasksLoading, + error: completedAllTasksError, + } = useQuery(HAS_COMPLETED_ALL_REQUIRED_TASKS, { variables: { pid }, }); - const hasCompletedAllTasks = completedAllTasksData?.hasCompletedAllRequiredTasks || false; - if (hasCompletedAllTasks) return null; + const hasCompletedAllTasks = + completedAllTasksData?.hasCompletedAllRequiredTasks || false; + if (!hasCompletedAllTasks) return null; return ( - - - Mandatory Tasks Completed! + + Mandatory Tasks Completed! navigate(PARTICIPANTS_SCHEDULE_PAGE)} /> - - - + + + You've completed all your mandatory tasks for the week. diff --git a/frontend/src/participant/pages/home/components/TodoListWidget.tsx b/frontend/src/participant/pages/home/components/TodoListWidget.tsx index 5feed259..d634ea9d 100644 --- a/frontend/src/participant/pages/home/components/TodoListWidget.tsx +++ b/frontend/src/participant/pages/home/components/TodoListWidget.tsx @@ -14,58 +14,81 @@ import { Comment } from "../../../../ui/icons/ActionIcons"; import UnderlineButton from "../../../../ui/buttons/UnderlineButton"; import { ScheduleView } from "../../../../constants/views"; import { AssignedTask } from "../../../../types/models"; -import { Assigned, Excused, Complete, Incomplete } from "../../../../ui/icons/StatusIcons"; +import { + Assigned, + Excused, + Complete, + Incomplete, +} from "../../../../ui/icons/StatusIcons"; type TodoListWidgetProps = { pid: number; -} +}; const TodoListWidget = ({ pid }: TodoListWidgetProps) => { const navigate = useNavigate(); - const { data, loading, error } = useQuery( - GET_ASSIGNED_TASKS_FOR_TODAY, { - variables: { pid }, - } - ); + const { data, loading, error } = useQuery(GET_ASSIGNED_TASKS_FOR_TODAY, { + variables: { pid }, + }); const tasks = data?.getAssignedTasksForToday ?? []; return ( - - 0 ? "8px" : "0px"}> - Todo List + 0 ? "8px" : "0px"} + > + Todo List navigate(PARTICIPANTS_SCHEDULE_PAGE, { state: { view: ScheduleView.CALENDAR } })} + action={() => + navigate(PARTICIPANTS_SCHEDULE_PAGE, { + state: { view: ScheduleView.CALENDAR }, + }) + } /> {tasks.map((task: AssignedTask, index: number) => { - const noSpecificTime = ( - isEqual(startOfDay(new Date(task.start_date)), new Date(task.start_date)) && - isEqual(endOfDay(new Date(task.end_date)), new Date(task.end_date)) - ); + const noSpecificTime = + isEqual( + startOfDay(new Date(task.start_date)), + new Date(task.start_date) + ) && + isEqual(endOfDay(new Date(task.end_date)), new Date(task.end_date)); return ( - + {task.status === TaskStatus.ASSIGNED && } {task.status === TaskStatus.COMPLETE && } {task.status === TaskStatus.EXCUSED && } {task.status === TaskStatus.INCOMPLETE && } - {task.name} + {task.name} {task.comment && } - {noSpecificTime ? "Anytime" : formatDateV2(new Date(task.start_date)) + " - " + formatDateV2(new Date(task.end_date))} + + {noSpecificTime + ? "Anytime" + : formatDateV2(new Date(task.start_date)) + + " - " + + formatDateV2(new Date(task.end_date))} + - ) + ); })} diff --git a/frontend/src/participant/pages/login/Main.tsx b/frontend/src/participant/pages/login/Main.tsx index 381ef132..9cd0f4d2 100644 --- a/frontend/src/participant/pages/login/Main.tsx +++ b/frontend/src/participant/pages/login/Main.tsx @@ -69,7 +69,7 @@ export default function ParticipantsLoginPage() { h="100vh" alignItems="flex-start" justifyContent="center" - bg="primary.100" + bg="brand.primaryLight" > - Sign in - - Please enter your login information. - + Sign in + Please enter your login information. @@ -112,12 +110,17 @@ export default function ParticipantsLoginPage() { {error && ( - + {error} )} - + + diff --git a/frontend/src/participant/pages/progress/components/BadgeDisplay.tsx b/frontend/src/participant/pages/progress/components/BadgeDisplay.tsx index da06f769..3908a881 100644 --- a/frontend/src/participant/pages/progress/components/BadgeDisplay.tsx +++ b/frontend/src/participant/pages/progress/components/BadgeDisplay.tsx @@ -1,5 +1,5 @@ import React, { useState, useContext } from "react"; -import { Divider, HStack, Text } from "@chakra-ui/react"; +import { Divider, Flex, HStack, Text } from "@chakra-ui/react"; import { useQuery } from "@apollo/client"; import WidgetContainer from "../../../../ui/containers/WidgetContainer"; import { toTitleCase } from "../../../../helpers/stringUtils"; @@ -8,10 +8,14 @@ import ErrorScreen from "../../../../ui/screens/ErrorScreen"; import LoadingScreen from "../../../../ui/screens/LoadingScreen"; import { GET_BADGE_LEVEL_PROGRESS } from "../../../../gql/badgeLevelProgressRequests"; import { GET_ACHIEVED_BADGE_LEVELS } from "../../../../gql/achievedBadgeLevelRequests"; -import BadgeLevelRow from "../../../../ui/misc/BadgeLevelRow"; -import { AchievedBadgeLevel, BadgeLevelProgress, EarnedCustomBadge } from "../../../../types/models"; +import BadgeLevelRow from "../../../../ui/badge/BadgeLevelRow"; +import { + AchievedBadgeLevel, + BadgeLevelProgress, + EarnedCustomBadge, +} from "../../../../types/models"; import { GET_EARNED_CUSTOM_BADGES } from "../../../../gql/earnedCustomBadgeRequests"; -import CustomBadgeRow from "../../../../ui/misc/CustomBadgeRow"; +import CustomBadgeRow from "../../../../ui/badge/CustomBadgeRow"; const BadgeDisplay = () => { const { pid } = useContext(ParticipantContext); @@ -45,7 +49,9 @@ const BadgeDisplay = () => { }); if (pid === -1 || errorProgress || errorAchieved || errorEarnedCustomBadge) { - return ; + return ( + + ); } if (loadingProgress || loadingAchieved || loadingEarnedCustomBadge) { @@ -53,47 +59,69 @@ const BadgeDisplay = () => { } return ( - - + + {(["badges", "achieved"] as const).map((tab) => ( setActiveTab(tab)} transition="all 0.2s ease" > {toTitleCase(tab)} ))} - - {activeTab === "badges" ? progressData?.getBadgeLevelProgress?.map((badge: BadgeLevelProgress, index: number) => ( - - )) : ( + + {activeTab === "badges" ? ( + progressData?.getBadgeLevelProgress?.map( + (badge: BadgeLevelProgress, index: number) => ( + + ) + ) + ) : ( <> - {achievedData?.getAchievedBadgeLevels?.map((badge: AchievedBadgeLevel, index: number) => ( - - ))} - { achievedData?.getAchievedBadgeLevels?.length > 0 && - earnedCustomBadgeData?.getEarnedCustomBadges?.length > 0 && - - } - {earnedCustomBadgeData?.getEarnedCustomBadges?.map((badge: EarnedCustomBadge, index: number) => ( - - ))} + {achievedData?.getAchievedBadgeLevels?.map( + (badge: AchievedBadgeLevel, index: number) => ( + + ) + )} + {achievedData?.getAchievedBadgeLevels?.length > 0 && + earnedCustomBadgeData?.getEarnedCustomBadges?.length > 0 && ( + + )} + {earnedCustomBadgeData?.getEarnedCustomBadges?.map( + (badge: EarnedCustomBadge, index: number) => ( + + ) + )} )} ); }; -export default BadgeDisplay; \ No newline at end of file +export default BadgeDisplay; diff --git a/frontend/src/participant/pages/progress/components/BucksGoalCard.tsx b/frontend/src/participant/pages/progress/components/BucksGoalCard.tsx index fae5c299..7576d710 100644 --- a/frontend/src/participant/pages/progress/components/BucksGoalCard.tsx +++ b/frontend/src/participant/pages/progress/components/BucksGoalCard.tsx @@ -14,7 +14,10 @@ import { useMutation, useQuery } from "@apollo/client"; import WidgetContainer from "../../../../ui/containers/WidgetContainer"; import UnderlineButton from "../../../../ui/buttons/UnderlineButton"; import { ParticipantContext } from "../../../ParticipantContext"; -import { CREATE_EARNING_GOAL, GET_EARNING_GOAL } from "../../../../gql/earningGoalRequests"; +import { + CREATE_EARNING_GOAL, + GET_EARNING_GOAL, +} from "../../../../gql/earningGoalRequests"; import ErrorScreen from "../../../../ui/screens/ErrorScreen"; import LoadingScreen from "../../../../ui/screens/LoadingScreen"; import { GoalAction } from "../../../../types/enums"; @@ -26,7 +29,8 @@ import { SetGoal } from "./SetGoal"; export default function BucksGoalCard() { const { pid, totalEarnings } = useContext(ParticipantContext); - const [createGoal, { loading: createGoalLoading, error: createGoalError }] = useMutation(CREATE_EARNING_GOAL); + const [createGoal, { loading: createGoalLoading, error: createGoalError }] = + useMutation(CREATE_EARNING_GOAL); const { data, loading, error, refetch } = useQuery(GET_EARNING_GOAL, { variables: { pid }, }); @@ -49,7 +53,7 @@ export default function BucksGoalCard() { const [editGoal, setEditGoal] = useState(null); const [setGoal, setSetGoal] = useState(false); - + const goal = data?.getEarningGoal ?? null; const metGoal = goal?.action === GoalAction.REACHED; const percentComplete = (totalEarnings / goal?.value) * 100; @@ -69,7 +73,9 @@ export default function BucksGoalCard() { }; if (pid === -1 || error || createGoalError) { - return ; + return ( + + ); } if (loading || createGoalLoading) { @@ -78,29 +84,50 @@ export default function BucksGoalCard() { return ( <> - {editGoal && setEditGoal(null)} refetchGoal={refetch} currentGoal={editGoal} />} - {setGoal && setSetGoal(false)} refetchGoal={refetch} prevGoal={goal?.value ?? 0} />} + {editGoal && ( + setEditGoal(null)} + refetchGoal={refetch} + currentGoal={editGoal} + /> + )} + {setGoal && ( + setSetGoal(false)} + refetchGoal={refetch} + prevGoal={goal?.value ?? 0} + /> + )} - - Marillac Bucks Goal + + Marillac Bucks Goal editGoalAction()} /> {!goal ? ( - Set a new goal to track your progress! + Set a new goal to track your progress! ) : !metGoal ? ( - = 15 && percentComplete <= 85 ? "8px" : "0px"}> + = 15 && percentComplete <= 85 ? "8px" : "0px"} + > - + $0 - {(percentComplete >= 15 && percentComplete <= 85) && ( + {percentComplete >= 15 && percentComplete <= 85 && ( - ${totalEarnings} + ${totalEarnings} )} - + ${goal.value} @@ -136,7 +163,7 @@ export default function BucksGoalCard() { ) : ( - + Congratulations on completing your goal! Make sure to tell Marillac staff about your achievement. diff --git a/frontend/src/participant/pages/progress/components/EditGoal.tsx b/frontend/src/participant/pages/progress/components/EditGoal.tsx index 0ec81736..1406998c 100644 --- a/frontend/src/participant/pages/progress/components/EditGoal.tsx +++ b/frontend/src/participant/pages/progress/components/EditGoal.tsx @@ -67,15 +67,17 @@ export const EditGoal: React.FC = ({ loading={loading} > - Previous goal: + Previous Goal: - {currentGoal.value} + + {currentGoal.value} + - New goal: + New Goal: = ({ handleClose, refetchGoal, prevGoal }) => { +export const SetGoal: React.FC = ({ + handleClose, + refetchGoal, + prevGoal, +}) => { const [goal, setGoal] = useState(null); const [error, setError] = useState(""); const { pid } = useContext(ParticipantContext); @@ -62,7 +66,7 @@ export const SetGoal: React.FC = ({ handleClose, refetchGoal, prev loading={loading} > - New goal: + New Goal: { const today = new Date(); const dayOfWeek = today.getDay(); - const [selectedDay, setSelectedDay] = useState(dayOfWeek); const chartData: ChartDataItem[] = useMemo(() => { return weeklyEarnings.map((earnings, index) => ({ amount: earnings, - day: DAY_ABBREVIATIONS[DAYS[index]] + day: DAY_ABBREVIATIONS[DAYS[index]], })); }, [weeklyEarnings, dayOfWeek]); const maxEarnings = Math.max(...weeklyEarnings); - const upperBound = Math.ceil(maxEarnings * 1.2) || 10; + const upperBound = Math.ceil(maxEarnings * 1.2); if (pid === -1) { - return ; + return ( + + ); } return ( - Weekly Earnings + + Weekly Earnings + { tickLine={false} tickCount={4} tickMargin={8} - tick={{ fill: colors.text.light.secondary, fontSize: 12 }} + tick={{ fill: colors.text.medium, fontSize: 12 }} tickFormatter={(value) => `$${value}`} width={35} fontFamily="Nunito" /> - + {chartData.map((entry, index) => ( ))} - { - const { x, y, width, index, value } = props; - console.log(props); - if (index !== selectedDay) return null; - return ( - - ${value} - - ); - }} + { + const { x, y, width, index, value } = props; + console.log(props); + if (index !== dayOfWeek) return null; + return ( + + ${value} + + ); + }} /> diff --git a/frontend/src/participant/pages/schedule/Main.tsx b/frontend/src/participant/pages/schedule/Main.tsx index cf085223..51528984 100644 --- a/frontend/src/participant/pages/schedule/Main.tsx +++ b/frontend/src/participant/pages/schedule/Main.tsx @@ -1,20 +1,36 @@ import { Button, Divider, Flex, HStack, Text } from "@chakra-ui/react"; import { useQuery } from "@apollo/client"; import React, { useContext, useEffect, useState } from "react"; -import { endOfDay, isEqual, isSameDay, startOfDay, startOfWeek } from "date-fns"; +import { + endOfDay, + isEqual, + isSameDay, + startOfDay, + startOfWeek, +} from "date-fns"; import { useLocation } from "react-router-dom"; import { ParticipantContext } from "../../ParticipantContext"; import { GET_ASSIGNED_TASKS_BY_WEEK } from "../../../gql/assignedTaskRequests"; import ErrorScreen from "../../../ui/screens/ErrorScreen"; import { DisplayView, ScheduleView } from "../../../constants/views"; -import { formatDateV1, formatDateV2, formatDateV6, formatDateV7 } from "../../../helpers/formatDateTime"; +import { + formatDateV1, + formatDateV2, + formatDateV6, + formatDateV7, +} from "../../../helpers/formatDateTime"; import MarillacPlaceCalendar from "../../../ui/misc/MarillacPlaceCalendar"; import LoadingScreen from "../../../ui/screens/LoadingScreen"; import { Calendar, List } from "../../../ui/icons/MiscIcons"; import { AssignedTask } from "../../../types/models"; import TaskDetailsModal from "./components/TaskDetailsModal"; import { TaskStatus } from "../../../types/enums"; -import { Assigned, Complete, Excused, Incomplete } from "../../../ui/icons/StatusIcons"; +import { + Assigned, + Complete, + Excused, + Incomplete, +} from "../../../ui/icons/StatusIcons"; import { Comment } from "../../../ui/icons/ActionIcons"; export default function ParticipantsSchedulePage() { @@ -22,7 +38,9 @@ export default function ParticipantsSchedulePage() { const location = useLocation(); const { view } = location.state || { view: ScheduleView.LIST }; const [currentView, setCurrentView] = useState(view); - const [viewTaskDetails, setViewTaskDetails] = useState(null); + const [viewTaskDetails, setViewTaskDetails] = useState( + null + ); const { data, loading, error } = useQuery(GET_ASSIGNED_TASKS_BY_WEEK, { variables: { pid, weekStart: startOfWeek(new Date()).toISOString() }, @@ -33,16 +51,25 @@ export default function ParticipantsSchedulePage() { } if (error || pid === -1) { - return ; + return ( + + ); } const tasks = data?.getAssignedTasksByWeek ?? []; return ( <> - - - {currentView === ScheduleView.CALENDAR ? formatDateV1(new Date()) : "This Week"} + + + {currentView === ScheduleView.CALENDAR + ? formatDateV1(new Date()) + : "This Week"}