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

Create event modal #159

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
299 changes: 299 additions & 0 deletions src/components/calendar/CreateEventModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
import {
Box,
Button,
Checkbox,
CheckboxGroup,
Container,
FormLabel,
Input,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Stack,
Text,
Textarea,
VStack,
useToast
} from '@chakra-ui/react'
import { zodResolver } from '@hookform/resolvers/zod'
import { useContext, useState } from 'react'
import { useForm } from 'react-hook-form'
import type { z } from 'zod'
import { ModalContext } from '~/context/ModalContext'
import { trpc } from '~/utils/trpc'
import LoadingScreen from '../common/LoadingScreen'
import type { Attendees } from '../events/DataTable'
import { DataTable } from '../events/DataTable'
import createEventFormSchema from '../../utils/calendar/createEventFormSchema'

type useInFormProps = {
setAttendees: React.Dispatch<React.SetStateAction<string[]>>;
invalidAttendees: boolean;
register: any;
errors: any;
isSubmitting: boolean;
submitBefore: boolean;
};

const CreateEventModal = () => {
const modal = useContext(ModalContext)

if (!modal.id) {
console.log("modal id is undefined")
// return null
}

const toast = useToast()
const [attendees, setAttendees] = useState<string[]>([])
// hacky use for attendees validation
const [submitBefore, setSubmitBefore] = useState(false)
const FormSchema = createEventFormSchema()

type FormSchemaType = z.infer<typeof FormSchema>

// useForm for state management except attendees which belongs in DataTable (child)
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormSchemaType>({
resolver: zodResolver(FormSchema),
})

const { data } = trpc.attendance.getAllAttendanceButSelf.useQuery()
const { mutateAsync, isLoading: isSubmitting } =
trpc.event.createEvent.useMutation()

const invalidAttendees = attendees.length === 0
const useInForm = { setAttendees, invalidAttendees, register, errors, isSubmitting, submitBefore }

if (!data) return <LoadingScreen />

const formSubmit = async (formData: FormSchemaType) => {
try {
// Hacky soluton since attendees not linked to React-hook-form
if (invalidAttendees) {
throw new Error("At least one attendee must be selected")
}

await mutateAsync({
name: formData.eventName,
startDate: new Date(formData.startDate),
endDate: new Date(formData.endDate),
venue: formData.venue,
departments: formData.dept,
secretCode: formData.secretCode,
description: formData.description,
invitedAttendees: attendees,
})
toast({
duration: 3000,
status: 'success',
title: 'Success',
description: 'A new event has been successfully created',
})
modal.onClose()
} catch (e) {
toast({
description: (e as Error).message,
duration: 3000,
status: 'error',
title: 'Oops, an error occured!',
})
}
}

return (
<Modal
isCentered={true}
isOpen={modal.isOpen}
onClose={modal.onClose}
scrollBehavior="inside"
>
<ModalOverlay />
<ModalContent minW="1000px" borderRadius="1.2rem" overflowY={"scroll"}>
<ModalHeader className="bg-[#01003D] text-white">
<Header />
</ModalHeader>
<form onSubmit={handleSubmit(formSubmit)}>
<ModalBody className="text-xl" textColor="#01003D">
<Body attendeeData={data} useInForm={useInForm} />
</ModalBody>
<ModalFooter display="flex" justifyContent="space-between">
<Button
bgColor="#0C1747"
width={150}
className="mb-10 text-white"
type="submit"
onClick={() => modal.onClose()}
>
Back
</Button>



<Button
bgColor="#0C1747"
width={215}
className="mb-10 text-white"
type="submit"
onClick={() => setSubmitBefore(true)}
>
Submit
</Button>
</ModalFooter>
</form>
</ModalContent>
</Modal >
)
}

const Header: React.FC<object> = () => {
return (
<Box py={2}
display="flex"
alignItems="center"
justifyContent="center"
fontSize={30}
>
Create an Event
</Box>

)
}

