Skip to content

Commit afb9d1f

Browse files
committed
#2228 - update slack ping, setup confirm availability flow
1 parent a305fa9 commit afb9d1f

9 files changed

Lines changed: 108 additions & 16 deletions

File tree

src/backend/src/services/design-reviews.services.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,16 @@ export default class DesignReviewsService {
158158
throw new NotFoundException('User Settings', 'Cannot find settings of members');
159159
}
160160

161+
console.log(memberUserSettings);
161162
// send a slack message to all leadership invited to the design review
162163
for (const memberUserSetting of memberUserSettings) {
163164
if (memberUserSetting.slackId) {
164165
try {
165-
await sendSlackDesignReviewNotification(memberUserSetting.slackId, designReview.designReviewId);
166+
await sendSlackDesignReviewNotification(
167+
memberUserSetting.slackId,
168+
designReview.designReviewId,
169+
designReview.wbsElement.name
170+
);
166171
} catch (err: unknown) {
167172
if (err instanceof Error) {
168173
throw new HttpException(500, `Failed to send slack notification: ${err.message}`);
@@ -321,19 +326,16 @@ export default class DesignReviewsService {
321326
if (!isUserOnDesignReview(submitter, designReviewTransformer(designReview)))
322327
throw new HttpException(400, 'Current user is not in the list of this design reviews members');
323328

324-
// Update user schedule settings
325-
const validAvailability = validateMeetingTimes(availability);
326-
327329
await prisma.schedule_Settings.upsert({
328330
where: { userId: submitter.userId },
329331
update: {
330-
availability: validAvailability
332+
availability
331333
},
332334
create: {
333335
userId: submitter.userId,
334336
personalGmail: '',
335337
personalZoomLink: '',
336-
availability: validAvailability
338+
availability
337339
}
338340
});
339341

src/backend/src/utils/slack.utils.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,15 @@ export const sendReimbursementRequestDeniedNotification = async (slackId: string
9191
}
9292
};
9393

94-
export const sendSlackDesignReviewNotification = async (slackId: string, designReviewId: string) => {
94+
export const sendSlackDesignReviewNotification = async (
95+
slackId: string,
96+
designReviewId: string,
97+
designReviewName: string
98+
) => {
9599
if (process.env.NODE_ENV !== 'production') return; // don't send msgs unless in prod
96-
const msg = `You have been invited to a Design Review!`;
97-
const fullLink = `https://finishlinebyner.com/design-reviews/${designReviewId}`;
98-
const linkButtonText = 'RSVP for the Design Review';
100+
const msg = `You have been invited to the ${designReviewName} Design Review!`;
101+
const fullLink = `https://finishlinebyner.com/settings/preferences?drId=${designReviewId}`;
102+
const linkButtonText = 'Confirm Availability';
99103

100104
try {
101105
await sendMessage(slackId, msg, fullLink, linkButtonText);

src/frontend/src/apis/design-reviews.api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,7 @@ export const getSingleDesignReview = async (id: string) => {
5757
transformResponse: (data) => designReviewTransformer(JSON.parse(data))
5858
});
5959
};
60+
61+
export const markUserConfirmed = async (id: string, payload: { availability: number[] }) => {
62+
return axios.post<DesignReview>(apiUrls.designReviewMarkUserConfirmed(id), payload);
63+
};

src/frontend/src/hooks/design-reviews.hooks.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import {
99
createDesignReviews,
1010
getAllDesignReviews,
1111
getAllTeamTypes,
12-
getSingleDesignReview
12+
getSingleDesignReview,
13+
markUserConfirmed
1314
} from '../apis/design-reviews.api';
15+
import { useCurrentUser } from './users.hooks';
1416

1517
export interface CreateDesignReviewsPayload {
1618
dateScheduled: Date;
@@ -107,3 +109,21 @@ export const useSingleDesignReview = (id: string) => {
107109
return data;
108110
});
109111
};
112+
113+
export const useMarkUserConfirmed = (id: string) => {
114+
const user = useCurrentUser();
115+
const queryClient = useQueryClient();
116+
return useMutation<DesignReview, Error, { availability: number[] }>(
117+
['design-reviews', 'mark-confirmed'],
118+
async (designReviewPayload: { availability: number[] }) => {
119+
const { data } = await markUserConfirmed(id, designReviewPayload);
120+
return data;
121+
},
122+
{
123+
onSuccess: () => {
124+
queryClient.invalidateQueries(['design-reviews']);
125+
queryClient.invalidateQueries(['users', user.userId, 'schedule-settings']);
126+
}
127+
}
128+
);
129+
};

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@ interface DRCEditModalProps {
77
availabilites: number[];
88
setAvailabilities: (availabilities: number[]) => void;
99
onHide: () => void;
10+
onSubmit: () => void;
1011
}
1112

12-
const AvailabilityEditModal: React.FC<DRCEditModalProps> = ({ open, onHide, header, availabilites, setAvailabilities }) => {
13+
const AvailabilityEditModal: React.FC<DRCEditModalProps> = ({
14+
open,
15+
onHide,
16+
header,
17+
availabilites,
18+
setAvailabilities,
19+
onSubmit
20+
}) => {
1321
const existingMeetingData = new Map<number, string>();
1422

1523
return (
16-
<NERModal open={open} onHide={onHide} title={header} onSubmit={onHide} submitText="Save">
24+
<NERModal open={open} onHide={onHide} title={header} onSubmit={onSubmit} submitText="Save">
1725
<EditAvailability
1826
selectedTimes={availabilites}
1927
setSelectedTimes={setAvailabilities}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { useUpdateUserScheduleSettings, useUserScheduleSettings } from '../../..
1616
import LoadingIndicator from '../../../components/LoadingIndicator';
1717
import ErrorPage from '../../ErrorPage';
1818
import { useToast } from '../../../hooks/toasts.hooks';
19+
import { useSingleDesignReview } from '../../../hooks/design-reviews.hooks';
20+
import { useQuery } from '../../../hooks/utils.hooks';
1921

2022
export interface ScheduleSettingsFormInput {
2123
personalGmail: string;
@@ -29,6 +31,8 @@ export interface ScheduleSettingsPayload extends ScheduleSettingsFormInput {
2931
const UserScheduleSettings = ({ user }: { user: User }) => {
3032
const [edit, setEdit] = useState(false);
3133
const toast = useToast();
34+
const query = useQuery();
35+
const designReviewId = query.get('drId');
3236

3337
const { data, isLoading, isError, error } = useUserScheduleSettings(user.userId);
3438
const {
@@ -37,9 +41,17 @@ const UserScheduleSettings = ({ user }: { user: User }) => {
3741
isError: updateUserScheduleSettingsIsError,
3842
error: updateUserScheduleSettingsError
3943
} = useUpdateUserScheduleSettings();
44+
const {
45+
data: designReview,
46+
isError: designReviewIsError,
47+
error: designReviewError,
48+
isLoading: designReviewIsLoading
49+
} = useSingleDesignReview(designReviewId || '');
4050

51+
if (designReviewId && (!designReview || designReviewIsLoading)) return <LoadingIndicator />;
4152
if (!data || isLoading || updateUserScheduleSettingsIsLoading) return <LoadingIndicator />;
4253

54+
if (designReviewId && designReviewIsError) return <ErrorPage message={designReviewError.message} />;
4355
if (isError) return <ErrorPage error={error} message={error.message} />;
4456
if (updateUserScheduleSettingsIsError)
4557
return <ErrorPage error={updateUserScheduleSettingsError!} message={updateUserScheduleSettingsError?.message} />;
@@ -81,7 +93,7 @@ const UserScheduleSettings = ({ user }: { user: User }) => {
8193
}
8294
>
8395
{!edit ? (
84-
<UserScheduleSettingsView scheduleSettings={data} />
96+
<UserScheduleSettingsView scheduleSettings={data} designReview={designReview} />
8597
) : (
8698
<UserScheduleSettingsEdit onSubmit={handleConfirm} defaultValues={data} />
8799
)}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const UserScheduleSettingsEdit: React.FC<UserScheduleSettingsEditProps> = ({ onS
5353
<AvailabilityEditModal
5454
open={editAvailabilityOpen}
5555
onHide={() => setEditAvailability(false)}
56+
onSubmit={() => setEditAvailability(false)}
5657
header="Edit Availability"
5758
availabilites={availabilities}
5859
setAvailabilities={setAvailabilities}

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

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,43 @@
66
import { Grid } from '@mui/material';
77
import DetailDisplay from '../../../components/DetailDisplay';
88
import { NERButton } from '../../../components/NERButton';
9-
import { UserScheduleSettings } from 'shared';
9+
import { DesignReview, UserScheduleSettings } from 'shared';
1010
import { useState } from 'react';
1111
import SingleAvailabilityModal from './Availability/SingleAvailabilityModal';
12+
import { useCurrentUser } from '../../../hooks/users.hooks';
13+
import AvailabilityEditModal from './Availability/AvailabilityEditModal';
14+
import { useMarkUserConfirmed } from '../../../hooks/design-reviews.hooks';
15+
import { useToast } from '../../../hooks/toasts.hooks';
1216

13-
const UserScheduleSettingsView = ({ scheduleSettings }: { scheduleSettings: UserScheduleSettings }) => {
17+
const UserScheduleSettingsView = ({
18+
scheduleSettings,
19+
designReview
20+
}: {
21+
scheduleSettings: UserScheduleSettings;
22+
designReview?: DesignReview;
23+
}) => {
1424
const [availabilityOpen, setAvailabilityOpen] = useState(false);
25+
const toast = useToast();
26+
const user = useCurrentUser();
27+
const defaultOpen = designReview && !designReview.confirmedMembers.map((user) => user.userId).includes(user.userId);
28+
const [confirmAvailabilityOpen, setConfirmAvailabilityOpen] = useState(defaultOpen || false);
29+
const [confirmedAvailabilities, setConfirmedAvailabilities] = useState(scheduleSettings.availability);
30+
const { mutateAsync } = useMarkUserConfirmed(designReview?.designReviewId || '');
31+
const confirmModalTitle = `Update your availability for the ${
32+
designReview?.wbsName
33+
} Design Review on the week of ${designReview?.dateScheduled.toLocaleDateString()}`;
34+
35+
const handleConfirm = async (payload: { availability: number[] }) => {
36+
setConfirmAvailabilityOpen(false);
37+
try {
38+
await mutateAsync(payload);
39+
toast.success('Availability Confirmed!');
40+
} catch (e) {
41+
if (e instanceof Error) {
42+
toast.error(e.message);
43+
}
44+
}
45+
};
1546

1647
return (
1748
<Grid container spacing={6} sx={{ pt: '10px' }}>
@@ -21,6 +52,14 @@ const UserScheduleSettingsView = ({ scheduleSettings }: { scheduleSettings: User
2152
header={'Availability'}
2253
availabilites={scheduleSettings.availability}
2354
/>
55+
<AvailabilityEditModal
56+
open={confirmAvailabilityOpen}
57+
onHide={() => setConfirmAvailabilityOpen(false)}
58+
header={confirmModalTitle}
59+
availabilites={confirmedAvailabilities}
60+
setAvailabilities={setConfirmedAvailabilities}
61+
onSubmit={() => handleConfirm({ availability: confirmedAvailabilities })}
62+
/>
2463
<Grid item xs={12} sm={6} lg={4}>
2564
<DetailDisplay label="Personal Google Email" content={scheduleSettings.personalGmail} />
2665
</Grid>

src/frontend/src/utils/urls.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ const designReviewsCreate = () => `${designReviews()}/create`;
137137
const teamTypes = () => `${designReviews()}/teamType/all`;
138138
const designReviewsEdit = (designReviewId: string) => `${designReviews()}/${designReviewId}/edit`;
139139
const designReviewById = (id: string) => `${designReviews()}/${id}`;
140+
const designReviewMarkUserConfirmed = (id: string) => `${designReviewById(id)}/confirm-schedule`;
140141

141142
/**************** Other Endpoints ****************/
142143
const version = () => `https://api.github.com/repos/Northeastern-Electric-Racing/FinishLine/releases/latest`;
@@ -248,6 +249,7 @@ export const apiUrls = {
248249
designReviewsCreate,
249250
designReviewById,
250251
designReviewsEdit,
252+
designReviewMarkUserConfirmed,
251253
teamTypes,
252254

253255
version

0 commit comments

Comments
 (0)