diff --git a/components/Logs/LogsDashboard.tsx b/components/Logs/LogsDashboard.tsx new file mode 100644 index 0000000..72c4964 --- /dev/null +++ b/components/Logs/LogsDashboard.tsx @@ -0,0 +1,60 @@ +import { DashboardContainer } from '@/styles/volunteerDashboard.styles'; +import { useEffect, useState } from 'react'; +import { + VolunteerLogData, + VolunteerEventData, +} from 'bookem-shared/src/types/database'; +import LogsTable from './LogsTable'; +import LogsStats from './LogsStats'; +import { Greeting, Header } from '@/styles/dashboard.styles'; +import { Card } from 'antd'; + +type VolunteerLogWithEvent = VolunteerLogData & { + event: VolunteerEventData; +}; + +export type LogTableData = VolunteerLogWithEvent & { + eventName: string; +}; + +// unpack the eventName from the event object so that it can be used as a column in the table +const processLogDataForTable = ( + logData: VolunteerLogWithEvent[] +): LogTableData[] => { + return logData.map(log => { + return { + ...log, + eventName: log.event.name, + }; + }); +}; + +export default function LogsDashboard() { + const [logData, setLogData] = useState([]); + + useEffect(() => { + const fetchLogs = async () => { + const response = await fetch('/api/volunteer-logs'); + const data: VolunteerLogWithEvent[] = await response.json(); + // console.log("Logs data: ", data); + setLogData(processLogDataForTable(data)); + }; + fetchLogs(); + }, []); + + console.log('Log data: ', logData); + + return ( + + Your Logs + + {/* Table of logs */} +
Log Details:
+ + + +
+ ); +} diff --git a/components/Logs/LogsStats.tsx b/components/Logs/LogsStats.tsx new file mode 100644 index 0000000..a171b7f --- /dev/null +++ b/components/Logs/LogsStats.tsx @@ -0,0 +1,65 @@ +import { LogTableData } from './LogsDashboard'; +import { VolunteerStatsContainer } from '@/styles/volunteerDashboard.styles'; +import { + Greeting, + StatsFlex, + FlexChild, + StatsNumber, + StatsDescription, + Header, +} from '@/styles/dashboard.styles'; +import { VolunteerLogStatus } from 'bookem-shared/src/types/database'; + +// only count the approved logs +const calcTotalHours = (logData: LogTableData[]) => { + let totalHours = 0; + logData.forEach(log => { + if (log.status === VolunteerLogStatus.Approved) { + totalHours += log.hours; + } + }); + return totalHours; +}; + +const calcTotalBooks = (logData: LogTableData[]) => { + let totalBooks = 0; + logData.forEach(log => { + if (log.status === VolunteerLogStatus.Approved) { + totalBooks += log.numBooks || 0; + } + }); + return totalBooks; +}; + +// only count approved logs of events and only count unique events +const calcNumEvents = (logData: LogTableData[]) => { + const eventSet = new Set(); + logData.forEach(log => { + if (log.status === VolunteerLogStatus.Approved) { + eventSet.add(log.eventName); + } + }); + return eventSet.size; +}; + +export default function LogsStats({ logData }) { + return ( + +
Your accomplishments at a glance:
+ + + {calcNumEvents(logData)} + Events helped + + + {calcTotalHours(logData)} + Hours volunteered + + + {calcTotalBooks(logData)} + Books distributed + + +
+ ); +} diff --git a/components/Logs/LogsTable.tsx b/components/Logs/LogsTable.tsx new file mode 100644 index 0000000..2f906d9 --- /dev/null +++ b/components/Logs/LogsTable.tsx @@ -0,0 +1,79 @@ +import { Table, Tag } from 'antd'; +import { LogTableData } from './LogsDashboard'; +import { VolunteerLogStatus } from 'bookem-shared/src/types/database'; +import Link from 'next/link'; +// use antd link icon +import { LinkOutlined } from '@ant-design/icons'; + +export default function LogsTable({ logData }: { logData: LogTableData[] }) { + const columns = [ + { + title: 'Event', + dataIndex: 'eventName', + key: 'event', + }, + // add a see event link + { + title: 'Event Details', + dataIndex: 'event', + key: 'seeEvent', + render: (event: any) => { + return ( + + {/* light blue color text */} +
+ See event +
+ + ); + }, + }, + + { + title: 'Atended on', + dataIndex: 'date', + key: 'date', + render: (date: string) => { + return new Date(date).toLocaleDateString(); + }, + }, + { + title: 'Log submitted on', + dataIndex: 'createdAt', + key: 'createdAt', + render: (createdAt: string) => { + return new Date(createdAt).toLocaleDateString(); + }, + }, + { + title: 'Hours', + dataIndex: 'hours', + key: 'hours', + }, + { + title: 'Books', + dataIndex: 'numBooks', + key: 'numBooks', + }, + { + title: 'Status', + dataIndex: 'status', + key: 'status', + // render the status with tags of different colors + render: (status: string) => { + let color = 'blue'; + if (status === VolunteerLogStatus.Approved) { + color = 'green'; + } else if (status === VolunteerLogStatus.Rejected) { + color = 'red'; + } + return {status}; + }, + }, + ]; + + return ( + // allow the table to scroll horizontally + + ); +} diff --git a/pages/api/volunteer-logs/index.ts b/pages/api/volunteer-logs/index.ts index 096457f..3fc066e 100644 --- a/pages/api/volunteer-logs/index.ts +++ b/pages/api/volunteer-logs/index.ts @@ -61,7 +61,7 @@ export default async function handler( // get all volunteerEvents from collection that match the user's Id const volunteerLogs = await VolunteerLogs.find({ userId: usersId, - }); + }).populate('event'); // return the result res.status(200).json(volunteerLogs); diff --git a/pages/logs.tsx b/pages/logs.tsx new file mode 100644 index 0000000..edf1dc9 --- /dev/null +++ b/pages/logs.tsx @@ -0,0 +1,8 @@ +import LogsDashboard from '@/components/Logs/LogsDashboard'; + +export default function Logs() { + return ; +} + +// perform automatic redirection to login page if user not logged in. +export { getServerSideProps } from '@/lib/getServerSideProps'; diff --git a/public/sidebar/logs-black.svg b/public/sidebar/logs-black.svg new file mode 100644 index 0000000..365ba8b --- /dev/null +++ b/public/sidebar/logs-black.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/sidebar/logs-white.svg b/public/sidebar/logs-white.svg new file mode 100644 index 0000000..d938414 --- /dev/null +++ b/public/sidebar/logs-white.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/styles/logs.style.tsx b/styles/logs.style.tsx new file mode 100644 index 0000000..26c888e --- /dev/null +++ b/styles/logs.style.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +/** + * Container for volunteer dashboard + */ +export const DashboardContainer = styled.div` + height: fit-content; + width: 100%; + padding: 40px; + position: relative; +`; diff --git a/utils/constants.ts b/utils/constants.ts index 73386ab..e73e160 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -42,6 +42,7 @@ export const AVAILABLE_ROUTES = { HOME: '/', VOLUNTEER: '/volunteer', SETTINGS: '/settings', + LOGS: '/logs', }; /** @@ -75,6 +76,14 @@ export const SIDEBAR_ICON_PARAMS: SidebarIconParams[] = [ linkTo: AVAILABLE_ROUTES.HOME, text: 'Home', }, + { + desktopDefaultSrc: '/sidebar/logs-white.svg', + mobileDefaultSrc: '/sidebar/logs-black.svg', + desktopHoveredSrc: '/sidebar/logs-black.svg', + mobileHoveredSrc: '/sidebar/logs-white.svg', + linkTo: '/logs', + text: 'Logs', + }, { desktopDefaultSrc: '/sidebar/volunteer-white.png', mobileDefaultSrc: '/sidebar/volunteer-black.png',