Skip to content

Commit

Permalink
Integration of React-Select into Add Student, proper styling and cust…
Browse files Browse the repository at this point in the history
…om needs
  • Loading branch information
Atikpui007 committed Nov 15, 2024
1 parent b835bc1 commit 844c568
Show file tree
Hide file tree
Showing 5 changed files with 501 additions and 118 deletions.
184 changes: 161 additions & 23 deletions frontend/src/components/Modal/RiderModalInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { useForm, SubmitHandler } from 'react-hook-form';
import { useForm, SubmitHandler, Controller } from 'react-hook-form';
import Select, { StylesConfig } from 'react-select';
import cn from 'classnames';
import { Button, Input, Label } from '../FormElements/FormElements';
import styles from './ridermodal.module.css';
Expand All @@ -12,11 +13,16 @@ type ModalFormProps = {
rider?: Rider;
};

type NeedOption = {
value: Accessibility | string;
label: string;
};

type FormData = {
name: string;
netid: string;
phoneNumber: string;
needs: Accessibility[];
needs: NeedOption[];
address: string;
joinDate: string;
endDate: string;
Expand All @@ -29,23 +35,99 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
setFormData,
rider,
}) => {
const [showCustomInput, setShowCustomInput] = useState(false);
const [customNeed, setCustomNeed] = useState('');

const {
register,
control,
formState: { errors },
handleSubmit,
getValues,
setValue,
} = useForm<FormData>({
defaultValues: {
name: (rider?.firstName ?? '') + (rider?.lastName ?? ''),
netid: rider?.email.split('@')[0] ?? '',
phoneNumber: rider?.phoneNumber ?? '',
needs: [],
needs:
rider?.accessibility?.map((need) => ({
value: need as Accessibility,
label: need,
})) ?? [],
address: rider?.address ?? '',
joinDate: rider?.joinDate ?? '',
endDate: rider?.endDate ?? '',
},
});

const customStyles: StylesConfig<NeedOption, true> = {
option: (baseStyles, { data }) => ({
...baseStyles,
...(data.value === 'OTHER' && {
color: '#0066cc',
fontStyle: 'italic',
backgroundColor: '#f8f9fa',
borderTop: '1px solid #e9ecef',
marginTop: '4px',
paddingTop: '8px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
'&:before': {
content: '"+"',
marginRight: '8px',
fontSize: '14px',
fontWeight: 'bold',
},
'&:hover': {
backgroundColor: '#e9ecef',
color: '#004c99',
},
}),
}),
menu: (baseStyles) => ({
...baseStyles,
padding: '4px 0',
}),
};

const handleNeedsChange = (
selectedOptions: readonly NeedOption[] | null,
{ action }: any
) => {
if (selectedOptions?.some((option) => option.value === 'OTHER')) {
const filteredOptions = [
...selectedOptions.filter((opt) => opt.value !== 'OTHER'),
];
setValue('needs', filteredOptions);
setShowCustomInput(true);
setCustomNeed('');
} else {
setValue('needs', selectedOptions ? [...selectedOptions] : []);
setShowCustomInput(false);
}
};

const handleAddCustomNeed = () => {
if (customNeed.trim()) {
const currentNeeds = getValues('needs') || [];
const newNeed: NeedOption = {
value: customNeed.toUpperCase().replace(/\s+/g, '_'),
label: customNeed.trim(),
};

setValue('needs', [...currentNeeds, newNeed]);
setCustomNeed('');
setShowCustomInput(false);
}
};

const handleCancelCustomNeed = () => {
setCustomNeed('');
setShowCustomInput(false);
};

const beforeSubmit: SubmitHandler<FormData> = ({
name,
netid,
Expand All @@ -56,7 +138,7 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
endDate,
}) => {
const email = netid ? `${netid}@cornell.edu` : undefined;
const accessibility = needs;
const accessibility = needs.map((option) => option.value.toString());
const nameParts = name.trim().split(/\s+/);
const firstName =
nameParts.length > 1 ? nameParts.slice(0, -1).join(' ') : nameParts[0];
Expand All @@ -74,7 +156,6 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
};

console.log('Form payload:', payload);

onSubmit(payload);
};

