diff --git a/src/components/allocation/automaticAllocation.accordion.tsx b/src/components/allocation/automaticAllocation.accordion.tsx index 4939ae0..385100d 100644 --- a/src/components/allocation/automaticAllocation.accordion.tsx +++ b/src/components/allocation/automaticAllocation.accordion.tsx @@ -19,7 +19,7 @@ import Event from 'models/event.model'; import { useEffect, useState } from 'react'; import { weekDaysFormatter } from 'utils/classes/classes.formatter'; import ClassroomsService from 'services/classrooms.service'; -import { sortClassrooms, sortEventsByClassroomAndTime } from 'utils/sorter'; +import { sortClassrooms, sortEventsBySubjectAndClass } from 'utils/sorter'; interface AutomaticAllocationAccordionProps { onEdit: (event: Event) => void; @@ -50,8 +50,8 @@ export default function AutomaticAllocationAccordion({ }); } - allocatedEvents.sort(sortEventsByClassroomAndTime); - unallocatedEvents.sort(sortEventsByClassroomAndTime); + allocatedEvents.sort(sortEventsBySubjectAndClass); + unallocatedEvents.sort(sortEventsBySubjectAndClass); return ( { toastError( diff --git a/src/components/allocation/editEvent.modal.tsx b/src/components/allocation/editEvent.modal.tsx index 04d65e1..c89815b 100644 --- a/src/components/allocation/editEvent.modal.tsx +++ b/src/components/allocation/editEvent.modal.tsx @@ -12,13 +12,14 @@ import { ModalFooter, ModalHeader, ModalOverlay, - Select, Spinner, Stack, Text, useCheckboxGroup, useDisclosure, } from '@chakra-ui/react'; +import { Select as CSelect } from '@chakra-ui/react'; +import Select from 'react-select'; import Dialog from 'components/common/dialog.component'; import { appContext } from 'context/AppContext'; import { Building } from 'models/building.model'; @@ -29,6 +30,11 @@ import BuildingsService from 'services/buildings.service'; import ClassroomsService from 'services/classrooms.service'; import { Capitalize } from 'utils/formatters'; +interface ClassroomOption { + value: string; + label: string; +} + interface EditEventModalProps { isOpen: boolean; onClose: () => void; @@ -53,7 +59,7 @@ export default function EditEventModal({ onOpen: onOpenDialog, onClose: onCloseDialog, } = useDisclosure(); - const { dbUser } = useContext(appContext); + const { loggedUser } = useContext(appContext); const [availableClassrooms, setAvailableClassrooms] = useState< AvailableClassroom[] >([]); @@ -82,7 +88,7 @@ export default function EditEventModal({ useEffect(() => { getBuildingsList(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dbUser]); + }, [loggedUser]); useEffect(() => { getAvailableClassrooms(); @@ -92,10 +98,12 @@ export default function EditEventModal({ useEffect(() => { resetClassroomsDropdown(); getAvailableClassrooms(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [checkedEvents]); useEffect(() => { setCheckedEvents(classEvents.map((it) => it.id ?? '')); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [classEvents]); async function getAvailableClassrooms() { @@ -124,21 +132,23 @@ export default function EditEventModal({ value.sort((a, b) => { if (a.conflicted && !b.conflicted) return 1; if (!a.conflicted && b.conflicted) return -1; + if (a.classroom_name < b.classroom_name) return -1; + if (a.classroom_name > b.classroom_name) return 1; return 0; }), ); } function getBuildingsList() { - if (dbUser) { - if (dbUser.isAdmin) { + if (loggedUser) { + if (loggedUser.isAdmin) { setBuildingsLoading(true); buildingsService.list().then((response) => { setBuildingsList(response.data); setBuildingsLoading(false); }); } else { - setBuildingsList(dbUser.buildings); + setBuildingsList(loggedUser.buildings); } } } @@ -241,7 +251,7 @@ export default function EditEventModal({ {buildingsList.length !== 1 && ( <> Prédio - + )} Salas disponíveis @@ -267,38 +277,28 @@ export default function EditEventModal({ )} + /> { - setSubjectCode(event.target.value); + setSubjectCode(event.target.value.toUpperCase()); if (event.target.value) setHasSubjectCodeError(false); }} /> diff --git a/src/components/classes/jupiterCrawler.popover.tsx b/src/components/classes/jupiterCrawler.popover.tsx index 771f358..d4a3435 100644 --- a/src/components/classes/jupiterCrawler.popover.tsx +++ b/src/components/classes/jupiterCrawler.popover.tsx @@ -38,7 +38,7 @@ export default function JupiterCrawlerPopover({ subjects = [], onSave, }: JupiterCrawlerPopoverPrpos) { - const { dbUser } = useContext(appContext); + const { loggedUser } = useContext(appContext); const buildingsService = new BuildingsService(); @@ -61,18 +61,16 @@ export default function JupiterCrawlerPopover({ useEffect(() => { getBuildingsList(); - }, [dbUser]); + }, [loggedUser]); function handleAddClick() { - if (subjectInput.length > 6 && !subjectsList.includes(subjectInput)) { + if (subjectInput.length === 7 && !subjectsList.includes(subjectInput)) { setSubjectsList((prev) => [...prev, subjectInput.replace(' ', '')]); setSubjectInput(''); } if (multSubjectInput.length > 6) { - const formatedInput = multSubjectInput.replace(' ', ''); - const subjects = formatedInput - .split(',') - .filter((value) => value.length === 7); + const formatedInput = multSubjectInput.replaceAll(' ', ''); + const subjects = formatedInput.split(',').filter((value) => (value.length === 7 && !subjectsList.includes(value))); setSubjectsList((prev) => prev.concat(subjects)); setMultSubjectInput(''); } @@ -91,15 +89,15 @@ export default function JupiterCrawlerPopover({ } function getBuildingsList() { - if (dbUser) { - if (dbUser.isAdmin) { + if (loggedUser) { + if (loggedUser.isAdmin) { setBuildingsLoading(true); buildingsService.list().then((response) => { setBuildingsList(response.data); setBuildingsLoading(false); }); } else { - setBuildingsList(dbUser.buildings); + setBuildingsList(loggedUser.buildings); } } } diff --git a/src/components/classes/multipleEdit.accordion.tsx b/src/components/classes/multipleEdit.accordion.tsx new file mode 100644 index 0000000..614f453 --- /dev/null +++ b/src/components/classes/multipleEdit.accordion.tsx @@ -0,0 +1,163 @@ +import { + Alert, + AlertIcon, + Accordion, + AccordionItem, + AccordionButton, + AccordionPanel, + AccordionIcon, + Box, + Text, + VStack, + Skeleton, + StackDivider, +} from '@chakra-ui/react'; +import Class from 'models/class.model'; +import BuildingsService from 'services/buildings.service'; +import { useContext, useEffect, useState } from 'react'; +import { Building } from 'models/building.model'; +import { appContext } from 'context/AppContext'; +import { MultipleEditAllocation } from './multipleEdit.allocation'; +import { ClassroomSchedule } from 'models/classroom.model'; + +interface MultipleEditAccordionProps { + subjectsMap: [string, Class[]][]; + schedulesMap: [string, string, ClassroomSchedule][]; + isLoadingSchedules: boolean; + isUpdatingSchedules: boolean; + handleSelectBuilding: ( + building_id: string, + building_name: string, + event_id: string, + ) => void; + handleSelectClassroom: ( + new_classroom: string, + new_building: string, + event_id: string, + week_day: string, + start_time: string, + end_time: string, + old_classroom?: string, + ) => void; + handleRemoveClassroom: ( + classroom: string, + building: string, + event_id: string, + week_day: string, + start_time: string, + end_time: string, + ) => void; +} + +export default function MultipleEditAccordion({ + subjectsMap, + schedulesMap, + isLoadingSchedules, + isUpdatingSchedules, + handleSelectBuilding, + handleSelectClassroom, + handleRemoveClassroom, +}: MultipleEditAccordionProps) { + const { loggedUser } = useContext(appContext); + + const [buildingsList, setBuildingsList] = useState([]); + const [buildingsLoading, setBuildingsLoading] = useState(true); + + const buildingsService = new BuildingsService(); + + useEffect(() => { + getBuildingsList(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loggedUser]); + + function getBuildingsList() { + if (loggedUser) { + if (loggedUser.isAdmin) { + setBuildingsLoading(true); + buildingsService.list().then((response) => { + setBuildingsList(response.data); + setBuildingsLoading(false); + }); + } else { + setBuildingsList(loggedUser.buildings); + } + } + } + + function getClassroomsSchedulesList() { + const list: ClassroomSchedule[] = []; + schedulesMap.forEach((map) => { + list.push(map[2]); + }); + return list; + } + + return ( + + + {subjectsMap.length === 0 ? ( + + + Nenhuma turma selecionada + + ) : undefined} + + {subjectsMap.map((subjectMap, index) => ( + + + + + {subjectMap[0]} - {subjectMap[1][0].subject_name} -{' '} + {subjectMap[1].length} Turmas + + + + + + }> + {subjectMap[1].map((cl, idx) => ( + + + {`Turma ${cl.class_code.slice(-2)} - ${ + cl.vacancies + } vagas: `} + + + }> + - + {cl.week_days.map((day, i) => ( + + ))} + + + ))} + + + + ))} + + + ); +} diff --git a/src/components/classes/multipleEdit.allocation.tsx b/src/components/classes/multipleEdit.allocation.tsx new file mode 100644 index 0000000..ab5278b --- /dev/null +++ b/src/components/classes/multipleEdit.allocation.tsx @@ -0,0 +1,289 @@ +import { HStack, Text, Box } from '@chakra-ui/react'; +import { Select as CSelect } from '@chakra-ui/react'; +import Select from 'react-select'; +import { Building } from 'models/building.model'; +import { Capitalize } from 'utils/formatters'; +import { ClassroomSchedule } from 'models/classroom.model'; +import { useEffect, useState } from 'react'; +import { WeekDaysShortText } from 'models/enums/weekDays.enum'; +import { ConflictCalculator } from 'utils/conflict.calculator'; + +interface MultipleEditAllocationProps { + eventID: string; + weekDay: string; + startTime: string; + endTime: string; + buildingsList: Building[]; + scheduleList: ClassroomSchedule[]; + isLoadingSchedules: boolean; + isUpdatingSchedules: boolean; + building?: string; + classroom?: string; + onSelectClassroom: ( + new_classroom: string, + new_building: string, + event_id: string, + week_day: string, + start_time: string, + end_time: string, + old_classroom?: string, + ) => void; + onRemoveClassroom: ( + classroom: string, + building: string, + event_id: string, + week_day: string, + start_time: string, + end_time: string, + ) => void; + onSelectBuilding: ( + building_id: string, + building_name: string, + event_id: string, + ) => void; +} + +interface ClassroomOption { + value: string; + label: string; +} + +export function MultipleEditAllocation({ + eventID, + weekDay, + startTime, + endTime, + buildingsList, + scheduleList, + building, + classroom, + isLoadingSchedules, + isUpdatingSchedules, + onSelectClassroom, + onRemoveClassroom, + onSelectBuilding, +}: MultipleEditAllocationProps) { + const [selectedSchedule, setSelectedSchedule] = useState(); + const [filteredSchedules, setFilteredSchedules] = + useState(); + + const [selectedBuilding, setSelectedBuilding] = useState(); + + const [hasConflict, setHasConflict] = useState(false); + + useEffect(() => { + if (scheduleList.length > 0 && building && !selectedBuilding) { + const list: ClassroomSchedule[] = []; + scheduleList.forEach((it) => { + if (it.building === building) list.push(it); + }); + setFilteredSchedules(list); + } + + if (scheduleList.length > 0 && selectedBuilding) { + const list: ClassroomSchedule[] = []; + scheduleList.forEach((it) => { + if (it.building === selectedBuilding.name) list.push(it); + }); + setFilteredSchedules(list); + } + + if (scheduleList.length > 0 && classroom && !selectedSchedule) { + const newSchedule = scheduleList.find( + (schedule) => schedule.classroom_name === classroom, + ); + setSelectedSchedule(newSchedule); + const conflict = verifyConflict(newSchedule); + setHasConflict(conflict); + } + + if (scheduleList.length > 0 && selectedSchedule) { + const newSchedule = scheduleList.find( + (schedule) => + schedule.classroom_name === selectedSchedule.classroom_name, + ); + setSelectedSchedule(newSchedule); + const conflict = verifyConflict(newSchedule); + setHasConflict(conflict); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + scheduleList, + building, + classroom, + selectedBuilding, + selectedSchedule, + hasConflict, + ]); + + useEffect(() => { + if (buildingsList.length === 1 && !selectedBuilding) { + const newBuilding = buildingsList[0]; + async function fetchBuilding() { + setSelectedBuilding(newBuilding); + onSelectBuilding(newBuilding.id, newBuilding.name, eventID); + } + fetchBuilding(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [buildingsList, eventID, onSelectBuilding]); + + useEffect(() => { + if ((classroom || building) && !selectedBuilding) { + setCurrentAllocation(); + } + // eslint-disable-next-line + }, [selectedBuilding]); + + async function setCurrentAllocation() { + if (building && !selectedBuilding) { + const currentBuilding = buildingsList.find( + (build) => build.name === building, + ); + if (currentBuilding) { + setSelectedBuilding(currentBuilding); + onSelectBuilding(currentBuilding.id, currentBuilding.name, eventID); + } + } + } + + async function handleSelectBuilding(building: Building) { + setSelectedBuilding(building); + onSelectBuilding(building.id, building.name, eventID); + } + + function verifyConflict(schedule: ClassroomSchedule | undefined) { + if (schedule) { + const times = schedule.conflict_map[weekDay as WeekDaysShortText]; + return ConflictCalculator.isTimeInTimeTupleArray( + times, + startTime, + endTime, + ); + } + return false; + } + + return ( + + {`${Capitalize( + weekDay, + )} - ${startTime} às ${endTime}: `} + {buildingsList.length !== 1 && ( + + Prédio + { + const newBuilding = buildingsList.find( + (it) => it.id === event.target.value, + ); + if (newBuilding) { + // Já estava alocado agora tem que remover do calendário antigo + handleSelectBuilding(newBuilding); + if (selectedBuilding && selectedSchedule) + onRemoveClassroom( + selectedSchedule.classroom_name, + selectedBuilding.name, + eventID, + weekDay, + startTime, + endTime, + ); + + setSelectedSchedule(undefined); + } + }} + value={selectedBuilding?.id} + > + {buildingsList.map((it) => ( + + ))} + + {!selectedBuilding && ( + + Selecione um prédio + + )} + + )} + + + Salas disponíveis + { + setSubjectSearchValue(event.target.value.toUpperCase()); + FilterSubjects(event.target.value.toUpperCase()); + }} + /> + + + + + + + + + + + + {hasMissingData ? ( + + + Todas turmas selecionadas devem ser alocadas + + ) : undefined} + {hasMissingData && subjectSearchValue ? ( + + + Cuidado, o filtro está ativo + + ) : undefined} + + + + + + + + + + ); +} diff --git a/src/components/classes/register.modal.tsx b/src/components/classes/register.modal.tsx index fbd70f0..82929ac 100644 --- a/src/components/classes/register.modal.tsx +++ b/src/components/classes/register.modal.tsx @@ -372,7 +372,7 @@ export default function RegisterModal(props: RegisterModalProps) { onChange={(event) => { setForm((prev) => ({ ...prev, - subject_code: event.target.value, + subject_code: event.target.value.toUpperCase(), })); if (event.target.value) setHasSubjectCodeError(false); }} diff --git a/src/components/classrooms/register.modal.tsx b/src/components/classrooms/register.modal.tsx index 8b6297d..e86fcdb 100644 --- a/src/components/classrooms/register.modal.tsx +++ b/src/components/classrooms/register.modal.tsx @@ -16,12 +16,14 @@ import { NumberInputField, Select, } from '@chakra-ui/react'; +import { appContext } from 'context/AppContext'; import { Building } from 'models/building.model'; import Classroom from 'models/classroom.model'; +import { User } from 'models/user.model'; -import { Buildings } from 'models/enums/buildings.enum'; - -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; +import BuildingsService from 'services/buildings.service'; +import UsersService from 'services/users.service'; interface RegisterModalProps { isOpen: boolean; @@ -33,15 +35,22 @@ interface RegisterModalProps { } export default function RegisterModal(props: RegisterModalProps) { + const { loggedUser } = useContext(appContext); + const buildingsService = new BuildingsService(); + const usersService = new UsersService(); + const [usersList, setUsersList] = useState([]); + const [buildingsList, setBuildingsList] = useState([]); + const initialForm: Classroom = { classroom_name: '', - building: Buildings.BIENIO, + building: '', floor: 0, capacity: 0, ignore_to_allocate: false, air_conditioning: false, projector: false, accessibility: false, + created_by: undefined, }; const [form, setForm] = useState(initialForm); @@ -50,6 +59,31 @@ export default function RegisterModal(props: RegisterModalProps) { if (props.formData) setForm(props.formData); }, [props.formData]); + useEffect(() => { + getBuildingsList(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loggedUser]); + + useEffect(() => { + if (loggedUser?.isAdmin) { + usersService.list().then((response) => { + setUsersList(response.data.map((it) => it)); + }); + } + }, [loggedUser]); + + function getBuildingsList() { + if (loggedUser) { + if (loggedUser.isAdmin) { + buildingsService.list().then((response) => { + setBuildingsList(response.data); + }); + } else { + setBuildingsList(loggedUser.buildings); + } + } + } + function handleSaveClick() { if (isEmpty(form.classroom_name)) return; props.onSave(form); @@ -109,8 +143,8 @@ export default function RegisterModal(props: RegisterModalProps) { })); }} > - {props.buildingsOptions.map((it, index) => ( - ))} @@ -203,6 +237,28 @@ export default function RegisterModal(props: RegisterModalProps) { Ignorar para alocar + + {loggedUser?.isAdmin && ( + + Criado por + + + )} diff --git a/src/components/common/automaticAllocation.accordion.tsx b/src/components/common/automaticAllocation.accordion.tsx index 06480f3..f932cf8 100644 --- a/src/components/common/automaticAllocation.accordion.tsx +++ b/src/components/common/automaticAllocation.accordion.tsx @@ -18,8 +18,8 @@ import Event from 'models/event.model'; import { useEffect, useState } from 'react'; import { weekDaysFormatter } from 'utils/classes/classes.formatter'; -import { WeekDayInt } from 'utils/mappers/allocation.mapper'; import ClassroomsService from 'services/classrooms.service'; +import { sortClassrooms, sortEventsBySubjectCode } from 'utils/sorter'; interface AutomaticAllocationAccordionProps { onEdit: (event: Event) => void; @@ -45,33 +45,13 @@ export default function AutomaticAllocationAccordion({ function fetchClassrooms() { classroomService.list().then((it) => { - it.data.sort(sortClassroomByName); + it.data.sort(sortClassrooms); setClassrooms(it.data); }); } - function sortClassroomByName(a: Classroom, b: Classroom) { - if (a.classroom_name < b.classroom_name) return -1; - if (a.classroom_name > b.classroom_name) return 1; - return 0; - } - - function sortEventByClassCode(a: Event, b: Event) { - if (a.subject_code < b.subject_code) return -1; - if (a.subject_code > b.subject_code) return 1; - return 0; - } - - function sortEventByClassAndTime(a: Event, b: Event) { - const classResult = sortEventByClassCode(a, b); - if (classResult === 0) { - return WeekDayInt(a.week_day) - WeekDayInt(b.week_day); - } - return classResult; - } - - allocatedEvents.sort(sortEventByClassAndTime); - unallocatedEvents.sort(sortEventByClassAndTime); + allocatedEvents.sort(sortEventsBySubjectCode); + unallocatedEvents.sort(sortEventsBySubjectCode); return ( = { data: Data[]; columns: ColumnDef[]; + filteredData?: (filteredData: Data[]) => void; }; export default function DataTable({ @@ -72,6 +75,11 @@ export default function DataTable({ // eslint-disable-next-line }, [table.getState().columnFilters[0]?.id]); + function getFilteredData() { + const filteredData = table.getFilteredRowModel().rows; + return filteredData; + } + return ( {loading && } @@ -89,6 +97,7 @@ export default function DataTable({ isNumeric={meta?.isNumeric} pb='2' color='uspolis.blue' + maxW={header.column.columnDef.maxSize} > ({ {header.column.getCanFilter() ? ( ) : null} + + {meta?.isCheckBox ? ( + + + } + onClick={() => + meta.markAllClickFn(getFilteredData(), true) + } + /> + + + } + onClick={() => + meta.dismarkAllClickFn(getFilteredData(), false) + } + /> + + + ) : null} ); })} @@ -126,7 +164,17 @@ export default function DataTable({ // see https://tanstack.com/table/v8/docs/api/core/column-def#meta to type this correctly const meta: any = cell.column.columnDef.meta; return ( - + {meta?.isBoolean ? ( cell.getValue() ? ( diff --git a/src/components/common/form/Input/index.tsx b/src/components/common/form/Input/index.tsx index fdc572a..86451d6 100644 --- a/src/components/common/form/Input/index.tsx +++ b/src/components/common/form/Input/index.tsx @@ -1,12 +1,23 @@ -import { FormLabel, Input as ChakraInput, FormControl, FormErrorMessage } from '@chakra-ui/react'; +import { + FormLabel, + Input as ChakraInput, + FormControl, + FormErrorMessage, +} from '@chakra-ui/react'; import { FieldProps } from 'models/interfaces'; +import React from 'react'; import { useFormContext } from 'react-hook-form'; interface InputProps extends FieldProps { type?: React.HTMLInputTypeAttribute; } -export function Input({ label, name, type = 'text', disabled = false }: InputProps) { +export function Input({ + label, + name, + type = 'text', + disabled = false, +}: InputProps) { const { register, formState: { errors }, diff --git a/src/components/common/navbar.component.tsx b/src/components/common/navbar.component.tsx index fd57bc7..929c7ca 100644 --- a/src/components/common/navbar.component.tsx +++ b/src/components/common/navbar.component.tsx @@ -19,10 +19,10 @@ import { useDisclosure, } from '@chakra-ui/react'; import { Auth } from 'aws-amplify'; -import { appContext } from 'context/AppContext'; import { ReactNode, useContext } from 'react'; import { FaUser } from 'react-icons/fa'; import Logo from 'assets/uspolis.logo.png'; +import { appContext } from 'context/AppContext'; const Links = [ { text: 'Salas', value: '/classrooms', admin: false }, @@ -52,11 +52,10 @@ const NavLink = ({ children, to }: { children: ReactNode; to: string }) => ( export default function Navbar() { const { isOpen, onOpen, onClose } = useDisclosure(); - const { username } = useContext(appContext); - const { dbUser } = useContext(appContext); + const { loggedUser, logout } = useContext(appContext); function handleClickLogout() { - Auth.signOut(); + logout(); } return ( @@ -88,7 +87,7 @@ export default function Navbar() { > {Links.map( (link) => - (!link.admin || (link.admin && dbUser?.isAdmin)) && ( + (!link.admin || (link.admin && loggedUser?.isAdmin)) && ( {link.text} @@ -107,27 +106,27 @@ export default function Navbar() { colorScheme='dark' > - {dbUser?.name} + {loggedUser?.name} {/* Perfil */} - - Sair - - - + + Sair + + + + - {isOpen ? ( {Links.map( (link) => - (!link.admin || (link.admin && dbUser?.isAdmin)) && ( + (!link.admin || (link.admin && loggedUser?.isAdmin)) && ( {link.text} diff --git a/src/components/events/EventFormModal/form.ts b/src/components/events/EventFormModal/form.ts index b786e56..86c6ac8 100644 --- a/src/components/events/EventFormModal/form.ts +++ b/src/components/events/EventFormModal/form.ts @@ -8,21 +8,27 @@ export const formFields = { validator: yup .string() .nullable() - .when('location', ([location], s) => (!location ? s.required('Necessário informar local do evento') : s)), + .when('location', ([location], s) => + !location ? s.required('Necessário informar local do evento') : s, + ), defaultValue: '', }, classroom: { validator: yup .string() .nullable() - .when('location', ([location], s) => (!location ? s.required('Necessário informar local do evento') : s)), + .when('location', ([location], s) => + !location ? s.required('Necessário informar local do evento') : s, + ), defaultValue: '', }, category: { validator: yup .string() .required('Campo obrigatório') - .test('is-valid-option', 'Campo obrigatório', (value) => Object.keys(EventTypes).includes(value)), + .test('is-valid-option', 'Campo obrigatório', (value) => + Object.keys(EventTypes).includes(value), + ), defaultValue: '', }, description: { @@ -37,7 +43,9 @@ export const formFields = { validator: yup .string() .nullable() - .when('building', ([building], s) => (!building ? s.required('Necessário informar local do evento') : s)), + .when('building', ([building], s) => + !building ? s.required('Necessário informar local do evento') : s, + ), defaultValue: '', }, title: { @@ -54,10 +62,17 @@ export const formFields = { end_datetime: { validator: yup .string() - .required('Campo obrigatório') - .test('is-valid', 'Data inválida', (value) => moment(value).isValid()) - .test('is-after-start', 'Data precisa ser posterior a data de início', (value, context) => - moment(value).isAfter(moment(context.parent['start_datetime'])), + .test( + 'is-valid', + 'Data inválida', + (value) => !value || moment(value).isValid(), + ) + .test( + 'is-after-start', + 'Data precisa ser posterior a data de início', + (value, context) => + !value || + moment(value).isAfter(moment(context.parent['start_datetime'])), ), defaultValue: '', }, diff --git a/src/components/events/EventFormModal/index.tsx b/src/components/events/EventFormModal/index.tsx index 21a46dd..c3fb5fb 100644 --- a/src/components/events/EventFormModal/index.tsx +++ b/src/components/events/EventFormModal/index.tsx @@ -14,6 +14,7 @@ import { VStack, Text, useToast, + Checkbox, } from '@chakra-ui/react'; import { yupResolver } from '@hookform/resolvers/yup'; import { FormProvider, useForm } from 'react-hook-form'; @@ -45,8 +46,26 @@ interface EventFormModalProps extends ModalProps { const service = new InstutionalEventsService(); -function EventFormModal({ isOpen, onClose, refetch, selectedEvent, buildings }: EventFormModalProps) { +function EventFormModal({ + isOpen, + onClose, + refetch, + selectedEvent, + buildings, +}: EventFormModalProps) { const [loading, setLoading] = useState(false); + const [fullDayEvent, setFullDayEvent] = useState(false); + + useEffect(() => { + if ( + selectedEvent && + selectedEvent.end_datetime === selectedEvent.start_datetime + ) { + setFullDayEvent(true); + } else { + setFullDayEvent(false); + } + }, [selectedEvent]); const form = useForm({ defaultValues: defaultValues, @@ -69,6 +88,10 @@ function EventFormModal({ isOpen, onClose, refetch, selectedEvent, buildings }: const values = getValues(); + if (fullDayEvent) { + values['end_datetime'] = values['start_datetime']; + } + await service.create(values); toast({ @@ -102,6 +125,10 @@ function EventFormModal({ isOpen, onClose, refetch, selectedEvent, buildings }: const values = getValues(); + if (fullDayEvent) { + values['end_datetime'] = values['start_datetime']; + } + await service.update(selectedEvent._id, values); toast({ @@ -139,10 +166,18 @@ function EventFormModal({ isOpen, onClose, refetch, selectedEvent, buildings }: }, [reset, selectedEvent]); return ( - + - {!!selectedEvent ? 'Atualizar evento' : 'Cadastrar de evento'} + + {!!selectedEvent ? 'Atualizar evento' : 'Cadastrar de evento'} +
@@ -152,26 +187,64 @@ function EventFormModal({ isOpen, onClose, refetch, selectedEvent, buildings }: