From d7f6222bb9a8687bfbe5ee8eaee7ce06131a4869 Mon Sep 17 00:00:00 2001 From: frankmangone Date: Fri, 4 Jun 2021 10:27:20 -0300 Subject: [PATCH 1/5] Working on calendar view --- package.json | 4 +- src/App.styles.tsx | 7 ++ src/Home.tsx | 9 +-- src/components/Button.tsx | 16 +++++ src/components/Calendar/Calendar.tsx | 50 ++++--------- .../Calendar/graphql/getSchoolQuery.graphql | 13 ++++ .../graphql/listCalendarEventsQuery.graphql | 34 +++++++++ .../graphql/listSubjectsQuery.graphql | 25 +++++++ src/components/CalendarSidebar.tsx | 46 ++++++++++++ src/components/CalendarTitle.tsx | 36 ++++++++++ src/components/Logo.tsx | 9 +++ src/components/Navbar.tsx | 22 ++++++ src/components/VerticalAccordion.tsx | 71 +++++++++++++++++++ src/helpers/mapEdges.tsx | 12 ++++ src/layouts/CalendarLayout.tsx | 28 ++++++++ src/pages/CalendarPage/CalendarPage.tsx | 8 ++- tsconfig.json | 19 +++++ yarn.lock | 10 +++ 18 files changed, 374 insertions(+), 45 deletions(-) create mode 100644 src/components/Button.tsx create mode 100644 src/components/Calendar/graphql/getSchoolQuery.graphql create mode 100644 src/components/Calendar/graphql/listCalendarEventsQuery.graphql create mode 100644 src/components/Calendar/graphql/listSubjectsQuery.graphql create mode 100644 src/components/CalendarSidebar.tsx create mode 100644 src/components/CalendarTitle.tsx create mode 100644 src/components/Logo.tsx create mode 100644 src/components/Navbar.tsx create mode 100644 src/components/VerticalAccordion.tsx create mode 100644 src/helpers/mapEdges.tsx create mode 100644 src/layouts/CalendarLayout.tsx create mode 100644 tsconfig.json diff --git a/package.json b/package.json index b93b903..4c67b29 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,11 @@ "interval-tree-type": "^1.0.1", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-icons": "^4.2.0", "react-router-dom": "^5.2.0", "rrule": "^2.6.8", - "styled-components": "^5.2.1" + "styled-components": "^5.2.1", + "typescript": "^4.3.2" }, "devDependencies": { "@babel/core": "^7.13.10", diff --git a/src/App.styles.tsx b/src/App.styles.tsx index 3501ed1..88a732e 100644 --- a/src/App.styles.tsx +++ b/src/App.styles.tsx @@ -6,7 +6,14 @@ export const AppGlobalStyles = createGlobalStyle` } :root { + /* colors */ --colors-primary: #000; + --primary-purple: #3D2D6B; + --primary-purple-transparent: rgba(73,58,117,0.33); + --primary-blue: #2825A6; + --primary-blue-transparent: rgba(40,37,166,0.33); + + --navbar-height: 70px; } html, diff --git a/src/Home.tsx b/src/Home.tsx index d7f6dac..9e89f4d 100644 --- a/src/Home.tsx +++ b/src/Home.tsx @@ -1,19 +1,14 @@ import * as React from "react" import styled from "styled-components" -import LOGO from "./assets/logo.svg" +import { Logo } from "./components/Logo" class Home extends React.Component { render() { return (

- ClassTimes Logo +

Follow your school classes

diff --git a/src/components/Button.tsx b/src/components/Button.tsx new file mode 100644 index 0000000..89ec503 --- /dev/null +++ b/src/components/Button.tsx @@ -0,0 +1,16 @@ +import styled from "styled-components" + +export const Button = styled.a` + border-radius: 5px; + color: var(--primary-blue); + cursor: pointer; + margin: 5px; + padding: 12px; + text-decoration: none; + transition: all 0.2s linear; + + &:hover { + box-shadow: inset 0 0 0 2px var(--primary-blue), + 0 1px 4px 0 var(--primary-blue-transparent); + } +` diff --git a/src/components/Calendar/Calendar.tsx b/src/components/Calendar/Calendar.tsx index b4b961d..02174d6 100644 --- a/src/components/Calendar/Calendar.tsx +++ b/src/components/Calendar/Calendar.tsx @@ -1,4 +1,4 @@ -import React from "react" +import * as React from "react" import styled, { createGlobalStyle } from "styled-components" import dayjs, { Dayjs } from "dayjs" import localeData from "dayjs/plugin/localeData" @@ -7,7 +7,7 @@ import localizedFormat from "dayjs/plugin/localizedFormat" import weekday from "dayjs/plugin/weekday" import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" -import { useQuery, gql } from "@apollo/client" +import { useQuery } from "@apollo/client" import IntervalTree from "interval-tree-type" import { RRule, @@ -25,6 +25,12 @@ import { } from "../../types" import { WeekView } from "./WeekView" +// Queries +import LIST_CALENDAR_EVENTS_QUERY from "./graphql/listCalendarEventsQuery.graphql" + +// Helpers +import mapEdges from "../../helpers/mapEdges" + dayjs.extend(duration) dayjs.extend(localeData) dayjs.extend(localizedFormat) @@ -33,34 +39,6 @@ dayjs.extend(utc) dayjs.extend(timezone) //dayjs.locale('en') - -const ALL_EVENTS = gql` - query getAllEvents { - listCalendarEvents(filters: null, first: 2) { - edges { - node { - _id - title - isAllDay - durationHours - startDateUtc - endDateUtc - rrule - exceptionsDatesUtc - subject { - name - } - } - cursor - } - pageInfo { - endCursor - hasNextPage - } - } - } -` - interface ICalendar { startAtHour?: number title?: React.ReactNode @@ -90,10 +68,7 @@ const generateWeekdays = (selectedDate: Dayjs) => { const generateNow = () => { const now = dayjs() // dayjs.localeData/().weekdays() - - // const week = dayjs.duration({ weeks: 1 }).days() - // const day = - return { now } //JSON.stringify(week) + return { now } } export const Calendar: React.FC = (props) => { @@ -284,13 +259,16 @@ export const eventTreeGenerator: TEventGeneratorTyped = ( } export const CalendarWithData = () => { - // TODO: Handle unauthorized? - const { loading, error, data } = useQuery(ALL_EVENTS) + const { loading, error, data } = useQuery( + LIST_CALENDAR_EVENTS_QUERY, + { variables: { filters: null, first: 5 } } + ) const calendarEventEdges = data?.listCalendarEvents?.edges const title = calendarEventEdges?.[0]?.node?.subject?.name // Placeholder! const events = calendarEventEdges?.map((edge) => edge.node) + // const events = mapEdges(calendarEventEdges) const eventTreeCallback = React.useCallback( (range: [Dayjs, Dayjs]) => { diff --git a/src/components/Calendar/graphql/getSchoolQuery.graphql b/src/components/Calendar/graphql/getSchoolQuery.graphql new file mode 100644 index 0000000..026bdaa --- /dev/null +++ b/src/components/Calendar/graphql/getSchoolQuery.graphql @@ -0,0 +1,13 @@ +query getSchoolQuery($_id: ID!) { + school(_id: $_id) { + name + subjectsConnection { + edges { + node { + _id + name + } + } + } + } +} diff --git a/src/components/Calendar/graphql/listCalendarEventsQuery.graphql b/src/components/Calendar/graphql/listCalendarEventsQuery.graphql new file mode 100644 index 0000000..d8d7fa0 --- /dev/null +++ b/src/components/Calendar/graphql/listCalendarEventsQuery.graphql @@ -0,0 +1,34 @@ +query listCalendarEventsQuery( + $filters: ListCalendarEventsInput + $first: Float + $after: String + $before: String +) { + listCalendarEvents( + filters: $filters + first: $first + after: $after + before: $before + ) { + edges { + node { + _id + title + isAllDay + durationHours + startDateUtc + endDateUtc + rrule + exceptionsDatesUtc + subject { + name + } + } + cursor + } + pageInfo { + endCursor + hasNextPage + } + } +} diff --git a/src/components/Calendar/graphql/listSubjectsQuery.graphql b/src/components/Calendar/graphql/listSubjectsQuery.graphql new file mode 100644 index 0000000..64ae0c6 --- /dev/null +++ b/src/components/Calendar/graphql/listSubjectsQuery.graphql @@ -0,0 +1,25 @@ +query listSubjectsQuery( + $filters: ListSubjectInput + $first: Float + $after: String + $before: String +) { + listSubjects( + filters: $filters + first: $first + after: $after + before: $before + ) { + edges { + node { + _id + name + } + cursor + } + pageInfo { + endCursor + hasNextPage + } + } +} diff --git a/src/components/CalendarSidebar.tsx b/src/components/CalendarSidebar.tsx new file mode 100644 index 0000000..5cda392 --- /dev/null +++ b/src/components/CalendarSidebar.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import styled from "styled-components" +import { useQuery } from "@apollo/client" +// import { Link } from "react-router" + +// Components +import { CalendarTitle } from "./CalendarTitle" +import { Logo } from "./Logo" +import { VerticalAccordion } from "./VerticalAccordion" + +// Queries +import GET_SCHOOL_QUERY from "./Calendar/graphql/getSchoolQuery.graphql" + +export const CalendarSidebar = () => { + const { loading, error, data } = useQuery(GET_SCHOOL_QUERY, { + variables: { _id: "607a4bfc1c2f030cfa277157" }, // TODO: Placeholder for now + }) + + // TODO: Handle errors + const school = data?.school + const subjectsConnection = school?.subjectsConnection + const subjects = subjectsConnection?.edges?.map((edge) => edge.node) || [] + + return ( + + {/* */} + + {/* */} + + + {subjects.map((subject) => ( +

