Skip to content

Commit

Permalink
Merge branch 'compsci-adl:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
xxori authored Dec 8, 2024
2 parents e282b27 + 08b0a78 commit 385f3bd
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 9 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/umami": "^2.10.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19",
"eslint": "^9.10.0",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added public/help/ready-button.webp
Binary file not shown.
44 changes: 42 additions & 2 deletions src/components/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Button, Tooltip } from '@nextui-org/react';
import { Button, Tooltip, useDisclosure } from '@nextui-org/react';
import clsx from 'clsx';
import { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { create } from 'zustand';

import { WEEK_DAYS } from '../constants/week-days';
import { YEAR } from '../constants/year';
import { useCourseColor, useEnrolledCourse } from '../data/enrolled-courses';
import {
useCourseColor,
useEnrolledCourse,
useEnrolledCourses,
} from '../data/enrolled-courses';
import { useCalendar, useOtherWeekCourseTimes } from '../helpers/calendar';
import { useCalendarHourHeight } from '../helpers/calendar-hour-height';
import { calcHoursDuration } from '../helpers/hours-duration';
Expand All @@ -15,6 +19,7 @@ import type dayjs from '../lib/dayjs';
import type { DateTimeRange, WeekCourse, WeekCourses } from '../types/course';
import { timeToDayjs } from '../utils/date';
import { useDrag, useDrop } from '../utils/dnd';
import { EnrolmentModal } from './EnrolmentModal';

type DraggingCourseState = {
isDragging: boolean;
Expand Down Expand Up @@ -175,6 +180,38 @@ const CalendarHeader = ({
);
};

const EndActions = () => {
const { t } = useTranslation();

const blockHeight = useCalendarHourHeight((s) => s.height);

const {
isOpen: isReadyModalOpen,
onOpen: onReadyModalOpen,
onOpenChange: onReadyModalOpenChange,
} = useDisclosure();
return (
<div
className="absolute -bottom-[0.5rem] left-0 flex w-full items-center justify-center gap-4"
style={{ height: blockHeight + 'rem' }}
>
{/* TODO: Share Button */}
<Button
color="primary"
size="lg"
className="font-semibold"
onPress={onReadyModalOpen}
>
{t('calendar.end-actions.ready')} 🚀
</Button>
<EnrolmentModal
isOpen={isReadyModalOpen}
onOpenChange={onReadyModalOpenChange}
/>
</div>
);
};

const CalendarBg = ({ currentWeek }: { currentWeek: dayjs.Dayjs }) => {
const { t } = useTranslation();

Expand Down Expand Up @@ -373,6 +410,8 @@ export const Calendar = () => {
},
});

const noCourses = useEnrolledCourses((s) => s.courses.length === 0);

return (
<div ref={ref} className="touch-pan-y">
<CalendarHeader
Expand All @@ -384,6 +423,7 @@ export const Calendar = () => {
<CalendarBg currentWeek={currentWeek} />
<CalendarCourses courses={courses} currentWeek={currentWeek} />
{isDragging && <CalendarCourseOtherTimes currentWeek={currentWeek} />}
{!noCourses && <EndActions />}
</div>
</div>
);
Expand Down
92 changes: 92 additions & 0 deletions src/components/EnrolmentModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
Button,
Card,
CardBody,
CardHeader,
Divider,
Link,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
Tooltip,
} from '@nextui-org/react';
import clsx from 'clsx';
import { useTranslation } from 'react-i18next';

import { useDetailedEnrolledCourses } from '../data/enrolled-courses';
import { useExportCalendar } from '../helpers/export-calendar';

type ReadyModalProps = {
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
};
export const EnrolmentModal = ({ isOpen, onOpenChange }: ReadyModalProps) => {
const { t } = useTranslation();
const { copyText } = useExportCalendar();

const enrolledCourses = useDetailedEnrolledCourses();
const isOnlyCourse = enrolledCourses.length === 1;

return (
<Modal
isOpen={isOpen}
onOpenChange={onOpenChange}
size={isOnlyCourse ? 'xs' : '2xl'}
>
<ModalContent>
<ModalHeader className="flex-col">
<div>{t('calendar.end-actions.ready')}</div>
<div className="text-sm font-normal">
{t('calendar.end-actions.enrolment-instruction')}
</div>
</ModalHeader>
<ModalBody className={clsx(!isOnlyCourse && 'grid grid-cols-2')}>
{enrolledCourses.map((c) => (
<Card key={c.id}>
<CardHeader className="flex-col text-center">
<p className="text-lg font-black">
{c.name.subject} {c.name.code}
</p>
<p className="text-sm">{c.name.title}</p>
</CardHeader>
<Divider />
<CardBody className="grid grid-cols-2 items-center justify-center gap-2">
{c.classes.map((cls) => (
<div
key={cls.typeId}
className="rounded-lg border *:p-1 *:text-center"
>
<div className="border-b font-bold">{cls.type}</div>
<div>{cls.classNumber}</div>
</div>
))}
</CardBody>
</Card>
))}
</ModalBody>
<ModalFooter className="justify-between">
<Tooltip content={t('calendar.end-actions.copy')} size="sm">
<Button
isIconOnly
className="text-xl"
onPress={copyText}
variant="flat"
>
📋
</Button>
</Tooltip>
<Button
href="https://access.adelaide.edu.au/"
as={Link}
showAnchorIcon
target="_blank"
>
Access Adelaide
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
};
7 changes: 7 additions & 0 deletions src/components/HelpModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ export const HelpModal = () => {
alt: 'Course modal to change class time',
},
},
{
content: t('help.steps.ready-button'),
image: {
path: '/help/ready-button.webp',
alt: 'Ready button at bottom',
},
},
{
content: t('help.steps.access-adelaide'),
image: {
Expand Down
4 changes: 3 additions & 1 deletion src/components/SearchForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ export const SearchForm = () => {
e.preventDefault();
const course = courses?.find((c) => c.id === selectedCourseId);
if (!course) return;
const name = `${course.name.subject} ${course.name.code}`;
await umami.track('Add course', { subject: course.name.subject, name });
enrolledCourses.addCourse({
name: `${course.name.subject} ${course.name.code}`,
name,
id: course.id,
});
setSelectedCourseId(null);
Expand Down
1 change: 1 addition & 0 deletions src/components/Tips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const TIPS = [
<a href="https://csclub.org.au/open-source">CS Club Open Source Team</a> to
work on projects like this!
</>,
<>You can search for courses using abbreviations</>,
];

const tips = shuffle(TIPS);
Expand Down
24 changes: 24 additions & 0 deletions src/helpers/export-calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';

import { useDetailedEnrolledCourses } from '../data/enrolled-courses';

export const useExportCalendar = () => {
const { t } = useTranslation();

const enrolledCourses = useDetailedEnrolledCourses();
const copyText = async () => {
const res = enrolledCourses.map((c) => ({
name: c.name.title + '\n' + c.name.subject + ' ' + c.name.code,
classes: c.classes
.map(({ type, classNumber }) => type + ': ' + classNumber)
.join('\n'),
}));
const resStr = res.map((d) => d.name + '\n\n' + d.classes).join('\n\n');
const advertisement =
'Planned with MyTimetable\nhttps://mytimetable.csclub.org.au/';
await navigator.clipboard.writeText(resStr + '\n\n\n' + advertisement);
toast.success(t('calendar.end-actions.copy-success'));
};
return { copyText };
};
11 changes: 9 additions & 2 deletions src/locales/en-au.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@
"November",
"December"
],
"immoveable-course": "Immoveable course"
"immoveable-course": "Immoveable course",
"end-actions": {
"copy": "Copy to clipboard",
"ready": "Ready for Enrolment",
"copy-success": "Copied to clipboard!",
"enrolment-instruction": "Copy the numbers below and enter them on the enrolment page of Access Adelaide."
}
},
"help": {
"title": "How to use MyTimetable",
Expand All @@ -51,7 +57,8 @@
"change-week": "Change the calendar week to see more classes.",
"course-details": "Click your enrolled course to see details of your enrolled classes.",
"course-modal": "If you encounter any class clashes when using MyTimetable, you can open the modal to change the class.",
"access-adelaide": "You can enrol for courses in Access Adelaide by using the class numbers, once you are happy with your class times."
"ready-button": "Once you are satisfied with your class times, click the \"Ready for Enrolment\" button at the bottom of the calendar.",
"access-adelaide": "You can easily enroll in courses on Access Adelaide using the class numbers shown in the modal."
},
"actions": {
"next-step": "Next Step",
Expand Down
11 changes: 9 additions & 2 deletions src/locales/zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@
"十一月",
"十二月"
],
"immoveable-course": "此课程无法移动"
"immoveable-course": "此课程无法移动",
"end-actions": {
"copy": "复制到剪切板",
"ready": "准备选课",
"copy-success": "已成功复制到剪切板!",
"enrolment-instruction": "复制课程代号并前往 Access Adelaide 的 Enrolment 页面输入以正式选课"
}
},
"help": {
"title": "如何使用 MyTimetable",
Expand All @@ -51,7 +57,8 @@
"change-week": "切换周数查看更多课程",
"course-details": "点击你的选课查看详情",
"course-modal": "如果在使用 MyTimetable 时遇到任何课程冲突,可以随时打开详情弹窗来更改课程",
"access-adelaide": "课程调整完毕后,可以使用详情中的 Class Number 在 Access Adelaide 中进行选课"
"ready-button": "课程调整完毕后,点击日历底部的“准备选课”按钮",
"access-adelaide": "你可以用弹窗中的 Class Number 在 Access Adelaide 中进行选课"
},
"actions": {
"next-step": "下一步",
Expand Down
3 changes: 1 addition & 2 deletions tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,

/* Testing */
"types": ["vitest/globals"]
"types": ["vitest/globals", "umami"]
},
"include": ["src", "__tests__"]
}

0 comments on commit 385f3bd

Please sign in to comment.