Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change input type for volunteer log popup; Work on rendering coluntee… #72

Merged
merged 13 commits into from
Dec 4, 2023
Merged
69 changes: 37 additions & 32 deletions components/Forms/LogHoursPopupWindowForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,63 @@ import {
LargeFormInput,
LogHoursForm,
} from '@/styles/components/Forms/logHoursPopupWindowForm.styles';
import React from 'react';
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import PopupWindow from '@/components/shared/PopupWindow';
import WindowFlow from '@/components/shared/WindowFlow';
import VolunteerSignedEvents from '../Volunteer/VolunteerSignedEvents';
import { QueriedVolunteerEventData } from 'bookem-shared/src/types/database';

const LogHoursPopupWindowForm = ({
setShowPopup,
successMessage,
errorMessage,
}: {
setShowPopup: React.Dispatch<React.SetStateAction<boolean>>;
successMessage: (message: string) => void;
errorMessage: (message: string) => void;
}) => {
// get functions from react hook form
const { register, handleSubmit } = useForm();

const [selectedEvent, setSelectedEvent] =
useState<QueriedVolunteerEventData>();

// TODO: combine event and program into one so users only need to select event
// that they signed up for (which is automatically under a program)
const pages = ['Event', 'Program', 'Numbers', 'Comments'];
const pages = ['Event', 'Numbers', 'Comments'];

// handle form submission by parsing data and calling createVolunteerLog
const onSubmit = (data: any) => {
const results = JSON.stringify({
// event: data.Event,
eventId: selectedEvent?._id,
hours: parseInt(data.NumberOfHours),
date: data.DateOfVisit,
feedback: data.Comment,
numBooks: data.NumberOfBooks,
});
console.log(results);
createVolunteerLog(results);
};

// sends a post request to insert the volunteer form
const createVolunteerLog = async (data: any) => {
// TODO: implement endpoint for VolunteerEventApplication and call it
await fetch('/api/volunteerLogs/create', {
method: 'POST',
body: data,
});
try {
// Implement endpoint for VolunteerEventApplication and call it
const response = await fetch('/api/volunteerLogs/create', {
method: 'POST',
body: data,
});
if (response.status === 200) {
const message = (await response.json()).message;
setShowPopup(false);
successMessage(message);
} else {
const message = (await response.json()).message;
errorMessage(message);
}
} catch (err) {
errorMessage('Sorry an error occurred');
}
Comment on lines +48 to +64
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we get the response from backend, we need to decide whether it's a success or a failure.

};

return (
Expand All @@ -52,29 +72,14 @@ const LogHoursPopupWindowForm = ({
components={[
// Page 1 - Event
<LogHoursForm key={pages[0]}>
TODO: show events volunteer signed up for
<VolunteerSignedEvents
selectedEvent={selectedEvent}
setSelectedEvent={setSelectedEvent}
/>
</LogHoursForm>,

// Page 2 - Program
// TODO: remove this
// Page 2 - Numbers
<LogHoursForm key={pages[1]}>
<FormHeader>Please select one program</FormHeader>
<FormLabel>
<input {...register('Program')} type="radio" value="RIF" />
Reading is Fundamental (RIF)
</FormLabel>
<FormLabel>
<input {...register('Program')} type="radio" value="RFR" />
Ready for Reading (RFR)
</FormLabel>
<FormLabel>
<input {...register('Program')} type="radio" value="BFNK" />
Books for Nashville Kids (BFNK)
</FormLabel>
</LogHoursForm>,

// Page 3 - Numbers
<LogHoursForm key={pages[2]}>
<FormLabel>
{/* TODO: Add an icon here to display tool tip */}
Please log your volunteer hours <br /> (rounded to the nearest
Expand All @@ -90,7 +95,7 @@ const LogHoursPopupWindowForm = ({
<FormLabel>Date of visit</FormLabel>
<FormInput
{...register('DateOfVisit')}
type="text"
type="date"
placeholder="MM/DD/YYYY"
pattern="^((0|1)\d{1})\/((0|1|2)\d{1})\/((19|20)\d{2})"
title="Input must be in MM/DD/YYYY format"
Expand All @@ -107,8 +112,8 @@ const LogHoursPopupWindowForm = ({
/>
</LogHoursForm>,

// Page 4 - Comments
<LogHoursForm key={pages[3]}>
// Page 3 - Comments
<LogHoursForm key={pages[2]}>
<FormLabel>Anything else you&apos;d like to share?</FormLabel>
<LargeFormInput
{...register('Comment')}
Expand Down
29 changes: 28 additions & 1 deletion components/Volunteer/VolunteerDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Media } from '@/lib/media';
import { BOOKEM_THEME } from '@/utils/constants';
import { QueriedUserData } from 'bookem-shared/src/types/database';
import { formatDate } from '@/utils/utils';
import { message } from 'antd';

const VolunteerDashboard = ({
userData,
Expand All @@ -29,12 +30,38 @@ const VolunteerDashboard = ({
}) => {
// set pop up window to false
const [showPopup, setShowPopup] = useState(false);
const [messageApi, contextHolder] = message.useMessage();

// Display success message
const successMessage = (message: string) => {
messageApi.open({
type: 'success',
content: message,
});
};

// Display error message
const errorMessage = (message: string) => {
messageApi.open({
type: 'error',
content: message,
});
};
Comment on lines +35 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use antd message here to display success/error message in frontend.


return (
<>
<DashboardContainer>
{/* Context for antd messages */}
{contextHolder}

{/* based on whether or not hideppopup is true, displays popup */}
{showPopup && <LogHoursPopupWindowForm setShowPopup={setShowPopup} />}
{showPopup && (
<LogHoursPopupWindowForm
successMessage={successMessage}
errorMessage={errorMessage}
setShowPopup={setShowPopup}
/>
)}

<Greeting>Volunteer</Greeting>

Expand Down
55 changes: 55 additions & 0 deletions components/Volunteer/VolunteerSignedEvents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { useEffect, useState } from 'react';
import { QueriedVolunteerEventData } from 'bookem-shared/src/types/database';
import { fetchData } from '@/utils/utils';
import { MainContainer } from '@/styles/volunteerHistory.styles';
import SelectableLongEventCard from '../shared/SelectableLongEventCard';

/**
* format horizontal upcoming event scroll bar on home page
*/
const VolunteerSignedEvents = ({
selectedEvent,
setSelectedEvent,
}: {
selectedEvent: QueriedVolunteerEventData | undefined;
setSelectedEvent: React.Dispatch<
React.SetStateAction<QueriedVolunteerEventData | undefined>
>;
}) => {
const [events, setEvents] = useState<QueriedVolunteerEventData[]>();
const [error, setError] = useState<Error>();

const handleEventClick = (event: QueriedVolunteerEventData) => {
// Update the selected event when an event is clicked
setSelectedEvent(event);
};

// Fetch upcoming events when rendered
useEffect(() => {
fetchData('/api/events/log-hour')
.then(data => setEvents(data))
.catch(err => setError(err));
}, []);
return (
<>
{/* TODO: render 404 page */}
{error && <>404 Event not found!</>}
{!events && !error && <div>Loading...</div>}
{events && (
<MainContainer>
{/* Loop through each VolunteerEvent specific to that user */}
{events.map(event => (
<SelectableLongEventCard
eventData={event}
key={event._id.toString()}
isSelected={selectedEvent === event}
onClick={() => handleEventClick(event)}
/>
))}
</MainContainer>
)}
</>
);
};

export default VolunteerSignedEvents;
17 changes: 1 addition & 16 deletions components/shared/LongEventCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,7 @@ import {
} from '@/styles/components/longEventCard.styles';
import { QueriedVolunteerEventData } from 'bookem-shared/src/types/database';
import { convertLocationToString } from 'bookem-shared/src/utils/utils';

/**
* Helper function to format the time into a readable AM/PM format.
*
* Takes in an unformatted time and returns a formatted one.
*/
const formatAMPM = (date: { getHours: () => any; getMinutes: () => any }) => {
var hours = date.getHours();
var minutes = date.getMinutes();
var ampm = hours >= 12 ? 'PM' : 'AM';
hours = hours % 12;
hours = hours ? hours : 12; // the hour '0' should be '12'
minutes = minutes < 10 ? '0' + minutes : minutes;
var strTime = hours + ':' + minutes + ' ' + ampm;
return strTime;
};
import { formatAMPM } from '@/utils/utils';

// this component takes in and displays all of an event's data
const LongEventCard = ({
Expand Down
103 changes: 103 additions & 0 deletions components/shared/SelectableLongEventCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React from 'react';
import Image from 'next/image';
import {
EventImage,
Name,
AddressContainer,
AddressIcon,
InfoContainer,
Description,
ClockIcon,
Address,
CalendarIcon,
CheckmarkIcon,
Container,
} from '@/styles/components/longEventCard.styles';
import { QueriedVolunteerEventData } from 'bookem-shared/src/types/database';
import { convertLocationToString } from 'bookem-shared/src/utils/utils';
import { formatAMPM } from '@/utils/utils';

// this component takes in and displays all of an event's data
const SelectableLongEventCard = ({
eventData,
isSelected,
onClick,
}: {
eventData: QueriedVolunteerEventData;
isSelected: boolean;
onClick: () => void;
}) => {
// create a date object with JavaScript's Date constructor
const date = new Date(eventData.startDate);

return (
<Container
style={{
// Add styles for selected event
border: isSelected ? '2px solid blue' : '1px solid #ddd',
padding: '10px',
cursor: 'pointer',
}}
onClick={onClick}>
<EventImage>
<Image
src="/eventCard/event-image.png"
alt="Event image icon"
width={`${Math.round((300 / 328) * 53)}`}
height={`${Math.round((300 / 328) * 53)}`}
/>
</EventImage>
<Name>{eventData.name}</Name>

<AddressContainer>
<AddressIcon>
<Image
src="/eventCard/map.png"
alt="Map icon"
width={`${Math.round((300 / 328) * 21)}`}
height={`${Math.round((300 / 328) * 23.99)}`}
/>
</AddressIcon>
<Address>{convertLocationToString(eventData.location)}</Address>
</AddressContainer>

<InfoContainer>
<CalendarIcon>
<Image
src="/event/calendar.png"
alt="Calendar icon"
width={`${Math.round((300 / 328) * 21)}`}
height={`${Math.round((300 / 328) * 23.99)}`}
/>
{/* calls a JavaScript method to format the date into a readable format */}
<Description>{date.toDateString()}</Description>
</CalendarIcon>

<ClockIcon>
<Image
src="/event/clock.png"
alt="Clock icon"
width={`${Math.round((300 / 328) * 21.27)}`}
height={`${Math.round((300 / 328) * 22.14)}`}
/>
<Description>{formatAMPM(date)}</Description>
</ClockIcon>

<CheckmarkIcon>
<Image
src="/eventCard/checkmark.png"
alt="Checkmark icon"
width={`${Math.round((300 / 328) * 21)}`}
height={`${Math.round((300 / 328) * 23.99)}`}
/>
<Description>
{/* TODO: add data for number of books distributed*/}X books
distributed
</Description>
</CheckmarkIcon>
</InfoContainer>
</Container>
);
};

export default SelectableLongEventCard;
1 change: 1 addition & 0 deletions pages/api/events/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default async function handler(
// get all volunteerEvents from collection that match the user's Id
// sorted in descending order
const user = await Users.findById(session.user._id);

if (!user) {
return res.status(404).json({ message: 'User not found' });
}
Expand Down
Loading
Loading