{subject.name}

+ ))} +
+
+ ) +} + +// Styled components + +const SidebarWrapper = styled.div` + height: 100vh; + background-color: #fff; + grid-row: span 2; + padding: 25px 15px; +` diff --git a/src/components/CalendarTitle.tsx b/src/components/CalendarTitle.tsx new file mode 100644 index 0000000..a7f34ab --- /dev/null +++ b/src/components/CalendarTitle.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import styled from "styled-components" + +interface ICalendarTitleProps { + title: string +} + +export const CalendarTitle = (props: ICalendarTitleProps) => { + const { title } = props + + return ( + + +

{title}

+
+ ) +} + +// Styled components + +const CalendarTitleWrapper = styled.div` + align-items: center; + display: flex; + margin-bottom: 50px; + margin-top: 50px; +` + +const AvatarImage = styled.div` + background-color: #ccc; + border-radius: 50%; + display: inline-block; + flex-basis: 70px; + flex-shrink: 0; + height: 70px; + margin-right: 15px; +` diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx new file mode 100644 index 0000000..2609a34 --- /dev/null +++ b/src/components/Logo.tsx @@ -0,0 +1,9 @@ +import * as React from "react" +import LOGO from "../assets/logo.svg" + +export const Logo = (props) => { + const { height } = props + return ( + ClassTimes Logo + ) +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx new file mode 100644 index 0000000..d0a6822 --- /dev/null +++ b/src/components/Navbar.tsx @@ -0,0 +1,22 @@ +import * as React from "react" +import styled from "styled-components" + +// Components +import { Button } from "../components/Button" + +export const Navbar = () => { + return ( + + + + + ) +} + +const NavbarWrapper = styled.header` + background-color: #fff; + display: flex; + flex-direction: row-reverse; + align-items: center; + padding-right: 20px; +` diff --git a/src/components/VerticalAccordion.tsx b/src/components/VerticalAccordion.tsx new file mode 100644 index 0000000..d514b13 --- /dev/null +++ b/src/components/VerticalAccordion.tsx @@ -0,0 +1,71 @@ +import * as React from "react" +import styled from "styled-components" +import { FiChevronDown } from "react-icons/fi" + +interface IVerticalAccordionProps { + title: string + children: JSX.Element[] | JSX.Element +} + +export const VerticalAccordion = (props: IVerticalAccordionProps) => { + const { title, children } = props + + const [visible, setVisible] = React.useState(false) + + const toggleAccordion = () => { + setVisible(!visible) + } + + return ( + + +

{title}

