Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import ErrorPage from '../../ErrorPage';
import LoadingIndicator from '../../../components/LoadingIndicator';
import { useCreateGuestDefinition } from '../../../hooks/recruitment.hooks';
import { GuestDefinitionType } from 'shared';
import GuestDefinitionFormModal from './GuestDefinitionFormModal';

interface CreateGuestDefinitionFormModalProps {
open: boolean;
handleClose: () => void;
type: GuestDefinitionType;
}

const CreateGuestDefinitionFormModal = ({ open, handleClose, type }: CreateGuestDefinitionFormModalProps) => {
const { isLoading, isError, error, mutateAsync } = useCreateGuestDefinition();

if (isError) return <ErrorPage message={error?.message} />;
if (isLoading) return <LoadingIndicator />;

return <GuestDefinitionFormModal open={open} handleClose={handleClose} type={type} onSubmit={mutateAsync} />;
};

export default CreateGuestDefinitionFormModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import ErrorPage from '../../ErrorPage';
import LoadingIndicator from '../../../components/LoadingIndicator';
import { useEditGuestDefinitions } from '../../../hooks/recruitment.hooks';
import { GuestDefinition } from 'shared';
import GuestDefinitionFormModal from './GuestDefinitionFormModal';

interface EditGuestDefinitionFormModalProps {
open: boolean;
handleClose: () => void;
definition: GuestDefinition;
}

const EditGuestDefinitionFormModal = ({ open, handleClose, definition }: EditGuestDefinitionFormModalProps) => {
const { isLoading, isError, error, mutateAsync } = useEditGuestDefinitions(definition.definitionId);

if (isError) return <ErrorPage message={error?.message} />;
if (isLoading) return <LoadingIndicator />;

return (
<GuestDefinitionFormModal
open={open}
handleClose={handleClose}
type={definition.type}
defaultValues={definition}
onSubmit={mutateAsync}
/>
);
};

export default EditGuestDefinitionFormModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useForm } from 'react-hook-form';
import NERFormModal from '../../../components/NERFormModal';
import { FormControl, FormLabel, FormHelperText } from '@mui/material';
import ReactHookTextField from '../../../components/ReactHookTextField';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import useFormPersist from 'react-hook-form-persist';
import { FormStorageKey } from '../../../utils/form';
import { GuestDefinitionPayload } from '../../../hooks/recruitment.hooks';
import { GuestDefinition, GuestDefinitionType } from 'shared';

interface GuestDefinitionFormModalProps {
open: boolean;
handleClose: () => void;
type: GuestDefinitionType;
defaultValues?: GuestDefinition;
onSubmit: (data: GuestDefinitionPayload) => Promise<GuestDefinition>;
}

const schema = yup.object().shape({
term: yup.string().required('Term is required'),
description: yup.string().required('Description is required'),
order: yup.number().required('Order is required').min(0, 'Order must be non-negative'),
icon: yup.string().optional(),
buttonText: yup.string().optional(),
buttonLink: yup.string().optional()
});

