From 419e3066f1b0946d48724bcb10519fc42b0c0b28 Mon Sep 17 00:00:00 2001 From: DhruvArora-03 Date: Sat, 17 Feb 2024 15:30:14 -0600 Subject: [PATCH 01/10] cleanup useFlattenedCourseSchedule --- src/views/hooks/useFlattenedCourseSchedule.ts | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/views/hooks/useFlattenedCourseSchedule.ts b/src/views/hooks/useFlattenedCourseSchedule.ts index 60f52cdc..eaa4444d 100644 --- a/src/views/hooks/useFlattenedCourseSchedule.ts +++ b/src/views/hooks/useFlattenedCourseSchedule.ts @@ -1,7 +1,7 @@ import { CalendarCourseCellProps } from 'src/views/components/common/CalendarCourseCell/CalendarCourseCell'; import useSchedules from './useSchedules'; -const dayToNumber = { +const dayToNumber: { [day: string]: number } = { Monday: 0, Tuesday: 1, Wednesday: 2, @@ -15,6 +15,9 @@ interface CalendarGridPoint { endIndex: number; } +/** + * Return type of useFlattenedCourseSchedule + */ export interface CalendarGridCourse { calendarGridPoint?: CalendarGridPoint; componentProps: CalendarCourseCellProps; @@ -22,11 +25,15 @@ export interface CalendarGridCourse { const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30); -export function useFlattenedCourseSchedule() { +/** + * Get the active schedule, and convert it to be render-able into a calendar. + * @returns CalendarGridCourse + */ +export function useFlattenedCourseSchedule(): CalendarGridCourse[] { const [activeSchedule] = useSchedules(); const { courses } = activeSchedule; - const out = courses.flatMap(course => { + return courses.flatMap(course => { const { status, department, @@ -43,6 +50,7 @@ export function useFlattenedCourseSchedule() { courseDeptAndInstr, status, colors: { + // TODO: figure out colors - these are defaults primaryColor: 'ut-gray', secondaryColor: 'ut-gray', }, @@ -50,36 +58,30 @@ export function useFlattenedCourseSchedule() { }, ]; } + + // in-person return meetings.flatMap(meeting => { const { days, startTime, endTime, location } = meeting; const time = meeting.getTimeString({ separator: ' - ', capitalize: true }); const timeAndLocation = `${time} - ${location ? location.building : 'WB'}`; - return days.map(d => { - const dayIndex = dayToNumber[d]; - const startIndex = convertMinutesToIndex(startTime); - const endIndex = convertMinutesToIndex(endTime); - const calendarGridPoint: CalendarGridPoint = { - dayIndex, - startIndex, - endIndex, - }; - - return { - calendarGridPoint, - componentProps: { - courseDeptAndInstr, - timeAndLocation, - status, - colors: { - primaryColor: 'ut-orange', - secondaryColor: 'ut-orange', - }, + return days.map(d => ({ + calendarGridPoint: { + dayIndex: dayToNumber[d], + startIndex: convertMinutesToIndex(startTime), + endIndex: convertMinutesToIndex(endTime), + }, + componentProps: { + courseDeptAndInstr, + timeAndLocation, + status, + colors: { + // TODO: figure out colors - these are defaults + primaryColor: 'ut-orange', + secondaryColor: 'ut-orange', }, - }; - }); + }, + })); }); }); - - return out; } From a363efb2d246875c033b6da387465f3b16eda6ee Mon Sep 17 00:00:00 2001 From: Lukas Zenick Date: Sat, 17 Feb 2024 15:40:42 -0600 Subject: [PATCH 02/10] Save as PNG functionality --- package.json | 1 + pnpm-lock.yaml | 327 +++--------------- .../common/CalendarGrid/CalendarGrid.tsx | 25 +- 3 files changed, 63 insertions(+), 290 deletions(-) diff --git a/package.json b/package.json index 86490fea..20d99036 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "clsx": "^2.1.0", "highcharts": "^11.3.0", "highcharts-react-official": "^3.2.1", + "html2canvas": "^1.4.1", "react": "^18.2.0", "react-devtools-core": "^5.0.0", "react-dom": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db198baf..05332dc5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,9 @@ dependencies: highcharts-react-official: specifier: ^3.2.1 version: 3.2.1(highcharts@11.3.0)(react@18.2.0) + html2canvas: + specifier: ^1.4.1 + version: 1.4.1 react: specifier: ^18.2.0 version: 18.2.0 @@ -65,7 +68,7 @@ devDependencies: version: 1.1.72 '@storybook/addon-designs': specifier: ^7.0.9 - version: 7.0.9(@storybook/addon-docs@7.6.12)(@storybook/addons@7.6.11)(@storybook/components@7.6.12)(@storybook/manager-api@7.6.12)(@storybook/preview-api@7.6.12)(@storybook/theming@7.6.12)(react-dom@18.2.0)(react@18.2.0) + version: 7.0.9(@storybook/addon-docs@7.6.15)(@storybook/addons@7.6.11)(@storybook/components@7.6.15)(@storybook/manager-api@7.6.15)(@storybook/preview-api@7.6.15)(@storybook/theming@7.6.15)(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-essentials': specifier: ^7.6.13 version: 7.6.15(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) @@ -3202,7 +3205,7 @@ packages: - supports-color dev: true - /@storybook/addon-designs@7.0.9(@storybook/addon-docs@7.6.12)(@storybook/addons@7.6.11)(@storybook/components@7.6.12)(@storybook/manager-api@7.6.12)(@storybook/preview-api@7.6.12)(@storybook/theming@7.6.12)(react-dom@18.2.0)(react@18.2.0): + /@storybook/addon-designs@7.0.9(@storybook/addon-docs@7.6.15)(@storybook/addons@7.6.11)(@storybook/components@7.6.15)(@storybook/manager-api@7.6.15)(@storybook/preview-api@7.6.15)(@storybook/theming@7.6.15)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-xJdw1/FgkC8ovTdRIL5FyEJaXtF1XPxsb6rsl2jByG+8tXyM0PJ/yFEkBrqn35Dei2i4N7x8EHXFd8DnzriBRg==} peerDependencies: '@storybook/addon-docs': ^7.0.0 @@ -3220,50 +3223,16 @@ packages: optional: true dependencies: '@figspec/react': 1.0.3(react@18.2.0) - '@storybook/addon-docs': 7.6.12(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-docs': 7.6.15(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@storybook/addons': 7.6.11(react-dom@18.2.0)(react@18.2.0) - '@storybook/components': 7.6.12(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@storybook/manager-api': 7.6.12(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.6.12 - '@storybook/theming': 7.6.12(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': 7.6.15(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + '@storybook/manager-api': 7.6.15(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.6.15 + '@storybook/theming': 7.6.15(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/addon-docs@7.6.12(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-AzMgnGYfEg+Z1ycJh8MEp44x1DfjRijKCVYNaPFT6o+TjN/9GBaAkV4ydxmQzMEMnnnh/0E9YeHO+ivBVSkNog==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@jest/transform': 29.7.0 - '@mdx-js/react': 2.3.0(react@18.2.0) - '@storybook/blocks': 7.6.12(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.6.12 - '@storybook/components': 7.6.12(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@storybook/csf-plugin': 7.6.12 - '@storybook/csf-tools': 7.6.12 - '@storybook/global': 5.0.0 - '@storybook/mdx2-csf': 1.1.0 - '@storybook/node-logger': 7.6.12 - '@storybook/postinstall': 7.6.12 - '@storybook/preview-api': 7.6.12 - '@storybook/react-dom-shim': 7.6.12(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 7.6.12(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.12 - fs-extra: 11.2.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - remark-external-links: 8.0.0 - remark-slug: 6.1.0 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - - encoding - - supports-color - dev: true - /@storybook/addon-docs@7.6.15(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UPODqO+mrYaKyTSAtfRslxOFgSP/v/5vfDx896pbNTC4Sf8xLytoudw4I14hzkHmRdXiOnd21FqXJfmF/Onsvw==} peerDependencies: @@ -3382,44 +3351,6 @@ packages: - react-dom dev: true - /@storybook/blocks@7.6.12(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-T47KOAjgZmhV+Ov59A70inE5edInh1Jh5w/5J5cjpk9a2p4uhd337SnK4B8J5YLhcM2lbKRWJjzIJ0nDZQTdnQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@storybook/channels': 7.6.12 - '@storybook/client-logger': 7.6.12 - '@storybook/components': 7.6.12(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.6.12 - '@storybook/csf': 0.1.2 - '@storybook/docs-tools': 7.6.12 - '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.6.12(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.6.12 - '@storybook/theming': 7.6.12(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.12 - '@types/lodash': 4.14.202 - color-convert: 2.0.1 - dequal: 2.0.3 - lodash: 4.17.21 - markdown-to-jsx: 7.4.1(react@18.2.0) - memoizerific: 1.11.3 - polished: 4.2.2 - react: 18.2.0 - react-colorful: 5.6.1(react-dom@18.2.0)(react@18.2.0) - react-dom: 18.2.0(react@18.2.0) - telejson: 7.2.0 - tocbot: 4.25.0 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - - encoding - - supports-color - dev: true - /@storybook/blocks@7.6.15(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-ODP7AVh2iIGblI5WKGokWSHbp9YQHc+Uce7JCGcnDbNavoy64Z6R6G+wXzF5jfl7xQlbhQ8yQCuSSL4GNdYTeA==} peerDependencies: @@ -3531,17 +3462,6 @@ packages: tiny-invariant: 1.3.1 dev: true - /@storybook/channels@7.6.12: - resolution: {integrity: sha512-TaPl5Y3lOoVi5kTLgKNRX8xh2sUPekH0Id1l4Ymw+lpgriEY6r60bmkZLysLG1GhlskpQ/da2+S2ap2ht8P2TQ==} - dependencies: - '@storybook/client-logger': 7.6.12 - '@storybook/core-events': 7.6.12 - '@storybook/global': 5.0.0 - qs: 6.11.2 - telejson: 7.2.0 - tiny-invariant: 1.3.1 - dev: true - /@storybook/channels@7.6.15: resolution: {integrity: sha512-UPDYRzGkygYFa8QUpEiumWrvZm4u4RKVzgiBt9C4RmHORqkkZzL9LXhaZJp2SmIz1ND5gx6KR5ze8ZnAdwxxoQ==} dependencies: @@ -3610,12 +3530,6 @@ packages: '@storybook/global': 5.0.0 dev: true - /@storybook/client-logger@7.6.12: - resolution: {integrity: sha512-hiRv6dXsOttMPqm9SxEuFoAtDe9rs7TUS8XcO5rmJ9BgfwBJsYlHzAxXkazxmvlyZtKL7gMx6m8OYbCdZgUqtA==} - dependencies: - '@storybook/global': 5.0.0 - dev: true - /@storybook/client-logger@7.6.15: resolution: {integrity: sha512-n+K8IqnombqiQNnywVovS+lK61tvv/XSfgPt0cgvoF/hJZB0VDOMRjWsV+v9qQpj1TQEl1lLWeJwZMthTWupJA==} dependencies: @@ -3643,29 +3557,6 @@ packages: - supports-color dev: true - /@storybook/components@7.6.12(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-PCijPqmlZd7qyTzNr+vD0Kf8sAI9vWJIaxbSjXwn/De3e63m4fsEcIf8FaUT8cMZ46AWZvaxaxX5km2u0UISJQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@radix-ui/react-select': 1.2.2(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toolbar': 1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.6.12 - '@storybook/csf': 0.1.2 - '@storybook/global': 5.0.0 - '@storybook/theming': 7.6.12(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.12 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - dev: true - /@storybook/components@7.6.15(@types/react-dom@18.2.19)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-xD+maP7+C9HeZXi2vJ+uK9hXN4S4spP4uDj9pyZ9yViKb+ztEO6WpovUMT8WRQ0mMegWyLXkx3zqu43hZvXM1g==} peerDependencies: @@ -3696,37 +3587,6 @@ packages: '@storybook/preview-api': 7.6.15 dev: true - /@storybook/core-common@7.6.12: - resolution: {integrity: sha512-kM9YiBBMM2x5v/oylL7gdO1PS4oehgJC21MivS9p5QZ8uuXKtCQ6UQvI3rzaV+1ZzUA4n+I8MyaMrNIQk8KDbw==} - dependencies: - '@storybook/core-events': 7.6.12 - '@storybook/node-logger': 7.6.12 - '@storybook/types': 7.6.12 - '@types/find-cache-dir': 3.2.1 - '@types/node': 18.19.11 - '@types/node-fetch': 2.6.11 - '@types/pretty-hrtime': 1.0.3 - chalk: 4.1.2 - esbuild: 0.18.20 - esbuild-register: 3.5.0(esbuild@0.18.20) - file-system-cache: 2.3.0 - find-cache-dir: 3.3.2 - find-up: 5.0.0 - fs-extra: 11.2.0 - glob: 10.3.10 - handlebars: 4.7.8 - lazy-universal-dotenv: 4.0.0 - node-fetch: 2.7.0 - picomatch: 2.3.1 - pkg-dir: 5.0.0 - pretty-hrtime: 1.0.3 - resolve-from: 5.0.0 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@storybook/core-common@7.6.15: resolution: {integrity: sha512-VGmcLJ5U1r1s8/YnLbKcyB4GnNL+/sZIPqwlcSKzDXO76HoVFv1kywf7PbASote7P3gdhLSxBdg95LH2bdIbmw==} dependencies: @@ -3764,12 +3624,6 @@ packages: ts-dedent: 2.2.0 dev: true - /@storybook/core-events@7.6.12: - resolution: {integrity: sha512-IO4cwk7bBCKH6lLnnIlHO9FwQXt/9CzLUAoZSY9msWsdPppCdKlw8ynJI5YarSNKDBUn8ArIfnRf0Mve0KQr9Q==} - dependencies: - ts-dedent: 2.2.0 - dev: true - /@storybook/core-events@7.6.15: resolution: {integrity: sha512-i4YnjGecbpGyrFe0340sPhQ9QjZZEBqvMy6kF4XWt6DYLHxZmsTj1HEdvxVl4Ej7V49Vw0Dm8MepJ1d4Y8MKrQ==} dependencies: @@ -3827,15 +3681,6 @@ packages: - utf-8-validate dev: true - /@storybook/csf-plugin@7.6.12: - resolution: {integrity: sha512-fe/84AyctJcrpH1F/tTBxKrbjv0ilmG3ZTwVcufEiAzupZuYjQ/0P+Pxs8m8VxiGJZZ1pWofFFDbYi+wERjamQ==} - dependencies: - '@storybook/csf-tools': 7.6.12 - unplugin: 1.6.0 - transitivePeerDependencies: - - supports-color - dev: true - /@storybook/csf-plugin@7.6.15: resolution: {integrity: sha512-5Pm2B8XKNdG3fHyItWKbWnXHSRDFSvetlML+sMWGWYIjwOsnvPqt+gAvLksWhv/uJgDujGxNcPEh+/Y5C8ZAjQ==} dependencies: @@ -3845,22 +3690,6 @@ packages: - supports-color dev: true - /@storybook/csf-tools@7.6.12: - resolution: {integrity: sha512-MdhkYYxSW5I6Jpk34gTkAZsuj9sxe0xdyeUQpNa8CgJxG43F+ehZ6scW/IPjoSG9gCXBUJMekq26UrmbVfsLCQ==} - dependencies: - '@babel/generator': 7.23.6 - '@babel/parser': 7.23.9 - '@babel/traverse': 7.23.9 - '@babel/types': 7.23.9 - '@storybook/csf': 0.1.2 - '@storybook/types': 7.6.12 - fs-extra: 11.2.0 - recast: 0.23.4 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - supports-color - dev: true - /@storybook/csf-tools@7.6.15: resolution: {integrity: sha512-8iKgg2cmbFTpVhRRJOqouhPcEh0c8ywabG4S8ICZvnJooSXUI9mD9p3tYCS7MYuSiHj0epa1Kkn9DtXJRo9o6g==} dependencies: @@ -3893,21 +3722,6 @@ packages: resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==} dev: true - /@storybook/docs-tools@7.6.12: - resolution: {integrity: sha512-nY2lqEDTd/fR/D91ZLlIp+boSuJtkb8DqHW7pECy61rJqzGq4QpepRaWjQDKnGTgPItrsPfTPOu6iXvXNK07Ow==} - dependencies: - '@storybook/core-common': 7.6.12 - '@storybook/preview-api': 7.6.12 - '@storybook/types': 7.6.12 - '@types/doctrine': 0.0.3 - assert: 2.1.0 - doctrine: 3.0.0 - lodash: 4.17.21 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@storybook/docs-tools@7.6.15: resolution: {integrity: sha512-npZEaI9Wpn9uJcRXFElqyiRw8bSxt95mLywPiEEGMT2kE5FfXM8d5Uj5O64kzoXdRI9IhRPEEZZidOtA/UInfQ==} dependencies: @@ -3961,28 +3775,6 @@ packages: - react-dom dev: true - /@storybook/manager-api@7.6.12(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-XA5KQpY44d6mlqt0AlesZ7fsPpm1PCpoV+nRGFBR0YtF6RdPFvrPyHhlGgLkJC4xSyb2YJmLKn8cERSluAcEgQ==} - dependencies: - '@storybook/channels': 7.6.12 - '@storybook/client-logger': 7.6.12 - '@storybook/core-events': 7.6.12 - '@storybook/csf': 0.1.2 - '@storybook/global': 5.0.0 - '@storybook/router': 7.6.12 - '@storybook/theming': 7.6.12(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.6.12 - dequal: 2.0.3 - lodash: 4.17.21 - memoizerific: 1.11.3 - store2: 2.14.2 - telejson: 7.2.0 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - react - - react-dom - dev: true - /@storybook/manager-api@7.6.15(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-cPBsXcnJiaO3QyaEum2JgdihYea3cI03FeV35JdrBYLIelT4oqbYFnzjznsFg9+Ia9iAbz7aOBNyyRsWnC/UKw==} dependencies: @@ -4013,18 +3805,10 @@ packages: resolution: {integrity: sha512-TXJJd5RAKakWx4BtpwvSNdgTDkKM6RkXU8GK34S/LhidQ5Pjz3wcnqb0TxEkfhK/ztbP8nKHqXFwLfa2CYkvQw==} dev: true - /@storybook/node-logger@7.6.12: - resolution: {integrity: sha512-iS44/EjfF6hLecKzICmcpQoB9bmVi4tXx5gVXnbI5ZyziBibRQcg/U191Njl7wY2ScN/RCQOr8lh5k57rI3Prg==} - dev: true - /@storybook/node-logger@7.6.15: resolution: {integrity: sha512-C+sCvRjR+5uVU3VTrfyv7/RlPBxesAjIucUAK0keGyIZ7sFQYCPdkm4m/C4s+TcubgAzVvuoUHlRrSppdA7WzQ==} dev: true - /@storybook/postinstall@7.6.12: - resolution: {integrity: sha512-uR0mDPxLzPaouCNrLp8vID8lATVTOtG7HB6lfjjzMdE3sN6MLmK9n2z2nXjb5DRRxOFWMeE1/4Age1/Ml2tnmA==} - dev: true - /@storybook/postinstall@7.6.15: resolution: {integrity: sha512-DXQQ4kjAbQ7BSd9M4lDI/12vEEciYMP8uYFDlrPFjwD9LezsxtRiORkazjNRRX4730faO5zZsnWhXxCVkxck0g==} dev: true @@ -4048,25 +3832,6 @@ packages: util-deprecate: 1.0.2 dev: true - /@storybook/preview-api@7.6.12: - resolution: {integrity: sha512-uSzeMSLnCRROjiofJP0F0niLWL+sboQ5ktHW6BAYoPwprumXduPxKBUVEZNxMbVYoAz9v/kEZmaLauh8LRP2Hg==} - dependencies: - '@storybook/channels': 7.6.12 - '@storybook/client-logger': 7.6.12 - '@storybook/core-events': 7.6.12 - '@storybook/csf': 0.1.2 - '@storybook/global': 5.0.0 - '@storybook/types': 7.6.12 - '@types/qs': 6.9.11 - dequal: 2.0.3 - lodash: 4.17.21 - memoizerific: 1.11.3 - qs: 6.11.2 - synchronous-promise: 2.0.17 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - dev: true - /@storybook/preview-api@7.6.15: resolution: {integrity: sha512-2KN9vlizF6sFlYsJEGnFqcQaJXs4TTdawC1VazVdtaMSHANDxxDu8F1cP+u7lpPH3DkNZUmTGQDBYfYY9xR0eQ==} dependencies: @@ -4090,16 +3855,6 @@ packages: resolution: {integrity: sha512-q8d9v0+Bo/DHLV68OyV3Klep4knf2GAbrlHhLW1X4jlPccuEDUojIfqfK7m48ayeIxJzO48fcO0JdKM1XABx7g==} dev: true - /@storybook/react-dom-shim@7.6.12(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-P8eu/s/RQlc/7Yvr260lqNa6rttxIYiPUuHQBu9oCacwkpB3Xep2R/PUY2CifRHqlDhaOINO/Z79oGZl4EBQRQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@storybook/react-dom-shim@7.6.15(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-2+X0HIxIyvjfSKVyGGjSJJLEFJ2ox7Rr8FjlMiRo5QfoOJhohZuWH7p4Lw7JMwm5PotnjrwlfsZI3cCilYJeYA==} peerDependencies: @@ -4185,14 +3940,6 @@ packages: qs: 6.11.2 dev: true - /@storybook/router@7.6.12: - resolution: {integrity: sha512-1fqscJbePFJXhapqiv7fAIIqAvouSsdPnqWjJGJrUMR6JBtRYMcrb3MnDeqi9OYnU73r65BrQBPtSzWM8nP0LQ==} - dependencies: - '@storybook/client-logger': 7.6.12 - memoizerific: 1.11.3 - qs: 6.11.2 - dev: true - /@storybook/router@7.6.15: resolution: {integrity: sha512-5yhXXoVZ1iKUgeZoO8PGqBclrLgoJisxIYVK/Y1iJMXZ2ZvwUiTswLALT6lu97tSrcoBVxmqSghg0+U0YEU4Fg==} dependencies: @@ -4254,20 +4001,6 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/theming@7.6.12(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-P4zoMKlSYbNrWJjQROuz+DZSDEpdf3TUvk203EqBRdElqw2EMHcqZ8+0HGPFfVHpqEj05+B9Mr6R/Z/BURj0lw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) - '@storybook/client-logger': 7.6.12 - '@storybook/global': 5.0.0 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@storybook/theming@7.6.15(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-9PpsHAbUf6o0w33/P3mnb7QheTmfGlTYCismj5HMM1O2/zY0kQK9XcG9W+Cyvu56D/lFC19fz9YHQY8W4AbfnQ==} peerDependencies: @@ -4291,15 +4024,6 @@ packages: file-system-cache: 2.3.0 dev: true - /@storybook/types@7.6.12: - resolution: {integrity: sha512-Wsbd+NS10/2yMHQ/26rXHflXam0hm2qufTFiHOX6VXZWxij3slRU88Fnwzp+1QSyjXb0qkEr8dOx7aG00+ItVw==} - dependencies: - '@storybook/channels': 7.6.12 - '@types/babel__core': 7.20.5 - '@types/express': 4.17.21 - file-system-cache: 2.3.0 - dev: true - /@storybook/types@7.6.15: resolution: {integrity: sha512-tLH0lK6SXECSfMpKin9bge+7XiHZII17n6jc9ZI1TfSBZJyq3M6VzWh2r1C2lC97FlkcKXjIwM3n8h1xNjnI+A==} dependencies: @@ -6111,6 +5835,11 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + dev: false + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true @@ -6794,6 +6523,12 @@ packages: postcss: 8.4.35 dev: true + /css-line-break@2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + dependencies: + utrie: 1.0.2 + dev: false + /css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} dependencies: @@ -8886,6 +8621,14 @@ packages: engines: {node: '>=8'} dev: true + /html2canvas@1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + dev: false + /htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} dependencies: @@ -12609,6 +12352,12 @@ packages: minimatch: 3.1.2 dev: true + /text-segmentation@1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + dependencies: + utrie: 1.0.2 + dev: false + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true @@ -13147,6 +12896,12 @@ packages: engines: {node: '>= 0.4.0'} dev: true + /utrie@1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + dependencies: + base64-arraybuffer: 1.0.2 + dev: false + /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true diff --git a/src/views/components/common/CalendarGrid/CalendarGrid.tsx b/src/views/components/common/CalendarGrid/CalendarGrid.tsx index cd0c0e8c..b8af8394 100644 --- a/src/views/components/common/CalendarGrid/CalendarGrid.tsx +++ b/src/views/components/common/CalendarGrid/CalendarGrid.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import React, {useRef} from 'react'; +import html2canvas from 'html2canvas'; import { DAY_MAP } from 'src/shared/types/CourseMeeting'; import CalendarCell from '../CalendarGridCell/CalendarGridCell'; import { CalendarGridCourse } from 'src/views/hooks/useFlattenedCourseSchedule'; @@ -35,11 +36,25 @@ interface Props { * @param props */ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren ): JSX.Element { + const calendarRef = useRef(null); // Create a ref for the calendar grid + + const saveAsPNG = () => { + if (calendarRef.current) { + html2canvas(calendarRef.current).then((canvas) => { + // Create an a element to trigger download + const a = document.createElement('a'); + a.href = canvas.toDataURL('image/png'); + a.download = 'calendar.png'; + a.click(); + }); + } + }; + return (
{/* Displaying the rest of the calendar */} -
+
{/*
{hoursOfDay.map((hour) => ( @@ -58,7 +73,9 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren ))} - {grid.map(row => row)} + {grid.map((row, index) => ( + {row} + ))}
{/* {courseCells.map((Block: typeof CalendarCourseCell) => ( @@ -79,7 +96,7 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren
{/* Second divider */} - From af991e66099d4e99ce99b21c26b3c7cb5846865d Mon Sep 17 00:00:00 2001 From: Lukas Zenick Date: Sat, 17 Feb 2024 15:42:30 -0600 Subject: [PATCH 03/10] broken close bracket --- src/views/components/common/CalendarGrid/CalendarGrid.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/components/common/CalendarGrid/CalendarGrid.tsx b/src/views/components/common/CalendarGrid/CalendarGrid.tsx index 43050e42..2a07107f 100644 --- a/src/views/components/common/CalendarGrid/CalendarGrid.tsx +++ b/src/views/components/common/CalendarGrid/CalendarGrid.tsx @@ -88,7 +88,7 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren
- ))} */} + ))}
{/* First divider */} + +
+
+ ); +}; diff --git a/src/views/components/common/CalendarCourseCell/CalendarCourseCell.tsx b/src/views/components/common/CalendarCourseCell/CalendarCourseCell.tsx index e67d3484..1ea60c25 100644 --- a/src/views/components/common/CalendarCourseCell/CalendarCourseCell.tsx +++ b/src/views/components/common/CalendarCourseCell/CalendarCourseCell.tsx @@ -12,6 +12,7 @@ export interface CalendarCourseCellProps { timeAndLocation?: string; status: Status; colors: CourseColors; + className?: string; } const CalendarCourseCell: React.FC = ({ @@ -19,6 +20,7 @@ const CalendarCourseCell: React.FC = ({ timeAndLocation, status, colors, + className, }: CalendarCourseCellProps) => { let rightIcon: React.ReactNode | null = null; if (status === Status.WAITLISTED) { @@ -34,7 +36,7 @@ const CalendarCourseCell: React.FC = ({ return (
!['S', 'SU'].includes(key)); const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8); @@ -34,12 +34,12 @@ interface Props { * Grid of CalendarGridCell components forming the user's course schedule calendar view * @param props */ -function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren ): JSX.Element { +function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren): JSX.Element { const calendarRef = useRef(null); // Create a ref for the calendar grid const saveAsPNG = () => { if (calendarRef.current) { - html2canvas(calendarRef.current).then((canvas) => { + html2canvas(calendarRef.current).then(canvas => { // Create an a element to trigger download const a = document.createElement('a'); a.href = canvas.toDataURL('image/png'); @@ -85,19 +85,22 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren - +
- ))} + ))}
-
{/* First divider */} +
{/* First divider */} -
{/* Second divider */} +
{/* Second divider */}
From 79c5c97d98e3e7d365ab7420cbefedd2c8ca25d3 Mon Sep 17 00:00:00 2001 From: DhruvArora-03 Date: Sat, 17 Feb 2024 16:37:35 -0600 Subject: [PATCH 08/10] fix extra space --- src/views/hooks/useFlattenedCourseSchedule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/hooks/useFlattenedCourseSchedule.ts b/src/views/hooks/useFlattenedCourseSchedule.ts index eaa4444d..6d551ae6 100644 --- a/src/views/hooks/useFlattenedCourseSchedule.ts +++ b/src/views/hooks/useFlattenedCourseSchedule.ts @@ -62,7 +62,7 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] { // in-person return meetings.flatMap(meeting => { const { days, startTime, endTime, location } = meeting; - const time = meeting.getTimeString({ separator: ' - ', capitalize: true }); + const time = meeting.getTimeString({ separator: '-', capitalize: true }); const timeAndLocation = `${time} - ${location ? location.building : 'WB'}`; return days.map(d => ({ From 4f5753917b9ba4b3381c238bea133da0c16e4a12 Mon Sep 17 00:00:00 2001 From: Abhinav Chadaga Date: Sat, 17 Feb 2024 16:57:51 -0600 Subject: [PATCH 09/10] sort the flattened course schedule (#93) first by dayIndex, then startIndex, then endIndex. --- src/views/hooks/useFlattenedCourseSchedule.ts | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/src/views/hooks/useFlattenedCourseSchedule.ts b/src/views/hooks/useFlattenedCourseSchedule.ts index 6d551ae6..da1a3bb3 100644 --- a/src/views/hooks/useFlattenedCourseSchedule.ts +++ b/src/views/hooks/useFlattenedCourseSchedule.ts @@ -19,7 +19,7 @@ interface CalendarGridPoint { * Return type of useFlattenedCourseSchedule */ export interface CalendarGridCourse { - calendarGridPoint?: CalendarGridPoint; + calendarGridPoint: CalendarGridPoint; componentProps: CalendarCourseCellProps; } @@ -33,55 +33,70 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] { const [activeSchedule] = useSchedules(); const { courses } = activeSchedule; - return courses.flatMap(course => { - const { - status, - department, - instructors, - schedule: { meetings }, - } = course; - const courseDeptAndInstr = `${department} ${instructors[0].lastName}`; + return courses + .flatMap(course => { + const { + status, + department, + instructors, + schedule: { meetings }, + } = course; + const courseDeptAndInstr = `${department} ${instructors[0].lastName}`; - if (meetings.length === 0) { - // asynch, online course - return [ - { + if (meetings.length === 0) { + // asynch, online course + return [ + { + calendarGridPoint: { + dayIndex: 0, + startIndex: 0, + endIndex: 0, + }, + componentProps: { + courseDeptAndInstr, + status, + colors: { + // TODO: figure out colors - these are defaults + primaryColor: 'ut-gray', + secondaryColor: 'ut-gray', + }, + }, + }, + ]; + } + + // in-person + return meetings.flatMap(meeting => { + const { days, startTime, endTime, location } = meeting; + const time = meeting.getTimeString({ separator: '-', capitalize: true }); + const timeAndLocation = `${time} - ${location ? location.building : 'WB'}`; + + return days.map(d => ({ + calendarGridPoint: { + dayIndex: dayToNumber[d], + startIndex: convertMinutesToIndex(startTime), + endIndex: convertMinutesToIndex(endTime), + }, componentProps: { courseDeptAndInstr, + timeAndLocation, status, colors: { // TODO: figure out colors - these are defaults - primaryColor: 'ut-gray', - secondaryColor: 'ut-gray', + primaryColor: 'ut-orange', + secondaryColor: 'ut-orange', }, }, - }, - ]; - } - - // in-person - return meetings.flatMap(meeting => { - const { days, startTime, endTime, location } = meeting; - const time = meeting.getTimeString({ separator: '-', capitalize: true }); - const timeAndLocation = `${time} - ${location ? location.building : 'WB'}`; - - return days.map(d => ({ - calendarGridPoint: { - dayIndex: dayToNumber[d], - startIndex: convertMinutesToIndex(startTime), - endIndex: convertMinutesToIndex(endTime), - }, - componentProps: { - courseDeptAndInstr, - timeAndLocation, - status, - colors: { - // TODO: figure out colors - these are defaults - primaryColor: 'ut-orange', - secondaryColor: 'ut-orange', - }, - }, - })); + })); + }); + }) + .sort((a: CalendarGridCourse, b: CalendarGridCourse) => { + if (a.calendarGridPoint.dayIndex !== b.calendarGridPoint.dayIndex) { + return a.calendarGridPoint.dayIndex - b.calendarGridPoint.dayIndex; + } + if (a.calendarGridPoint.startIndex !== b.calendarGridPoint.startIndex) { + return a.calendarGridPoint.startIndex - b.calendarGridPoint.startIndex; + } + return a.calendarGridPoint.endIndex - b.calendarGridPoint.endIndex; }); - }); } From ac71b838db5c19461bbd3449c5446ffbb05b8c08 Mon Sep 17 00:00:00 2001 From: Derek Chen Date: Sat, 17 Feb 2024 17:02:36 -0600 Subject: [PATCH 10/10] feat: Derek vinson/calendar header (#94) * CalendarHeader alignment progress * Boom * css * Between * Lol * Gap fix * whitespace-nowrap * Gaps * Finished alignment of CourseStatus and Buttons * Colors * ESLint auto format * Color UT Registration Plus text * Reverting vscode --------- Co-authored-by: Vinson Zheng --- .../components/CalendarHeader.stories.tsx | 2 +- .../common/CalendarHeader/CalenderHeader.tsx | 67 ++++++++++--------- .../ScheduleTotalHoursAndCourses.tsx | 2 +- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/stories/components/CalendarHeader.stories.tsx b/src/stories/components/CalendarHeader.stories.tsx index 6833b7b1..047d8694 100644 --- a/src/stories/components/CalendarHeader.stories.tsx +++ b/src/stories/components/CalendarHeader.stories.tsx @@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react'; import CalendarHeader from '@views/components/common/CalendarHeader/CalenderHeader'; const meta = { - title: 'Components/CalendarHeader', + title: 'Components/Common/CalendarHeader', component: CalendarHeader, parameters: { layout: 'centered', diff --git a/src/views/components/common/CalendarHeader/CalenderHeader.tsx b/src/views/components/common/CalendarHeader/CalenderHeader.tsx index b5d07274..7f11d348 100644 --- a/src/views/components/common/CalendarHeader/CalenderHeader.tsx +++ b/src/views/components/common/CalendarHeader/CalenderHeader.tsx @@ -12,40 +12,41 @@ import ScheduleTotalHoursAndCourses from '../ScheduleTotalHoursAndCourses/Schedu import CourseStatus from '../CourseStatus/CourseStatus'; const CalendarHeader = () => ( -
-
+
+ + DATA UPDATED ON: 12:00 AM 02/01/2024 +
+
+
+
+ + + +
+
+
+
- - - - - - - -
-
- -
); diff --git a/src/views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses.tsx b/src/views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses.tsx index fd91d1f7..11e6a5b4 100644 --- a/src/views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses.tsx +++ b/src/views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses.tsx @@ -21,7 +21,7 @@ export default function ScheduleTotalHoursAndCourses({ totalCourses, }: ScheduleTotalHoursAndCoursesProps): JSX.Element { return ( -
+
{`${scheduleName}: `}