Expand All @@ -86,7 +167,14 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
const localUserType = localStorage.getItem('userType');
const isEditing = rider !== undefined;
const isStudentEditing = isEditing && localUserType === 'Rider';
const [needsOption, setNeedsOption] = useState('');

const needsOptions: NeedOption[] = [
...Object.values(Accessibility).map((value) => ({
value: value as Accessibility,
label: value,
})),
{ value: 'OTHER', label: 'Add Custom Need' },
];

return (
<form onSubmit={handleSubmit(beforeSubmit)} className={styles.form}>
Expand All @@ -106,6 +194,7 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
/>
{errors.name && <p className={styles.error}>Name cannot be empty</p>}
</div>

<div className={cn(styles.gridR1, styles.gridCSmall2)}>
<Label className={styles.label} htmlFor="netid">
NetID:{' '}
Expand All @@ -125,6 +214,7 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
<p className={styles.error}>NetId cannot be empty</p>
)}
</div>

<div className={cn(styles.gridR1, styles.gridCSmall3)}>
<Label className={styles.label} htmlFor="phoneNumber">
Phone Number:{' '}
Expand All @@ -144,26 +234,69 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
)}
</div>

{/* Replacing SelectComponent with native <select> */}
<div className={cn(styles.gridR2, styles.gridCBig1)}>
<Label className={styles.label} htmlFor="needs">
Needs:{' '}
</Label>
<select
id="needs"
{...register('needs', { required: true })}
multiple
className={styles.firstRow}
>
{Object.entries(Accessibility).map(([key, value]) => (
<option key={key} value={value}>
{value}
</option>
))}
</select>
{errors.needs && (
<p className={styles.error}>Please select at least one need</p>
)}
<div className={styles.needsContainer}>
<Controller
name="needs"
control={control}
rules={{ required: true }}
render={({ field: { onChange, value, ...field } }) => (
<Select<NeedOption, true>
{...field}
value={value}
isMulti
options={needsOptions}
className={styles.customSelect}
classNamePrefix="customSelectValueContainer"
placeholder="Select needs..."
styles={customStyles}
onChange={(newValue, actionMeta) =>
handleNeedsChange(newValue, actionMeta)
}
/>
)}
/>
{showCustomInput && (
<div className={styles.customNeedInput}>
<input
type="text"
value={customNeed}
onChange={(e) => setCustomNeed(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleAddCustomNeed();
}
}}
placeholder="Type custom need"
className={styles.customNeedField}
autoFocus
/>
<div className={styles.customNeedActions}>
<button
type="button"
onClick={handleAddCustomNeed}
className={styles.customNeedButton}
>
</button>
<button
type="button"
onClick={handleCancelCustomNeed}
className={styles.customNeedButton}
>
</button>
</div>
</div>
)}
{errors.needs && (
<p className={styles.error}>Please select at least one need</p>
)}
</div>
</div>

<div className={cn(styles.gridR2, styles.gridCBig2)}>
Expand All @@ -178,11 +311,13 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
})}
type="text"
aria-required="true"
style={{ height: '60px' }}
/>
{errors.address && (
<p className={styles.error}>Please enter an address</p>
)}
</div>

<div className={cn(styles.gridR3, styles.gridCAll)}>
<p>Duration</p>
<div className={styles.lastRow}>
Expand All @@ -202,7 +337,9 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
<p className={styles.error}>Please enter a join date</p>
)}
</div>
<p className={styles.to}>to</p>
<div className={styles.to}>
<p></p>
</div>
<div>
<Label className={styles.label} htmlFor="endDate">
End Date:{' '}
Expand Down Expand Up @@ -231,6 +368,7 @@ const RiderModalInfo: React.FC<ModalFormProps> = ({
</div>
</div>
</div>

<div className={styles.buttonContainer}>
<Button
type="button"
Expand Down
Loading

0 comments on commit 844c568

Please sign in to comment.