Commit 47856176 authored by Zac Wood's avatar Zac Wood

Deleted schedules_web rip

parent 8a7f09b7
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/dist
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.vscode/
{
"printWidth": 120,
"tabWidth": 4,
"singleQuote": true,
"useTabs": false,
"jsxBracketSameLine": true,
"trailingComma": "es5"
}
FROM node:9
COPY . /web
WORKDIR /web
RUN yarn install && yarn run build
FROM nginx:1.15.0
COPY --from=0 /web/dist /web
FROM node:9
# Copy the project files to /web
COPY . /web
# Tell Docker to run all commands in /api
WORKDIR /web
# Install project dependencies
RUN yarn
var path = require('path');
var express = require('express');
var DIST_DIR = path.join(__dirname, 'dist');
var PORT = 8080;
var app = express();
//Serving the files on the dist folder
app.use(express.static(DIST_DIR));
//Send index.html when the user access the web
app.get('*', function(req, res) {
res.sendFile(path.join(DIST_DIR, 'index.html'));
});
app.listen(PORT);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>SRCT Schedules • Welcome</title>
<meta name="theme-color" content="#006633" />
<!-- FB/Opengraph tags -->
<meta property="og:url" content="https://schedules.gmu.edu/">
<meta property="og:type" content="website">
<meta property="og:title" content="SRCT Schedules">
<meta property="og:description" content="Easily generate a calendar with your class schedule.">
<meta property="og:site_name" content="SRCT Schedules">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="SRCT">
<!-- Twitter card tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@MasonSRCT">
<meta name="twitter:creator" content="@MasonSRCT">
<meta name="twitter:url" content="https://schedules.gmu.edu/">
<meta name="twitter:title" content="SRCT Schedules">
<meta name="twitter:description" content="Easily generate a calendar with your class schedule.">
</head>
<body>
<div id="root"></div>
<!-- Matomo -->
<script type="text/javascript">
var _paq = _paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//matomo.srct.gmu.edu/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', '3']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
</body>
</html>
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
upstream backend {
server api:3000;
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
location / {
root /web;
index index.html;
}
location /api {
proxy_pass http://backend/;
}
}
}
{
"name": "schedules_web",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.prod.js --debug --progress",
"start": "webpack-dev-server --config webpack.dev.js --inline",
"prod": "node app.js"
},
"license": "Apache",
"private": true,
"devDependencies": {
"@types/file-saver": "^1.3.0",
"@types/node": "^9.6.6",
"awesome-typescript-loader": "^5.0.0",
"source-map-loader": "^0.2.3",
"typescript": "^2.8.3",
"webpack": "^4.6.0",
"webpack-cli": "^2.0.15",
"webpack-dev-server": "^3.1.3"
},
"dependencies": {
"@types/react": "^16.3.12",
"@types/react-dom": "^16.0.5",
"@types/react-redux": "^6.0.2",
"@types/reactstrap": "^6.0.0",
"babel-loader": "^7.1.4",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^0.28.11",
"express": "^4.16.3",
"file-loader": "^1.1.11",
"file-saver": "^1.3.8",
"html-webpack-plugin": "^3.2.0",
"masonstrap": "https://git.gmu.edu/srct/masonstrap.git",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-redux": "^5.0.7",
"reactstrap": "^6.1.0",
"redux": "^4.0.0",
"redux-thunk": "^2.3.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack-merge": "^4.1.4"
},
"babel": {
"presets": [
"es2015"
]
}
}
export const ADD_COURSE_SECTION: string = '[Schedule] ADD_COURSE_SECTION';
export const REMOVE_COURSE_SECTION: string = '[Schedule] REMOVE_COURSE_SECTION';
import CourseSection from '../../util/CourseSection';
import { ADD_COURSE_SECTION, REMOVE_COURSE_SECTION } from './schedule.action-types';
export interface ScheduleAction {
type: string; // What action is to be performed
courseSection: CourseSection; // The section that is being added/removed
}
/**
* Add a section to the Schedule
* @param section The section that is to be added
*/
export const addCourseSection = (courseSectionToAdd: CourseSection): ScheduleAction => ({
type: ADD_COURSE_SECTION,
courseSection: courseSectionToAdd,
});
/**
* Remove a section from the Schedule
* @param section The section that is to be removed
*/
export const removeCourseSection = (courseSectionToRemove: CourseSection): ScheduleAction => ({
type: REMOVE_COURSE_SECTION,
courseSection: courseSectionToRemove,
});
export const SET_SEARCH_RESULTS: string = '[Search] SET_SEARCH_RESULTS';
import ApiService from '../../util/ApiService';
import CourseSection from '../../util/CourseSection';
import { SET_SEARCH_RESULTS } from './search.action-types';
export interface SearchAction {
type: string;
searchResults: CourseSection[];
error: string;
}
export const searchCourseSections = (crn: string) => async (dispatch: any) => {
const objects = await ApiService.searchCourseSections(crn);
const results: CourseSection[] = objects.map(
(object: any): CourseSection => ({
id: object.id,
name: object.name,
title: object.title,
crn: object.crn,
instructor: object.instructor_name,
location: object.location,
days: object.days,
startTime: object.start_time,
endTime: object.end_time,
})
);
dispatch({
type: SET_SEARCH_RESULTS,
searchResults: results,
error: results.length === 0 ? 'No course sections found with the given CRN.' : '',
});
};
import * as React from 'react';
import { Container } from 'reactstrap';
import Schedule from '../containers/Schedule';
import Search from '../containers/Search';
import Header from './Header';
require('../css/core.css');
/**
* The root component for the app
*/
const App = () => (
<div>
<Container>
<Schedule />
<Header />
<Search />
</Container>
</div>
);
export default App;
import * as React from 'react';
import { Button, Card, CardBody, CardTitle, Col, Row } from 'reactstrap';
import CourseSection from '../util/CourseSection';
interface CourseSectionCardProps {
courseSectionAction: (courseSection: CourseSection) => void;
courseSection: CourseSection;
courseSectionActionButtonText: string;
destructive?: boolean;
}
require('../css/button-text-override.css');
/**
* Renders information about a single course section, and includes a
* button for adding/removing it from the current schedule.
*/
const CourseSectionCard = ({
courseSection,
courseSectionAction,
courseSectionActionButtonText,
destructive,
}: CourseSectionCardProps) => (
<Row className="justify-content-center my-3">
<Col md="9">
<Card>
<CardBody>
<CardTitle className="mb-4">
<i className="fas fa-hashtag" /> {courseSection.crn}
</CardTitle>
<Row>
<Col md="6">
<div className="mb-4">
<h4>{courseSection.title}</h4>
<p>{courseSection.name}</p>
</div>
<i className="fas fa-chalkboard-teacher fa-fw" /> {courseSection.instructor}
<br />
<i className="fas fa-clock fa-fw" /> {courseSection.days}, {courseSection.startTime} -{' '}
{courseSection.endTime}
<br />
<i className="fas fa-school fa-fw" /> {courseSection.location}
</Col>
<Col md="6">
<Button
onClick={() => courseSectionAction(courseSection)}
color={destructive ? 'danger' : 'primary'}
size="lg"
block
className="shadow-sm mt-3">
<i className={`fas fa-${destructive ? 'minus' : 'plus'}-circle mr-2 fa-fw`} />{' '}
{courseSectionActionButtonText}
</Button>
</Col>
</Row>
</CardBody>
</Card>
</Col>
</Row>
);
export default CourseSectionCard;
import * as React from 'react';
import CourseSection from '../util/CourseSection';
import CourseSectionCard from './CourseSectionCard';
interface CourseSectionListProps {
courseSections: CourseSection[];
courseSectionAction: (courseSection: CourseSection) => void;
courseSectionActionButtonText: string;
destructive?: boolean;
}
/**
* Renders a list of CourseSectionCards for every course section in
* the current schedule.
*/
const CourseSectionList = ({
courseSections,
courseSectionAction,
courseSectionActionButtonText,
destructive,
}: CourseSectionListProps) => (
<div>
{courseSections.map(courseSection => (
<CourseSectionCard
key={courseSection.crn}
courseSection={courseSection}
courseSectionAction={courseSectionAction}
courseSectionActionButtonText={courseSectionActionButtonText}
destructive={destructive}
/>
))}
</div>
);
export default CourseSectionList;
import * as React from 'react';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { downloadFile } from '../util/utilities';
interface ExportModalProps {
isModalOpen: boolean;
toggleModal: () => void;
calendarUrl: () => string;
openCalendarAsWebcal: () => void;
downloadIcs: () => Promise<void>;
}
/**
* Modal view that contains buttons for exporting your schedule as
* well as instructions for importing your schedule into different
* calendar managers
*/
const ExportModal = ({
isModalOpen,
toggleModal,
calendarUrl,
openCalendarAsWebcal,
downloadIcs,
}: ExportModalProps) => (
<Modal isOpen={isModalOpen} toggle={toggleModal}>
<ModalHeader toggle={toggleModal}>Your calendar has been generated!</ModalHeader>
<ModalBody>
<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={downloadIcs}>
Download calendar file
</Button>
<Button color="primary" onClick={openCalendarAsWebcal}>
Add to calendar
</Button>{' '}
</ModalFooter>
</Modal>
);
export default ExportModal;
import * as React from 'react';
import { Col, Row, UncontrolledTooltip } from 'reactstrap';
/**
* Renders the app header with information and instructions for using Schedules.
*/
const Header = () => (
<div>
<Row className="justify-content-center my-5">
<h1>
<i className="far fa-calendar-alt" /> Schedules
</h1>
<div className="w-100 mb-3" />
<Col md="6">
<p>
An application to generate a schedule
{' ('}
<span style={{ textDecoration: 'underline' }} id="UncontrolledTooltipExample">
<i className="fas fa-question" />
</span>
<UncontrolledTooltip placement="right" target="UncontrolledTooltipExample">
Find your class' CRNs on Patriot Web under Student Services > Registration > Student Schedule
</UncontrolledTooltip>
{') '}
to place into your calendar populated with class times. Built and maintained by{' '}
<a href="https://srct.gmu.edu">Mason SRCT</a>.
</p>
</Col>
</Row>
</div>
);
export default Header;
import * as React from 'react';
import { Button, Card, CardBody, CardTitle, Collapse, Row } from 'reactstrap';
import CourseSection from '../util/CourseSection';
import CourseSectionList from './CourseSectionList';
import ExportModal from './ExportModal';
interface ScheduleBadgeProps {
schedule: CourseSection[];
removeCourseSection: (courseSection: CourseSection) => void;
generateCalendarUrl: () => string;
openCalendarAsWebcal: () => void;
downloadIcs: () => Promise<void>;
}
interface State {
collapse: boolean;
isModalOpen: boolean;
}
require('../css/icon-badge.css');
/**
* Contains all functionality for viewing your schedule, such as the
* shopping cart, list of course sections, and the generate calendar modal.
*
* TODO: Split this component up
*/
class ScheduleBadge extends React.Component<ScheduleBadgeProps, State> {
constructor(props: ScheduleBadgeProps) {
super(props);
this.state = { collapse: false, isModalOpen: false };
}
toggleCollapse = () => this.setState({ collapse: !this.state.collapse });
toggleModal = () => this.setState({ isModalOpen: !this.state.isModalOpen });
render() {
const { schedule, removeCourseSection, generateCalendarUrl, openCalendarAsWebcal, downloadIcs } = this.props;
return (
<div>
<Row className="justify-content-end">
<Button
children={
<span className="fa-stack fa-3x has-badge" data-count={this.props.schedule.length}>
<i className="fa fas fa-shopping-bag fa-stack-1x" />
</span>
}
onClick={this.toggleCollapse}
id="cart"
/>
</Row>
<Collapse isOpen={this.state.collapse}>
<Card>
<CardBody>
<Row className="my-3">
<h1 className="px-5">Your Schedule</h1>
<Button className="ml-auto px-5" outline color="danger" onClick={this.toggleCollapse}>
Close
</Button>
</Row>
<CourseSectionList
courseSections={schedule}
courseSectionAction={removeCourseSection}
courseSectionActionButtonText="Remove from schedule"
destructive
/>
<Row className="justify-content-center">
<Button
size="sm"
outline
color="primary"
onClick={this.toggleModal}
disabled={schedule.length === 0}>
Generate
</Button>
</Row>
</CardBody>
</Card>
</Collapse>
<ExportModal
isModalOpen={this.state.isModalOpen}
toggleModal={this.toggleModal}
calendarUrl={generateCalendarUrl}
openCalendarAsWebcal={openCalendarAsWebcal}
downloadIcs={downloadIcs}
/>
</div>
);
}
}
export default ScheduleBadge;
import * as React from 'react';
import CourseSection from '../util/CourseSection';
import ScheduleBadge from './ScheduleBadge';
interface ScheduleRootProps {
schedule: CourseSection[];
removeCourseSection: (courseSection: CourseSection) => any;
generateCalendarUrl: () => string;
openCalendarAsWebcal: () => void;
downloadIcs: () => Promise<void>;
}
/**
* Weird component that renders the ScheduleBadge
*
* TODO: Remove this component? Or maybe refactor some of ScheduleBadge into this
*/
const ScheduleRoot = ({
schedule,
removeCourseSection,