import React, { useEffect, useState } from 'react'; import { useRouter } from 'next/router'; import axios from 'axios'; import { Controller, useForm } from 'react-hook-form'; import queryString from 'query-string'; import cloneDeep from 'lodash.clonedeep'; import { DateTimePicker, MuiPickersUtilsProvider } from '@material-ui/pickers'; import DayJsUtils from '@date-io/dayjs'; import { BannerUpload, PresenterImageUpload } from './components'; import { Button } from 'components/common'; import { windowSupported } from 'utils/checkSupport'; import { toastErrorCenter } from 'utils/generalUtils'; import { API_ROOT } from 'pages/_app'; import eventEditorStyles from './EventEditor.module.scss'; import formStyles from 'styles/shared/Form.module.scss'; import commonStyles from 'styles/commonStyles.module.scss'; const EventEditor = (props) => { const { formType, submitFunction, submitting } = props; const router = useRouter(); const { register, handleSubmit, control, watch, reset } = useForm(); const useCustomInternalName = watch('useCustomInternalName'); const [bannerFile, setBannerFile] = useState(null); const [presenterImageFile, setPresenterImageFile] = useState(null); const [editorReady, setEditorReady] = useState(formType !== 'edit'); useEffect(async () => { if (formType === 'edit') { // Get event ID from query string const parsedQueryString = queryString.parse(location.search); if (!parsedQueryString.id) { toastErrorCenter('No event ID was specified for editing.'); await router.push('/events'); return; } const eventId = parsedQueryString.id; // Get the event data to edit let res; try { res = await axios.get(`${API_ROOT}/events/${eventId}`); } catch (err) { toastErrorCenter('An error occurred while getting the event data.'); await router.push(`/events/${eventId}`); return; } // Load event data into the form reset(res.data); setEditorReady(true); } }, []); // Prepare form data to be submitted const prepareFormData = (submittedData) => { const eventData = cloneDeep(submittedData); // Set empty string values and undefined to null for (const [key, value] of Object.entries(eventData)) { if (value === '' || value === undefined) { eventData[key] = null; } } // Convert datetimes to ISO strings if (eventData.startDateTime) { eventData.startDateTime = new Date(eventData.startDateTime).toISOString(); } if (eventData.endDateTime) { eventData.endDateTime = new Date(eventData.endDateTime).toISOString(); } // Prepare FormData by serializing form data to JSON and appending files if they exist // Remove files from form data before serializing delete eventData.banner; delete eventData.presenterImage; const formData = new FormData(); formData.append('formDataJson', JSON.stringify(eventData)); if (bannerFile) { formData.append('bannerFile', bannerFile); } if (presenterImageFile) { formData.append('presenterImageFile', presenterImageFile); } // Submit the form submitFunction(formData); }; return ( <> <form onSubmit={handleSubmit(prepareFormData)}> <div className={formStyles.labeledInput}> <label htmlFor="title">Title*</label> <input type="text" id="title" name="title" disabled={!editorReady || submitting} placeholder={!editorReady ? 'Loading...' : ''} ref={register} required /> </div> {(formType === 'create' || formType === 'edit') && ( <div className={eventEditorStyles.presenterInputs}> <div className={formStyles.labeledInput}> <label htmlFor="presenter">Presenter</label> <input type="text" id="presenter" name="presenter" disabled={!editorReady || submitting} ref={register} /> </div> <div className={formStyles.labeledInput}> <label className={eventEditorStyles.presenterImageLabel} htmlFor="presenterImage" > {windowSupported() && window.innerWidth <= 700 ? 'Presenter Image' : 'Image'} </label> <Controller name="presenterImage" control={control} render={({ onChange }) => ( <PresenterImageUpload onChange={onChange} setPresenterImageFile={setPresenterImageFile} /> )} /> </div> </div> )} <div className={formStyles.labeledInput}> <label htmlFor="banner"> Banner (Will be resized to a 16:9 aspect ratio) {formType === 'request' && '*'} </label> <Controller name="banner" control={control} render={({ onChange }) => ( <BannerUpload onChange={onChange} setBannerFile={setBannerFile} /> )} /> </div> <MuiPickersUtilsProvider utils={DayJsUtils}> <div className={formStyles.twoColumn}> <div className={formStyles.labeledInput}> <label htmlFor="startDateTime"> Start Date and Time{formType === 'request' && '*'} </label> <Controller control={control} name="startDateTime" id="startDateTime" as={ <DateTimePicker defaultValue={null} variant="dialog" format="MM/DD/YYYY, h:mm A" TextFieldComponent={(props) => <input {...props} />} readOnly={!editorReady || submitting} required={formType === 'request'} emptyLabel showTodayButton /> } /> </div> <div className={formStyles.labeledInput}> <label htmlFor="endDateTime"> End Date and Time{formType === 'request' && '*'} </label> <Controller control={control} name="endDateTime" id="endDateTime" as={ <DateTimePicker variant="dialog" format="MM/DD/YYYY, h:mm A" TextFieldComponent={(props) => <input {...props} />} readOnly={!editorReady || submitting} required={formType === 'request'} emptyLabel showTodayButton /> } /> </div> </div> </MuiPickersUtilsProvider> <div className={formStyles.labeledInput}> <label htmlFor="eventLocation"> Location (Either Online or a building's address and room number) {formType === 'request' && '*'} </label> <input type="text" id="eventLocation" name="eventLocation" disabled={!editorReady || submitting} required={formType === 'request'} ref={register} /> </div> <div className={formStyles.labeledInput}> <label htmlFor="externalLink"> External Link (This can be a direct Zoom/Google Meet link) {formType === 'request' && '*'} </label> <input type="text" id="externalLink" name="externalLink" disabled={!editorReady || submitting} required={formType === 'request'} ref={register} /> </div> <div className={formStyles.labeledInput}> <label htmlFor="externalLinkButtonText"> External Link Button Text (The green button on the event page) {formType === 'request' && '*'} </label> <input type="text" id="externalLinkButtonText" name="externalLinkButtonText" disabled={!editorReady || submitting} required={formType === 'request'} ref={register} /> </div> <div className={formStyles.labeledInput}> <label htmlFor="shortDescription"> Short Event Description (Under 250 characters) {formType === 'request' && '*'} </label> <input type="text" id="shortDescription" name="shortDescription" disabled={!editorReady || submitting} required={formType === 'request'} ref={register} /> </div> <div className={formStyles.labeledInput}> <label htmlFor="longDescription"> Long Description{formType === 'request' && '*'} </label> <textarea rows="10" id="longDescription" name="longDescription" disabled={!editorReady || submitting} required={formType === 'request'} ref={register} /> </div> <div className={`${formStyles.customInternalName} ${formStyles.checkbox} ${formStyles.checkboxCentered}`} > <input type="checkbox" name="useCustomInternalName" id="useCustomInternalName" disabled={!editorReady || submitting} ref={register} /> <label htmlFor="useCustomInternalName"> Use Custom Internal Name </label> </div> {useCustomInternalName && ( <div className={`${formStyles.labeledInput}`}> <label htmlFor="internalName"> Internal Name (must-be-lowercase-kebab-case-like-this)* </label> <input type="text" id="internalName" name="internalName" pattern="^([a-z][a-z0-9]*)(-[a-z0-9]+)*$" disabled={!editorReady || submitting} required={useCustomInternalName} ref={register} /> </div> )} {formType === 'create' && ( <Button classNamePassed={`${formStyles.formButton} ${commonStyles.actionButton}`} type="submit" disabled={!editorReady || submitting} > {submitting ? 'Creating Event...' : 'Create Event'} </Button> )} {formType === 'request' && ( <Button classNamePassed={`${formStyles.formButton} ${commonStyles.actionButton}`} type="submit" disabled={!editorReady || submitting} > {submitting ? 'Submitting Request...' : 'Submit Request'} </Button> )} {formType === 'edit' && ( <Button classNamePassed={`${formStyles.formButton} ${commonStyles.actionButton}`} type="submit" disabled={!editorReady || submitting} > {submitting ? 'Saving...' : 'Save Changes'} </Button> )} </form> </> ); }; export default EventEditor;