Skip to content

Commit ac435cd

Browse files
committed
Implement Minh's and Thomas's suggestions
1 parent 3cdbd54 commit ac435cd

File tree

3 files changed

+31
-23
lines changed

3 files changed

+31
-23
lines changed

course-matrix/frontend/src/pages/Home/TimetableCardKebabMenu.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
import { EllipsisVertical } from "lucide-react";
2020
import { useDeleteTimetableMutation } from "@/api/timetableApiSlice";
2121
import { EmailNotificationSettings } from "./EmailNotificationSettings";
22+
import ShareDialog from "../TimetableBuilder/ShareDialog";
23+
import { useState } from "react";
2224

2325
interface TimetableCardKebabMenuProps {
2426
refetchMyTimetables: () => void;
@@ -35,6 +37,7 @@ const TimetableCardKebabMenu = ({
3537
refetchSharedTimetables,
3638
timetableId,
3739
}: TimetableCardKebabMenuProps) => {
40+
const [openShareDialog, setOpenShareDialog] = useState(false);
3841
const [deleteTimetable] = useDeleteTimetableMutation();
3942

4043
const handleDelete = async () => {
@@ -64,6 +67,10 @@ const TimetableCardKebabMenu = ({
6467
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
6568
<EmailNotificationSettings timetableId={timetableId} />
6669
</DropdownMenuItem>
70+
<DropdownMenuItem onSelect={(e) => e.preventDefault()} className="cursor-pointer" onClick={() => setOpenShareDialog(true)}>
71+
Share Timetable
72+
</DropdownMenuItem>
73+
<ShareDialog open={openShareDialog} setOpen={setOpenShareDialog} calendar_id={timetableId} />
6774
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
6875
<Dialog>
6976
<DialogTrigger asChild>

course-matrix/frontend/src/pages/TimetableBuilder/ShareButton.tsx renamed to course-matrix/frontend/src/pages/TimetableBuilder/ShareDialog.tsx

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,49 +13,47 @@ import { Input } from "@/components/ui/input";
1313
import { Label } from "@/components/ui/label";
1414
import { useRef, useState } from "react";
1515
import { useCreateShareMutation } from "@/api/sharedApiSlice";
16-
import TimetableErrorDialog from "./TimetableErrorDialog";
1716
import TimetableSuccessDialog from "./TimetableSuccessDialog";
17+
import { Spinner } from "@/components/ui/spinner";
1818

19-
interface ShareButtonProps {
19+
interface ShareDialogProps {
20+
open: boolean;
21+
setOpen: (open: boolean) => void;
2022
calendar_id: number;
2123
}
2224

23-
const ShareButton = ({ calendar_id }: ShareButtonProps) => {
25+
const ShareDialog = ({ open, setOpen, calendar_id }: ShareDialogProps) => {
2426
const emailRef = useRef<HTMLInputElement>(null);
2527
const [shareTimetable] = useCreateShareMutation();
28+
29+
const [loading, setLoading] = useState(false);
2630
const [errorMessage, setErrorMessage] = useState<string | null>(null);
2731
const [successMessage, setSuccessMessage] = useState<string | null>(null);
2832

2933
const handleShare = async () => {
34+
setLoading(true);
3035
const email = emailRef.current?.value;
31-
3236
const { error } = await shareTimetable({
3337
shared_email: email,
3438
calendar_id,
3539
});
3640
if (error) {
3741
const errorData = (error as { data?: { error?: string } }).data;
3842
setErrorMessage(errorData?.error ?? "Unknown error occurred");
39-
return;
4043
} else {
4144
setSuccessMessage("Timetable shared successfully!");
45+
setOpen(false);
46+
setErrorMessage(null);
4247
}
48+
setLoading(false);
4349
};
4450

45-
return (
46-
<Dialog>
47-
<TimetableErrorDialog
48-
errorMessage={errorMessage}
49-
setErrorMessage={setErrorMessage}
50-
/>
51+
return (<Dialog open={open} onOpenChange={() => { setOpen(!open); setErrorMessage(null); } }>
5152
<TimetableSuccessDialog
5253
successMessage={successMessage}
5354
setSuccessMessage={setSuccessMessage}
5455
/>
5556
<DialogTrigger asChild>
56-
<Button size="sm" variant="outline">
57-
Share
58-
</Button>
5957
</DialogTrigger>
6058
<DialogContent className="gap-5">
6159
<DialogHeader>
@@ -64,6 +62,7 @@ const ShareButton = ({ calendar_id }: ShareButtonProps) => {
6462
Who would you like to share your timetable with?
6563
</DialogDescription>
6664
</DialogHeader>
65+
{loading && <Spinner />}
6766
<Label htmlFor="sharedEmail">
6867
Enter the email of the person you want to share your timetable with
6968
</Label>
@@ -74,17 +73,16 @@ const ShareButton = ({ calendar_id }: ShareButtonProps) => {
7473
placeholder="Email"
7574
className="w-full"
7675
/>
76+
<DialogDescription className="text-red-500">{errorMessage}</DialogDescription>
7777
<DialogFooter>
7878
<DialogClose asChild>
7979
<Button variant="secondary">Cancel</Button>
8080
</DialogClose>
81-
<DialogClose asChild>
82-
<Button onClick={handleShare}>Share</Button>
83-
</DialogClose>
81+
<Button onClick={handleShare}>Share</Button>
8482
</DialogFooter>
8583
</DialogContent>
8684
</Dialog>
8785
);
8886
};
8987

90-
export default ShareButton;
88+
export default ShareDialog;

course-matrix/frontend/src/pages/TimetableBuilder/TimetableBuilder.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,15 @@ import {
4646
Timetable,
4747
Restriction,
4848
} from "@/utils/type-utils";
49-
import { useNavigate, useSearchParams } from "react-router-dom";
49+
import { useSearchParams } from "react-router-dom";
5050
import OfferingInfo from "./OfferingInfo";
5151
import { Checkbox } from "@/components/ui/checkbox";
5252
import { CourseModel, TimetableGenerateResponseModel } from "@/models/models";
5353
import LoadingPage from "@/pages/Loading/LoadingPage";
5454
import { GeneratedCalendars } from "./GeneratedCalendars";
5555
import { Spinner } from "@/components/ui/spinner";
5656
import { convertRestrictionTimes } from "@/utils/convert-restriction-times";
57-
import ShareButton from "./ShareButton";
58-
import SharedCalendar from "./SharedCalendar";
57+
import ShareDialog from "./ShareDialog";
5958

6059
type FormContextType = UseFormReturn<z.infer<typeof TimetableFormSchema>>;
6160
export const FormContext = createContext<FormContextType | null>(null);
@@ -135,6 +134,7 @@ const TimetableBuilder = () => {
135134
const noSearchAndFilter = () => {
136135
return !searchQuery && !filters;
137136
};
137+
const [openShareDialog, setOpenShareDialog] = useState(false);
138138

139139
// limit search number if no search query or filters for performance purposes.
140140
// Otherwise, limit is 10k, which effectively gets all results.
@@ -301,7 +301,7 @@ const TimetableBuilder = () => {
301301
setIsGeneratingTimetables(true);
302302
setGeneratedTimetables(data);
303303
setErrorMsg("");
304-
} catch (error) {
304+
} catch {
305305
setIsGeneratingTimetables(false);
306306
setErrorMsg("No valid timetables found");
307307
}
@@ -362,8 +362,11 @@ const TimetableBuilder = () => {
362362
<Button size="sm" variant="outline" onClick={handleReset}>
363363
Reset
364364
</Button>
365+
<Button size="sm" variant="outline" onClick={() => setOpenShareDialog(true)}>
366+
Share
367+
</Button>
365368
{isEditingTimetable && (
366-
<ShareButton calendar_id={timetableId} />
369+
<ShareDialog open={openShareDialog} setOpen={setOpenShareDialog} calendar_id={timetableId} />
367370
)}
368371
</div>
369372
</div>

0 commit comments

Comments
 (0)