diff --git a/client/src/App.tsx b/client/src/App.tsx
index d01bc14d2..eacc218f9 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -4,7 +4,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import * as Sentry from '@sentry/react';
import React, { useContext, useEffect } from 'react';
-import { Outlet } from 'react-router-dom';
+import { Outlet, useLocation } from 'react-router-dom';
import getCourseInfo from './api/getCourseInfo';
import getCoursesList from './api/getCoursesList';
@@ -15,7 +15,6 @@ import Sidebar from './components/sidebar/Sidebar';
import Sponsors from './components/Sponsors';
import SubcomPromotion from './components/SubcomPromotion';
import Timetable from './components/timetable/Timetable';
-import TimetableShared from './components/timetableShared.tsx/TimetableShared';
import { TimetableTabs } from './components/timetableTabs/TimetableTabs';
import { contentPadding, leftContentPadding, rightContentPadding, themes } from './constants/theme';
import {
@@ -93,6 +92,7 @@ const ICSButton = styled(Button)`
`;
const App: React.FC = () => {
+ const location = useLocation();
const {
themeObject,
currentTheme,
@@ -141,7 +141,7 @@ const App: React.FC = () => {
} = useContext(CourseContext);
const decodedAssignedColors = useColorsDecoder(assignedColors);
- const { user, setUser, groupsSidebarCollapsed, setGroupsSidebarCollapsed } = useContext(UserContext);
+ const { user, setUser } = useContext(UserContext);
setDropzoneRange(days.length, earliestStartTime, latestEndTime);
@@ -631,30 +631,30 @@ const App: React.FC = () => {
-
- {groupsSidebarCollapsed ? (
+ {location.pathname === '/friends' ? (
+ <>>
+ ) : (
<>
+
+ downloadIcsFile(selectedCourses, createdEvents, selectedClasses, firstDayOfTerm)}
+ >
+ save to calendar
+
+
+
+
+
>
- ) : (
-
)}
- downloadIcsFile(selectedCourses, createdEvents, selectedClasses, firstDayOfTerm)}
- >
- save to calendar
-
-
-
-
-
diff --git a/client/src/components/friends/ActivityBar.tsx b/client/src/components/friends/ActivityBar.tsx
new file mode 100644
index 000000000..f7f403bc4
--- /dev/null
+++ b/client/src/components/friends/ActivityBar.tsx
@@ -0,0 +1,33 @@
+import { styled } from '@mui/system';
+
+import FriendsActivity from './FriendsActivity';
+
+const ActivityBarContainer = styled('div')`
+ position: fixed;
+ top: 64px;
+ right: 0;
+ padding: 12px 32px;
+ z-index: 999;
+ background: white;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+`;
+
+const StyledTitle = styled('p')`
+ font-size: 1.2rem;
+ font-weight: 700;
+ margin: 0;
+`;
+
+const ActivityBar = () => {
+ return (
+
+ Your Friends Activity
+
+
+ );
+};
+
+export default ActivityBar;
diff --git a/client/src/components/friends/Friends.tsx b/client/src/components/friends/Friends.tsx
new file mode 100644
index 000000000..4acee3b8f
--- /dev/null
+++ b/client/src/components/friends/Friends.tsx
@@ -0,0 +1,43 @@
+import styled from '@emotion/styled';
+import { useContext } from 'react';
+
+import { unknownErrorMessage } from '../../constants/timetable';
+import { AppContext } from '../../context/AppContext';
+import { CourseContext } from '../../context/CourseContext';
+import { UserContext } from '../../context/UserContext';
+import { ClassData } from '../../interfaces/Periods';
+import Timetable from '../timetable/Timetable';
+import GroupInfoNavbar from './GroupInfoNavbar';
+
+const Container = styled('div')`
+ margin-top: 80px;
+`;
+
+const Friends = () => {
+ const { assignedColors, setSelectedClasses } = useContext(CourseContext);
+ const { setAlertMsg, setErrorVisibility } = useContext(AppContext);
+ const { groups } = useContext(UserContext);
+
+ const handleSelectClass = (classData: ClassData) => {
+ setSelectedClasses((prev) => {
+ prev = { ...prev };
+
+ try {
+ prev[classData.courseCode][classData.activity] = classData;
+ } catch (err) {
+ setAlertMsg(unknownErrorMessage);
+ setErrorVisibility(true);
+ }
+
+ return prev;
+ });
+ };
+ return (
+
+
+ {groups.length > 0 && }
+
+ );
+};
+
+export default Friends;
diff --git a/client/src/components/friends/FriendsActivity.tsx b/client/src/components/friends/FriendsActivity.tsx
new file mode 100644
index 000000000..65f17e563
--- /dev/null
+++ b/client/src/components/friends/FriendsActivity.tsx
@@ -0,0 +1,201 @@
+import { styled } from '@mui/system';
+import { useContext, useEffect } from 'react';
+
+import { API_URL } from '../../api/config';
+import { UserContext } from '../../context/UserContext';
+
+
+
+const Container = styled('div')`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+`;
+
+const FriendContainer = styled('div')`
+ display: flex;
+ flex-direction: column;
+ border-radius: 8px;
+ background-color: #f2f3f5;
+ padding: 16px;
+ gap: 12px;
+`;
+
+const UserNameText = styled('p')`
+ font-weight: 600;
+ font-size: 1rem;
+ margin: 0;
+`;
+
+const UserActivity = styled('p')`
+ margin: 0;
+ color: #90949a;
+ font-size: 0.8rem;
+`;
+
+const ClassLocationContainer = styled('div')`
+ display: flex;
+ flex-direction: row;
+ background-color: white;
+ border-radius: 4px;
+ padding: 10px;
+ gap: 12px;
+`;
+
+const Location = styled('div')`
+ font-size: 0.8rem;
+`;
+
+const ClassTime = styled('div')`
+ font-size: 0.8rem;
+`;
+
+const TextContainer = styled('div')`
+ display: flex;
+ flex-direction: column;
+`;
+
+const UserDetailsContainer = styled('div')`
+ display: flex;
+ flex-direction: row;
+ gap: 16px;
+`;
+
+const UserProfile = styled('div')`
+ width: 35px;
+ height: 35px;
+ border-radius: 20px;
+ background-color: white;
+`;
+
+/*
+TODO: tldr just need to fix getCurrentActivity and findCurrentActivity to return a useful object like the below from the database timetable object
+export type CurrentActivity = {
+ friendName: string;
+ activityTitle: string; // i.e. in a [activity] tutorial
+ location: string; // so this will be `[course_code] at [room location]`
+ time_slot: string; // i.e. 12:00 - 14:00
+};
+^ or if its a custom event then minor changes based on the event info
+*/
+
+////////////////////////////////////////////////////// CHANEL'S BE STUFF //////////////////////////////////////////////////////
+export interface Friend {
+ name: string;
+ userId: string;
+ // show next event within the hour
+ currentActivity: CurrentActivity | null;
+}
+
+export type FriendsList = Friend[];
+
+export type CurrentActivity = {
+ friendName: string;
+ activityTitle: string; // i.e. in a [activity] tutorial
+ location: string; // so this will be `[course_code] at [room location]`
+ time_slot: string; // i.e. 12:00 - 14:00
+};
+
+const getCurrentActivity = async (userId: string): Promise => {
+ try {
+ const res = await fetch(`${API_URL.server}/user/timetable/${userId}`, {
+ method: 'GET',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ });
+
+ if (!res.ok) {
+ throw new Error('Failed to fetch timetable data');
+ }
+
+ const timetable = await res.json();
+ // Currently we only consider the first timetable as the main
+ return findCurrentActivity(timetable.data[0]);
+ } catch (error) {
+ console.error('Error fetching current activity:', error);
+ return null;
+ }
+};
+
+const findCurrentActivity = (timetable: any): CurrentActivity | null => {
+ const now = new Date();
+ const currentDay = now.toLocaleString('en-US', { weekday: 'long' }).slice(0, 3);
+ const currentTime = now.toTimeString().split(' ')[0].slice(0, 5);
+
+ for (const clz of timetable.selectedClasses) {
+ if (clz.times.day === currentDay && clz.times.time.start <= currentTime && clz.times.time.end >= currentTime) {
+ return {
+ friendName: timetable.friendName,
+ activityTitle: `in a ${clz.activity} tutorial`,
+ location: `${clz.courseCode} at ${clz.times.location}`,
+ time_slot: `${clz.times.time.start} - ${clz.times.time.end}`,
+ };
+ }
+ }
+
+ return null;
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+const FriendsActivity = () => {
+ const { user } = useContext(UserContext);
+
+ useEffect(() => {
+ // TODO: fix this integration here and store friend activity in state...
+ const dosomething = async () => {
+ for (const friend of user.friends) {
+ console.log('friend', friend);
+ const res = await getCurrentActivity(friend.userID);
+ console.log('friend activity res', res);
+ }
+ };
+ dosomething();
+ }, []);
+
+ return (
+
+
+
+
+
+ Raiyan Ahmed
+ Currently teaching
+
+
+
+ COMP1531 at TablaK17G7
+ 14:00 - 16:00
+
+
+
+
+
+
+ Shaam Jevan
+ Currently teaching
+
+
+
+ COMP3121 at DA_BASEMENT
+ 14:00 - 16:00
+
+
+
+
+
+
+ Jeremy Le
+ Chilln
+
+
+
+ Ainsworth L3
+
+
+
+ );
+};
+
+export default FriendsActivity;
diff --git a/client/src/components/friends/FriendsTimetable.tsx b/client/src/components/friends/FriendsTimetable.tsx
new file mode 100644
index 000000000..c734ba025
--- /dev/null
+++ b/client/src/components/friends/FriendsTimetable.tsx
@@ -0,0 +1,78 @@
+import { Box } from '@mui/material';
+import { styled } from '@mui/system';
+import React, { useContext, useState } from 'react';
+
+import { contentPadding, inventoryMargin } from '../../constants/theme';
+import { timetableWidth } from '../../constants/timetable';
+import { AppContext } from '../../context/AppContext';
+import { EventPeriod } from '../../interfaces/Periods';
+import ActivityBar from './ActivityBar';
+
+const StyledTimetable = styled(Box, {
+ shouldForwardProp: (prop) => !['rows', 'cols'].includes(prop.toString()),
+})<{
+ rows: number;
+ cols: number;
+}>`
+ display: grid;
+ min-width: ${timetableWidth}px;
+ padding: 0px ${contentPadding}px ${contentPadding}px ${contentPadding}px;
+ box-sizing: content-box;
+ user-select: none;
+ grid-gap: 1px;
+ grid-template:
+ auto repeat(${({ rows }) => rows}, 1fr)
+ / auto repeat(${({ cols }) => cols}, minmax(0, 1fr)) ${inventoryMargin}px minmax(0, 1fr);
+`;
+
+const StyledTimetableScroll = styled(Box)`
+ padding: ${1 / devicePixelRatio}px;
+ position: relative;
+ left: -${contentPadding}px;
+ width: calc(100% + ${contentPadding * 2 - (1 / devicePixelRatio) * 2}px);
+ overflow-x: none;
+ overflow-y: hidden;
+
+ ${({ theme }) => theme.breakpoints.down('sm')} {
+ overflow-x: scroll;
+ }
+`;
+
+const TimetableContainer = styled('div')`
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ width: 100%;
+ height: 100%;
+`;
+
+const Timetable = styled('div')`
+ display: flex;
+ flex-direction: row;
+ width: 1000px;
+ height: 100%;
+`;
+
+const FriendsTimetable: React.FC = () => {
+ const { days, earliestStartTime, latestEndTime } = useContext(AppContext);
+ const [copiedEvent, setCopiedEvent] = useState(null);
+
+ // Calculate the correct number of rows, accounting for when the earliest start time is later than latest end time.
+ // E.g. starting at 7pm and ending at 4am.
+ const numRows =
+ latestEndTime > earliestStartTime ? latestEndTime - earliestStartTime : 24 - earliestStartTime + latestEndTime;
+
+ return (
+
+
+
+
+ {/* */}
+
+
+
+
+ );
+};
+
+export default FriendsTimetable;
diff --git a/client/src/components/friends/GroupInfoNavbar.tsx b/client/src/components/friends/GroupInfoNavbar.tsx
new file mode 100644
index 000000000..facaa22f2
--- /dev/null
+++ b/client/src/components/friends/GroupInfoNavbar.tsx
@@ -0,0 +1,103 @@
+import styled from '@emotion/styled';
+import { ConnectWithoutContact as FriendsActivityIcon } from '@mui/icons-material';
+import { IconButton, Tooltip } from '@mui/material';
+import { useContext, useState } from 'react';
+
+import { UserContext } from '../../context/UserContext';
+import UserIcon from '../user/UserIcon';
+import ActivityBar from './ActivityBar';
+
+const Container = styled('div')`
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 1000;
+
+ display: flex;
+ gap: 24px;
+ align-items: center;
+ padding: 12px 66px;
+ background: white;
+ width: 90%;
+ margin-left: 140px;
+ border-bottom: 1px solid #e0e0e0;
+`;
+
+const FriendsActivityButtonContainer = styled('div')`
+ position: fixed;
+ top: 6px;
+ right: 32px;
+ z-index: 1000;
+`;
+
+const Members = styled('div')`
+ display: flex;
+ gap: 8px;
+`;
+
+const MemberText = styled('div')`
+ display: flex;
+ gap: 2px;
+ flex-direction: column;
+ align-items: start;
+`;
+
+const GroupName = styled('div')`
+ font-size: 18px;
+ font-weight: 600;
+`;
+
+const GroupDescription = styled('div')`
+ font-size: 14px;
+`;
+
+const GroupInfoNavbar = () => {
+ const { groups, selectedGroupIndex } = useContext(UserContext);
+
+ const group = groups.length !== 0 ? groups[selectedGroupIndex] : undefined;
+
+ const [showFriendsActivities, setShowFriendsActivities] = useState(false);
+
+ return (
+ <>
+
+ {group ? (
+ <>
+
+ {group.name}
+ {group.description}
+
+
+ {[...group.groupAdmins, ...group.members].map((member, i) => (
+
+ ))}
+
+ >
+ ) : (
+ <>
+
+ You are currently not apart of any groups.
+ To create a group, click '+' on the left, blue side bar.
+
+ >
+ )}
+
+
+ {
+ setShowFriendsActivities(!showFriendsActivities);
+ }}
+ size="large"
+ >
+
+
+
+
+
+ {showFriendsActivities && }
+ >
+ );
+};
+
+export default GroupInfoNavbar;
diff --git a/client/src/components/landingPage/flip-words.tsx b/client/src/components/landingPage/flip-words.tsx
index b35a3d726..95b1d144a 100644
--- a/client/src/components/landingPage/flip-words.tsx
+++ b/client/src/components/landingPage/flip-words.tsx
@@ -1,5 +1,5 @@
'use client';
-import { AnimatePresence,motion } from 'framer-motion';
+import { AnimatePresence, motion } from 'framer-motion';
import { useCallback, useEffect, useState } from 'react';
import { cn } from '../../lib/utils';
diff --git a/client/src/components/sidebar/CustomModal.tsx b/client/src/components/sidebar/CustomModal.tsx
index 5d101442c..545d1cd96 100644
--- a/client/src/components/sidebar/CustomModal.tsx
+++ b/client/src/components/sidebar/CustomModal.tsx
@@ -2,6 +2,7 @@ import { Close } from '@mui/icons-material';
import { Dialog, DialogContent, DialogTitle, Divider, IconButton, Tooltip, Typography } from '@mui/material';
import { styled } from '@mui/system';
import React from 'react';
+import { useNavigate } from 'react-router-dom';
import { CustomModalProps } from '../../interfaces/PropTypes';
@@ -49,21 +50,28 @@ const CustomModal: React.FC = ({
description,
content,
collapsed,
- isClickable,
isSelected = false,
+ onNavigate,
}) => {
const [isOpen, setIsOpen] = React.useState(false);
+ const navigate = useNavigate();
const toggleIsOpen = () => {
- if (isClickable) {
- setIsOpen(!isOpen);
+ setIsOpen(!isOpen);
+ };
+
+ const handleClick = () => {
+ if (onNavigate) {
+ navigate(onNavigate);
+ } else {
+ toggleIsOpen();
}
};
return (
<>
-
-
+
+
{showIcon}
{collapsed ? '' : title}
diff --git a/client/src/components/sidebar/FriendsButton.tsx b/client/src/components/sidebar/FriendsButton.tsx
deleted file mode 100644
index 90e43f281..000000000
--- a/client/src/components/sidebar/FriendsButton.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { SwitchAccount } from '@mui/icons-material';
-import { IconButton, Tooltip, Typography } from '@mui/material';
-import { styled } from '@mui/system';
-import React, { useContext } from 'react';
-
-import { UserContext } from '../../context/UserContext';
-
-interface FriendsButtonProps {
- collapsed: boolean;
-}
-
-const StyledFriendsButton = styled(IconButton)<{ isSelected: boolean }>`
- display: flex;
- border-radius: 8px;
- gap: 16px;
- justify-content: flex-start;
- padding: 12px 12px 12px 12px;
- background-color: ${({ isSelected }) => (isSelected ? 'rgb(157, 157, 157, 0.15)' : 'transparent')};
-`;
-
-const IndividualComponentTypography = styled(Typography)<{ collapsed: boolean }>`
- font-size: 16px;
-`;
-
-const FriendsButton: React.FC = ({ collapsed }) => {
- const { groupsSidebarCollapsed, setGroupsSidebarCollapsed } = useContext(UserContext);
-
- return (
- <>
-
- setGroupsSidebarCollapsed(!groupsSidebarCollapsed)} UNCOMMENT to see shared timetables
- isSelected={!groupsSidebarCollapsed}
- >
-
-
- {collapsed ? '' : 'Shared Timetables'}
-
-
-
- >
- );
-};
-
-export default FriendsButton;
diff --git a/client/src/components/sidebar/Sidebar.tsx b/client/src/components/sidebar/Sidebar.tsx
index 6f09dcc4f..910538667 100644
--- a/client/src/components/sidebar/Sidebar.tsx
+++ b/client/src/components/sidebar/Sidebar.tsx
@@ -1,17 +1,16 @@
-import { CalendarMonth, Description, Info, Security, Settings as SettingsIcon } from '@mui/icons-material';
+import { CalendarMonth, Description, Group, Info, Security, Settings as SettingsIcon } from '@mui/icons-material';
import { AppBar, AppBarProps, Divider, Typography } from '@mui/material';
import { styled } from '@mui/system';
-import React, { useContext, useEffect, useRef, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
+import { useLocation } from 'react-router-dom';
import notanglesLogoGif from '../../assets/notangles.gif';
import notanglesLogo from '../../assets/notangles_1.png';
-import { UserContext } from '../../context/UserContext';
import About from './About';
import Changelog from './Changelog';
import CollapseButton from './CollapseButton';
import CustomModal from './CustomModal';
import DarkModeButton from './DarkModeButton';
-import FriendsButton from './FriendsButton';
import GroupsSidebar from './groupsSidebar/GroupsSidebar';
import Privacy from './Privacy';
import Settings from './Settings';
@@ -123,7 +122,7 @@ const Sidebar: React.FC = () => {
const [currLogo, setCurrLogo] = useState(notanglesLogo);
const [collapsed, setCollapsed] = useState(true);
const sideBarRef = useRef(null);
- const { groupsSidebarCollapsed } = useContext(UserContext);
+ const location = useLocation();
const handleCollapse = (val: boolean) => {
setCollapsed(val);
@@ -180,7 +179,7 @@ const Sidebar: React.FC = () => {
return (
- {!groupsSidebarCollapsed && (
+ {location.pathname === '/friends' && (
@@ -207,39 +206,42 @@ const Sidebar: React.FC = () => {
-
}
- description={'Current Timetable'}
- content={null}
+ description={'Your Timetable'}
collapsed={collapsed}
- // currently not clickable since this is our current page
- isClickable={false}
- // hardcoded until we move away from single page site
- isSelected={true}
+ isSelected={location.pathname === '/'}
+ onNavigate="/"
+ />
+ }
+ description={'View Friends Timetables'}
+ collapsed={collapsed}
+ isSelected={location.pathname === '/friends'}
+ onNavigate="/friends"
/>
-
{modalData.map((modal, index) => (
-
+ <>
-
+ >
))}
-
diff --git a/client/src/components/timetable/TimetableLayout.tsx b/client/src/components/timetable/TimetableLayout.tsx
index 236f5b220..e6922964c 100644
--- a/client/src/components/timetable/TimetableLayout.tsx
+++ b/client/src/components/timetable/TimetableLayout.tsx
@@ -2,6 +2,7 @@ import { ContentPaste } from '@mui/icons-material';
import { ListItemIcon, ListItemText, MenuItem } from '@mui/material';
import { styled } from '@mui/system';
import React, { useContext, useRef, useState } from 'react';
+import { useLocation } from 'react-router-dom';
import {
classMargin,
@@ -32,6 +33,7 @@ const BaseCell = styled('div', {
yTo?: number;
isEndX?: boolean;
isEndY?: boolean;
+ locationPathname: string;
onDoubleClick?: React.MouseEventHandler;
}>`
grid-column: ${({ x }) => x};
@@ -49,6 +51,8 @@ const BaseCell = styled('div', {
border-bottom-left-radius: ${({ theme, x, isEndY }) => (x === 2 && isEndY ? theme.shape.borderRadius : 0)}px;
border-top-right-radius: ${({ theme, isEndX, y }) => (isEndX && y === 1 ? theme.shape.borderRadius : 0)}px;
+ border-top-left-radius: ${({ theme, x, y, locationPathname }) =>
+ locationPathname !== '/' && x === 2 && y === 1 ? theme.shape.borderRadius : 0}px;
border-bottom-right-radius: ${({ theme, isEndX, isEndY }) => (isEndX && isEndY ? theme.shape.borderRadius : 0)}px;
`;
@@ -162,6 +166,7 @@ export const TimetableLayout: React.FC = ({ copiedEvent, s
const [createEventAnchorEl, setCreateEventAnchorEl] = useState(null);
const [contextMenu, setContextMenu] = useState(null);
const open = Boolean(createEventAnchorEl);
+ const location = useLocation();
const {
is12HourMode,
@@ -190,19 +195,19 @@ export const TimetableLayout: React.FC = ({ copiedEvent, s
isConvertToLocalTimezone,
);
const hourCells = hours.map((hour, i) => (
-
+
{hour}
));
const dayCells = days.map((day, i) => (
-
+
{day}
));
dayCells.push(
-
+
Unscheduled
,
);
@@ -259,6 +264,7 @@ export const TimetableLayout: React.FC = ({ copiedEvent, s
y={y + 2}
isEndX={x === days.length - 1}
isEndY={y === hours.length - 1}
+ locationPathname={location.pathname}
id={x === 0 && y === 0 ? 'origin' : undefined}
onDoubleClick={(event) => {
handleOpen(event);
@@ -275,11 +281,13 @@ export const TimetableLayout: React.FC = ({ copiedEvent, s
)),
);
- otherCells.push();
+ otherCells.push(
+ ,
+ );
return (
<>
-
+
{
// Invisible guide for the column width for
// consistency between 24 and 12 hour time.
diff --git a/client/src/components/timetableShared.tsx/TimetableShared.tsx b/client/src/components/timetableShared.tsx/TimetableShared.tsx
deleted file mode 100644
index f31463465..000000000
--- a/client/src/components/timetableShared.tsx/TimetableShared.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import styled from '@emotion/styled';
-import { Tooltip } from '@mui/material';
-import React, { useContext } from 'react';
-
-import { UserContext } from '../../context/UserContext';
-import { TimetableProps } from '../../interfaces/PropTypes';
-import { emptyProfile } from '../sidebar/groupsSidebar/friends/UserProfile';
-import Timetable from '../timetable/Timetable';
-
-const Container = styled('div')`
- display: flex;
- flex-direction: column;
- gap: 12px;
-`;
-
-const Legend = styled('div')`
- display: flex;
- gap: 24px;
- align-items: center;
- padding: 12px 32px;
- border-radius: 12px;
- border: 1px dotted grey;
- width: fit-content;
- margin: 12px;
- align-self: center;
-`;
-
-const Members = styled('div')`
- display: flex;
- gap: 2px;
-`;
-
-const MemberText = styled('div')`
- display: flex;
- gap: 2px;
- flex-direction: column;
- align-items: start;
-`;
-const GroupName = styled('div')`
- font-size: 18px;
- font-weight: 600;
-`;
-const GroupDescription = styled('div')`
- font-size: 14px;
-`;
-const TimetableShared: React.FC = ({ assignedColors, handleSelectClass }) => {
- const { groups, selectedGroupIndex } = useContext(UserContext);
- if (groups.length === 0) return <>>;
- const group = groups[selectedGroupIndex];
-
- return (
-
-
-
-
- );
-};
-
-export default TimetableShared;
diff --git a/client/src/components/user/UserIcon.tsx b/client/src/components/user/UserIcon.tsx
new file mode 100644
index 000000000..1dd2ae076
--- /dev/null
+++ b/client/src/components/user/UserIcon.tsx
@@ -0,0 +1,28 @@
+import styled from '@emotion/styled';
+import { Tooltip } from '@mui/material';
+import React from 'react';
+
+const emptyProfile = 'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_960_720.png';
+const defaultSize = 38;
+
+interface UserIconProps {
+ url?: string;
+ size?: number;
+ tooltipTitle?: string;
+}
+
+const StyledUserImage = styled('img')`
+ border-radius: 999px;
+ background-color: white;
+ border: 1px grey solid;
+`;
+
+const UserIcon: React.FC = ({ url, size, tooltipTitle }) => {
+ return (
+
+
+
+ );
+};
+
+export default UserIcon;
diff --git a/client/src/context/UserContext.tsx b/client/src/context/UserContext.tsx
index 1dabb21c1..93775c96a 100644
--- a/client/src/context/UserContext.tsx
+++ b/client/src/context/UserContext.tsx
@@ -34,8 +34,6 @@ export interface IUserContext {
fetchUserInfo: (userID: string) => void;
selectedGroupIndex: number; // selected group is the index of groups;
setSelectedGroupIndex: (newSelectedGroupIndex: number) => void;
- groupsSidebarCollapsed: boolean;
- setGroupsSidebarCollapsed: (isCollapsed: boolean) => void;
}
export const UserContext = createContext({
@@ -46,15 +44,12 @@ export const UserContext = createContext({
fetchUserInfo: () => {},
selectedGroupIndex: -1,
setSelectedGroupIndex: () => {},
- groupsSidebarCollapsed: true,
- setGroupsSidebarCollapsed: () => {},
});
const UserContextProvider = ({ children }: UserContextProviderProps) => {
const [user, setUser] = useState(undefinedUser);
const [groups, setGroups] = useState([]);
const [selectedGroupIndex, setSelectedGroupIndex] = useState(-1);
- const [groupsSidebarCollapsed, setGroupsSidebarCollapsed] = useState(true);
const { setDisplayTimetables, setSelectedTimetable, term, year } = useContext(AppContext);
const { setSelectedClasses, setSelectedCourses, setCreatedEvents, setAssignedColors } = useContext(CourseContext);
@@ -162,10 +157,8 @@ const UserContextProvider = ({ children }: UserContextProviderProps) => {
fetchUserInfo,
selectedGroupIndex,
setSelectedGroupIndex,
- groupsSidebarCollapsed,
- setGroupsSidebarCollapsed,
}),
- [user, groups, selectedGroupIndex, groupsSidebarCollapsed],
+ [user, groups, selectedGroupIndex],
);
return {children};
diff --git a/client/src/index.tsx b/client/src/index.tsx
index c5b4c1106..253817777 100644
--- a/client/src/index.tsx
+++ b/client/src/index.tsx
@@ -1,16 +1,15 @@
import '@fontsource-variable/roboto-flex';
import './index.css';
-import { ApolloProvider } from '@apollo/client';
import { browserTracingIntegration } from '@sentry/browser';
import * as Sentry from '@sentry/react';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
-import { client } from './api/config';
import App from './App';
import EventShareModal from './components/EventShareModal';
+import Friends from './components/friends/Friends';
import LandingPage from './components/landingPage/LandingPage';
import AppContextProvider from './context/AppContext';
import CourseContextProvider from './context/CourseContext';
@@ -27,25 +26,24 @@ const Root: React.FC = () => {
const hasVisited = localStorage.getItem('visited');
return (
-
-
-
-
-
-
- {hasVisited ? (
- } path="/">
- } />
-
- ) : (
- } path="/" />
- )}
-
-
-
-
-
-
+
+
+
+
+
+ {hasVisited ? (
+ } path="/">
+ } />
+ } />
+
+ ) : (
+ } path="/" />
+ )}
+
+
+
+
+
);
};
diff --git a/client/src/interfaces/PropTypes.ts b/client/src/interfaces/PropTypes.ts
index ed2ce1292..8343ddad7 100644
--- a/client/src/interfaces/PropTypes.ts
+++ b/client/src/interfaces/PropTypes.ts
@@ -25,10 +25,10 @@ export interface CustomModalProps {
toolTipTitle: string;
showIcon: ReactNode;
description: string;
- content: ReactNode;
+ content?: ReactNode;
collapsed: boolean;
- isClickable: boolean;
isSelected?: boolean;
+ onNavigate?: string;
}
export interface CourseSelectProps {
diff --git a/client/src/utils/syncTimetables.ts b/client/src/utils/syncTimetables.ts
index 412262cfa..5f3d0f38a 100644
--- a/client/src/utils/syncTimetables.ts
+++ b/client/src/utils/syncTimetables.ts
@@ -117,6 +117,7 @@ const parseTimetableDTO = async (timetableDTO: TimetableDTO, currentYear: string
selectedClasses[courseCode][scrapedClassDTO.activity] =
classDataMap[courseCode].find((clz) => clz.classNo === classID) || null;
});
+ console.log('selected classes conversion', selectedClasses);
// Finally, reverse created events
const eventsList: EventPeriod[] = timetableDTO.createdEvents.map((eventDTO: any) => {
diff --git a/client/src/utils/timetableHelpers.ts b/client/src/utils/timetableHelpers.ts
index 733383fe4..0f3c92865 100644
--- a/client/src/utils/timetableHelpers.ts
+++ b/client/src/utils/timetableHelpers.ts
@@ -32,7 +32,6 @@ const duplicateClasses = (selectedClasses: SelectedClasses) => {
} else {
newActivityCopy[activity] = null;
}
- // newActivityCopy[activity] = classData !== null ? { ...classData } : null;
});
newClasses[courseCode] = { ...newActivityCopy };
});
diff --git a/server/src/user/user.controller.ts b/server/src/user/user.controller.ts
index 50d4ec6c5..61c98236a 100644
--- a/server/src/user/user.controller.ts
+++ b/server/src/user/user.controller.ts
@@ -96,6 +96,7 @@ export class UserController {
@Body('userId') userId: string,
@Body('timetable') timetable: TimetableDto,
) {
+ console.log(userId + 'test ');
return this.userService.editUserTimetable(userId, timetable).then((id) => {
return {
status: 'Successfully edited timetable',
@@ -106,6 +107,7 @@ export class UserController {
@Delete('timetable/:timetableId')
deleteUserTimetable(@Param('timetableId') timetableId: string) {
+ console.log('deleting timetable');
return this.userService.deleteUserTimetable(timetableId).then((id) => {
return {
status: 'Successfully deleted timetable',
diff --git a/server/src/user/user.service.ts b/server/src/user/user.service.ts
index dfdf8f0e6..bc6ac0122 100644
--- a/server/src/user/user.service.ts
+++ b/server/src/user/user.service.ts
@@ -108,7 +108,16 @@ export class UserService {
selectedClasses: true,
},
},
- friends: true,
+ friends: {
+ include: {
+ timetables: {
+ include: {
+ createdEvents: true,
+ selectedClasses: true,
+ },
+ },
+ },
+ },
outgoing: true,
incoming: true,
},