Skip to content

Commit

Permalink
New event without conference hall & random color for new event catego…
Browse files Browse the repository at this point in the history
…ries (#56)

* Add new event dialog without conference hall

* Add new event dialog without conference hall

* Generate random colors for new categories

* Fix event deletion bug
  • Loading branch information
HugoGresse authored Nov 5, 2023
1 parent c616e4b commit 2a1087e
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 138 deletions.
4 changes: 2 additions & 2 deletions src/events/actions/addNewEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { addDoc, serverTimestamp } from 'firebase/firestore'
import { loadConferenceHallSpeakers } from '../../conferencehall/firebase/loadFromConferenceHallUtils'
import { importSpeakers } from './conferenceHallUtils/importSpeakers'
import { importSessions } from './conferenceHallUtils/importSessions'
import { DEFAULT_SESSION_CARD_BACKGROUND_COLOR } from '../page/schedule/scheduleConstants'
import { getNewEventDates } from './conferenceHallUtils/addNewEventDateUtils'
import { randomColor } from '../../utils/randomColor'

export const addNewEvent = async (
chEvent: ConferenceHallEvent,
Expand Down Expand Up @@ -51,7 +51,7 @@ const addNewEventInternal = async (
categories: (chEvent.categories || []).map((e) => ({
name: e.name,
id: e.id,
color: DEFAULT_SESSION_CARD_BACKGROUND_COLOR,
color: randomColor(),
})),
scheduleVisible: true,
webhooks: [],
Expand Down
35 changes: 22 additions & 13 deletions src/events/list/EventsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,42 @@ import { FirestoreQueryLoaderAndErrorDisplay } from '../../components/FirestoreQ
import { EventsListItem } from './EventsListItem'
import { Event } from '../../types'
import { Box, Button } from '@mui/material'
import { NewEventDialog } from '../new/NewEventDialog'
import { NewEventFromConferenceHallDialog } from '../new/NewEventFromConferenceHallDialog'
import { NewEventCreatedDialog } from '../new/NewEventCreatedDialog'
import { useNotification } from '../../hooks/notificationHook'
import { NewEventDialog } from '../new/NewEventDialog'

export const EventsScreen = ({}) => {
const userId = useSelector(selectUserIdOpenPlanner)
const events = useEvents(userId)
const [newEventCHOpen, setNewEventFromCHOpen] = useState(false)
const [newEventOpen, setNewEventOpen] = useState(false)
const [newEventId, setNewEventId] = useState<null | string>(null)

const { createNotification } = useNotification()

const onEventCreated = (eventId: string | null) => {
if (eventId) {
setNewEventId(eventId)
createNotification('Event created', { type: 'success' })
}
setNewEventFromCHOpen(false)
setNewEventOpen(false)
}

return (
<EventsLayout>
<FirestoreQueryLoaderAndErrorDisplay hookResult={events} />

<Box component="ul" display="flex" flexWrap="wrap" padding={0}>
<Box key="import" component="li" marginRight={1} marginBottom={1} sx={{ listStyle: 'none' }}>
<Box key="importch" component="li" marginRight={1} marginBottom={1} sx={{ listStyle: 'none' }}>
<Button variant="outlined" size="large" onClick={() => setNewEventFromCHOpen(true)}>
New event from Conference Center
</Button>
</Box>
<Box key="new" component="li" marginRight={1} marginBottom={1} sx={{ listStyle: 'none' }}>
<Button variant="outlined" size="large" onClick={() => setNewEventOpen(true)}>
Import from Conference Center
New event
</Button>
</Box>

Expand All @@ -35,16 +52,8 @@ export const EventsScreen = ({}) => {
))}
</Box>

<NewEventDialog
isOpen={newEventOpen}
onClose={(eventId: string | null) => {
if (eventId) {
setNewEventId(eventId)
createNotification('Event created', { type: 'success' })
}
setNewEventOpen(false)
}}
/>
<NewEventFromConferenceHallDialog isOpen={newEventCHOpen} onClose={onEventCreated} />
<NewEventDialog isOpen={newEventOpen} onClose={onEventCreated} />

{newEventId && <NewEventCreatedDialog eventId={newEventId} onClose={() => setNewEventId(null)} />}
</EventsLayout>
Expand Down
191 changes: 82 additions & 109 deletions src/events/new/NewEventDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,128 +1,101 @@
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Typography,
} from '@mui/material'
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material'
import * as React from 'react'
import { useState } from 'react'
import { RequireConferenceHallLogin } from '../../conferencehall/RequireConferenceHallLogin'
import { ConferenceHallEventsPicker } from '../../conferencehall/ConferenceHallEventsPicker'
import { ConferenceHallEvent } from '../../types'
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'
import { addNewEvent } from '../actions/addNewEvent'
import { useSelector } from 'react-redux'
import { selectUserIdOpenPlanner } from '../../auth/authReducer'
import { ConferenceHallProposalsPickerConnected } from '../../conferencehall/components/ConferenceHallProposalsPickerConnected'
import { FormContainer, TextFieldElement, useForm } from 'react-hook-form-mui'
import { yupResolver } from '@hookform/resolvers/yup'
import * as yup from 'yup'
import { useFirestoreCollectionMutation } from '../../services/hooks/firestoreMutationHooks'
import { collections } from '../../services/firebase'
import LoadingButton from '@mui/lab/LoadingButton'
import { NewEvent } from '../../types'
import { serverTimestamp } from 'firebase/firestore'
import { useNotification } from '../../hooks/notificationHook'

export type NewEventDialogProps = {
isOpen: boolean
onClose: (eventId: string | null) => void
}

const schema = yup
.object({
name: yup.string().required(),
})
.required()

export const NewEventDialog = ({ isOpen, onClose }: NewEventDialogProps) => {
const userId = useSelector(selectUserIdOpenPlanner)
const [selectedConferenceHallEvent, setSelectedConferenceHallEvent] = useState<ConferenceHallEvent | null>(null)
const [status, setStatus] = useState('')
const [newResult, setNewResults] = useState<null | { eventId: string | null; errors: string[] }>(null)

const hasErrors = (newResult?.errors || []).length > 0
const { createNotification } = useNotification()
const formContext = useForm()
const { formState } = formContext

const mutation = useFirestoreCollectionMutation(collections.events)

return (
<Dialog open={isOpen} onClose={() => onClose(null)} maxWidth="md" fullWidth={true} scroll="body">
<DialogTitle>Import a new event from Conference Hall</DialogTitle>
<DialogContent>
<DialogContentText>
To import an event from ConferenceHall.io, we first need to log in into ConferenceHall on your
behalf. This is done client side, no credentials will be sent to our servers.
</DialogContentText>

<Box display="flex" alignItems="center" marginY={2}>
<Typography variant="h4">1. Login with Conference-Hall.io</Typography>
<img src="/logos/conference-hall.png" alt="conference hall logo" width={70} />
</Box>

{(!newResult || hasErrors) && (
<RequireConferenceHallLogin>
{(conferenceHallUserId) => {
return (
<>
<Box display="flex" alignItems="center" marginY={2}>
<Typography variant="h4">2. Select the event you want to import</Typography>
</Box>
{selectedConferenceHallEvent ? (
<Typography variant="body1" sx={{ display: 'flex' }}>
<CheckCircleOutlineIcon color="success" /> Selected ConferenceHall event:{' '}
{selectedConferenceHallEvent.name}
</Typography>
) : (
<ConferenceHallEventsPicker
onEventPicked={(event) => setSelectedConferenceHallEvent(event)}
userId={conferenceHallUserId}
/>
)}
{selectedConferenceHallEvent && (
<>
<Box display="flex" alignItems="center" marginY={2}>
<Typography variant="h4">
3. Select the proposals to import (optional)
</Typography>
</Box>
<ConferenceHallProposalsPickerConnected
conferenceHallEventId={selectedConferenceHallEvent.id}
chFormats={selectedConferenceHallEvent.formats}
onSubmit={async ({ formats, proposals }) => {
setNewResults(null)
const result = await addNewEvent(
selectedConferenceHallEvent,
userId,
proposals,
formats,
setStatus
)
setNewResults({
eventId: result[0],
errors: result[1],
})
if (result[1].length === 0) {
onClose(result[0])
}

return null
}}
/>
<Typography variant="body1">{status}</Typography>
</>
)}
</>
)
}}
</RequireConferenceHallLogin>
)}
<DialogTitle>New event creation</DialogTitle>

{hasErrors && newResult && (
<>
<Typography variant="h6">Error during creation: </Typography>
<ul>
{newResult.errors.map((error) => (
<li key={error}>
<Typography>{error}</Typography>
</li>
))}
</ul>
<Typography variant="h6">
The event was still created though, you may want to close this dialog.
</Typography>
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={() => onClose(newResult?.eventId || null)}>Cancel</Button>
</DialogActions>
<FormContainer
formContext={formContext}
// @ts-ignore
resolver={yupResolver(schema)}
onSuccess={async (data) => {
const newEventData: NewEvent = {
name: data.name,
owner: userId,
members: [userId],
scheduleVisible: true,
conferenceHallId: null,
dates: {
start: null,
end: null,
},
formats: [],
categories: [],
tracks: [],
webhooks: [],
createdAt: serverTimestamp(),
updatedAt: serverTimestamp(),
files: null,
statusBadgeImage: null,
statusBadgeLink: null,
}
mutation
.mutate(newEventData)
.then((eventId: any) => {
console.log('new event created', eventId)
onClose(eventId)
})
.catch((error: Error) => {
createNotification('Error while creating event: ' + error.message, { type: 'error' })
console.error('error creating event', error)
})
}}>
<DialogContent>
<TextFieldElement
margin="normal"
required
fullWidth
id="name"
label="Event name"
name="name"
variant="filled"
disabled={formState.isSubmitting}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => onClose(null)}>Cancel</Button>
<LoadingButton
type="submit"
disabled={formState.isSubmitting}
loading={formState.isSubmitting}
variant="contained"
sx={{ mt: 2, mb: 2 }}>
Create event
</LoadingButton>
</DialogActions>
</FormContainer>
</Dialog>
)
}
Loading

0 comments on commit 2a1087e

Please sign in to comment.