1- import {
2- useGetTimetableQuery ,
3- useGetTimetablesQuery ,
4- } from "@/api/timetableApiSlice" ;
1+ import { useGetTimetablesQuery } from "@/api/timetableApiSlice" ;
52import { Button } from "@/components/ui/button" ;
6- import { Timetable , TimetableEvents } from "@/utils/type-utils" ;
7- import { useEffect , useState } from "react" ;
3+ import { Timetable } from "@/utils/type-utils" ;
4+ import { useCallback , useEffect , useState } from "react" ;
85import { Link , useSearchParams } from "react-router-dom" ;
9- import Calendar from "../TimetableBuilder/Calendar" ;
10- import { useGetEventsQuery } from "@/api/eventsApiSlice" ;
11- import { Spinner } from "@/components/ui/spinner" ;
126import { zodResolver } from "@hookform/resolvers/zod" ;
137import { useForm } from "react-hook-form" ;
148import { z } from "zod" ;
159import { CompareFormSchema } from "../Home/TimetableCompareButton" ;
16- import { format } from "path" ;
1710import {
1811 Form ,
1912 FormControl ,
2013 FormField ,
2114 FormItem ,
22- FormLabel ,
2315 FormMessage ,
2416} from "@/components/ui/form" ;
2517import {
@@ -31,99 +23,73 @@ import {
3123} from "@/components/ui/select" ;
3224import { SemesterIcon } from "@/components/semester-icon" ;
3325import { GitCompareArrows } from "lucide-react" ;
26+ import { useGetTimetablesSharedWithMeQuery } from "@/api/sharedApiSlice" ;
27+ import { TimetableShare } from "../Home/Home" ;
28+ import ViewCalendar from "../TimetableBuilder/ViewCalendar" ;
29+ import { sortTimetablesComparator } from "@/utils/calendar-utils" ;
3430
3531export const CompareTimetables = ( ) => {
3632 const [ timetable1 , setTimetable1 ] = useState < Timetable > ( ) ;
3733 const [ timetable2 , setTimetable2 ] = useState < Timetable > ( ) ;
38- const [ offeringIds1 , setOfferingIds1 ] = useState < number [ ] > ( ) ;
39- const [ offeringIds2 , setOfferingIds2 ] = useState < number [ ] > ( ) ;
4034 const [ queryParams ] = useSearchParams ( ) ;
35+ const [ loadedPreselectedTimetables , setLoadedPreselectedTimetables ] = useState ( false ) ;
36+
37+ const preselectedTimetableId1 = queryParams . get ( "id1" ) ;
38+ const preselectedTimetableId2 = queryParams . get ( "id2" ) ;
39+ const preselectedUserId1 = queryParams . get ( "userId1" ) ;
40+ const preselectedUserId2 = queryParams . get ( "userId2" ) ;
4141
4242 const compareForm = useForm < z . infer < typeof CompareFormSchema > > ( {
4343 resolver : zodResolver ( CompareFormSchema ) ,
4444 defaultValues : {
4545 timetable1 : queryParams . has ( "id1" )
46- ? parseInt ( queryParams . get ( "id1" ) ?? "0" )
46+ ? `timetable1/ ${ preselectedTimetableId1 } / ${ preselectedUserId1 } `
4747 : undefined ,
4848 timetable2 : queryParams . has ( "id2" )
49- ? parseInt ( queryParams . get ( "id2" ) ?? "0" )
49+ ? `timetable2/ ${ preselectedTimetableId2 } / ${ preselectedUserId2 } `
5050 : undefined ,
5151 } ,
5252 } ) ;
5353
54- const onSubmit = ( values : z . infer < typeof CompareFormSchema > ) => {
55- console . log ( "Compare Form submitted:" , values ) ;
56- const timetableId1 = compareForm . getValues ( "timetable1" ) ;
57- const timetableId2 = compareForm . getValues ( "timetable2" ) ;
58- setTimetable1 ( timetables . find ( ( t ) => t . id === timetableId1 ) ) ;
59- refetchEvents1 ( ) ;
60- setTimetable2 ( timetables . find ( ( t ) => t . id === timetableId2 ) ) ;
61- refetchEvents2 ( ) ;
62- } ;
63-
64- const {
65- data : timetables ,
66- isLoading,
67- refetch,
68- } = useGetTimetablesQuery ( ) as {
69- data : Timetable [ ] ;
70- isLoading : boolean ;
71- refetch : ( ) => void ;
72- } ;
54+ const { data : myTimetablesData } = useGetTimetablesQuery ( ) as { data : Timetable [ ] } ;
55+ const { data : sharedWithMeTimetablesData } = useGetTimetablesSharedWithMeQuery ( ) as { data : TimetableShare [ ] } ;
7356
74- const { data : timetableEventsData1 , refetch : refetchEvents1 } =
75- useGetEventsQuery ( compareForm . getValues ( "timetable1" ) ?? undefined , {
76- skip : compareForm . getValues ( "timetable1" ) === undefined ,
77- } ) as {
78- data : TimetableEvents ;
79- refetch : ( ) => void ;
80- } ;
81- const { data : timetableEventsData2 , refetch : refetchEvents2 } =
82- useGetEventsQuery ( compareForm . getValues ( "timetable2" ) , {
83- skip : compareForm . getValues ( "timetable2" ) === undefined ,
84- } ) as {
85- data : TimetableEvents ;
86- refetch : ( ) => void ;
87- } ;
57+ const myOwningTimetables = [ ...( myTimetablesData ?? [ ] ) ] . sort ( sortTimetablesComparator ) ;
58+ const sharedWithMeTimetables = [ ...( sharedWithMeTimetablesData ?? [ ] ) ]
59+ . flatMap ( ( share ) => share . timetables )
60+ . sort ( sortTimetablesComparator ) ;
61+ const allTimetables = [ ...myOwningTimetables , ...sharedWithMeTimetables ]
62+ . map ( ( timetable , index ) => ( {
63+ ...timetable ,
64+ isShared : index >= myOwningTimetables . length ,
65+ } ) )
66+ . sort ( sortTimetablesComparator ) ;
8867
8968 useEffect ( ( ) => {
90- if ( queryParams . has ( "id1" ) && timetables ) {
91- const id = parseInt ( queryParams . get ( "id1" ) || "0" ) ;
92- compareForm . setValue ( "timetable1" , id ) ;
93- setTimetable1 ( timetables . find ( ( t ) => t . id === id ) ) ;
69+ if (
70+ preselectedTimetableId1 &&
71+ preselectedUserId1 &&
72+ preselectedTimetableId2 &&
73+ preselectedUserId2 &&
74+ ! loadedPreselectedTimetables
75+ ) {
76+ setTimetable1 ( allTimetables . find ( ( t ) => t . id === parseInt ( preselectedTimetableId1 ) && t . user_id === preselectedUserId1 ) ) ;
77+ setTimetable2 ( allTimetables . find ( ( t ) => t . id === parseInt ( preselectedTimetableId2 ) && t . user_id === preselectedUserId2 ) ) ;
78+ setLoadedPreselectedTimetables ( true ) ;
9479 }
95- } , [ timetables ] ) ;
80+ } , [ preselectedTimetableId1 , preselectedUserId1 , preselectedTimetableId2 , preselectedUserId2 , allTimetables , loadedPreselectedTimetables ] ) ;
9681
97- useEffect ( ( ) => {
98- if ( queryParams . has ( "id2" ) && timetables ) {
99- const id = parseInt ( queryParams . get ( "id2" ) || "0" ) ;
100- compareForm . setValue ( "timetable2" , id ) ;
101- setTimetable2 ( timetables . find ( ( t ) => t . id === id ) ) ;
102- }
103- } , [ timetables ] ) ;
82+ const onSubmit = useCallback ( ( values : z . infer < typeof CompareFormSchema > ) => {
83+ console . log ( "Compare Form submitted:" , values ) ;
10484
105- // get unique offeringIds for calendar
106- useEffect ( ( ) => {
107- if ( timetableEventsData1 ) {
108- const uniqueOfferingIds = new Set < number > ( ) ;
109- for ( const event of timetableEventsData1 . courseEvents ) {
110- if ( ! uniqueOfferingIds . has ( event . offering_id ) )
111- uniqueOfferingIds . add ( event . offering_id ) ;
112- }
113- setOfferingIds1 ( Array . from ( uniqueOfferingIds ) ) ;
114- }
115- } , [ timetableEventsData1 ] ) ;
85+ const timetableId1 = parseInt ( values . timetable1 . split ( "/" ) [ 1 ] ) ;
86+ const timetableId2 = parseInt ( values . timetable2 . split ( "/" ) [ 1 ] ) ;
87+ const timetableUserId1 = values . timetable1 . split ( "/" ) [ 2 ] ;
88+ const timetableUserId2 = values . timetable2 . split ( "/" ) [ 2 ] ;
11689
117- useEffect ( ( ) => {
118- if ( timetableEventsData2 ) {
119- const uniqueOfferingIds = new Set < number > ( ) ;
120- for ( const event of timetableEventsData2 . courseEvents ) {
121- if ( ! uniqueOfferingIds . has ( event . offering_id ) )
122- uniqueOfferingIds . add ( event . offering_id ) ;
123- }
124- setOfferingIds2 ( Array . from ( uniqueOfferingIds ) ) ;
125- }
126- } , [ timetableEventsData2 ] ) ;
90+ setTimetable1 ( allTimetables . find ( ( t ) => t . id === timetableId1 && t . user_id === timetableUserId1 ) ) ;
91+ setTimetable2 ( allTimetables . find ( ( t ) => t . id === timetableId2 && t . user_id === timetableUserId2 ) ) ;
92+ } , [ allTimetables ] ) ;
12793
12894 return (
12995 < >
@@ -146,10 +112,10 @@ export const CompareTimetables = () => {
146112 render = { ( { field } ) => (
147113 < FormItem >
148114 < Select
149- onValueChange = { ( value ) => field . onChange ( Number ( value ) ) }
115+ onValueChange = { ( value ) => field . onChange ( value ) }
150116 defaultValue = {
151117 queryParams . has ( "id1" )
152- ? ( queryParams . get ( "id1" ) ?? "" )
118+ ? `timetable1/ ${ preselectedTimetableId1 } / ${ preselectedUserId1 } `
153119 : undefined
154120 }
155121 >
@@ -159,11 +125,11 @@ export const CompareTimetables = () => {
159125 </ SelectTrigger >
160126 </ FormControl >
161127 < SelectContent >
162- { timetables &&
163- timetables . map ( ( timetable ) => (
128+ { allTimetables &&
129+ allTimetables . map ( ( timetable ) => (
164130 < SelectItem
165- key = { timetable . id }
166- value = { timetable . id . toString ( ) }
131+ key = { `timetable1/ ${ timetable . id } / ${ timetable . user_id } ` }
132+ value = { `timetable1/ ${ timetable . id } / ${ timetable . user_id } ` }
167133 >
168134 < div className = "flex items-center gap-2" >
169135 < SemesterIcon
@@ -190,10 +156,10 @@ export const CompareTimetables = () => {
190156 render = { ( { field } ) => (
191157 < FormItem >
192158 < Select
193- onValueChange = { ( value ) => field . onChange ( Number ( value ) ) }
159+ onValueChange = { ( value ) => field . onChange ( value ) }
194160 defaultValue = {
195161 queryParams . has ( "id1" )
196- ? ( queryParams . get ( "id2" ) ?? "" )
162+ ? `timetable2/ ${ preselectedTimetableId2 } / ${ preselectedUserId2 } `
197163 : undefined
198164 }
199165 >
@@ -203,11 +169,11 @@ export const CompareTimetables = () => {
203169 </ SelectTrigger >
204170 </ FormControl >
205171 < SelectContent >
206- { timetables &&
207- timetables . map ( ( timetable ) => (
172+ { allTimetables &&
173+ allTimetables . map ( ( timetable ) => (
208174 < SelectItem
209- key = { timetable . id }
210- value = { timetable . id . toString ( ) }
175+ key = { `timetable2/ ${ timetable . id } / ${ timetable . user_id } ` }
176+ value = { `timetable2/ ${ timetable . id } / ${ timetable . user_id } ` }
211177 >
212178 < div className = "flex items-center gap-2" >
213179 < SemesterIcon
@@ -237,48 +203,32 @@ export const CompareTimetables = () => {
237203 < hr className = "mb-4" />
238204 < div className = "flex gap-4" >
239205 < div className = "w-1/2" >
240- { ! offeringIds1 ? (
241- < >
242- { queryParams . has ( "id1" ) ? (
243- < Spinner />
244- ) : (
245- < div className = "w-full text-center py-[8rem] text-sm bg-gray-100/50 rounded" >
246- Select a timetable to compare
247- </ div >
248- ) }
249- </ >
206+ { ! timetable1 ? (
207+ < div className = "w-full text-center py-[8rem] text-sm bg-gray-100/50 rounded" >
208+ Select a timetable to compare
209+ </ div >
250210 ) : (
251- < Calendar
252- setShowLoadingPage = { ( ) => { } }
253- isChoosingSectionsManually = { false }
211+ < ViewCalendar
212+ user_id = { timetable1 . user_id }
213+ calendar_id = { timetable1 . id }
214+ timetable_title = { timetable1 ?. timetable_title ?? "" }
254215 semester = { timetable1 ?. semester ?? "Fall 2025" }
255- selectedCourses = { [ ] }
256- newOfferingIds = { offeringIds1 }
257- restrictions = { [ ] }
258- header = { timetable1 ?. timetable_title }
216+ show_fancy_header = { false }
259217 />
260218 ) }
261219 </ div >
262220 < div className = "w-1/2" >
263- { ! offeringIds2 ? (
264- < >
265- { queryParams . has ( "id2" ) ? (
266- < Spinner />
267- ) : (
268- < div className = "w-full text-center py-[8rem] text-sm bg-gray-100/50 rounded" >
269- Select a timetable to compare
270- </ div >
271- ) }
272- </ >
221+ { ! timetable2 ? (
222+ < div className = "w-full text-center py-[8rem] text-sm bg-gray-100/50 rounded" >
223+ Select a timetable to compare
224+ </ div >
273225 ) : (
274- < Calendar
275- setShowLoadingPage = { ( ) => { } }
276- isChoosingSectionsManually = { false }
226+ < ViewCalendar
227+ user_id = { timetable2 . user_id }
228+ calendar_id = { timetable2 . id }
229+ timetable_title = { timetable2 ?. timetable_title ?? "" }
277230 semester = { timetable2 ?. semester ?? "Fall 2025" }
278- selectedCourses = { [ ] }
279- newOfferingIds = { offeringIds2 }
280- restrictions = { [ ] }
281- header = { timetable2 ?. timetable_title }
231+ show_fancy_header = { false }
282232 />
283233 ) }
284234 </ div >
0 commit comments