+ + + +
+ {children} +
+ ) +} + +// Styled components + +interface IVisibilityProps { + visible: boolean +} + +const AccordionWrapper = styled.div` + border-radius: 5px; + padding: 5px 10px; + transition: background 0.1s linear; + &:hover { + background-color: #eee; + } +` + +const Arrow = styled.div` + display: flex; + justify-content: center; + align-items: center; + transform: rotate(${(props) => (props.visible ? "180deg" : "0deg")}); + transform-origin: center center; + /* transition: all 0.1s linear; */ +` + +const Dropdown = styled.div` + display: ${(props) => (props.visible ? undefined : "none")}; + user-select: none; +` + +const DropdownToggle = styled.div` + align-items: center; + cursor: pointer; + display: flex; + justify-content: space-between; + width: 100%; + p { + margin-bottom: 10px; + margin-top: 10px; + } +` diff --git a/src/helpers/mapEdges.tsx b/src/helpers/mapEdges.tsx new file mode 100644 index 0000000..27ada81 --- /dev/null +++ b/src/helpers/mapEdges.tsx @@ -0,0 +1,12 @@ +// TODO: Testing!! + +interface edge { + node: T + cursor: string +} + +const mapEdges = (edges: edge[]): T[] => { + return edges.map((edge) => edge.node) +} + +export default mapEdges diff --git a/src/layouts/CalendarLayout.tsx b/src/layouts/CalendarLayout.tsx new file mode 100644 index 0000000..40116c4 --- /dev/null +++ b/src/layouts/CalendarLayout.tsx @@ -0,0 +1,28 @@ +import * as React from "react" +import styled from "styled-components" + +// Components +import { Navbar } from "../components/Navbar" +import { CalendarSidebar } from "../components/CalendarSidebar" + +interface ICalendarLayoutProps { + children: JSX.Element[] +} + +export const CalendarLayout = (props: ICalendarLayoutProps) => { + const { children } = props + + return ( + + + + {children} + + ) +} + +const LayoutWrapper = styled.div` + display: grid; + grid-template-columns: 250px auto; + grid-template-rows: var(--navbar-height) auto; +` diff --git a/src/pages/CalendarPage/CalendarPage.tsx b/src/pages/CalendarPage/CalendarPage.tsx index b761ad9..bfba449 100644 --- a/src/pages/CalendarPage/CalendarPage.tsx +++ b/src/pages/CalendarPage/CalendarPage.tsx @@ -1,9 +1,15 @@ +import * as React from "react" import { CalendarWithData } from "../../components/Calendar" +// Layout +import { CalendarLayout } from "../../layouts/CalendarLayout" + export function Page() { return (
- + + +
) } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e40525a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react" + }, + "include": ["src"] +} diff --git a/yarn.lock b/yarn.lock index 4f013bd..d51c78d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11702,6 +11702,11 @@ react-hotkeys@2.0.0: dependencies: prop-types "^15.6.1" +react-icons@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.2.0.tgz#6dda80c8a8f338ff96a1851424d63083282630d0" + integrity sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ== + react-inspector@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.0.tgz#45a325e15f33e595be5356ca2d3ceffb7d6b8c3a" @@ -13768,6 +13773,11 @@ typescript-plugin-styled-components@^1.4.4: resolved "https://registry.yarnpkg.com/typescript-plugin-styled-components/-/typescript-plugin-styled-components-1.4.4.tgz#fff26d516ff213dffe1a5627fe76f23ae3ee6ada" integrity sha512-w5S5lSpzRFM+61KNNpGtlF46DuTJTyzfWM4g6ic9m189ILEoU3sgoTNHNS2MxQhXsGtQZwAlINKG+Dwy0euwUg== +typescript@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" + integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== + unbox-primitive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.0.tgz#eeacbc4affa28e9b3d36b5eaeccc50b3251b1d3f" From c065e8411a68e428332ad21e65ad40d88f06791c Mon Sep 17 00:00:00 2001 From: frankmangone Date: Fri, 4 Jun 2021 16:32:37 -0300 Subject: [PATCH 2/5] mapEdges helper and typescript support for Connections --- .../Calendar/graphql/getSchoolQuery.graphql | 15 +++++++ src/components/CalendarSidebar.tsx | 40 ++++++++++++++----- src/components/VerticalAccordion.tsx | 1 - src/helpers/mapEdges.tsx | 9 +---- src/types/Connection.ts | 15 +++++++ 5 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 src/types/Connection.ts diff --git a/src/components/Calendar/graphql/getSchoolQuery.graphql b/src/components/Calendar/graphql/getSchoolQuery.graphql index 026bdaa..f1941d6 100644 --- a/src/components/Calendar/graphql/getSchoolQuery.graphql +++ b/src/components/Calendar/graphql/getSchoolQuery.graphql @@ -1,6 +1,21 @@ query getSchoolQuery($_id: ID!) { school(_id: $_id) { name + careersConnection { + edges { + node { + name + subjectsConnection { + edges { + node { + _id + name + } + } + } + } + } + } subjectsConnection { edges { node { diff --git a/src/components/CalendarSidebar.tsx b/src/components/CalendarSidebar.tsx index 5cda392..e1a34e4 100644 --- a/src/components/CalendarSidebar.tsx +++ b/src/components/CalendarSidebar.tsx @@ -11,6 +11,22 @@ import { VerticalAccordion } from "./VerticalAccordion" // Queries import GET_SCHOOL_QUERY from "./Calendar/graphql/getSchoolQuery.graphql" +// Types +import { IConnection } from "../types/Connection" + +// Helpers +import mapEdges from "../helpers/mapEdges" + +interface ICareer { + name?: string + subjectsConnection?: IConnection +} + +interface ISubject { + _id?: string + name?: string +} + export const CalendarSidebar = () => { const { loading, error, data } = useQuery(GET_SCHOOL_QUERY, { variables: { _id: "607a4bfc1c2f030cfa277157" }, // TODO: Placeholder for now @@ -18,20 +34,26 @@ export const CalendarSidebar = () => { // TODO: Handle errors const school = data?.school - const subjectsConnection = school?.subjectsConnection - const subjects = subjectsConnection?.edges?.map((edge) => edge.node) || [] + + const careersConnection: IConnection = school?.careersConnection + const careers = mapEdges(careersConnection?.edges) return ( - {/* */} - {/* */} - - {subjects.map((subject) => ( -

{subject.name}

