Skip to content

Commit 60b0758

Browse files
committed
#2132 - hook up schedule settings edit
1 parent fdc8ce3 commit 60b0758

13 files changed

Lines changed: 161 additions & 43 deletions

File tree

src/backend/src/services/users.services.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { User_Settings, User as PrismaUser, Schedule_Settings } from '@prisma/client';
1+
import { User_Settings, User as PrismaUser } from '@prisma/client';
22
import { OAuth2Client } from 'google-auth-library/build/src/auth/oauth2client';
33
import {
44
AuthenticatedUser,
@@ -353,7 +353,7 @@ export default class UsersService {
353353
personalGmail: string,
354354
personalZoomLink: string,
355355
availability: number[]
356-
): Promise<Schedule_Settings> {
356+
): Promise<UserScheduleSettings> {
357357
const existingUser = await prisma.schedule_Settings.findFirst({
358358
where: { personalGmail, userId: { not: user.userId } } // excludes the current user from check
359359
});
@@ -376,6 +376,6 @@ export default class UsersService {
376376
availability
377377
}
378378
});
379-
return newUserScheduleSettings;
379+
return userScheduleSettingsTransformer(newUserScheduleSettings);
380380
}
381381
}

src/frontend/src/apis/users.api.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ export const updateUserSecureSettings = (settings: UserSecureSettings) => {
117117
return axios.post<{ message: string }>(apiUrls.userSecureSettingsSet(), settings);
118118
};
119119

120+
/**
121+
* Update the given user's schedule settings by UserId
122+
*/
123+
export const updateUserScheduleSettings = (settings: UserScheduleSettings) => {
124+
return axios.post<UserScheduleSettings>(apiUrls.userScheduleSettingsSet(), settings);
125+
};
126+
120127
export const updateUserRole = (id: number, role: string) => {
121128
return axios.post<{ message: string }>(apiUrls.userRoleByUserId(`${id}`), { role });
122129
};