const Body: React.FC<{ attendeeData: Attendees[], useInForm: useInFormProps }> = ({ attendeeData, useInForm }) => {
if (!attendeeData) return <LoadingScreen />
const { setAttendees, invalidAttendees, register, errors, isSubmitting, submitBefore }: useInFormProps = useInForm

return (
<Container>
<VStack align="left" spacing="6">
<div>
<FormLabel>Event Name</FormLabel>
<Input
type="text"
disabled={isSubmitting}
{...register('eventName', { required: true })}
/>
{errors.eventName && (
<Text color="tomato" className="pt-1">
{errors.eventName.message}
</Text>
)}
</div>
<div>
<FormLabel>Description</FormLabel>
<Textarea
disabled={isSubmitting}
maxH={200}
{...register('description', { required: true })}

/>
{errors.description && (
<Text color="tomato" className="pt-1">
{errors.description.message}
</Text>
)}
</div>
<div>
<FormLabel>Venue</FormLabel>
<Input
type="text"
disabled={isSubmitting}
{...register('venue', { required: true })}
/>
{errors.venue && (
<Text color="tomato" className="pt-1">
{errors.venue.message}
</Text>
)}
</div>
<VStack align="left">
<div className="flex">
<FormLabel>Department</FormLabel>
<CheckboxGroup>
<Stack spacing={[1, 5]} direction={['row', 'column']}>
<Checkbox value="Machine Learning" {...register('dept')}>
Machine Learning
</Checkbox>
<Checkbox
value="Software Development"
{...register('dept')}
>
Software Development
</Checkbox>
<Checkbox value="Blockchain" {...register('dept')}>
Blockchain
</Checkbox>
<Checkbox value="Internal Affairs" {...register('dept')}>
Internal Affairs
</Checkbox>
<Checkbox value="External Relations" {...register('dept')}>
External Relations
</Checkbox>
</Stack>
</CheckboxGroup>
</div>
{errors.dept && (
<Text color="tomato" className="pt-1">
{'At least one department is required'}
</Text>
)}
</VStack>
<DataTable data={attendeeData} setAttendees={setAttendees} />
{submitBefore && invalidAttendees && (
<Text color="tomato" className="pt-1">At least one attendee is required</Text>
)}
<div>
<FormLabel>Start Date</FormLabel>
<Input
placeholder="Select Date and Time"
size="md"
type="datetime-local"
disabled={isSubmitting}
{...register('startDate', { required: true })}
/>
{errors.startDate && (
<Text color="tomato" className="pt-1">
{errors.startDate.message}
</Text>
)}
</div>
<div>
<FormLabel>End Date</FormLabel>
<Input
placeholder="Select Date and Time"
size="md"
type="datetime-local"
disabled={isSubmitting}
{...register('endDate', { required: true })}
/>
{errors.endDate && (
<Text color="tomato" className="pt-1">
{errors.endDate.message}
</Text>
)}
</div>
<div>
<FormLabel>Secret Code</FormLabel>
<Input
type="text"
disabled={isSubmitting}
{...register('secretCode', { required: true })}
/>
{errors.secretCode && (
<Text color="tomato" className="pt-1">
{errors.secretCode.message}
</Text>
)}
</div>
</VStack>
</Container>
)
}

export default CreateEventModal
44 changes: 31 additions & 13 deletions src/components/calendar/MainCalendar.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
import { useDisclosure } from '@chakra-ui/react'
import { Button, useDisclosure } from '@chakra-ui/react'
import styled from '@emotion/styled'
import type { EventClickArg } from '@fullcalendar/core'
import FullCalendar from '@fullcalendar/react' // must go before plugins
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'
import FullCalendar from '@fullcalendar/react'; // must go before plugins
import timeGridPlugin from '@fullcalendar/timegrid'
import styled from '@emotion/styled'
import { useState } from 'react'
import { ModalContext } from '~/context/ModalContext'
import { Button } from '@chakra-ui/react'
import { trpc } from '~/utils/trpc'
import CreateEventModal from './CreateEventModal'
import EventModal from './EventModal'
import { useState } from 'react'

export const StyleWrapper = styled.div``

const MainCalendar = () => {
const { data } = trpc.event.populateCalendar.useQuery()
const { onOpen, isOpen, onClose } = useDisclosure()
const { onOpen: onOpenDisplayEvent, isOpen: isOpenDisplayEvent, onClose: onCloseDisplayEvent } = useDisclosure()
const { onOpen: onOpenCreateEvent, isOpen: isOpenCreateEvent, onClose: onCloseCreateEvent } = useDisclosure()

const [id, setId] = useState<string>('')
const [displayEventId, setDisplayEventId] = useState<string>("")
const [createEventId, setCreateEventId] = useState<string>("")

const handleEventClick = (info: EventClickArg) => {
setId(info.event.id)
onOpen()
setDisplayEventId(info.event.id)
onOpenDisplayEvent()
}

const handleCreateEvent = () => {
const uniqueId = Date.now().toString();
rmj1405 marked this conversation as resolved.
Show resolved Hide resolved
setCreateEventId(uniqueId);
onOpenCreateEvent();
}

return (
Expand All @@ -37,7 +45,7 @@ const MainCalendar = () => {
width={215}
className="mb-10 text-white"
type="submit"
onClick={() => alert('Create an Event Modal (Pop-Up)')}
onClick={handleCreateEvent}
>
Create Event
</Button>
Expand All @@ -59,13 +67,23 @@ const MainCalendar = () => {

<ModalContext.Provider
value={{
isOpen,
id,
onClose,
isOpen: isOpenDisplayEvent,
id: displayEventId,
onClose: onCloseDisplayEvent,
}}
>
<EventModal />
</ModalContext.Provider>

<ModalContext.Provider
value={{
isOpen: isOpenCreateEvent,
id: createEventId,
onClose: onCloseCreateEvent,
}}
>
<CreateEventModal />
</ModalContext.Provider>
</StyleWrapper>
)
}
Expand Down
Loading