- ))} -
+ {careers.map((career) => { + const { name, subjectsConnection }: ICareer = career + const subjects = mapEdges(subjectsConnection?.edges) + + return ( + + {subjects.map((subject) => ( +

{subject.name}

+ ))} +
+ ) + })}
) } diff --git a/src/components/VerticalAccordion.tsx b/src/components/VerticalAccordion.tsx index d514b13..66fa421 100644 --- a/src/components/VerticalAccordion.tsx +++ b/src/components/VerticalAccordion.tsx @@ -50,7 +50,6 @@ const Arrow = styled.div` align-items: center; transform: rotate(${(props) => (props.visible ? "180deg" : "0deg")}); transform-origin: center center; - /* transition: all 0.1s linear; */ ` const Dropdown = styled.div` diff --git a/src/helpers/mapEdges.tsx b/src/helpers/mapEdges.tsx index 27ada81..12bae7d 100644 --- a/src/helpers/mapEdges.tsx +++ b/src/helpers/mapEdges.tsx @@ -1,11 +1,6 @@ -// TODO: Testing!! +import { IEdge } from "../types/Connection" -interface edge { - node: T - cursor: string -} - -const mapEdges = (edges: edge[]): T[] => { +const mapEdges = (edges: IEdge[]): T[] => { return edges.map((edge) => edge.node) } diff --git a/src/types/Connection.ts b/src/types/Connection.ts new file mode 100644 index 0000000..dad4861 --- /dev/null +++ b/src/types/Connection.ts @@ -0,0 +1,15 @@ +export interface IEdge { + node?: NodeType + cursor?: string +} + +interface IPageInfo { + hasNextPage?: boolean + endCursor?: string +} + +export interface IConnection { + edges?: IEdge[] + pageInfo?: IPageInfo + totalCount?: number +} From 63233424b220e3c336d32fa5107cbc10f1d220a0 Mon Sep 17 00:00:00 2001 From: frankmangone Date: Sun, 6 Jun 2021 15:41:39 -0300 Subject: [PATCH 3/5] Moved school variable to Context, and added a hook --- src/components/Calendar/Calendar.tsx | 2 +- src/components/CalendarSidebar.tsx | 59 +++++++------- src/components/CalendarTitle.tsx | 2 +- src/components/ItemToggle.tsx | 76 +++++++++++++++++++ src/components/Logo.tsx | 6 +- src/components/Navbar.tsx | 2 +- src/components/VerticalAccordion.tsx | 10 +-- src/helpers/addStateVariablesToSchool.ts | 21 +++++ src/helpers/addTogglesToCareers.tsx | 20 +++++ src/helpers/deepClone.ts | 3 + src/helpers/mapEdges.tsx | 6 +- src/layouts/CalendarLayout.tsx | 4 +- src/pages/CalendarPage/CalendarPage.tsx | 30 +++++++- .../CalendarPage/SchoolCalendarContext.tsx | 16 ++++ src/types/Career.ts | 7 ++ src/types/School.ts | 7 ++ src/types/Subject.ts | 5 ++ 17 files changed, 229 insertions(+), 47 deletions(-) create mode 100644 src/components/ItemToggle.tsx create mode 100644 src/helpers/addStateVariablesToSchool.ts create mode 100644 src/helpers/addTogglesToCareers.tsx create mode 100644 src/helpers/deepClone.ts create mode 100644 src/pages/CalendarPage/SchoolCalendarContext.tsx create mode 100644 src/types/Career.ts create mode 100644 src/types/School.ts create mode 100644 src/types/Subject.ts diff --git a/src/components/Calendar/Calendar.tsx b/src/components/Calendar/Calendar.tsx index 02174d6..164e018 100644 --- a/src/components/Calendar/Calendar.tsx +++ b/src/components/Calendar/Calendar.tsx @@ -29,7 +29,7 @@ import { WeekView } from "./WeekView" import LIST_CALENDAR_EVENTS_QUERY from "./graphql/listCalendarEventsQuery.graphql" // Helpers -import mapEdges from "../../helpers/mapEdges" +import { mapEdges } from "../../helpers/mapEdges" dayjs.extend(duration) dayjs.extend(localeData) diff --git a/src/components/CalendarSidebar.tsx b/src/components/CalendarSidebar.tsx index e1a34e4..d405f31 100644 --- a/src/components/CalendarSidebar.tsx +++ b/src/components/CalendarSidebar.tsx @@ -1,55 +1,58 @@ import * as React from "react" import styled from "styled-components" -import { useQuery } from "@apollo/client" // import { Link } from "react-router" +// Hooks +import { useSchoolCalendar } from "../pages/CalendarPage/SchoolCalendarContext" + // Components import { CalendarTitle } from "./CalendarTitle" import { Logo } from "./Logo" import { VerticalAccordion } from "./VerticalAccordion" - -// Queries -import GET_SCHOOL_QUERY from "./Calendar/graphql/getSchoolQuery.graphql" +import { ItemToggle } from "./ItemToggle" // Types -import { IConnection } from "../types/Connection" +import { ICareer } from "../types/Career" +import { ISubject } from "../types/Subject" // Helpers -import mapEdges from "../helpers/mapEdges" +import { deepClone } from "../helpers/deepClone" +import { mapEdges } from "../helpers/mapEdges" -interface ICareer { - name?: string - subjectsConnection?: IConnection -} - -interface ISubject { - _id?: string - name?: string -} +export const CalendarSidebar: React.FC = () => { + const { school, setSchool } = useSchoolCalendar() -export const CalendarSidebar = () => { - const { loading, error, data } = useQuery(GET_SCHOOL_QUERY, { - variables: { _id: "607a4bfc1c2f030cfa277157" }, // TODO: Placeholder for now - }) + const toggleSubject = (careerIndex: number, subjectIndex: number) => { + const updatedSchool = deepClone(school) + const updatedCareerEdges = updatedSchool?.careersConnection?.edges + const updatedSubjectEdges = + updatedCareerEdges[careerIndex]?.node?.subjectsConnection?.edges + const updatedSubject = updatedSubjectEdges[subjectIndex]?.node + updatedSubject.toggled = !updatedSubject?.toggled - // TODO: Handle errors - const school = data?.school + setSchool(updatedSchool) + } - const careersConnection: IConnection = school?.careersConnection - const careers = mapEdges(careersConnection?.edges) + const careers = mapEdges(school?.careersConnection?.edges) return ( - {careers.map((career) => { + {careers.map((career, cIndex) => { const { name, subjectsConnection }: ICareer = career - const subjects = mapEdges(subjectsConnection?.edges) + const subjects: ISubject[] = mapEdges(subjectsConnection?.edges) return ( - {subjects.map((subject) => ( -

{subject.name}

+ {subjects?.map((subject, sIndex) => ( + { + toggleSubject(cIndex, sIndex) + }} + /> ))}
) @@ -64,5 +67,5 @@ const SidebarWrapper = styled.div` height: 100vh; background-color: #fff; grid-row: span 2; - padding: 25px 15px; + padding: 25px 10px; ` diff --git a/src/components/CalendarTitle.tsx b/src/components/CalendarTitle.tsx index a7f34ab..6e4c8d8 100644 --- a/src/components/CalendarTitle.tsx +++ b/src/components/CalendarTitle.tsx @@ -5,7 +5,7 @@ interface ICalendarTitleProps { title: string } -export const CalendarTitle = (props: ICalendarTitleProps) => { +export const CalendarTitle: React.FC = (props) => { const { title } = props return ( diff --git a/src/components/ItemToggle.tsx b/src/components/ItemToggle.tsx new file mode 100644 index 0000000..b266017 --- /dev/null +++ b/src/components/ItemToggle.tsx @@ -0,0 +1,76 @@ +import * as React from "react" +import styled from "styled-components" + +interface IItemToggleProps { + text: string + toggled: boolean + setToggled: () => void +} + +export const ItemToggle: React.FC = (props) => { + const { text, toggled, setToggled } = props + + return ( + { + setToggled() + }} + > + + {text} + + ) +} + +// Local components + +interface ICheckboxProps { + toggled: boolean +} + +const Checkbox: React.FC = (props) => { + const { toggled } = props + return ( + + {toggled ? : null} + + ) +} + +// Styled Components + +const ToggleWrapper = styled.div` + align-items: center; + cursor: pointer; + display: flex; + justify-content: space-between; +` + +const Text = styled.p` + text-align: right; +` + +interface ICheckboxWrapperProps { + toggled: boolean +} + +const CheckboxWrapper = styled.div` + align-items: center; + background-color: #CCC; + /* background-color: ${(props) => (props.toggled ? "#222" : "#CCC")}; */ + border-radius: 5px; + box-shadow: ${(props) => + props.toggled ? "inset 0 0 0 2px #222" : undefined}; + display: flex; + flex-basis: 20px; + flex-shrink: 0; + justify-content: center; + height: 20px; +` + +const CheckboxMarker = styled.div` + background-color: #222; + border-radius: 2px; + height: 12px; + width: 12px; +` diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx index 2609a34..1dee7f9 100644 --- a/src/components/Logo.tsx +++ b/src/components/Logo.tsx @@ -1,7 +1,11 @@ import * as React from "react" import LOGO from "../assets/logo.svg" -export const Logo = (props) => { +interface ILogoProps { + height?: number +} + +export const Logo: React.FC = (props) => { const { height } = props return ( ClassTimes Logo diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index d0a6822..0a17703 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components" // Components import { Button } from "../components/Button" -export const Navbar = () => { +export const Navbar: React.FC = () => { return ( diff --git a/src/components/VerticalAccordion.tsx b/src/components/VerticalAccordion.tsx index 66fa421..5dd0e5a 100644 --- a/src/components/VerticalAccordion.tsx +++ b/src/components/VerticalAccordion.tsx @@ -7,7 +7,7 @@ interface IVerticalAccordionProps { children: JSX.Element[] | JSX.Element } -export const VerticalAccordion = (props: IVerticalAccordionProps) => { +export const VerticalAccordion: React.FC = (props) => { const { title, children } = props const [visible, setVisible] = React.useState(false) @@ -37,10 +37,10 @@ interface IVisibilityProps { const AccordionWrapper = styled.div` border-radius: 5px; - padding: 5px 10px; - transition: background 0.1s linear; + padding: 5px 15px; + transition: all 0.15s linear; &:hover { - background-color: #eee; + box-shadow: 0 1px 4px 0 #666; } ` @@ -54,7 +54,7 @@ const Arrow = styled.div` const Dropdown = styled.div` display: ${(props) => (props.visible ? undefined : "none")}; - user-select: none; + /* user-select: none; */ ` const DropdownToggle = styled.div` diff --git a/src/helpers/addStateVariablesToSchool.ts b/src/helpers/addStateVariablesToSchool.ts new file mode 100644 index 0000000..1be6172 --- /dev/null +++ b/src/helpers/addStateVariablesToSchool.ts @@ -0,0 +1,21 @@ +// Helpers +import { deepClone } from "./deepClone" + +// Types +import { ISchool } from "../types/School" + +export const addStateVariablesToSchool = (schoolRawData: ISchool): ISchool => { + const updatedSchool = deepClone(schoolRawData) + + if (updatedSchool?.careersConnection) { + for (let careerEdge of updatedSchool.careersConnection?.edges) { + if (careerEdge.node?.subjectsConnection) { + for (let subjectEdge of careerEdge.node.subjectsConnection.edges) { + subjectEdge.node.toggled = true + } + } + } + } + + return updatedSchool +} diff --git a/src/helpers/addTogglesToCareers.tsx b/src/helpers/addTogglesToCareers.tsx new file mode 100644 index 0000000..5c28f2e --- /dev/null +++ b/src/helpers/addTogglesToCareers.tsx @@ -0,0 +1,20 @@ +// Helpers +import { deepClone } from "./deepClone" + +// Types +import { IConnection } from "../types/Connection" +import { ICareer } from "../types/Career" +import { ISubject } from "../types/Subject" + +export const addTogglesToCareers = (careers: ICareer[]): ICareer[] => { + const updatedCareers: ICareer[] = deepClone(careers) + + for (const career of updatedCareers) { + const edges = career?.subjectsConnection?.edges + for (let edge of edges) { + edge.node.toggled = true + } + } + + return updatedCareers +} diff --git a/src/helpers/deepClone.ts b/src/helpers/deepClone.ts new file mode 100644 index 0000000..19c6a95 --- /dev/null +++ b/src/helpers/deepClone.ts @@ -0,0 +1,3 @@ +export const deepClone = (object: T): T => { + return JSON.parse(JSON.stringify(object)) +} diff --git a/src/helpers/mapEdges.tsx b/src/helpers/mapEdges.tsx index 12bae7d..df559d4 100644 --- a/src/helpers/mapEdges.tsx +++ b/src/helpers/mapEdges.tsx @@ -1,7 +1,5 @@ import { IEdge } from "../types/Connection" -const mapEdges = (edges: IEdge[]): T[] => { - return edges.map((edge) => edge.node) +export const mapEdges = (edges: IEdge[]): T[] => { + return edges?.map((edge) => edge?.node) } - -export default mapEdges diff --git a/src/layouts/CalendarLayout.tsx b/src/layouts/CalendarLayout.tsx index 40116c4..e1713b0 100644 --- a/src/layouts/CalendarLayout.tsx +++ b/src/layouts/CalendarLayout.tsx @@ -6,10 +6,10 @@ import { Navbar } from "../components/Navbar" import { CalendarSidebar } from "../components/CalendarSidebar" interface ICalendarLayoutProps { - children: JSX.Element[] + children: JSX.Element[] | JSX.Element } -export const CalendarLayout = (props: ICalendarLayoutProps) => { +export const CalendarLayout: React.FC = (props) => { const { children } = props return ( diff --git a/src/pages/CalendarPage/CalendarPage.tsx b/src/pages/CalendarPage/CalendarPage.tsx index bfba449..352b908 100644 --- a/src/pages/CalendarPage/CalendarPage.tsx +++ b/src/pages/CalendarPage/CalendarPage.tsx @@ -1,15 +1,37 @@ import * as React from "react" -import { CalendarWithData } from "../../components/Calendar" +import { Calendar, CalendarWithData } from "../../components/Calendar" +import { useQuery } from "@apollo/client" + +// Helpers +import { addStateVariablesToSchool } from "../../helpers/addStateVariablesToSchool" // Layout import { CalendarLayout } from "../../layouts/CalendarLayout" +// Context +import { SchoolCalendarContext } from "./SchoolCalendarContext" + +// Queries +import GET_SCHOOL_QUERY from "../../components/Calendar/graphql/getSchoolQuery.graphql" + export function Page() { + const { loading, error, data } = useQuery(GET_SCHOOL_QUERY, { + variables: { _id: "607a4bfc1c2f030cfa277157" }, // TODO: Placeholder for now + }) + + const schoolRawData = data?.school + /* Preprocessing step to add state variables to school object */ + const schoolInitialValue = addStateVariablesToSchool(schoolRawData) + + const [school, setSchool] = React.useState(schoolInitialValue) + return (
- - - + + + + +
) } diff --git a/src/pages/CalendarPage/SchoolCalendarContext.tsx b/src/pages/CalendarPage/SchoolCalendarContext.tsx new file mode 100644 index 0000000..cbf7f91 --- /dev/null +++ b/src/pages/CalendarPage/SchoolCalendarContext.tsx @@ -0,0 +1,16 @@ +import * as React from "react" + +interface IDefaultValue { + school: any + setSchool: React.Dispatch +} + +const defaultValue = { + school: undefined, + setSchool: undefined, +} + +export const SchoolCalendarContext = React.createContext(defaultValue) +export const useSchoolCalendar = () => { + return React.useContext(SchoolCalendarContext) +} diff --git a/src/types/Career.ts b/src/types/Career.ts new file mode 100644 index 0000000..8d9fc02 --- /dev/null +++ b/src/types/Career.ts @@ -0,0 +1,7 @@ +import { IConnection } from "./Connection" +import { ISubject } from "./Subject" + +export interface ICareer { + name?: string + subjectsConnection?: IConnection +} diff --git a/src/types/School.ts b/src/types/School.ts new file mode 100644 index 0000000..2db9d60 --- /dev/null +++ b/src/types/School.ts @@ -0,0 +1,7 @@ +import { IConnection } from "./Connection" +import { ICareer } from "./Career" + +export interface ISchool { + name?: string + careersConnection?: IConnection +} diff --git a/src/types/Subject.ts b/src/types/Subject.ts new file mode 100644 index 0000000..7b844ba --- /dev/null +++ b/src/types/Subject.ts @@ -0,0 +1,5 @@ +export interface ISubject { + _id?: string + name?: string + toggled?: boolean +} From bafb7b0134bd8bd34780c8229ca1ef50a4df7198 Mon Sep 17 00:00:00 2001 From: frankmangone Date: Sun, 6 Jun 2021 16:22:03 -0300 Subject: [PATCH 4/5] Basic router mapping for schools --- src/App.tsx | 2 +- .../Calendar/graphql/getSchoolQuery.graphql | 4 ++-- src/components/CalendarSidebar.tsx | 2 +- src/helpers/addTogglesToCareers.tsx | 20 ------------------- src/helpers/deepClone.ts | 1 + src/pages/CalendarPage/CalendarPage.tsx | 9 ++++++--- 6 files changed, 11 insertions(+), 27 deletions(-) delete mode 100644 src/helpers/addTogglesToCareers.tsx diff --git a/src/App.tsx b/src/App.tsx index e572847..d6a9652 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,7 +12,7 @@ const App = () => { - + ) diff --git a/src/components/Calendar/graphql/getSchoolQuery.graphql b/src/components/Calendar/graphql/getSchoolQuery.graphql index f1941d6..0ba5583 100644 --- a/src/components/Calendar/graphql/getSchoolQuery.graphql +++ b/src/components/Calendar/graphql/getSchoolQuery.graphql @@ -1,5 +1,5 @@ -query getSchoolQuery($_id: ID!) { - school(_id: $_id) { +query getSchoolQuery($shortName: String!) { + school(shortName: $shortName) { name careersConnection { edges { diff --git a/src/components/CalendarSidebar.tsx b/src/components/CalendarSidebar.tsx index d405f31..b6ac14f 100644 --- a/src/components/CalendarSidebar.tsx +++ b/src/components/CalendarSidebar.tsx @@ -39,7 +39,7 @@ export const CalendarSidebar: React.FC = () => { - {careers.map((career, cIndex) => { + {careers?.map((career, cIndex) => { const { name, subjectsConnection }: ICareer = career const subjects: ISubject[] = mapEdges(subjectsConnection?.edges) diff --git a/src/helpers/addTogglesToCareers.tsx b/src/helpers/addTogglesToCareers.tsx deleted file mode 100644 index 5c28f2e..0000000 --- a/src/helpers/addTogglesToCareers.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// Helpers -import { deepClone } from "./deepClone" - -// Types -import { IConnection } from "../types/Connection" -import { ICareer } from "../types/Career" -import { ISubject } from "../types/Subject" - -export const addTogglesToCareers = (careers: ICareer[]): ICareer[] => { - const updatedCareers: ICareer[] = deepClone(careers) - - for (const career of updatedCareers) { - const edges = career?.subjectsConnection?.edges - for (let edge of edges) { - edge.node.toggled = true - } - } - - return updatedCareers -} diff --git a/src/helpers/deepClone.ts b/src/helpers/deepClone.ts index 19c6a95..4b33d8b 100644 --- a/src/helpers/deepClone.ts +++ b/src/helpers/deepClone.ts @@ -1,3 +1,4 @@ export const deepClone = (object: T): T => { + if (!object) return object return JSON.parse(JSON.stringify(object)) } diff --git a/src/pages/CalendarPage/CalendarPage.tsx b/src/pages/CalendarPage/CalendarPage.tsx index 352b908..708e236 100644 --- a/src/pages/CalendarPage/CalendarPage.tsx +++ b/src/pages/CalendarPage/CalendarPage.tsx @@ -1,5 +1,6 @@ import * as React from "react" -import { Calendar, CalendarWithData } from "../../components/Calendar" +import { CalendarWithData } from "../../components/Calendar" +import { useParams } from "react-router" import { useQuery } from "@apollo/client" // Helpers @@ -15,14 +16,16 @@ import { SchoolCalendarContext } from "./SchoolCalendarContext" import GET_SCHOOL_QUERY from "../../components/Calendar/graphql/getSchoolQuery.graphql" export function Page() { + const { schoolShortName } = useParams() const { loading, error, data } = useQuery(GET_SCHOOL_QUERY, { - variables: { _id: "607a4bfc1c2f030cfa277157" }, // TODO: Placeholder for now + variables: { shortName: schoolShortName }, }) - const schoolRawData = data?.school /* Preprocessing step to add state variables to school object */ + const schoolRawData = data?.school const schoolInitialValue = addStateVariablesToSchool(schoolRawData) + /* Set school as state for Context */ const [school, setSchool] = React.useState(schoolInitialValue) return ( From 87e412480d170542a51ee9620f93aef7dedc477c Mon Sep 17 00:00:00 2001 From: frankmangone Date: Sun, 6 Jun 2021 19:51:05 -0300 Subject: [PATCH 5/5] Basic calendarEvent toggling from sidebar --- src/components/Calendar/Calendar.tsx | 63 +++---------------- src/components/Calendar/WeekView.tsx | 6 +- .../Calendar/graphql/getSchoolQuery.graphql | 28 --------- src/components/CalendarSidebar.tsx | 4 +- src/components/Navbar.tsx | 2 +- src/pages/CalendarPage/CalendarPage.tsx | 29 ++++++++- .../CalendarPage/SchoolCalendarContext.tsx | 14 ++++- .../graphql/getSchoolQuery.graphql | 32 ++++++++++ src/types/CalendarEvent.ts | 10 +++ src/types/Subject.ts | 4 ++ 10 files changed, 100 insertions(+), 92 deletions(-) delete mode 100644 src/components/Calendar/graphql/getSchoolQuery.graphql create mode 100644 src/pages/CalendarPage/graphql/getSchoolQuery.graphql create mode 100644 src/types/CalendarEvent.ts diff --git a/src/components/Calendar/Calendar.tsx b/src/components/Calendar/Calendar.tsx index 164e018..9aad84f 100644 --- a/src/components/Calendar/Calendar.tsx +++ b/src/components/Calendar/Calendar.tsx @@ -25,6 +25,9 @@ import { } from "../../types" import { WeekView } from "./WeekView" +// Hooks +import { useSchoolCalendar } from "../../pages/CalendarPage/SchoolCalendarContext" + // Queries import LIST_CALENDAR_EVENTS_QUERY from "./graphql/listCalendarEventsQuery.graphql" @@ -41,7 +44,7 @@ dayjs.extend(timezone) //dayjs.locale('en') interface ICalendar { startAtHour?: number - title?: React.ReactNode + showHoursCount?: number eventTreeCallback: (range: [Dayjs, Dayjs]) => TEventTree } @@ -72,7 +75,7 @@ const generateNow = () => { } export const Calendar: React.FC = (props) => { - const { eventTreeCallback, title, startAtHour } = props + const { eventTreeCallback, startAtHour } = props const [timezone, setTimezone] = React.useState(() => dayjs.tz.guess()) const { now } = generateNow() const [selectedDate, setSelectedDate] = React.useState(now) @@ -92,7 +95,6 @@ export const Calendar: React.FC = (props) => { return ( - {title && {title}} {selectedDate.format("MMMM YYYY")} @@ -203,15 +205,6 @@ export const eventTreeGenerator: TEventGeneratorTyped = ( value: clonedEventEnd.toISOString(), }, }) - console.log("clonedEvent", { - ruleDate, - clonedEvent, - // sd: clonedEvent.startDateUtc - }) - // clonedEvent.endDateUtc = clonedEvent.startDateUtc.add( - // clonedEvent.durationHours, - // "hours" - // ) tree.insert( clonedEvent._startDateUtc, clonedEvent._endDateUtc, @@ -220,33 +213,7 @@ export const eventTreeGenerator: TEventGeneratorTyped = ( // clonedEvent.startDateUtc = clonedEvent.startDateUtc // clonedEvent.endDateUtc = clonedEvent.endDateUtc.format() } - - console.log({ rule, ruleQuery }) - - // .fromString( - // "DTSTART;TZID=America/Denver:20181101T190000;\n" - // + "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,TH;INTERVAL=1;COUNT=3" - // ) - // const rule = RRule.fromString( - // "DTSTART;TZID=America/Denver:20181101T190000;\n" - // + "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,TH;INTERVAL=1;COUNT=3" - // ) - // // Create a rule: - // const rule = new RRule({ - // freq: RRule.WEEKLY, - // interval: 5, - // byweekday: [RRule.MO, RRule.FR], - // dtstart: new Date(Date.UTC(2012, 1, 1, 10, 30)), - // until: new Date(Date.UTC(2012, 12, 31)) - // }) } - // start.valueOf() - // console.log("Adding event to tree", { - // start, - // end, - // startValueOf: start.valueOf(), - // endValueOf: end.valueOf() - // }) } return { @@ -259,24 +226,15 @@ export const eventTreeGenerator: TEventGeneratorTyped = ( } export const CalendarWithData = () => { - const { loading, error, data } = useQuery( - LIST_CALENDAR_EVENTS_QUERY, - { variables: { filters: null, first: 5 } } - ) - - const calendarEventEdges = data?.listCalendarEvents?.edges - - const title = calendarEventEdges?.[0]?.node?.subject?.name // Placeholder! - const events = calendarEventEdges?.map((edge) => edge.node) - // const events = mapEdges(calendarEventEdges) + const { calendarEvents } = useSchoolCalendar() const eventTreeCallback = React.useCallback( (range: [Dayjs, Dayjs]) => { - const result = eventTreeGenerator(events, range) + const result = eventTreeGenerator(calendarEvents, range) console.log("save eventTree", result) return result }, - [data, events] + [calendarEvents] ) // if (eventTree) { @@ -293,7 +251,7 @@ export const CalendarWithData = () => { {/*
         loading: {loading ? "loading..." : "loaded"} 
@@ -305,14 +263,13 @@ export const CalendarWithData = () => { } const CalendarWrapper = styled.div` - border: 1px solid black; + height: 100%; ` const Pre = styled.pre` text-align: left; ` -const CalendarTitle = styled.h2`` const CalendarSubtitle = styled.div`` const CalendarTimezone = styled.div`` diff --git a/src/components/Calendar/WeekView.tsx b/src/components/Calendar/WeekView.tsx index b833346..d014c4b 100644 --- a/src/components/Calendar/WeekView.tsx +++ b/src/components/Calendar/WeekView.tsx @@ -258,7 +258,6 @@ const WeekViewBody: React.FC = (props) => { const colCount = group.length return Array.from(col).map((ev, eventIndex) => { const { data: event, startDate } = ev - let topPosition = startDate.hour() + startDate.minute() / 60 topPosition *= 100 @@ -267,6 +266,7 @@ const WeekViewBody: React.FC = (props) => { const duration = (event.durationHours * 100) / 24 const isRecurrent = !!event.rrule + const toggled = event.toggled // console.log("[WeekViewEventItem]", { // event, // startDate, @@ -278,6 +278,7 @@ const WeekViewBody: React.FC = (props) => { return ( ` - display: flex; + display: ${(props) => (props.toggled ? "flex" : "none")}; line-height: 1; position: absolute; top: ${(p) => (p.topPosition ? `${p.topPosition}%` : 0)}; diff --git a/src/components/Calendar/graphql/getSchoolQuery.graphql b/src/components/Calendar/graphql/getSchoolQuery.graphql deleted file mode 100644 index 0ba5583..0000000 --- a/src/components/Calendar/graphql/getSchoolQuery.graphql +++ /dev/null @@ -1,28 +0,0 @@ -query getSchoolQuery($shortName: String!) { - school(shortName: $shortName) { - name - careersConnection { - edges { - node { - name - subjectsConnection { - edges { - node { - _id - name - } - } - } - } - } - } - subjectsConnection { - edges { - node { - _id - name - } - } - } - } -} diff --git a/src/components/CalendarSidebar.tsx b/src/components/CalendarSidebar.tsx index b6ac14f..37a80b4 100644 --- a/src/components/CalendarSidebar.tsx +++ b/src/components/CalendarSidebar.tsx @@ -20,7 +20,7 @@ import { deepClone } from "../helpers/deepClone" import { mapEdges } from "../helpers/mapEdges" export const CalendarSidebar: React.FC = () => { - const { school, setSchool } = useSchoolCalendar() + const { careers, school, setSchool } = useSchoolCalendar() const toggleSubject = (careerIndex: number, subjectIndex: number) => { const updatedSchool = deepClone(school) @@ -33,8 +33,6 @@ export const CalendarSidebar: React.FC = () => { setSchool(updatedSchool) } - const careers = mapEdges(school?.careersConnection?.edges) - return ( diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 0a17703..049f3ad 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -13,7 +13,7 @@ export const Navbar: React.FC = () => { ) } -const NavbarWrapper = styled.header` +const NavbarWrapper = styled.nav` background-color: #fff; display: flex; flex-direction: row-reverse; diff --git a/src/pages/CalendarPage/CalendarPage.tsx b/src/pages/CalendarPage/CalendarPage.tsx index 708e236..0b05855 100644 --- a/src/pages/CalendarPage/CalendarPage.tsx +++ b/src/pages/CalendarPage/CalendarPage.tsx @@ -5,6 +5,7 @@ import { useQuery } from "@apollo/client" // Helpers import { addStateVariablesToSchool } from "../../helpers/addStateVariablesToSchool" +import { mapEdges } from "../../helpers/mapEdges" // Layout import { CalendarLayout } from "../../layouts/CalendarLayout" @@ -13,7 +14,12 @@ import { CalendarLayout } from "../../layouts/CalendarLayout" import { SchoolCalendarContext } from "./SchoolCalendarContext" // Queries -import GET_SCHOOL_QUERY from "../../components/Calendar/graphql/getSchoolQuery.graphql" +import GET_SCHOOL_QUERY from "./graphql/getSchoolQuery.graphql" + +// Types +import { ICalendarEvent } from "../../types/CalendarEvent" +import { ICareer } from "../../types/Career" +import { ISubject } from "../../types/Subject" export function Page() { const { schoolShortName } = useParams() @@ -21,6 +27,8 @@ export function Page() { variables: { shortName: schoolShortName }, }) + // TODO: Don't know if it's a good practice to have the entire state in one giant object + /* Preprocessing step to add state variables to school object */ const schoolRawData = data?.school const schoolInitialValue = addStateVariablesToSchool(schoolRawData) @@ -28,9 +36,26 @@ export function Page() { /* Set school as state for Context */ const [school, setSchool] = React.useState(schoolInitialValue) + /* Set related variables for faster access */ + const careers: ICareer[] = mapEdges(school?.careersConnection?.edges) || [] + let subjects: ISubject[] = [] + for (let career of careers) { + subjects = subjects.concat(mapEdges(career?.subjectsConnection?.edges)) + } + let calendarEvents: ICalendarEvent[] = [] + for (let subject of subjects) { + const mappedEvents = mapEdges(subject?.calendarEventsConnection?.edges) + for (let event of mappedEvents) { + event.toggled = subject.toggled + } + calendarEvents = calendarEvents.concat(mappedEvents) + } + return (
- + diff --git a/src/pages/CalendarPage/SchoolCalendarContext.tsx b/src/pages/CalendarPage/SchoolCalendarContext.tsx index cbf7f91..143549b 100644 --- a/src/pages/CalendarPage/SchoolCalendarContext.tsx +++ b/src/pages/CalendarPage/SchoolCalendarContext.tsx @@ -1,11 +1,19 @@ import * as React from "react" +// Types +import { ICalendarEvent } from "../../types/CalendarEvent" +import { ICareer } from "../../types/Career" +import { ISchool } from "../../types/School" interface IDefaultValue { - school: any - setSchool: React.Dispatch + calendarEvents?: ICalendarEvent[] + careers?: ICareer[] + school?: ISchool + setSchool?: React.Dispatch } -const defaultValue = { +const defaultValue: IDefaultValue = { + calendarEvents: undefined, + careers: undefined, school: undefined, setSchool: undefined, } diff --git a/src/pages/CalendarPage/graphql/getSchoolQuery.graphql b/src/pages/CalendarPage/graphql/getSchoolQuery.graphql new file mode 100644 index 0000000..c29e4db --- /dev/null +++ b/src/pages/CalendarPage/graphql/getSchoolQuery.graphql @@ -0,0 +1,32 @@ +query getSchoolQuery($shortName: String!) { + school(shortName: $shortName) { + name + careersConnection { + edges { + node { + name + subjectsConnection { + edges { + node { + name + calendarEventsConnection { + edges { + node { + title + isAllDay + durationHours + startDateUtc + endDateUtc + rrule + exceptionsDatesUtc + } + } + } + } + } + } + } + } + } + } +} diff --git a/src/types/CalendarEvent.ts b/src/types/CalendarEvent.ts new file mode 100644 index 0000000..770ade6 --- /dev/null +++ b/src/types/CalendarEvent.ts @@ -0,0 +1,10 @@ +export interface ICalendarEvent { + title?: string + isAllDay?: boolean + durationHours?: number + startDateUtc?: Date + endDateUtc?: Date + rrule?: string + exceptionsDatesUtc?: any // TODO: Better typing + toggled?: boolean +} diff --git a/src/types/Subject.ts b/src/types/Subject.ts index 7b844ba..bc74ed0 100644 --- a/src/types/Subject.ts +++ b/src/types/Subject.ts @@ -1,5 +1,9 @@ +import { IConnection } from "./Connection" +import { ICalendarEvent } from "./CalendarEvent" + export interface ISubject { _id?: string name?: string toggled?: boolean + calendarEventsConnection?: IConnection }