src/frontend/src/pages/CalendarPage/SchedulingComponents/TimeSlot.tsx renamed to src/frontend/src/components/TimeSlot.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const TimeSlot: React.FC<TimeSlotProps> = ({
5151
height: small ? '25px' : '4.7vh',
5252
width: small ? '81px' : '12.2%',
5353
backgroundColor,
54-
cursor: 'pointer',
54+
cursor: onMouseEnter ? 'pointer' : undefined,
5555
borderStyle: 'solid',
5656
borderColor: 'gray',
5757
borderWidth: '0.1px',

src/frontend/src/hooks/users.hooks.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import {
1616
updateUserSecureSettings,
1717
getCurrentUserSecureSettings,
1818
getUserSecureSettings,
19-
getUserScheduleSettings
19+
getUserScheduleSettings,
20+
updateUserScheduleSettings
2021
} from '../apis/users.api';
2122
import {
2223
User,
@@ -188,6 +189,28 @@ export const useUpdateUserSecureSettings = () => {
188189
);
189190
};
190191

192+
/**
193+
* Custom Hook to update a user's schedule settings
194+
*
195+
* @returns The mutation to update a user's schedule settings
196+
*/
197+
export const useUpdateUserScheduleSettings = () => {
198+
const user = useCurrentUser();
199+
const queryClient = useQueryClient();
200+
return useMutation<UserScheduleSettings, Error, UserScheduleSettings>(
201+
['users', 'schedule-settings', 'update'],
202+
async (settings: UserScheduleSettings) => {
203+
const { data } = await updateUserScheduleSettings(settings);
204+
return data;
205+
},
206+
{
207+
onSuccess: () => {
208+
queryClient.invalidateQueries(['users', user.userId, 'schedule-settings']);
209+
}
210+
}
211+
);
212+
};
213+
191214
/**
192215
* Custom React Hook to update a user's role.
193216
*/

src/frontend/src/pages/CalendarPage/DesignReviewDetailPage/AvailabilityScheduleView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
HeatmapColors,
99
getBackgroundColor
1010
} from '../../../utils/design-review.utils';
11-
import TimeSlot from '../SchedulingComponents/TimeSlot';
11+
import TimeSlot from '../../../components/TimeSlot';
1212

1313
interface AvailabilityScheduleViewProps {
1414
availableUsers: Map<number, User[]>;

src/frontend/src/pages/CalendarPage/SchedulingComponents/AvailabilityEditModal.tsx renamed to src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/AvailabilityEditModal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import NERModal from '../../../components/NERModal';
1+
import NERModal from '../../../../components/NERModal';
22
import EditAvailability from './EditAvailability';
33

44
interface DRCEditModalProps {
@@ -13,7 +13,7 @@ const AvailabilityEditModal: React.FC<DRCEditModalProps> = ({ open, onHide, head
1313
const existingMeetingData = new Map<number, string>();
1414

1515
return (
16-
<NERModal open={open} onHide={onHide} title={header} onSubmit={onHide}>
16+
<NERModal open={open} onHide={onHide} title={header} onSubmit={onHide} submitText="Save">
1717
<EditAvailability
1818
selectedTimes={availabilites}
1919
setSelectedTimes={setAvailabilities}

src/frontend/src/pages/CalendarPage/SchedulingComponents/EditAvailability.tsx renamed to src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/EditAvailability.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Grid } from '@mui/material';
22
import { useState } from 'react';
3-
import { HeatmapColors, EnumToArray, DAY_NAMES, REVIEW_TIMES } from '../../../utils/design-review.utils';
4-
import TimeSlot from './TimeSlot';
3+
import { HeatmapColors, EnumToArray, DAY_NAMES, REVIEW_TIMES } from '../../../../utils/design-review.utils';
4+
import TimeSlot from '../../../../components/TimeSlot';
55

66
interface EditAvailabilityProps {
77
selectedTimes: number[];
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import NERModal from '../../../../components/NERModal';
2+
import SingleAvailabilityView from './SingleAvailabilityView';
3+
4+
interface SingleAvailabilityModalProps {
5+
open: boolean;
6+
header: string;
7+
availabilites: number[];
8+
onHide: () => void;
9+
}
10+
11+
const SingleAvailabilityModal: React.FC<SingleAvailabilityModalProps> = ({ open, onHide, header, availabilites }) => {
12+
const existingMeetingData = new Map<number, string>();
13+
14+
return (
15+
<NERModal open={open} onHide={onHide} title={header} onSubmit={onHide} hideFormButtons showCloseButton>
16+
<SingleAvailabilityView selectedTimes={availabilites} existingMeetingData={existingMeetingData} />
17+
</NERModal>
18+
);
19+
};
20+
21+
export default SingleAvailabilityModal;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Grid } from '@mui/material';
2+
import { HeatmapColors, EnumToArray, DAY_NAMES, REVIEW_TIMES } from '../../../../utils/design-review.utils';
3+
import TimeSlot from '../../../../components/TimeSlot';
4+
5+
interface SingleAvailabilityViewProps {
6+
selectedTimes: number[];
7+
existingMeetingData: Map<number, string>;
8+
}
9+
10+
const SingleAvailabilityView: React.FC<SingleAvailabilityViewProps> = ({ selectedTimes, existingMeetingData }) => {
11+
return (
12+
<Grid container>
13+
<TimeSlot backgroundColor={HeatmapColors[0]} small={true} />
14+
{EnumToArray(DAY_NAMES).map((day) => (
15+
<TimeSlot key={day} backgroundColor={HeatmapColors[0]} small={true} text={day} fontSize={'12px'} />
16+
))}
17+
{EnumToArray(REVIEW_TIMES).map((time, timeIndex) => (
18+
<Grid container item>
19+
<TimeSlot backgroundColor={HeatmapColors[0]} small={true} text={time} fontSize={'13px'} />
20+
{EnumToArray(DAY_NAMES).map((_day, dayIndex) => {
21+
const index = dayIndex * EnumToArray(REVIEW_TIMES).length + timeIndex;
22+
const backgroundColor = selectedTimes.includes(index) ? HeatmapColors[3] : HeatmapColors[0];
23+
return (
24+
<TimeSlot key={index} backgroundColor={backgroundColor} small={true} icon={existingMeetingData.get(index)} />
25+
);
26+
})}
27+
</Grid>
28+
))}
29+
</Grid>
30+
);
31+
};
32+
33+
export default SingleAvailabilityView;

src/frontend/src/pages/SettingsPage/UserScheduleSettings/UserScheduleSettings.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,50 @@ import UserScheduleSettingsView from './UserScheduleSettingsView';
1212
import UserScheduleSettingsEdit from './UserScheduleSettingsEdit';
1313
import PageBlock from '../../../layouts/PageBlock';
1414
import { User } from 'shared';
15-
import { useUserScheduleSettings } from '../../../hooks/users.hooks';
15+
import { useUpdateUserScheduleSettings, useUserScheduleSettings } from '../../../hooks/users.hooks';
1616
import LoadingIndicator from '../../../components/LoadingIndicator';
1717
import ErrorPage from '../../ErrorPage';
18+
import { useToast } from '../../../hooks/toasts.hooks';
1819

1920
export interface ScheduleSettingsFormInput {
20-
email: string;
21-
zoomLink: string;
21+
personalGmail: string;
22+
personalZoomLink: string;
23+
}
24+
25+
export interface ScheduleSettingsPayload extends ScheduleSettingsFormInput {
26+
availability: number[];
2227
}
2328

2429
const UserScheduleSettings = ({ user }: { user: User }) => {
2530
const [edit, setEdit] = useState(false);
31+
const toast = useToast();
2632

2733
const { data, isLoading, isError, error } = useUserScheduleSettings(user.userId);
34+
const {
35+
mutateAsync: updateUserScheduleSettings,
36+
isLoading: updateUserScheduleSettingsIsLoading,
37+
isError: updateUserScheduleSettingsIsError,
38+
error: updateUserScheduleSettingsError
39+
} = useUpdateUserScheduleSettings();
2840

29-
if (!data || isLoading) return <LoadingIndicator />;
41+
if (!data || isLoading || updateUserScheduleSettingsIsLoading) return <LoadingIndicator />;
3042

3143
if (isError) return <ErrorPage error={error} message={error.message} />;
44+
if (updateUserScheduleSettingsIsError)
45+
return <ErrorPage error={updateUserScheduleSettingsError!} message={updateUserScheduleSettingsError?.message} />;
3246

33-
const handleConfirm = async (payload: { email: string; zoomLink: string; availabilities: number[] }) => {
47+
const handleConfirm = async (payload: ScheduleSettingsPayload) => {
3448
setEdit(false);
49+
try {
50+
await updateUserScheduleSettings({
51+
drScheduleSettingsId: data.drScheduleSettingsId,
52+
...payload
53+
});
54+
} catch (e) {
55+
if (e instanceof Error) {
56+
toast.error(e.message);
57+
}
58+
}
3559
};
3660

3761
return (

0 commit comments

Comments
 (0)