-
Notifications
You must be signed in to change notification settings - Fork 4
Confirm modal on meeting creation #106
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,6 +40,21 @@ subscription circleMeetings($circleId: uuid!) { | |
| } | ||
| } | ||
|
|
||
|
|
||
| query meetingsAtSameTime($NMstartDate: timestamptz!, $NMendDate: timestamptz!) { | ||
| meeting(where: { archived: {_eq: false}, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Il manque |
||
| _or: [ | ||
| { startDate: { _gt: $NMstartDate }, endDate: { _lt: $NMendDate } }, | ||
| { startDate: { _lte: $NMstartDate }, endDate: { _gte: $NMendDate } }, | ||
| {_and: [{ startDate: { _lte: $NMstartDate }, endDate: { _gte: $NMstartDate } }, { endDate: { _lte: $NMendDate } }]}, | ||
| {_and: [{ startDate: { _gte: $NMstartDate }, endDate: { _gte: $NMendDate } }, { startDate: { _lt: $NMendDate } }]} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tu dois pouvoir faire plus simple et performant avec la fonction postgres Je ne crois pas qu'Hasura supporte nativement cette fonction, donc tu peux créer une fonction Hasura |
||
| ]}) { | ||
| ...MeetingSummary | ||
| archived | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pas besoin de récupérer |
||
| } | ||
| } | ||
|
|
||
|
|
||
| mutation createMeeting($values: meeting_insert_input!) { | ||
| insert_meeting_one(object: $values) { | ||
| ...Meeting | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import MemberLinkOverlay from '@atoms/MemberLinkOverlay' | ||
| import { | ||
| AlertDialog, | ||
| AlertDialogBody, | ||
| AlertDialogContent, | ||
| AlertDialogFooter, | ||
| AlertDialogHeader, | ||
| AlertDialogOverlay, | ||
| AlertDialogProps, | ||
| Box, | ||
| Button, | ||
| Text, | ||
| } from '@chakra-ui/react' | ||
| import { MeetingSummaryFragment } from '@gql' | ||
| import useMatchMeetings from '@hooks/useMatchMeetings' | ||
| import { MeetingFormDataValues } from '@organisms/meeting/MeetingEditModal' | ||
| import React, { useEffect, useRef } from 'react' | ||
| import { useTranslation } from 'react-i18next' | ||
|
|
||
| interface Props | ||
| extends Omit<AlertDialogProps, 'children' | 'leastDestructiveRef'> { | ||
| newMeeting?: MeetingFormDataValues | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| conflictedMeetings: MeetingSummaryFragment[] | ||
| onAccept: (currentMeeting: MeetingFormDataValues) => void | ||
| } | ||
|
|
||
| export default function MeetingConfirmModal({ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renommer en |
||
| newMeeting, | ||
| conflictedMeetings, | ||
| onAccept, | ||
| ...alertProps | ||
| }: Props) { | ||
| const { t } = useTranslation() | ||
| const cancelRef = useRef<HTMLButtonElement>(null) | ||
|
|
||
| const { matchingParticipants } = useMatchMeetings( | ||
| newMeeting!, | ||
| conflictedMeetings | ||
| ) | ||
|
|
||
| useEffect(() => { | ||
| if (matchingParticipants.length === 0) { | ||
| handleCreate() | ||
| } | ||
| }, [matchingParticipants.length]) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ce check devrait être fait après le calcul de l'overlap, pas dans ce composant. Sinon on ouvre et ferme la modale instantanément, c'est pas super propre. |
||
|
|
||
| const handleCreate = () => { | ||
| onAccept(newMeeting!) | ||
| alertProps.onClose() | ||
| } | ||
|
|
||
| return ( | ||
| <AlertDialog {...alertProps} leastDestructiveRef={cancelRef}> | ||
| <AlertDialogOverlay> | ||
| <AlertDialogContent> | ||
| <AlertDialogHeader> | ||
| {t('MeetingConfirmModal.heading')} | ||
| </AlertDialogHeader> | ||
|
|
||
| <AlertDialogBody> | ||
| <Text> | ||
| {t('MeetingConfirmModal.info', { | ||
| count: matchingParticipants.length, | ||
| })} | ||
| </Text> | ||
| <Box px={2} py={1} gap="3"> | ||
| {matchingParticipants.map((p) => ( | ||
| <MemberLinkOverlay key={p.id} member={p} mt="4" /> | ||
| ))} | ||
| </Box> | ||
| </AlertDialogBody> | ||
|
|
||
| <AlertDialogFooter> | ||
| <Button ref={cancelRef} onClick={alertProps.onClose}> | ||
| {t('common.cancel')} | ||
| </Button> | ||
| <Button colorScheme="red" onClick={handleCreate} ml={3}> | ||
| {t('MeetingConfirmModal.button')} | ||
| </Button> | ||
| </AlertDialogFooter> | ||
| </AlertDialogContent> | ||
| </AlertDialogOverlay> | ||
| </AlertDialog> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Peux-tu mettre dans la description de la PR une capture d'écran ou une vidéo qui montre la modale stp ? |
||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,8 +22,10 @@ import { | |
| import { | ||
| Meeting_Step_Type_Enum, | ||
| MeetingFragment, | ||
| MeetingSummaryFragment, | ||
| MeetingTemplateFragment, | ||
| Member_Scope_Enum, | ||
| useMeetingsAtSameTimeLazyQuery, | ||
| useUpdateMeetingMutation, | ||
| } from '@gql' | ||
| import { yupResolver } from '@hookform/resolvers/yup' | ||
|
|
@@ -38,11 +40,12 @@ import MeetingStepsConfigController, { | |
| import MeetingTemplateMenu from '@molecules/meeting/MeetingTemplateMenu' | ||
| import VideoConfFormControl from '@molecules/meeting/VideoConfFormControl' | ||
| import ParticipantsFormControl from '@molecules/ParticipantsFormControl' | ||
| import MeetingConfirmModal from '@organisms/meeting/MeetingConfirmModal' | ||
| import { VideoConf, VideoConfTypes } from '@shared/model/meeting' | ||
| import { nameSchema, stepsConfigSchema } from '@shared/schemas' | ||
| import { getDateTimeLocal } from '@utils/dates' | ||
| import { nanoid } from 'nanoid' | ||
| import React, { useMemo } from 'react' | ||
| import React, { useMemo, useState } from 'react' | ||
| import { FormProvider, useForm } from 'react-hook-form' | ||
| import { useTranslation } from 'react-i18next' | ||
| import { FiChevronDown } from 'react-icons/fi' | ||
|
|
@@ -70,6 +73,11 @@ interface Values extends StepsValues { | |
| videoConfUrl: string | ||
| } | ||
|
|
||
| export type MeetingFormDataValues = Omit< | ||
| MeetingSummaryFragment, | ||
| 'id' | 'orgId' | 'ended' | ||
| > | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tu dois pouvoir utiliser l'interface |
||
|
|
||
| const resolver = yupResolver( | ||
| yup.object().shape({ | ||
| title: nameSchema.required(), | ||
|
|
@@ -98,6 +106,12 @@ export default function MeetingEditModal({ | |
| const createMeeting = useCreateMeeting() | ||
| const [updateMeeting] = useUpdateMeetingMutation() | ||
|
|
||
| const [newMeeting, setNewMeeting] = useState<MeetingFormDataValues>() | ||
| const [conflictingMeetings, setConflictingMeetings] = useState< | ||
| MeetingSummaryFragment[] | ||
| >([]) | ||
| const [confirmModal, setConfirmModal] = useState(false) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pour faire pareil que pour les autres modales et pour renommer en const overlapModal = useDisclosure() |
||
|
|
||
| const defaultValues: Values = useMemo( | ||
| () => ({ | ||
| title: meeting?.title ?? '', | ||
|
|
@@ -138,6 +152,7 @@ export default function MeetingEditModal({ | |
| resolver, | ||
| defaultValues, | ||
| }) | ||
|
|
||
| const { | ||
| handleSubmit, | ||
| register, | ||
|
|
@@ -151,12 +166,65 @@ export default function MeetingEditModal({ | |
| const circleId = watch('circleId') | ||
| const circle = useCircle(circleId) | ||
|
|
||
| const [checkConflictingMeetings] = useMeetingsAtSameTimeLazyQuery() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renommer en : |
||
|
|
||
| // Template change | ||
| const handleTemplateSelect = (template: MeetingTemplateFragment) => { | ||
| setValue('title', template.title) | ||
| setValue('stepsConfig', template.stepsConfig) | ||
| } | ||
|
|
||
| const checkOccupation = async (meetingUpdate: MeetingFormDataValues) => { | ||
| try { | ||
| const { data } = await checkConflictingMeetings({ | ||
| variables: { | ||
| NMstartDate: meetingUpdate.startDate?.toString()!, | ||
| NMendDate: meetingUpdate.endDate?.toString()!, | ||
| }, | ||
| }) | ||
| if (data && data.meeting && !!data.meeting.length) { | ||
| setNewMeeting(meetingUpdate) | ||
| setConflictingMeetings(data.meeting) | ||
| setConfirmModal(true) | ||
| return true | ||
| } else { | ||
| return false | ||
| } | ||
| } catch (error) { | ||
| console.error(error) | ||
| } | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tu peux réintégrer cette fonction dans |
||
|
|
||
| const handleConfirm = async (meetingUpdate: MeetingFormDataValues) => { | ||
| if (meeting && !duplicate) { | ||
| // Update meeting | ||
| await updateMeeting({ | ||
| variables: { | ||
| id: meeting.id, | ||
| values: meetingUpdate, | ||
| }, | ||
| }) | ||
| } else { | ||
| // Create meeting | ||
| const result = await createMeeting( | ||
| { | ||
| orgId, | ||
| ...meetingUpdate, | ||
| }, | ||
| meeting && duplicate ? meeting.id : undefined | ||
| ) | ||
| if (!result) return | ||
|
|
||
| if (onCreate) { | ||
| onCreate(result.id) | ||
| } else { | ||
| navigate(result.path) | ||
| } | ||
| } | ||
|
|
||
| modalProps.onClose() | ||
| } | ||
|
|
||
| // Submit | ||
| const onSubmit = handleSubmit( | ||
| async ({ | ||
|
|
@@ -168,7 +236,11 @@ export default function MeetingEditModal({ | |
| ...data | ||
| }) => { | ||
| if (!orgId || !currentMember || !circle) return | ||
|
|
||
| const startDateDate = new Date(startDate) | ||
| const endDateDate = new Date( | ||
| startDateDate.getTime() + duration * 60 * 1000 | ||
| ) | ||
|
|
||
| const videoConf: VideoConf | null = | ||
| videoConfType === VideoConfTypes.Url | ||
|
|
@@ -185,40 +257,15 @@ export default function MeetingEditModal({ | |
| const meetingUpdate = { | ||
| ...data, | ||
| startDate: startDateDate.toISOString(), | ||
| endDate: new Date( | ||
| startDateDate.getTime() + duration * 60 * 1000 | ||
| ).toISOString(), | ||
| endDate: endDateDate.toISOString(), | ||
| participantsMembersIds: participantsMembersIds.map((m) => m.memberId), | ||
| videoConf, | ||
| } | ||
|
|
||
| if (meeting && !duplicate) { | ||
| // Update meeting | ||
| await updateMeeting({ | ||
| variables: { | ||
| id: meeting.id, | ||
| values: meetingUpdate, | ||
| }, | ||
| }) | ||
| } else { | ||
| // Create meeting | ||
| const result = await createMeeting( | ||
| { | ||
| orgId, | ||
| ...meetingUpdate, | ||
| }, | ||
| meeting && duplicate ? meeting.id : undefined | ||
| ) | ||
| if (!result) return | ||
|
|
||
| if (onCreate) { | ||
| onCreate(result.id) | ||
| } else { | ||
| navigate(result.path) | ||
| } | ||
| if (await checkOccupation(meetingUpdate)) { | ||
| return | ||
| } | ||
|
|
||
| modalProps.onClose() | ||
| handleConfirm(meetingUpdate) | ||
| } | ||
| ) | ||
|
|
||
|
|
@@ -331,6 +378,15 @@ export default function MeetingEditModal({ | |
| </form> | ||
| </ModalContent> | ||
| </Modal> | ||
| {confirmModal && ( | ||
| <MeetingConfirmModal | ||
| isOpen | ||
| newMeeting={newMeeting} | ||
| conflictedMeetings={conflictingMeetings} | ||
| onClose={() => setConfirmModal(false)} | ||
| onAccept={handleConfirm} | ||
| /> | ||
| )} | ||
| </FormProvider> | ||
| ) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Je vais te proposer quelques changements de nomenclature pour mieux s'y retrouver.
Ici, renommer en
getOverlappingMeetings