Commit c9553734 authored by Zac Wood's avatar Zac Wood
Browse files

Added modal export box which contains multiple options for exporting

parent df064eb3
Pipeline #2866 passed with stage
in 2 minutes and 23 seconds
......@@ -4,7 +4,7 @@
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.prod.js --debug --progress",
"start": "webpack-dev-server --config webpack.dev.js --inline --open",
"start": "webpack-dev-server --config webpack.dev.js --inline",
"prod": "node app.js"
},
"license": "Apache",
......
import * as React from 'react';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { downloadFile } from '../util/utilities';
interface Props {
isModalOpen: boolean;
toggleModal: () => void;
calendarUrl: () => string;
openCalendarAsWebcal: () => void;
downloadIcs: () => Promise<void>;
}
class ExportModal extends React.Component<Props, {}> {
render() {
const { isModalOpen, toggleModal } = this.props;
const { isModalOpen, toggleModal, calendarUrl, openCalendarAsWebcal, downloadIcs } = this.props;
return (
<Modal isOpen={isModalOpen} toggle={toggleModal}>
<ModalHeader toggle={toggleModal}>Your calendar has been generated!</ModalHeader>
<ModalBody>
Here are instructions for adding you calendar to a bunch of different calendar managers.
<h5>Apple Calendar</h5>
To add your schedule to Apple Calendar, click the "Add to calendar" button below. If you are on a
device running macOS or iOS, this will open a dialogue which will walk you through adding the
calendar.
<hr />
<h5>Google Calendar</h5>
<strong>On desktop:</strong>
<br />
Open your <a href="https://calendar.google.com/">Google Calendar</a>. Click the "Settings" button in
the top right, and then click the Settings tab. In the menu on the left, click "Add calendar" and
"From URL". Now, paste the following link inside the text box: <br />
<code>{calendarUrl()}</code>
<br />
<strong>On mobile (Android only):</strong>
<br />
Click the "Download calendar file" button. This will download the calendar file which you may then
open and add to your calendar.
<hr />
<h5>.ics file</h5>
To download a .ics file containing your schedule, click the "Download calendar file" button below.
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={toggleModal}>
<Button color="secondary" onClick={downloadIcs}>
Download calendar file
</Button>
<Button color="primary" onClick={toggleModal}>
<Button color="primary" onClick={openCalendarAsWebcal}>
Add to calendar
</Button>{' '}
</ModalFooter>
......
......@@ -6,8 +6,10 @@ import ExportModal from './ExportModal';
interface ScheduleBadgeProps {
schedule: CourseSection[];
generateCalendar: (schedule: CourseSection[]) => Promise<void>;
removeCourseSection: (courseSection: CourseSection) => void;
generateCalendarUrl: () => string;
openCalendarAsWebcal: () => void;
downloadIcs: () => Promise<void>;
}
interface State {
......@@ -28,7 +30,7 @@ class ScheduleBadge extends React.Component<ScheduleBadgeProps, State> {
toggleModal = () => this.setState({ isModalOpen: !this.state.isModalOpen });
render() {
const { schedule, removeCourseSection, generateCalendar } = this.props;
const { schedule, removeCourseSection, generateCalendarUrl, openCalendarAsWebcal, downloadIcs } = this.props;
return (
<div>
<Row className="justify-content-end">
......@@ -73,7 +75,13 @@ class ScheduleBadge extends React.Component<ScheduleBadgeProps, State> {
</CardBody>
</Card>
</Collapse>
<ExportModal isModalOpen={this.state.isModalOpen} toggleModal={this.toggleModal} />
<ExportModal
isModalOpen={this.state.isModalOpen}
toggleModal={this.toggleModal}
calendarUrl={generateCalendarUrl}
openCalendarAsWebcal={openCalendarAsWebcal}
downloadIcs={downloadIcs}
/>
</div>
);
}
......
import * as React from 'react';
import CourseSection from '../util/CourseSection';
import ScheduleBadge from './ScheduleBadge';
import ApiService from '../util/ApiService';
import { downloadFile } from '../util/utilities';
interface ScheduleRootProps {
schedule: CourseSection[];
removeCourseSection: (courseSection: CourseSection) => any;
generateCalendarUrl: () => string;
openCalendarAsWebcal: () => void;
downloadIcs: () => Promise<void>;
}
const generateSchedule = async (schedule: CourseSection[]) => {
const crns = schedule.map(section => section.crn);
ApiService.subscribeToCalendar(crns);
};
const ScheduleRoot = ({ schedule, removeCourseSection }: ScheduleRootProps) => (
const ScheduleRoot = ({
schedule,
removeCourseSection,
generateCalendarUrl,
openCalendarAsWebcal,
downloadIcs,
}: ScheduleRootProps) => (
<div>
<ScheduleBadge
schedule={schedule}
removeCourseSection={removeCourseSection}
generateCalendar={generateSchedule}
generateCalendarUrl={generateCalendarUrl}
openCalendarAsWebcal={openCalendarAsWebcal}
downloadIcs={downloadIcs}
/>
{/* <ScheduleList courses={schedule} selectCourseCallback={removeCourseSection} /> */}
{/* <button onClick={generateSchedule}>Generate Schedule</button> */}
......
......@@ -2,11 +2,26 @@ import { connect } from 'react-redux';
import { removeCourseSection } from '../actions/schedule/schedule.actions';
import ScheduleRoot from '../components/ScheduleRoot';
import { State } from '../reducers';
import CourseSection from '../util/CourseSection';
import ApiService from '../util/ApiService';
import { downloadFile } from '../util/utilities';
const mapStateToProps = (state: State) => ({
// Takes the current Redux state and returns objects which will be
// passed to the component as Props
const mapStateToProps = (state: State) => {
const crns = state.schedule ? state.schedule.map(section => section.crn) : [];
return {
schedule: state.schedule,
});
generateCalendarUrl: () => ApiService.generateCalendarUrl(crns),
openCalendarAsWebcal: () => ApiService.openCalendarAsWebcal(crns),
downloadIcs: async () => {
const icsText = await ApiService.fetchICal(crns);
downloadFile(icsText, 'GMU Fall 2018.ics');
},
};
};
// Pass mapStateToProps and other values to the component's props
export default connect(
mapStateToProps,
{ removeCourseSection }
......
......@@ -9,8 +9,12 @@ class ApiService {
searchCourseSections = async (crn: string): Promise<any[]> =>
fetchJson(`${this.apiRoot}/course_sections?crn=${crn}`);
subscribeToCalendar = (crns: string[]) =>
generateCalendarUrl = (crns: string[]): string => `${this.apiRoot}/schedules?crns=${crns.join(',')}`;
openCalendarAsWebcal = (crns: string[]) => {
window.open(`${this.webcalUrl}/schedules?crns=${crns.join(',')}`, '_self');
};
fetchICal = async (crns: string[]): Promise<string> =>
fetch(this.generateCalendarUrl(crns)).then(response => response.text());
}
const fetchJson = async (url: string): Promise<any> => fetch(url).then(response => response.json());
......@@ -24,7 +28,7 @@ const postJson = (endpoint: string, data: any): Promise<Response> =>
});
const local = 'localhost:3000/api';
const remote = `${window.location.hostname}/api`
const remote = `${window.location.hostname}/api`;
const apiUrl = process.env.NODE_ENV === 'development' ? `http://${local}` : `https://${remote}`;
const webcalUrl = process.env.NODE_ENV === 'development' ? `webcal://${local}` : `webcal://${remote}`;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment