Skip to content
Snippets Groups Projects
index.tsx 5.09 KiB
Newer Older
  • Learn to ignore specific revisions
  • import React from "react";
    
    import MyBreadcrumbs from "components/atoms/MyBreadcrumbs";
    import styles from "./style.module.scss";
    import TermSwitcher from "./components/TermSwitcher";
    
    import { modulesList } from "../ModuleList/list";
    
    import WeekRow from "./components/WeekRow";
    import ModuleRows from "./components/ModuleRows";
    import DayIndicatorGrid from "./components/DayIndicatorGrid";
    import EventGrid from "./components/EventGrid";
    
    import { eventsData } from "./eventsData";
    
    import LoadingScreen from "components/molecules/LoadingScreen";
    
    import { Term, Module } from "constants/types";
    import { addDays } from "utils/functions";
    
    import EventModal from "./components/EventModal";
    
    export interface TimelineEvent {
    
      title: string;
    
    	id: number;
    	type: string;
    
      moduleCode: string;
      startDate: Date;
      endDate: Date;
    
    export type ModuleTracks = {
    
      [index: string]: TimelineEvent[][];
    
    interface TimelineProps {
      initSideBar: () => void;
      revertSideBar: () => void;
      term: Term;
      setTerm: React.Dispatch<React.SetStateAction<Term>>;
    }
    
    
    interface TimelineState {
    
      modulesTracks: ModuleTracks;
    
      modulesList: Module[];
    
      isLoaded: boolean;
    
      activeEvent?: TimelineEvent;
      showEventModal: boolean;
      eventsData: TimelineEvent[];
    
    class Timeline extends React.Component<TimelineProps, TimelineState> {
      constructor(props: TimelineProps) {
        super(props);
    
        this.state = {
          modulesTracks: {},
          isLoaded: false,
          modulesList: [],
    
          showEventModal: false,
          eventsData: [],
    
      componentDidMount() {
        this.props.initSideBar();
    
        let modulesTracks: ModuleTracks = {};
    
        modulesList.forEach(({ code }) => {
    
          modulesTracks[code] = [[], []];
    
    
        let timelineEvents = eventsData;
        for (let i = 0; i < timelineEvents.length; i++) {
          const event = timelineEvents[i];
    
          let tracks: TimelineEvent[][] = modulesTracks[event.moduleCode];
    
          let isPlaced = false;
          for (const track of tracks) {
            if (track.every((te) => !eventsOverlaps(te, event))) {
              isPlaced = true;
              track.push(event);
              break;
            }
    
          }
          if (!isPlaced) {
            tracks.push([event]);
          }
        }
    
    
        this.setState({
    
          modulesTracks: modulesTracks,
    
          isLoaded: true,
    
          modulesList: modulesList,
    
          eventsData: eventsData,
    
      }
    
      componentWillUnmount() {
        this.props.revertSideBar();
    
      dateToColumn(day: Date, termStart: Date) {
        return (
          Math.ceil(((day.getTime() - termStart.getTime()) / 86400000 / 7) * 6) + 1
        );
      }
    
    
      isInTerm(date: Date, termStart: Date, numWeeks: number) {
        return (
          termStart.getTime() < date.getTime() &&
          date.getTime() < addDays(termStart, numWeeks * 7).getTime()
        );
      }
    
    
      handleEventClick(id: number) {
        const event = this.state.eventsData.find((e) => e.id === id);
        this.setState({ activeEvent: event, showEventModal: true });
      }
    
    
        const [termStart, numWeeks] = getTermDates(this.props.term);
    
        const activeDay = new Date("2020-10-12");
        const trackHeight = 4;
    
        if (!this.state.isLoaded) {
          return <LoadingScreen successful={<></>} />;
        }
    
        let currModules = this.state.modulesList.filter(({ terms }) =>
          terms.includes(this.props.term)
        );
    
          <>
            <EventModal
              show={this.state.showEventModal}
              onHide={() => this.setState({ showEventModal: false })}
              event={this.state.activeEvent}
            />
            <div className={styles.timelineContainer}>
              <MyBreadcrumbs />
              <div className={styles.timelineGrid}>
                <TermSwitcher term={this.props.term} setTerm={this.props.setTerm} />
                <WeekRow
                  numWeeks={numWeeks}
                  termStart={termStart}
                  activeDay={activeDay}
                />
                <ModuleRows
                  numWeeks={numWeeks}
                  trackHeight={trackHeight}
                  modulesList={currModules}
                  modulesTracks={this.state.modulesTracks}
                />
    
                <DayIndicatorGrid
                  numWeeks={numWeeks}
                  activeDay={activeDay}
                  activeColumn={this.dateToColumn(activeDay, termStart)}
                  isInTerm={(date) => this.isInTerm(date, termStart, numWeeks)}
                />
    
                <EventGrid
                  numWeeks={numWeeks}
                  trackHeight={trackHeight}
                  modulesList={currModules}
                  modulesTracks={this.state.modulesTracks}
                  dateToColumn={(date) => this.dateToColumn(date, termStart)}
                  isInTerm={(date) => this.isInTerm(date, termStart, numWeeks)}
                  onEventClick={(id) => this.handleEventClick(id)}
                />
              </div>
    
    function eventsOverlaps(e1: TimelineEvent, e2: TimelineEvent) {
      return e1.startDate <= e2.endDate && e1.endDate >= e2.startDate;
    }
    
    
    function getTermDates(term: Term): [Date, number] {
      switch (term) {
        case Term.AUTUMN:
          return [new Date("2020-10-05"), 11];
        case Term.SPRING:
          return [new Date("2021-01-11"), 11];
        case Term.SUMMER:
          return [new Date("2021-04-26"), 9];
      }
    }
    
    
    export default Timeline;