Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/pages/swapPage/SectionFinderPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ const SectionFinderPanel = ({
skip: !displayCode,
});

console.log(data);

if (!displayCode) {
return (
<div className={PANEL_WRAPPER_CLASS}>
Expand Down Expand Up @@ -225,7 +223,6 @@ const SectionFinderPanel = ({
);

const prof = primaryMeeting?.prof;
console.log(prof);
const clearPct =
prof?.rating?.clear != null
? `${Math.round(prof.rating.clear * 100)}%`
Expand Down Expand Up @@ -341,8 +338,13 @@ const SectionFinderPanel = ({
isFull ? 'font-bold text-red' : 'font-normal text-dark2',
)}
>
{section.enrollment_total}/{section.enrollment_capacity}{' '}
Enrolled
<strong>
{Math.max(
0,
section.enrollment_capacity - section.enrollment_total,
)}
</strong>{' '}
of {section.enrollment_capacity} open
</div>
</div>
</div>
Expand Down
68 changes: 66 additions & 2 deletions src/pages/swapPage/SwapCalendar.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import React, { useEffect, useMemo, useState } from 'react';
import { Download } from 'react-feather';
import { UserScheduleFragment } from 'generated/graphql';

import { FadeInWrapper } from 'components/navigation/styles/Footer';
import {
BACKEND_ENDPOINT,
CALENDAR_EXPORT_ENDPOINT,
GOOGLE_CALENDAR_URL,
} from 'constants/Api';
import { SwapSection } from 'graphql/queries/course/SwapCourse';
import {
formatCourseCode,
getCurrentTermCode,
getNextTermCode,
termCodeToDate,
} from 'utils/Misc';
import { randString } from 'utils/Random';

import {
BlockCode,
Expand All @@ -28,12 +35,16 @@ import {
DropdownCourseCode,
DropdownCourseName,
EventBlock,
ExportButton,
ExportMenu,
ExportMenuItem,
GhostEventBlock,
GridBody,
HalfHourLine,
HourLineFull,
LegendDot,
LegendItem,
NewBadge,
SectionFinderContainer,
SWAP_GRID_END_HOUR,
SWAP_GRID_START_HOUR,
Expand Down Expand Up @@ -178,9 +189,11 @@ const buildGhostBlocks = (section: SwapSection | null): BlockPos[] =>

type SwapCalendarProps = {
schedule: UserScheduleFragment['schedule'];
/** Enables the calendar export menu; absent for ephemeral schedules. */
secretId?: string;
};

const SwapCalendar = ({ schedule }: SwapCalendarProps) => {
const SwapCalendar = ({ schedule, secretId }: SwapCalendarProps) => {
const termMap = useMemo(() => groupScheduleByTerm(schedule), [schedule]);

const thisTermCode = getCurrentTermCode();
Expand All @@ -204,6 +217,24 @@ const SwapCalendar = ({ schedule }: SwapCalendarProps) => {
>(null);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isSourceDropdownOpen, setIsSourceDropdownOpen] = useState(false);
const [isExportOpen, setIsExportOpen] = useState(false);

const handleCalendarExport = async (download: boolean) => {
if (!secretId) return;
const response = await fetch(
`${BACKEND_ENDPOINT}${CALENDAR_EXPORT_ENDPOINT(secretId)}`,
);
if (download) {
window.location.assign(response.url);
} else {
// Replace https:// with webcal:// and append random query
// parameter to avoid cache issues with Google Calendar
const calendarUrl = response.url
.replace(/^https:\/\//, 'webcal://')
.concat(`?noCache=${randString()}`);
window.open(`${GOOGLE_CALENDAR_URL}${calendarUrl}`, '_blank');
}
};

useEffect(() => {
setSelectedSwapCourseCode(null);
Expand Down Expand Up @@ -253,7 +284,9 @@ const SwapCalendar = ({ schedule }: SwapCalendarProps) => {
<FadeInWrapper>
<SwapCalendarOuter>
<SwapTitleRow>
<SwapPageTitle>Your schedule</SwapPageTitle>
<SwapPageTitle>
Swap classes <NewBadge>New</NewBadge>
</SwapPageTitle>
<SwapPageSubtitle>
Click any class to see other sections or swap it for a different
course.
Expand Down Expand Up @@ -347,6 +380,37 @@ const SwapCalendar = ({ schedule }: SwapCalendarProps) => {
'Select course from schedule'
)}
</CourseSelectTrigger>

{secretId && (
<CourseSelectBadgeWrapper>
<ExportButton onClick={() => setIsExportOpen((o) => !o)}>
<Download size={15} /> Export
</ExportButton>
{isExportOpen && (
<>
<SwapDropdownOverlay onClick={() => setIsExportOpen(false)} />
<ExportMenu>
<ExportMenuItem
onClick={() => {
handleCalendarExport(false);
setIsExportOpen(false);
}}
>
Add to Google Calendar
</ExportMenuItem>
<ExportMenuItem
onClick={() => {
handleCalendarExport(true);
setIsExportOpen(false);
}}
>
Download .ics file
</ExportMenuItem>
</ExportMenu>
</>
)}
</CourseSelectBadgeWrapper>
)}
</SwapHeaderRow>

<SwapBodyWrapper>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/swapPage/SwapPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const SwapPage = () => {
/>
)}
</Helmet>
<SwapCalendar schedule={schedule} />
<SwapCalendar schedule={schedule} secretId={user?.secret_id} />
<div style={{ display: 'flex', justifyContent: 'center' }}>
<ScheduleImportOverlay visible={!hasSchedule}>
<ScheduleImportCard>
Expand Down
59 changes: 59 additions & 0 deletions src/pages/swapPage/styles/SwapCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,62 @@ export const DropdownEmptyState = styled.div`
color: ${({ theme }) => theme.dark3};
text-align: center;
`;

export const NewBadge = styled.span`
display: inline-block;
vertical-align: middle;
background: ${({ theme }) => theme.accent};
color: ${({ theme }) => theme.dark1};
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.04em;
padding: 3px 8px;
border-radius: 4px;
margin-left: 12px;
`;

export const ExportButton = styled.button`
${Body}
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
padding: 8px 16px;
border: none;
border-radius: 8px;
background: ${({ theme }) => theme.primary};
color: ${({ theme }) => theme.white};
cursor: pointer;

&:hover {
background: ${({ theme }) => theme.primaryDark};
}
`;

export const ExportMenu = styled(SwapDropdownWrapper)`
min-width: 220px;
`;

export const ExportMenuItem = styled.button`
${Body}
display: block;
width: 100%;
padding: 10px 14px;
border: none;
border-bottom: 1px solid ${({ theme }) => theme.light2};
background: transparent;
font-size: 13px;
color: ${({ theme }) => theme.dark1};
cursor: pointer;
text-align: left;

&:last-child {
border-bottom: none;
}

&:hover {
background: ${({ theme }) => theme.light1};
}
`;