Skip to content

Commit e1fa6fe

Browse files
author
sunny chen
committed
merge from friend-timetable
2 parents c5f972d + 35db8eb commit e1fa6fe

File tree

9 files changed

+241
-85
lines changed

9 files changed

+241
-85
lines changed

client/src/App.tsx

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers';
44
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
55
import * as Sentry from '@sentry/react';
66
import React, { useContext, useEffect, useMemo } from 'react';
7-
import { Outlet } from 'react-router';
7+
import { Outlet, useLocation } from 'react-router';
88

99
import getCourseInfo from './api/getCourseInfo';
1010
import getCoursesList from './api/getCoursesList';
@@ -134,6 +134,7 @@ const App: React.FC = () => {
134134
setAssignedColors,
135135
} = useContext(CourseContext);
136136

137+
const location = useLocation();
137138
const { preferredTheme, isDarkMode, unscheduleClassesByDefault, convertToLocalTimezone } = useGetUserSettingsQuery();
138139

139140
const decodedAssignedColors = useColorsDecoder(assignedColors, preferredTheme);
@@ -569,6 +570,46 @@ const App: React.FC = () => {
569570
},
570571
};
571572

573+
const timetableView = useMemo(() => {
574+
const currentPathname = location.pathname;
575+
const searchParams = location.search;
576+
console.log(currentPathname);
577+
if (currentPathname === '/home') {
578+
return (
579+
<>
580+
<TimetableTabs />
581+
<Timetable assignedColors={decodedAssignedColors} handleSelectClass={handleSelectClass} />
582+
<ICSButton
583+
onClick={() => {
584+
downloadIcsFile(selectedCourses, createdEvents, selectedClasses, firstDayOfTerm)
585+
.then(() => {
586+
/* do nothing */
587+
})
588+
.catch(() => {
589+
/* do nothing */
590+
});
591+
}}
592+
>
593+
save to calendar
594+
</ICSButton>
595+
<Sponsors />
596+
<Footer />
597+
<Alerts />
598+
</>
599+
);
600+
} else {
601+
return <Timetable assignedColors={decodedAssignedColors} handleSelectClass={handleSelectClass} />;
602+
}
603+
}, [
604+
location,
605+
selectedClasses,
606+
createdEvents,
607+
selectedCourses,
608+
firstDayOfTerm,
609+
handleSelectClass,
610+
decodedAssignedColors,
611+
]);
612+
572613
return (
573614
<StyledEngineProvider injectFirst>
574615
<ThemeProvider theme={themeObject}>
@@ -586,16 +627,7 @@ const App: React.FC = () => {
586627
handleRemoveCourse={handleRemoveCourse}
587628
/>
588629
<Outlet />
589-
<TimetableTabs />
590-
<Timetable assignedColors={decodedAssignedColors} handleSelectClass={handleSelectClass} />
591-
<ICSButton
592-
onClick={() => downloadIcsFile(selectedCourses, createdEvents, selectedClasses, firstDayOfTerm)}
593-
>
594-
save to calendar
595-
</ICSButton>
596-
<Sponsors />
597-
<Footer />
598-
<Alerts />
630+
{timetableView}
599631
<SubcomPromotion />
600632
<PromotionPopup
601633
imgSrc={T3SelectGif}

client/src/components/controls/Controls.tsx

Lines changed: 80 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Box, Grid } from '@mui/material';
22
import { styled } from '@mui/material/styles';
3-
import React from 'react';
3+
import React, { useMemo } from 'react';
4+
import { useLocation } from 'react-router';
45

56
import { ControlsProps } from '../../interfaces/PropTypes';
7+
import UserProfile from '../sidebar/friends/UserProfile';
68
import Autotimetabler from './Autotimetabler';
79
import CourseSelect from './CourseSelect';
810
import CustomEvents from './CustomEvent';
@@ -18,6 +20,20 @@ const TermSelectWrapper = styled(Box)`
1820
align-items: flex-start;
1921
`;
2022

23+
const FriendTimetableLabelContainer = styled(Box)`
24+
display: flex;
25+
justify-content: center;
26+
align-items: center;
27+
width: 100%;
28+
background-color: ${({ theme }) => theme.palette.secondary.light};
29+
border: 0.75px solid;
30+
border-color: ${({ theme }) => theme.palette.text.primary};
31+
padding: 10px 0;
32+
border-top-left-radius: ${({ theme }) => theme.shape.borderRadius}px;
33+
border-top-right-radius: ${({ theme }) => theme.shape.borderRadius}px;
34+
font-weight: 700;
35+
`;
36+
2137
const SelectWrapper = styled(Box)`
2238
display: flex;
2339
flex-direction: row;
@@ -52,50 +68,71 @@ const Controls: React.FC<ControlsProps> = ({
5268
handleSelectCourse,
5369
handleRemoveCourse,
5470
}) => {
71+
const location = useLocation();
72+
const additionalControlsDisplay = useMemo(() => {
73+
if (location.pathname !== '/home') {
74+
return (
75+
<>
76+
<TermSelectWrapper>
77+
<TermSelect />
78+
</TermSelectWrapper>
79+
<FriendTimetableLabelContainer>
80+
<UserProfile firstName="Sunny" lastName="Chen" overrideCollapse={true} />
81+
</FriendTimetableLabelContainer>
82+
</>
83+
);
84+
}
85+
return (
86+
<>
87+
<Grid
88+
container
89+
direction="row"
90+
size={{
91+
xs: 12,
92+
md: 6.5,
93+
}}
94+
>
95+
<TermSelectWrapper>
96+
<TermSelect />
97+
</TermSelectWrapper>
98+
99+
<SelectWrapper minWidth={'296px'}>
100+
<CourseSelect
101+
assignedColors={assignedColors}
102+
handleSelect={handleSelectCourse}
103+
handleRemove={handleRemoveCourse}
104+
/>
105+
</SelectWrapper>
106+
</Grid>
107+
<Grid
108+
container
109+
direction="row"
110+
sx={{
111+
alignItems: 'center',
112+
justifyContent: 'space-between',
113+
}}
114+
size={{
115+
xs: 12,
116+
md: 5.5,
117+
}}
118+
>
119+
<CustomEventsWrapper>
120+
<CustomEvents />
121+
</CustomEventsWrapper>
122+
<AutotimetablerWrapper>
123+
<Autotimetabler handleSelectClass={handleSelectClass} />
124+
</AutotimetablerWrapper>
125+
<HistoryWrapper>
126+
<History />
127+
</HistoryWrapper>
128+
</Grid>
129+
</>
130+
);
131+
}, [location.pathname, assignedColors, handleSelectCourse, handleRemoveCourse, handleSelectClass]);
132+
55133
return (
56134
<Grid container sx={{ paddingLeft: '66px' }} spacing={2}>
57-
<Grid
58-
container
59-
direction="row"
60-
size={{
61-
xs: 12,
62-
md: 6.5,
63-
}}
64-
>
65-
<TermSelectWrapper>
66-
<TermSelect />
67-
</TermSelectWrapper>
68-
69-
<SelectWrapper minWidth={'296px'}>
70-
<CourseSelect
71-
assignedColors={assignedColors}
72-
handleSelect={handleSelectCourse}
73-
handleRemove={handleRemoveCourse}
74-
/>
75-
</SelectWrapper>
76-
</Grid>
77-
<Grid
78-
container
79-
direction="row"
80-
sx={{
81-
alignItems: 'center',
82-
justifyContent: 'space-between',
83-
}}
84-
size={{
85-
xs: 12,
86-
md: 5.5,
87-
}}
88-
>
89-
<CustomEventsWrapper>
90-
<CustomEvents />
91-
</CustomEventsWrapper>
92-
<AutotimetablerWrapper>
93-
<Autotimetabler handleSelectClass={handleSelectClass} />
94-
</AutotimetablerWrapper>
95-
<HistoryWrapper>
96-
<History />
97-
</HistoryWrapper>
98-
</Grid>
135+
{additionalControlsDisplay}
99136
</Grid>
100137
);
101138
};

client/src/components/sidebar/CustomModalOpener.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface CustomModalOpenerProps {
1313
content: ReactNode;
1414
isClickable: boolean;
1515
isSelected?: boolean;
16+
optionalHandleClick?: () => void;
1617
}
1718

1819
const ShowModalButton = styled(IconButton, { shouldForwardProp: (prop) => prop !== 'isSelected' })<{
@@ -40,6 +41,7 @@ const CustomModalOpener: React.FC<CustomModalOpenerProps> = ({
4041
content,
4142
isClickable,
4243
isSelected = false,
44+
optionalHandleClick,
4345
}) => {
4446
const [isOpen, setIsOpen] = useState(false);
4547
const { sidebarCollapsed } = useContext(AppContext);
@@ -48,6 +50,7 @@ const CustomModalOpener: React.FC<CustomModalOpenerProps> = ({
4850
if (isClickable) {
4951
setIsOpen(!isOpen);
5052
}
53+
if (optionalHandleClick) optionalHandleClick();
5154
};
5255

5356
return (

client/src/components/sidebar/Sidebar.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CalendarMonth, Description, Info, Security, Settings as SettingsIcon }
22
import { Divider, Drawer, Typography, useMediaQuery, useTheme } from '@mui/material';
33
import { styled } from '@mui/material/styles';
44
import { useContext, useMemo, useState } from 'react';
5+
import { useLocation, useNavigate } from 'react-router';
56

67
import notanglesLogoGif from '../../assets/notangles.gif';
78
import notanglesLogo from '../../assets/notangles_1.png';
@@ -158,6 +159,9 @@ const Sidebar = () => {
158159
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
159160
const collapsedWidth = useMemo(() => (isMobile ? 0 : 80), [isMobile]);
160161

162+
const location = useLocation();
163+
const navigate = useNavigate();
164+
161165
const [currLogo, setCurrLogo] = useState(notanglesLogo);
162166
const [friendsListOpen, setFriendsListOpen] = useState(false);
163167
const { sidebarCollapsed, setSidebarCollapsed } = useContext(AppContext);
@@ -231,7 +235,10 @@ const Sidebar = () => {
231235
// currently not clickable since this is our current page
232236
isClickable={false}
233237
// hardcoded until we move away from single page site
234-
isSelected={true}
238+
isSelected={location.pathname === '/home'}
239+
optionalHandleClick={() => {
240+
navigate('/home');
241+
}}
235242
/>
236243
<FriendsButton
237244
friendsListOpen={friendsListOpen}

client/src/components/sidebar/friends/Friend.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import MoreVertIcon from '@mui/icons-material/MoreVert';
22
import { IconButton, styled, Tooltip } from '@mui/material';
33
import { Box } from '@mui/system';
4-
import { useContext, useState } from 'react';
4+
import { useCallback, useContext, useState } from 'react';
5+
import { useNavigate } from 'react-router';
56

67
import { useGetUserSettingsQuery } from '../../../api/user/queries';
78
import { AppContext } from '../../../context/AppContext';
@@ -24,11 +25,14 @@ const StyledFriendContainer = styled(Box, {
2425
isDarkMode ? theme.palette.secondary.dark : theme.palette.secondary.light}
2526
`;
2627

27-
interface FriendProps {
28+
export interface FriendDTO {
2829
firstName: string;
30+
lastName: string;
31+
id: string;
32+
profileURL: string;
2933
}
3034

31-
const Friend = ({ firstName }: FriendProps) => {
35+
const Friend = ({ firstName, lastName, id, profileURL }: FriendDTO) => {
3236
const { sidebarCollapsed } = useContext(AppContext);
3337
const { isDarkMode } = useGetUserSettingsQuery();
3438
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
@@ -43,10 +47,16 @@ const Friend = ({ firstName }: FriendProps) => {
4347

4448
const kebabOpen = Boolean(anchorEl);
4549

50+
const navigate = useNavigate();
51+
52+
const handleFriendClick = useCallback(() => {
53+
navigate(`/friend/${id}`);
54+
}, [id, navigate]);
55+
4656
return (
47-
<Tooltip title={sidebarCollapsed ? firstName : ''} placement="right">
48-
<StyledFriendContainer isDarkMode={isDarkMode} sidebarCollapsed={sidebarCollapsed}>
49-
<UserProfile firstName={firstName} lastName="" />
57+
<Tooltip title={sidebarCollapsed ? `${firstName} ${lastName}` : ''} placement="right">
58+
<StyledFriendContainer isDarkMode={isDarkMode} sidebarCollapsed={sidebarCollapsed} onClick={handleFriendClick}>
59+
<UserProfile firstName={firstName} lastName={lastName} profileURL={profileURL} />
5060
{!sidebarCollapsed && (
5161
<>
5262
<IconButton onClick={handleKebabClick}>

client/src/components/sidebar/friends/FriendsList.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Fuse from 'fuse.js';
66
import { useContext, useMemo, useState } from 'react';
77

88
import { AppContext } from '../../../context/AppContext';
9-
import Friend from './Friend';
9+
import Friend, { FriendDTO } from './Friend';
1010
import friendList from './friends.json';
1111

1212
const FriendsListContainer = styled(Box)`
@@ -31,12 +31,28 @@ const FriendsList = () => {
3131
// TODO: replace hard coded data with integration with server
3232
let friends = friendList;
3333
if (searchVal.length === 0) {
34-
return friends.map((friend, index) => <Friend key={index} firstName={friend} />);
34+
return friends.map(({ firstName, lastName, id, profileURL }) => (
35+
<Friend
36+
key={id as string}
37+
firstName={firstName as string}
38+
lastName={lastName as string}
39+
id={id as string}
40+
profileURL={profileURL as string}
41+
/>
42+
));
3543
}
3644

37-
const fuzzy = new Fuse<string>(friendList, { threshold: 0.4 });
45+
const fuzzy = new Fuse<FriendDTO>(friendList, { threshold: 0.4, keys: ['firstName', 'lastName'] });
3846
friends = fuzzy.search(searchVal).map((result) => result.item);
39-
return friends.map((friend, index) => <Friend key={index} firstName={friend} />);
47+
return friends.map(({ firstName, lastName, id, profileURL }) => (
48+
<Friend
49+
key={id as string}
50+
firstName={firstName as string}
51+
lastName={lastName as string}
52+
id={id as string}
53+
profileURL={profileURL as string}
54+
/>
55+
));
4056
}, [searchVal]);
4157

4258
const handleClickSearchBarIcon = () => {

0 commit comments

Comments
 (0)