const GuestDefinitionFormModal: React.FC<GuestDefinitionFormModalProps> = ({
open,
handleClose,
type,
defaultValues,
onSubmit
}) => {
const onFormSubmit = async (data: Omit<GuestDefinitionPayload, 'type'>) => {
await onSubmit({
...data,
type,
icon: data.icon || undefined,
buttonText: data.buttonText || undefined,
buttonLink: data.buttonLink || undefined
});
handleClose();
};

const {
handleSubmit,
control,
reset,
formState: { errors },
watch,
setValue
} = useForm({
resolver: yupResolver(schema),
defaultValues: {
term: defaultValues?.term ?? '',
description: defaultValues?.description ?? '',
order: defaultValues?.order ?? 0,
icon: defaultValues?.icon ?? '',
buttonText: defaultValues?.buttonText ?? '',
buttonLink: defaultValues?.buttonLink ?? ''
}
});

const formStorageKey = defaultValues ? FormStorageKey.EDIT_GUEST_DEFINITION : FormStorageKey.CREATE_GUEST_DEFINITION;

useFormPersist(formStorageKey, { watch, setValue });

const handleCancel = () => {
reset({ term: '', description: '', order: 0, icon: '', buttonText: '', buttonLink: '' });
sessionStorage.removeItem(formStorageKey);
handleClose();
};

return (
<NERFormModal
open={open}
onHide={handleCancel}
title={`${defaultValues ? 'Edit' : 'New'} ${type === GuestDefinitionType.PROJECT_MANAGEMENT ? 'Project Management' : 'Info Page'} Definition`}
reset={() => reset({ term: '', description: '', order: 0, icon: '', buttonText: '', buttonLink: '' })}
handleUseFormSubmit={handleSubmit}
onFormSubmit={onFormSubmit}
formId="guest-definition-form"
showCloseButton
>
<FormControl fullWidth>
<FormLabel>Term*</FormLabel>
<ReactHookTextField name="term" control={control} placeholder="Enter term" sx={{ width: 1 }} />
<FormHelperText error>{errors.term?.message}</FormHelperText>
</FormControl>
<FormControl fullWidth>
<FormLabel>Description*</FormLabel>
<ReactHookTextField name="description" control={control} placeholder="Enter description" />
<FormHelperText error>{errors.description?.message}</FormHelperText>
</FormControl>
<FormControl fullWidth>
<FormLabel>Order*</FormLabel>
<ReactHookTextField name="order" control={control} type="number" placeholder="Enter display order" />
<FormHelperText error>{errors.order?.message}</FormHelperText>
</FormControl>
<FormControl fullWidth>
<FormLabel>Icon</FormLabel>
<ReactHookTextField name="icon" control={control} placeholder="Enter icon name (optional)" />
<FormHelperText error>{errors.icon?.message}</FormHelperText>
</FormControl>
<FormControl fullWidth>
<FormLabel>Button Text</FormLabel>
<ReactHookTextField name="buttonText" control={control} placeholder="Enter button text (optional)" />
<FormHelperText error>{errors.buttonText?.message}</FormHelperText>
</FormControl>
<FormControl fullWidth>
<FormLabel>Button Link</FormLabel>
<ReactHookTextField name="buttonLink" control={control} placeholder="Enter button link (optional)" />
<FormHelperText error>{errors.buttonLink?.message}</FormHelperText>
</FormControl>
</NERFormModal>
);
};

export default GuestDefinitionFormModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React, { useState } from 'react';
import { TableRow, TableCell, Box, Table as MuiTable, TableHead, TableBody, Typography, Button } from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import { GuestDefinition, GuestDefinitionType } from 'shared';
import { NERButton } from '../../../components/NERButton';
import { useAllGuestDefinitions, useDeleteGuestDefinition } from '../../../hooks/recruitment.hooks';
import LoadingIndicator from '../../../components/LoadingIndicator';
import { useHistoryState } from '../../../hooks/misc.hooks';
import ErrorPage from '../../ErrorPage';
import NERDeleteModal from '../../../components/NERDeleteModal';
import { useToast } from '../../../hooks/toasts.hooks';
import CreateGuestDefinitionFormModal from './CreateGuestDefinitionFormModal';
import EditGuestDefinitionFormModal from './EditGuestDefinitionFormModal';

interface GuestDefinitionsTableProps {
type: GuestDefinitionType;
}

const GuestDefinitionsTable = ({ type }: GuestDefinitionsTableProps) => {
const [createModalShow, setCreateModalShow] = useHistoryState<boolean>('', false);
const [definitionEditing, setDefinitionEditing] = useHistoryState<GuestDefinition | undefined>('', undefined);
const [definitionToDelete, setDefinitionToDelete] = useState<GuestDefinition | undefined>(undefined);
const { mutateAsync: deleteGuestDefinition } = useDeleteGuestDefinition();
const toast = useToast();

const { isLoading, isError, error, data: allDefinitions } = useAllGuestDefinitions();

const handleDelete = async (id: string) => {
setDefinitionToDelete(undefined);
try {
await deleteGuestDefinition(id);
toast.success('Guest definition deleted successfully');
} catch (e: unknown) {
if (e instanceof Error) {
toast.error(e.message, 3000);
}
}
};

if (isError) return <ErrorPage message={error.message} />;
Comment thread
staysgt marked this conversation as resolved.
if (!allDefinitions || isLoading) return <LoadingIndicator />;

const definitions = allDefinitions.filter((d) => d.type === type);

const rows = definitions.map((definition: GuestDefinition, index: number) => (
<TableRow key={definition.definitionId}>
<TableCell
align="left"
sx={{ borderBottom: index === definitions.length - 1 ? 'none' : 'default', alignItems: 'center' }}
>
<Typography>{definition.term}</Typography>
</TableCell>
<TableCell
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
borderBottom: index === definitions.length - 1 ? 'none' : 'default',
minHeight: '50px'
}}
>
<Typography sx={{ maxWidth: 300 }}>{definition.description}</Typography>
<Box sx={{ display: 'flex' }}>
<Button sx={{ p: 0.5, color: 'white' }} onClick={() => setDefinitionEditing(definition)}>
<EditIcon />
</Button>
<Button sx={{ p: 0.5, color: 'white' }} onClick={() => setDefinitionToDelete(definition)}>
<DeleteIcon />
</Button>
</Box>
</TableCell>
</TableRow>
));

