mirror of
https://github.com/mgerb/mywebsite
synced 2026-03-04 23:45:24 +00:00
@@ -2,6 +2,7 @@
|
||||
flex: 1;
|
||||
flex-wrap: wrap;
|
||||
min-width: 0;
|
||||
min-height: 600px;
|
||||
.post+ .post {
|
||||
margin-top: 2em;
|
||||
}
|
||||
@@ -14,6 +15,7 @@
|
||||
width: 7em;
|
||||
text-align: right;
|
||||
margin-left: -8em;
|
||||
line-height: 2.5em;
|
||||
}
|
||||
}
|
||||
.intro {
|
||||
|
||||
@@ -7,4 +7,13 @@
|
||||
border-color: #DADADA;
|
||||
font-weight: 300;
|
||||
margin-top: 1em;
|
||||
|
||||
display: flex;
|
||||
padding-right: calc(50% - 997px / 2);
|
||||
padding-left: calc(50% - 997px / 2);
|
||||
&:after,
|
||||
&:before {
|
||||
content: " ";
|
||||
width: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
12
client/assets/scss/Header.scss
Normal file
12
client/assets/scss/Header.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
.Header {
|
||||
width: 100%;
|
||||
background: url("../images/header.jpg");
|
||||
background-size: cover;
|
||||
height: 30em;
|
||||
border-bottom: solid;
|
||||
border-width: 1px;
|
||||
border-color: #DADADA;
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ h5,
|
||||
h6 {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
@@ -55,7 +54,19 @@ hr {
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.Footer,
|
||||
select{
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 0.2em;
|
||||
height: 35px;
|
||||
color: #555;
|
||||
&:focus{
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
|
||||
}
|
||||
}
|
||||
|
||||
.Main {
|
||||
display: flex;
|
||||
padding-right: calc(50% - 997px / 2);
|
||||
@@ -71,23 +82,11 @@ hr {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.Header {
|
||||
width: 100%;
|
||||
background: url("../images/header.jpg");
|
||||
background-size: cover;
|
||||
height: 30em;
|
||||
border-bottom: solid;
|
||||
border-width: 1px;
|
||||
border-color: #DADADA;
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.Loading {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
|
||||
@@ -38,8 +38,8 @@ ReactDOM.render((
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRoute component={Preview}/>
|
||||
<Route path="post/:category/:post" component={Post}/>
|
||||
<Route path="sensor/:location/:year/:month" component={SensorInfo}/>
|
||||
<Route path="post(/:category)/:post" component={Post}/>
|
||||
<Route path="sensor/:location" component={SensorInfo}/>
|
||||
</Route>
|
||||
</Router>
|
||||
</Provider>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default class Footer extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div class="Footer">
|
||||
Site created and maintained by Mitchell Gerber
|
||||
Site created by Mitchell Gerber
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import {bubble} from '../../assets/js/bubble';
|
||||
|
||||
import '../../assets/scss/Header.scss';
|
||||
|
||||
export default class Header extends React.Component {
|
||||
componentDidMount() {
|
||||
bubble();
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class Post extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
const params = this.props.params;
|
||||
this.props.appActions.fetchPost(params.category, params.post);
|
||||
this.props.appActions.fetchPost(params.post, params.category);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,14 +1,108 @@
|
||||
import React from 'react';
|
||||
import {Link} from 'react-router';
|
||||
|
||||
export default class SensorInfo extends React.Component{
|
||||
import Loading from '../utils/Loading';
|
||||
import _chartjs from 'chart.js';
|
||||
import Chart from 'react-chartjs';
|
||||
import {
|
||||
ChartOptions,
|
||||
DataTemplate
|
||||
}
|
||||
from './chartOptions';
|
||||
|
||||
componentDidMount(){
|
||||
this.props.sensorActions.fetchSensorInfoYear('Grand Meadow', '2016');
|
||||
this.props.sensorActions.fetchSensorInfoMonth('Grand Meadow', '2016', 'May');
|
||||
import './SensorInfo.scss';
|
||||
|
||||
const LineChart = Chart.Line;
|
||||
|
||||
export default class SensorInfo extends React.Component {
|
||||
|
||||
componentWillMount() {
|
||||
let location = this.props.params.location;
|
||||
this.props.sensorActions.fetchUniqueDates(location);
|
||||
}
|
||||
render(){
|
||||
return(
|
||||
<div class="Content">Test123</div>
|
||||
);
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
let currentLocation = this.props.params.location,
|
||||
nextLocation = nextProps.params.location;
|
||||
|
||||
if (currentLocation !== nextLocation){
|
||||
this.props.sensorActions.fetchUniqueDates(nextLocation);
|
||||
}
|
||||
}
|
||||
|
||||
loadYearOptions = (date, index) => {
|
||||
return (
|
||||
<option key={index} value={index}>{date.year}</option>
|
||||
);
|
||||
}
|
||||
|
||||
loadMonthOptions = (date, index) => {
|
||||
return (
|
||||
<option key={index} value={index}>{date.monthname}</option>
|
||||
);
|
||||
}
|
||||
|
||||
onChange(event, type) {
|
||||
let location = this.props.params.location,
|
||||
sensor = this.props.sensor,
|
||||
actions = this.props.sensorActions,
|
||||
yearIndex = sensor.selectedYearIndex,
|
||||
monthIndex = sensor.selectedMonthIndex;
|
||||
|
||||
if (type === 'year') {
|
||||
yearIndex = parseInt(event.target.value);
|
||||
monthIndex = 0;
|
||||
}
|
||||
else if (type === 'month') {
|
||||
monthIndex = parseInt(event.target.value);
|
||||
}
|
||||
|
||||
let year = sensor.uniqueDates[yearIndex].year;
|
||||
let monthname = sensor.uniqueDates[yearIndex].months[monthIndex].monthname;
|
||||
|
||||
actions.setSelectedMonthIndex(monthIndex);
|
||||
actions.setSelectedYearIndex(yearIndex);
|
||||
this.props.sensorActions.fetchSensorInfoMonth(location, year, monthname);
|
||||
}
|
||||
|
||||
filterData(data) {
|
||||
let temp = JSON.parse(JSON.stringify(DataTemplate));
|
||||
|
||||
for (let d of data) {
|
||||
let label = `${d.month}/${d.day}`;
|
||||
temp.labels.push(label);
|
||||
temp.datasets[0].data.push(d.maxtemp);
|
||||
temp.datasets[1].data.push(d.mintemp);
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
render() {
|
||||
let sensor = this.props.sensor;
|
||||
let data = this.filterData(sensor.info);
|
||||
return (
|
||||
<div class="SensorInfo">
|
||||
{sensor.fetchedUniqueDates ?
|
||||
<div class="selector-row">
|
||||
<h2>{this.props.params.location}</h2>
|
||||
<select onChange={(e) => {this.onChange(e, 'year')}}>
|
||||
{sensor.uniqueDates.map(this.loadYearOptions)}
|
||||
</select>
|
||||
|
||||
<select onChange={(e) => {this.onChange(e, 'month')}} value={sensor.selectedMonthIndex}>
|
||||
{sensor.uniqueDates[sensor.selectedYearIndex].months.map(this.loadMonthOptions)}
|
||||
</select>
|
||||
</div>
|
||||
: <Loading/>}
|
||||
|
||||
{sensor.fetchedUniqueDates && sensor.fetchedInfo
|
||||
? <LineChart data={data} options={ChartOptions} redraw/>
|
||||
: null}
|
||||
{sensor.fetchedUniqueDates && sensor.fetchedInfo
|
||||
? <div class="home"><Link to="/" class="link"><i class="fa fa-caret-left" aria-hidden="true"></i> Home</Link></div>
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
20
client/js/components/sensors/SensorInfo.scss
Normal file
20
client/js/components/sensors/SensorInfo.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
.SensorInfo{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
min-height: 600px;
|
||||
width: 80%;
|
||||
.selector-row{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 2em;
|
||||
select{
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
.home{
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,8 @@ export default class SensorList extends React.Component {
|
||||
this.openLink = this.openLink.bind(this);
|
||||
}
|
||||
|
||||
openLink(){
|
||||
browserHistory.push("/");
|
||||
openLink(location){
|
||||
browserHistory.push(`/sensor/${location}`);
|
||||
this.props.toggleOff();
|
||||
}
|
||||
|
||||
@@ -29,13 +29,18 @@ export default class SensorList extends React.Component {
|
||||
const date = new Date(sensor.updated);
|
||||
|
||||
return (
|
||||
<div key={index} class="row" onClick={this.openLink}>
|
||||
<div class="item">
|
||||
<div key={index} class="row" onClick={() => {this.openLink(sensor.location)}}>
|
||||
<div class="temperature">
|
||||
<h1>{sensor.temperature}°f</h1>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="info">
|
||||
<h3>{sensor.location}</h3>
|
||||
<span class="date">Updated: {date.toLocaleString('en-us', options)}</span>
|
||||
<span class="date">Updated: {date.toLocaleString('en-us', options)}
|
||||
{Date.now() - date < 420000
|
||||
? <span class="connected"> Connected</span>
|
||||
: <span class="disconnected"> Disconnected</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,11 +9,16 @@
|
||||
padding: 1em;
|
||||
|
||||
&:hover{
|
||||
background-color: gray;
|
||||
background-color: #D1D1D1;
|
||||
}
|
||||
|
||||
.item + .item{
|
||||
.temperature{
|
||||
flex: .5;
|
||||
}
|
||||
|
||||
.info{
|
||||
margin-left: 1em;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
h2{
|
||||
@@ -25,4 +30,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.connected{
|
||||
color: green;
|
||||
}
|
||||
.disconnected{
|
||||
color: red;
|
||||
}
|
||||
|
||||
}
|
||||
30
client/js/components/sensors/chartOptions.js
Normal file
30
client/js/components/sensors/chartOptions.js
Normal file
@@ -0,0 +1,30 @@
|
||||
export const ChartOptions = {
|
||||
responsive: true,
|
||||
// scaleOverride: true,
|
||||
//scaleSteps: 20,
|
||||
// scaleStartValue: 0,
|
||||
//scaleStepWidth: 5
|
||||
};
|
||||
|
||||
export let DataTemplate = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "Max Temperature °F",
|
||||
fillColor: "rgba(220,220,220,0.2)",
|
||||
strokeColor: "rgba(220,220,220,1)",
|
||||
pointColor: "rgba(220,220,220,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(220,220,220,1)",
|
||||
data: []
|
||||
}, {
|
||||
label: "Min Temperature °F",
|
||||
fillColor: "rgba(151,187,205,0)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||
data: []
|
||||
}]
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Link} from 'react-router';
|
||||
|
||||
import me from '../../../assets/images/me.jpg';
|
||||
|
||||
@@ -12,7 +13,10 @@ export default class AboutMe extends React.Component{
|
||||
<p>
|
||||
My name is Mitchell and I have a passion for software development. I am currently a software engineer and enjoy working on personal projects in my free time.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<i class="fa fa-home" aria-hidden="true"></i>
|
||||
<Link to={"/"} class="link"> Home</Link>
|
||||
</p>
|
||||
<p>
|
||||
<i class="fa fa-envelope" aria-hidden="true"></i>
|
||||
<a class="link" href="mailto:mgerb42@gmail.com"> eMail</a>
|
||||
@@ -25,10 +29,12 @@ export default class AboutMe extends React.Component{
|
||||
<i class="fa fa-github" aria-hidden="true"></i>
|
||||
<a class="link" href="https://github.com/mgerb" target="_blank"> GitHub</a>
|
||||
</p>
|
||||
{/*
|
||||
<p>
|
||||
<i class="fa fa-wpforms" aria-hidden="true"> </i>
|
||||
<a href="/resume" class="link"> Resume</a>
|
||||
</p>
|
||||
*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,14 @@ export function fetchPreview() {
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchPost(category, post) {
|
||||
//adjust url according to parameters
|
||||
//mainly used to load md files in the parent directory within /posts
|
||||
export function fetchPost(post, category = null) {
|
||||
let url;
|
||||
return (dispatch) => {
|
||||
dispatch(fetching());
|
||||
return fetch(`/public/posts/${category}/${post}.md`)
|
||||
url = category !== null || typeof category === 'undefined' ? `/public/posts/${category}/${post}.md` : `/public/posts/${post}.md`;
|
||||
return fetch(url)
|
||||
.then(response => response.text())
|
||||
.then(response => {
|
||||
dispatch(loadPost(response));
|
||||
|
||||
@@ -8,17 +8,31 @@ function loadSensorList(sensor_list){
|
||||
}
|
||||
}
|
||||
|
||||
function loadSensorInfoYear(sensor_info){
|
||||
function loadSensorInfo(sensor_info){
|
||||
return{
|
||||
type: types.LOAD_SENSOR_INFO_YEAR,
|
||||
type: types.LOAD_SENSOR_INFO,
|
||||
sensor_info
|
||||
}
|
||||
}
|
||||
|
||||
function loadSensorInfoMonth(sensor_info){
|
||||
function loadUniqueDates(dates){
|
||||
return{
|
||||
type: types.LOAD_SENSOR_INFO_MONTH,
|
||||
sensor_info
|
||||
type: types.LOAD_UNIQUE_DATES,
|
||||
dates
|
||||
}
|
||||
}
|
||||
|
||||
export function setSelectedYearIndex(index){
|
||||
return{
|
||||
type: types.SET_SELECTED_YEAR_INDEX,
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
export function setSelectedMonthIndex(index){
|
||||
return{
|
||||
type: types.SET_SELECTED_MONTH_INDEX,
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +42,15 @@ function fetchingList(){
|
||||
}
|
||||
}
|
||||
|
||||
function fetchingInfoYear(){
|
||||
function fetchingInfo(){
|
||||
return {
|
||||
type: types.FETCHING_INFO_YEAR
|
||||
type: types.FETCHING_INFO
|
||||
}
|
||||
}
|
||||
|
||||
function fetchingInfoMonth(){
|
||||
function fetchingUniqueDates(){
|
||||
return {
|
||||
type: types.FETCHING_INFO_MONTH
|
||||
type: types.FETCHING_UNIQUE_DATES
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,11 +70,11 @@ export function fetchSensorList(){
|
||||
|
||||
export function fetchSensorInfoYear(location, year){
|
||||
return (dispatch) => {
|
||||
dispatch(fetchingInfoYear());
|
||||
dispatch(fetchingInfo());
|
||||
return fetch(`/api/sensor/${location}/${year}`)
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
dispatch(loadSensorInfoYear(json));
|
||||
dispatch(loadSensorInfo(json));
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
@@ -70,11 +84,35 @@ export function fetchSensorInfoYear(location, year){
|
||||
|
||||
export function fetchSensorInfoMonth(location, year, month){
|
||||
return (dispatch) => {
|
||||
dispatch(fetchingInfoMonth());
|
||||
dispatch(fetchingInfo());
|
||||
return fetch(`/api/sensor/${location}/${year}/${month}`)
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
dispatch(loadSensorInfoMonth(json));
|
||||
dispatch(loadSensorInfo(json));
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//this is called to initialize the sensor info page
|
||||
//reloads unique dates and resets indexes
|
||||
//then fetches new data for chart
|
||||
export function fetchUniqueDates(location){
|
||||
return (dispatch) => {
|
||||
dispatch(fetchingUniqueDates());
|
||||
dispatch(setSelectedMonthIndex(0));
|
||||
dispatch(setSelectedYearIndex(0));
|
||||
return fetch(`/api/uniquedates/${location}`)
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
dispatch(loadUniqueDates(json));
|
||||
if(json.length > 0){
|
||||
let year = json[0].year;
|
||||
let month = json[0].months[0].monthname;
|
||||
dispatch(fetchSensorInfoMonth(location, year, month));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
//constants
|
||||
export const LOAD_SENSOR_LIST = 'LOAD_SENSOR_LIST';
|
||||
export const LOAD_SENSOR_INFO_YEAR = 'LOAD_SENSOR_INFO_YEAR';
|
||||
export const LOAD_SENSOR_INFO_MONTH = 'LOAD_SENSOR_INFO_MONTH';
|
||||
|
||||
export const LOAD_SENSOR_INFO = 'LOAD_SENSOR_INFO';
|
||||
export const LOAD_UNIQUE_DATES = 'LOAD_UNIQUE_DATES';
|
||||
|
||||
//fetching
|
||||
export const FETCHING_LIST = 'FETCHING_LIST';
|
||||
export const FETCHING_INFO_YEAR = 'FETCHING_INFO_YEAR';
|
||||
export const FETCHING_INFO_MONTH = 'FETCHING_INFO_MONTH';
|
||||
export const FETCHING_INFO = 'FETCHING_INFO_YEAR';
|
||||
export const FETCHING_UNIQUE_DATES = 'FETCHING_UNIQUE_DATES';
|
||||
|
||||
//indexes
|
||||
export const SET_SELECTED_YEAR_INDEX = 'SET_SELECTED_YEAR_INDEX';
|
||||
export const SET_SELECTED_MONTH_INDEX = 'SET_SELECTED_MONTH_INDEX';
|
||||
@@ -4,54 +4,69 @@ import * as types from '../constants/sensor';
|
||||
//defaults -
|
||||
const defaultState = {
|
||||
list : [],
|
||||
infoMonth: [],
|
||||
infoYear: [],
|
||||
info: [],
|
||||
uniqueDates: {},
|
||||
|
||||
selectedYearIndex: 0,
|
||||
selectedMonthIndex: 0,
|
||||
|
||||
fetchingList: false,
|
||||
fetchingInfoMonth: false,
|
||||
fetchingInfoYear: false,
|
||||
fetchingInfo: false,
|
||||
fetchingUniqueDates: false,
|
||||
|
||||
fetchedList: false,
|
||||
fetchedInfoMonth: false,
|
||||
fetchedInfoYear: false
|
||||
fetchedInfo: false,
|
||||
fetchedUniqueDates: false
|
||||
};
|
||||
|
||||
//default reducer
|
||||
export default function app(state = defaultState, action) {
|
||||
switch(action.type){
|
||||
//fetching functions - we use a fetching state to display loading images
|
||||
case types.FETCHING_LIST:
|
||||
return Object.assign({}, state, {
|
||||
fetchingList: true,
|
||||
fetchedList: false
|
||||
});
|
||||
case types.FETCHING_INFO_MONTH:
|
||||
case types.FETCHING_INFO:
|
||||
return Object.assign({}, state, {
|
||||
fetchingInfoMonth: true,
|
||||
fetchedInfoMonth: false
|
||||
fetchingInfo: true,
|
||||
fetchedInfo: false
|
||||
});
|
||||
case types.FETCHING_INFO_YEAR:
|
||||
case types.FETCHING_UNIQUE_DATES:
|
||||
return Object.assign({}, state, {
|
||||
fetchingInfoYear: true,
|
||||
fetchedInfoYear: false
|
||||
fetchingUniqueDates: true,
|
||||
fetchedUniqueDates: false
|
||||
});
|
||||
|
||||
//other functions
|
||||
case types.LOAD_SENSOR_LIST:
|
||||
return Object.assign({}, state, {
|
||||
list: action.sensor_list,
|
||||
fetchingList: false,
|
||||
fetchedList: true
|
||||
});
|
||||
case types.LOAD_SENSOR_INFO_MONTH:
|
||||
case types.LOAD_SENSOR_INFO:
|
||||
return Object.assign({}, state, {
|
||||
infoMonth: action.sensor_info,
|
||||
fetchingInfoMonth: false,
|
||||
fetchedInfoMonth: true
|
||||
info: action.sensor_info,
|
||||
fetchingInfo: false,
|
||||
fetchedInfo: true
|
||||
});
|
||||
case types.LOAD_SENSOR_INFO_YEAR:
|
||||
case types.LOAD_UNIQUE_DATES:
|
||||
return Object.assign({}, state, {
|
||||
infoYear: action.sensor_info,
|
||||
fetchingInfoYear: false,
|
||||
fetchedInfoYear: true
|
||||
uniqueDates: action.dates,
|
||||
fetchingUniqueDates: false,
|
||||
fetchedUniqueDates: true
|
||||
});
|
||||
|
||||
//indexes
|
||||
case types.SET_SELECTED_YEAR_INDEX:
|
||||
return Object.assign({}, state, {
|
||||
selectedYearIndex: action.index
|
||||
});
|
||||
case types.SET_SELECTED_MONTH_INDEX:
|
||||
return Object.assign({}, state, {
|
||||
selectedMonthIndex: action.index
|
||||
});
|
||||
}
|
||||
//return present state if no actions get called
|
||||
|
||||
@@ -6,7 +6,10 @@ import {syncHistoryWithStore} from 'react-router-redux';
|
||||
|
||||
import reducers from './reducers/reducers';
|
||||
|
||||
const middleware = applyMiddleware(thunk, logger());
|
||||
const debug = process.env.NODE_ENV !== "production";
|
||||
|
||||
//run redux logger if we are in dev mode
|
||||
const middleware = debug ? applyMiddleware(thunk, logger()) : applyMiddleware(thunk);
|
||||
|
||||
//create the new store with default state as an empty object
|
||||
const store = createStore(reducers, {}, middleware);
|
||||
|
||||
11
metadata.js
11
metadata.js
@@ -19,13 +19,15 @@ marked.setOptions({
|
||||
}
|
||||
});
|
||||
|
||||
const dir = './posts/';
|
||||
const rootDirectory = './posts/';
|
||||
const json = {
|
||||
posts: []
|
||||
};
|
||||
|
||||
//do everything synchronously to keep posts ordered
|
||||
function parse_dir(dir, folder_name){
|
||||
//we are not worried about execution time since this script only runs once when building
|
||||
//ignores files that are not in a directory
|
||||
function parse_dir(dir, folder_name = null){
|
||||
const posts = fs.readdirSync(dir);
|
||||
|
||||
for(let post of posts){
|
||||
@@ -33,7 +35,7 @@ function parse_dir(dir, folder_name){
|
||||
|
||||
if(stats.isDirectory()){
|
||||
parse_dir(dir + post + '/', post);
|
||||
} else {
|
||||
} else if(folder_name !== null){
|
||||
const file = fs.readFileSync(dir+post, 'utf8');
|
||||
const tokens = marked.lexer(file, null);
|
||||
const temp = {
|
||||
@@ -49,7 +51,8 @@ function parse_dir(dir, folder_name){
|
||||
}
|
||||
|
||||
//recursively parse posts directory for all markdown files
|
||||
parse_dir(dir, 'posts');
|
||||
//folder defaults to null and immediate child files are not added to json
|
||||
parse_dir(rootDirectory);
|
||||
|
||||
//sort posts by date
|
||||
json.posts.sort((a, b) => {
|
||||
|
||||
12
package.json
12
package.json
@@ -7,11 +7,12 @@
|
||||
"build": "webpack && babel-node metadata.js",
|
||||
"c9": "webpack-dev-server --port $PORT --host $IP --hot --content-base dist --history-api-fallback",
|
||||
"check_gzip_size": "gzip -9 -c ./public/client.min.js | wc -c | numfmt --to=iec-i --suffix=B --padding=10",
|
||||
"deploy" : "npm run get_dependencies && npm run prod && ./mywebsite",
|
||||
"deploy": "npm run get_dependencies && npm run prod && ./mywebsite",
|
||||
"dev": "webpack-dev-server --content-base public --inline --hot --history-api-fallback",
|
||||
"get_dependencies": "go get ./server && npm install",
|
||||
"prod": "export NODE_ENV=production && webpack -p && babel-node metadata.js && go build ./server/mywebsite.go",
|
||||
"prod-win": "set NODE_ENV=production && webpack -p && babel-node metadata.js && go build ./server/mywebsite.go"
|
||||
"prod": "export NODE_ENV=production && webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors && babel-node metadata.js && go build ./server/mywebsite.go",
|
||||
"prod-win": "set NODE_ENV=production && webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors && babel-node metadata.js && go build ./server/mywebsite.go",
|
||||
"watch": "webpack --watch --colors --progress"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -24,6 +25,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/mgerb/mywebsite#readme",
|
||||
"dependencies": {
|
||||
"autoprefixer": "^6.4.1",
|
||||
"babel": "^6.5.2",
|
||||
"babel-cli": "^6.11.4",
|
||||
"babel-core": "^6.13.2",
|
||||
@@ -35,6 +37,7 @@
|
||||
"babel-preset-es2015": "^6.13.2",
|
||||
"babel-preset-react": "^6.11.1",
|
||||
"babel-preset-stage-0": "^6.5.0",
|
||||
"chart.js": "^1.1.1",
|
||||
"css-loader": "^0.23.1",
|
||||
"exports-loader": "^0.6.3",
|
||||
"file-loader": "^0.9.0",
|
||||
@@ -44,7 +47,10 @@
|
||||
"marked": "^0.3.6",
|
||||
"ncp": "^2.0.0",
|
||||
"node-sass": "^3.8.0",
|
||||
"postcss-loader": "^0.13.0",
|
||||
"react": "^15.3.0",
|
||||
"react-addons-transition-group": "^15.3.1",
|
||||
"react-chartjs": "^0.8.0",
|
||||
"react-dom": "^15.3.0",
|
||||
"react-redux": "^4.4.5",
|
||||
"react-router": "^2.6.1",
|
||||
|
||||
@@ -61,6 +61,7 @@ func HandleSensorRequest(w http.ResponseWriter, r *http.Request, ps httprouter.P
|
||||
storedData.Month = int(t.Month())
|
||||
storedData.MonthName = t.Month().String()
|
||||
storedData.Year = t.Year()
|
||||
storedData.Updated = t
|
||||
|
||||
err := storedData.StoreData()
|
||||
|
||||
@@ -134,21 +135,7 @@ func HandleSensorByLocation(w http.ResponseWriter, r *http.Request, ps httproute
|
||||
|
||||
s, err := daily_sensor.GetAllSensorInfo(location)
|
||||
|
||||
var response string
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
response = "{message : \"Error loading data from database\""
|
||||
} else {
|
||||
js, err := json.MarshalIndent(s, "", " ")
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
response = "{message : \"Error loading data from database\""
|
||||
} else {
|
||||
response = string(js)
|
||||
}
|
||||
}
|
||||
response := createResponse(s, err)
|
||||
|
||||
fmt.Fprint(w, response)
|
||||
}
|
||||
@@ -162,21 +149,7 @@ func HandleSensorByLocationYear(w http.ResponseWriter, r *http.Request, ps httpr
|
||||
|
||||
s, err := daily_sensor.GetAllSensorInfoByYear(location, year)
|
||||
|
||||
var response string
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
response = "{message : \"Error loading data from database\""
|
||||
} else {
|
||||
js, err := json.MarshalIndent(s, "", " ")
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
response = "{message : \"Error loading data from database\""
|
||||
} else {
|
||||
response = string(js)
|
||||
}
|
||||
}
|
||||
response := createResponse(s, err)
|
||||
|
||||
fmt.Fprint(w, response)
|
||||
}
|
||||
@@ -191,6 +164,19 @@ func HandleSensorByLocationMonth(w http.ResponseWriter, r *http.Request, ps http
|
||||
|
||||
s, err := daily_sensor.GetAllSensorInfoByMonth(location, year, monthname)
|
||||
|
||||
response := createResponse(s, err)
|
||||
|
||||
fmt.Fprint(w, response)
|
||||
}
|
||||
|
||||
func HandleUniqueDates(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
|
||||
location := ps.ByName("location")
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
s, err := daily_sensor.GetUniqueSensorDates(location)
|
||||
|
||||
var response string
|
||||
|
||||
if err != nil {
|
||||
@@ -209,3 +195,23 @@ func HandleSensorByLocationMonth(w http.ResponseWriter, r *http.Request, ps http
|
||||
|
||||
fmt.Fprint(w, response)
|
||||
}
|
||||
|
||||
func createResponse(s []daily_sensor.Data, err error) string{
|
||||
var response string
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
response = "{message : \"Error loading data from database\""
|
||||
} else {
|
||||
js, err := json.MarshalIndent(s, "", " ")
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
response = "{message : \"Error loading data from database\""
|
||||
} else {
|
||||
response = string(js)
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
@@ -16,22 +16,24 @@ const (
|
||||
|
||||
type Data struct {
|
||||
ID bson.ObjectId `bson:"_id,omitempty"`
|
||||
MaxTemp float64 `json:"maxtemp" bson:"maxtemp"`
|
||||
MinTemp float64 `json:"mintemp" bson:"mintemp"`
|
||||
Location string `json:"location" bson:"location"`
|
||||
Month int `json:"month" bson:"month"`
|
||||
MonthName string `json:"monthname" bson:"monthname"`
|
||||
Day int `json:"day" bson:"day"`
|
||||
Year int `json:"year" bson:"year"`
|
||||
MaxTemp float64 `json:"maxtemp,omitempty" bson:"maxtemp"`
|
||||
MinTemp float64 `json:"mintemp,omitempty" bson:"mintemp"`
|
||||
Location string `json:"location,omitempty" bson:"location"`
|
||||
Month int `json:"month,omitempty" bson:"month"`
|
||||
MonthName string `json:"monthname,omitempty" bson:"monthname"`
|
||||
Day int `json:"day,omitempty" bson:"day"`
|
||||
Year int `json:"year,omitempty" bson:"year"`
|
||||
Updated time.Time `json:"updated,omitempty" bson:"updated"`
|
||||
}
|
||||
|
||||
//convert struct to json string
|
||||
func (s *Data) toJson() string {
|
||||
func (s *Data) ToJson() string {
|
||||
|
||||
b, err := json.MarshalIndent(s, "", " ")
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error)
|
||||
log.Println(err)
|
||||
return "{message : \"Error loading data from database\""
|
||||
}
|
||||
|
||||
return string(b)
|
||||
@@ -73,7 +75,7 @@ func (s *Data) UpdateData() error {
|
||||
c := session.DB(db.Mongo.Info.Database).C(collection)
|
||||
|
||||
colQuerier := bson.M{"location": s.Location, "month": s.Month, "monthname": s.MonthName, "day": s.Day, "year": s.Year}
|
||||
change := bson.M{"$set": bson.M{"maxtemp": s.MaxTemp, "mintemp": s.MinTemp}}
|
||||
change := bson.M{"$set": bson.M{"maxtemp": s.MaxTemp, "mintemp": s.MinTemp, "updated": time.Now()}}
|
||||
|
||||
err := c.Update(colQuerier, change)
|
||||
|
||||
@@ -130,7 +132,6 @@ func GetAllSensorInfo(sensor_location string) ([]Data, error) {
|
||||
|
||||
c := session.DB(db.Mongo.Info.Database).C(collection)
|
||||
|
||||
//err := c.Find(bson.M{"location": sensor_location}).Sort("-year, -month").All(&d)
|
||||
err := c.Pipe([]bson.M{{"$match": bson.M{"location": sensor_location}},
|
||||
{"$sort": bson.M{"year": -1, "month": 1}}}).All(&d)
|
||||
|
||||
@@ -198,3 +199,55 @@ func GetAllSensorInfoByMonth(sensor_location string, year int, monthname string)
|
||||
return d, errors.New("Query failed")
|
||||
}
|
||||
}
|
||||
|
||||
//I need to find a better implementation of this
|
||||
//MongoDB $addToSet creates an array of objecta
|
||||
//this ends up being a different type than the Data struct above
|
||||
//it would be nice to make all MongoDB queries load directly into a Data struct
|
||||
/*
|
||||
type UniqueDates struct{
|
||||
Dates []Data `json:"dates" bson:"dates"`
|
||||
}
|
||||
*/
|
||||
|
||||
type Years struct {
|
||||
Year int `json:"year", bson:"year"`
|
||||
Months []Month `json:"months", bson:"months"`
|
||||
}
|
||||
|
||||
type Month struct {
|
||||
Month int `json:"month", bson:"month"`
|
||||
MonthName string `json:"monthname", bson:"monthname"`
|
||||
}
|
||||
|
||||
func GetUniqueSensorDates(sensor_location string) ([]Years, error){
|
||||
d := []Years{}
|
||||
|
||||
if db.Mongo.Connected() == true {
|
||||
|
||||
session := db.Mongo.Session.Copy()
|
||||
defer session.Close()
|
||||
|
||||
c := session.DB(db.Mongo.Info.Database).C(collection)
|
||||
|
||||
err := c.Pipe([]bson.M{bson.M{"$match": bson.M{"location": sensor_location}},
|
||||
bson.M{"$group": bson.M{"_id": "$year", "months": bson.M{"$addToSet": bson.M{"month": "$month", "monthname": "$monthname"}}}},
|
||||
bson.M{"$project": bson.M{"year": "$_id", "months": "$months"}},
|
||||
bson.M{"$unwind": "$months"},
|
||||
bson.M{"$sort": bson.M{"months.month": -1}},
|
||||
bson.M{"$group": bson.M{"_id": "$year", "months": bson.M{"$push": "$months"}}},
|
||||
bson.M{"$project": bson.M{"year": "$_id", "months": "$months"}},
|
||||
|
||||
}).All(&d)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
return d, nil
|
||||
|
||||
} else {
|
||||
return d, errors.New("Query failed")
|
||||
}
|
||||
}
|
||||
@@ -59,9 +59,10 @@ func (s *Data) StoreData() error {
|
||||
|
||||
//handle queries for all sensors page
|
||||
type DataStore_AllSensors struct {
|
||||
ID string `json:"location" bson:"_id"`
|
||||
Temperature float64 `json:"temperature" bson:"temperature"`
|
||||
Updated time.Time `json:"updated" bson:"updated"`
|
||||
ID bson.ObjectId `bson:"_id,omitempty"`
|
||||
Location string `json:"location", bson:"location"`
|
||||
Temperature float64 `json:"temperature" bson:"temperature"`
|
||||
Updated time.Time `json:"updated" bson:"updated"`
|
||||
}
|
||||
|
||||
//get latest update from each unique sensor
|
||||
@@ -78,7 +79,9 @@ func GetAllSensors() ([]DataStore_AllSensors, error) {
|
||||
|
||||
err := c.Pipe([]bson.M{{"$group": bson.M{"_id": "$location", "temperature": bson.M{"$last": "$temperature"},
|
||||
"updated": bson.M{"$last": "$updated"}}},
|
||||
bson.M{"$sort": bson.M{"_id": 1}}}).All(&s)
|
||||
bson.M{"$sort": bson.M{"_id": 1}},
|
||||
bson.M{"$project": bson.M{"location": "$_id", "temperature": "$temperature", "updated": "$updated"}},
|
||||
}).All(&s)
|
||||
|
||||
if err != nil {
|
||||
return s, nil
|
||||
|
||||
@@ -20,6 +20,7 @@ func Routes() *httprouter.Router {
|
||||
r.GET("/api/sensor/:location", api.HandleSensorByLocation)
|
||||
r.GET("/api/sensor/:location/:year", api.HandleSensorByLocationYear)
|
||||
r.GET("/api/sensor/:location/:year/:monthname", api.HandleSensorByLocationMonth)
|
||||
r.GET("/api/uniquedates/:location", api.HandleUniqueDates)
|
||||
|
||||
r.GET("/discord", controller.DiscordRedirect)
|
||||
r.GET("/vpn", controller.VPNRedirect)
|
||||
|
||||
@@ -2,6 +2,7 @@ var debug = process.env.NODE_ENV !== "production";
|
||||
var webpack = require('webpack');
|
||||
var path = require('path');
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
var autoprefixer = require('autoprefixer');
|
||||
|
||||
module.exports = {
|
||||
devtool: debug ? "inline-sourcemap" : null,
|
||||
@@ -17,7 +18,7 @@ module.exports = {
|
||||
plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy'],
|
||||
}
|
||||
},
|
||||
{ test: /\.scss$/, loader: "style-loader!css-loader!sass-loader"},
|
||||
{ test: /\.scss$/, loader: "style-loader!css-loader!postcss-loader!sass-loader"},
|
||||
{ test: /\.css$/, loader: "style-loader!css-loader" },
|
||||
{ test: /\.png$/, loader: "url-loader?limit=100000&name=images/[hash].[ext]" },
|
||||
{ test: /\.jpg$/, loader: "url-loader?limit=100000&name=images/[hash].[ext]" },
|
||||
@@ -32,6 +33,7 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
},
|
||||
postcss: function(){ return [autoprefixer]},
|
||||
output: {
|
||||
path: __dirname + "/public/",
|
||||
publicPath: "/public/",
|
||||
|
||||
Reference in New Issue
Block a user