return (
<Box>
{createModalShow && (
<CreateGuestDefinitionFormModal open={createModalShow} handleClose={() => setCreateModalShow(false)} type={type} />
)}
{definitionEditing && (
<EditGuestDefinitionFormModal
open={!!definitionEditing}
handleClose={() => setDefinitionEditing(undefined)}
definition={definitionEditing}
/>
)}
<MuiTable>
<TableHead>
<TableRow>
<TableCell
sx={{
fontWeight: 'bold',
fontSize: '1em',
backgroundColor: '#ef4345',
color: 'white',
borderRadius: '10px 0px 0px 0px'
}}
>
Term
</TableCell>
<TableCell
sx={{
fontWeight: 'bold',
fontSize: '1em',
backgroundColor: '#ef4345',
color: 'white',
borderRadius: '0px 10px 0px 0px'
}}
>
Description
</TableCell>
</TableRow>
</TableHead>
<TableBody>{rows}</TableBody>
</MuiTable>
<Box sx={{ display: 'flex', justifyContent: 'right', marginTop: '20px' }}>
<NERButton variant="contained" onClick={() => setCreateModalShow(true)}>
Add Guest Definition
</NERButton>
</Box>
{definitionToDelete && (
<NERDeleteModal
open={!!definitionToDelete}
onHide={() => setDefinitionToDelete(undefined)}
formId="delete-guest-definition-form"
dataType="Guest Definition"
onFormSubmit={() => {
handleDelete(definitionToDelete.definitionId);
}}
/>
)}
</Box>
);
};

export default GuestDefinitionsTable;
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { useToast } from '../../../hooks/toasts.hooks';
import { MAX_FILE_SIZE } from 'shared';
import UsefulLinksTable from '../OnboardingConfig/UsefulLinks/UsefulLinksTable';
import LinkTypeTable from '../ProjectsConfig/LinkTypes/LinkTypeTable';
import GuestDefinitionsTable from './GuestDefinitionsTable';
import { GuestDefinitionType } from 'shared';

const platformDescriptionSchema = yup.object().shape({
platformDescription: yup.string().required()
Expand Down Expand Up @@ -178,6 +180,18 @@ const GuestViewConfig: React.FC = () => {
</Typography>
<LinkTypeTable isOnGuestHomePage={true} />
</Grid>
<Grid item xs={6}>
<Typography variant="h5" gutterBottom borderBottom={1} color="#ef4345" borderColor={'white'}>
Project Management Definitions
</Typography>
<GuestDefinitionsTable type={GuestDefinitionType.PROJECT_MANAGEMENT} />
</Grid>
<Grid item xs={6}>
<Typography variant="h5" gutterBottom borderBottom={1} color="#ef4345" borderColor={'white'}>
Info Page Definitions
</Typography>
<GuestDefinitionsTable type={GuestDefinitionType.INFO_PAGE} />
</Grid>
</Grid>
);
};
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/src/utils/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,7 @@ export enum FormStorageKey {
CREATE_MACHINERY = 'CREATE_MACHINERY',
EDIT_MACHINERY = 'EDIT_MACHINERY',
CREATE_EVENT_TYPE = 'CREATE_EVENT_TYPE',
EDIT_EVENT_TYPE = 'EDIT_EVENT_TYPE'
EDIT_EVENT_TYPE = 'EDIT_EVENT_TYPE',
EDIT_GUEST_DEFINITION = 'EDIT_GUEST_DEFINITION',
CREATE_GUEST_DEFINITION = 'CREATE_GUEST_DEFINITION'
}
Loading