Skip to content

Commit

Permalink
Adding a dashboard feature (JessicaYeh#7).
Browse files Browse the repository at this point in the history
  • Loading branch information
lucas-carneiro committed Feb 23, 2019
1 parent c87402c commit 48b2a40
Show file tree
Hide file tree
Showing 9 changed files with 4,879 additions and 3,725 deletions.
7,653 changes: 3,933 additions & 3,720 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"@material-ui/core": "^3.9.1",
"@material-ui/icons": "^3.0.2",
"@svgr/webpack": "2.4.1",
"@types/react-copy-to-clipboard": "^4.2.6",
"@types/react-select": "^2.0.13",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "9.0.0",
"babel-jest": "23.6.0",
Expand Down Expand Up @@ -47,13 +49,15 @@
"postcss-preset-env": "6.0.6",
"postcss-safe-parser": "4.0.1",
"query-string": "^6.2.0",
"react": "^16.6.3",
"react": "^16.8.3",
"react-app-polyfill": "^0.1.1",
"react-autobind": "^1.0.6",
"react-chartjs-2": "^2.7.4",
"react-copy-to-clipboard": "^5.0.1",
"react-dev-utils": "^6.0.1",
"react-dom": "^16.6.3",
"react-router-dom": "^4.3.1",
"react-select": "^2.4.1",
"request": "^2.88.0",
"request-promise": "^4.2.2",
"resolve": "1.8.1",
Expand Down Expand Up @@ -87,7 +91,7 @@
"@types/lodash": "^4.14.118",
"@types/node": "^10.12.10",
"@types/query-string": "^6.1.1",
"@types/react": "^16.7.7",
"@types/react": "^16.8.4",
"@types/react-chartjs-2": "^2.5.7",
"@types/react-dom": "^16.0.11",
"@types/react-router-dom": "^4.3.1",
Expand Down
31 changes: 30 additions & 1 deletion src/api.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ItemData, ServerData, DataPoint, RangeData } from './item-data';
import { ItemData, ServerData, DataPoint, RangeData, NameData } from './item-data';

const request = require('request-promise');

Expand Down Expand Up @@ -99,6 +99,24 @@ export class API {
});
}

static getNames(completion: (names: NameData[]) => void) {
const promise = window.location.hostname === 'www.romexchange.com' ?
request({
uri: 'https://www.romexchange.com/items.json',
json: true
}) :
new Promise((resolve) => {
setTimeout(() => {
const mocknames = require('./mocknames.json');
resolve(mocknames);
}, 500);
});
promise.then((response: any) => {
const names = this.parseNamesJSON(response);
completion(names);
});
}

private static parseItemsJSON(itemsJSON: []): ItemData[] {
return itemsJSON.map((item: {}) => this.parseItemJSON(item));
}
Expand Down Expand Up @@ -140,4 +158,15 @@ export class API {
snap: dataPointJSON['snap']
};
}

private static parseNamesJSON(namesJSON: []): NameData[] {
return namesJSON.map((name: {}) => this.parseNameJSON(name));
}

private static parseNameJSON(nameJSON: {}): NameData {
return {
name: nameJSON['name'],
type: nameJSON['type']
};
}
}
1 change: 1 addition & 0 deletions src/app.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import "index";
@import "dashboard-modal";

.app {
margin-top: 72px;
Expand Down
27 changes: 25 additions & 2 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import {
Button,
CircularProgress,
FormControl,
IconButton,
InputBase,
InputLabel,
Select,
Toolbar,
MenuItem
MenuItem,
Tooltip,
Modal
} from '@material-ui/core';
import SearchIcon from '@material-ui/icons/Search';
import DashboardIcon from '@material-ui/icons/Dashboard';
import BookIcon from '@material-ui/icons/LocalLibraryOutlined';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';

Expand All @@ -24,6 +28,7 @@ import { ItemChart } from './item-chart';
import { ItemData } from './item-data';

import styles from './app.module.scss';
import DashboardModal from './dashboard-modal';

const theme = createMuiTheme({
palette: {
Expand All @@ -45,6 +50,7 @@ interface AppState {
itemType: ItemType;
sortOptions: SortOptions;
loading: boolean;
openDashboardModal: boolean;
}

@observer
Expand Down Expand Up @@ -94,7 +100,8 @@ class App extends React.Component<any, AppState> {
sort: Sort.Change,
direction: Direction.Desc
},
loading: false
loading: false,
openDashboardModal: false
};
}

Expand Down Expand Up @@ -205,6 +212,11 @@ class App extends React.Component<any, AppState> {
<MenuItem value={Sort.Diff+Direction.Desc}>SEA > Global</MenuItem>
</Select>
</FormControl>
<Tooltip title="Create Dashboard">
<IconButton aria-label="Create Dashboard" onClick={this.openDashboardModal}>
<DashboardIcon />
</IconButton>
</Tooltip>
</Toolbar>
</AppBar>
<div className={styles["progress"]} hidden={!this.state.loading}>
Expand Down Expand Up @@ -240,6 +252,9 @@ class App extends React.Component<any, AppState> {
API Docs
</Button>
</div>
<Modal open={this.state.openDashboardModal} onClose={this.closeDashboardModal}>
<DashboardModal />
</Modal>
{this.renderItemCharts()}
</div>
</MuiThemeProvider>
Expand Down Expand Up @@ -375,6 +390,14 @@ class App extends React.Component<any, AppState> {
}
}

private openDashboardModal() {
this.setState({ openDashboardModal: true });
}

private closeDashboardModal() {
this.setState({ openDashboardModal: false });
}

}

export default App;
28 changes: 28 additions & 0 deletions src/dashboard-modal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.dashboardModal {
background-color: whitesmoke;
position: fixed;
width: 50%;
height: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
}

.nameSelect {
width: 75%;
z-index: 2;
}

.createDashboardButton {
margin-top: 15px;
}

.dashboardUrl {
color: #263238 !important;
border-color: #263238 !important;
border-width: 2px !important;
}
145 changes: 145 additions & 0 deletions src/dashboard-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { observer } from "mobx-react";
import * as React from 'react';
const autobind = require('react-autobind');

import { CircularProgress, Button, TextField, InputAdornment, IconButton, Tooltip, ClickAwayListener } from '@material-ui/core';
import Select from 'react-select';
import * as CopyToClipboard from 'react-copy-to-clipboard';

import { API } from './api';
import { NameData } from './item-data';

import styles from './app.module.scss';
import { Styles } from 'react-select/lib/styles';

interface ModalState {
selectedOptions: any;
loading: boolean;
dashboardUrl: string;
showTooltip: boolean;
}

interface NameOption {
value: string;
label: string;
}

let nameOptions: NameOption[] = [];

const customSelectStyle: Partial<Styles> = {
valueContainer: (provided) => ({
...provided,
overflowY: "scroll",
maxHeight: "100px"
})
};

@observer
class DashboardModal extends React.Component<any, ModalState> {
constructor(props: any) {
super(props);
autobind(this);
this.state = {
selectedOptions: null,
loading: false,
dashboardUrl: "",
showTooltip: false
};
}

componentWillMount() {
if (nameOptions.length === 0){
this.getNames();
}
}

render() {
return (
<div className={styles["dashboardModal"]}>
<h2>New Dashboard</h2>
<Select className={styles["nameSelect"]} placeholder="Add items for your dashboard" value={this.state.selectedOptions} onChange={this.handleChange} options={nameOptions} isMulti={true} styles={customSelectStyle} />
<Button onClick={this.createDashboardUrl} className={styles["createDashboardButton"]} variant="contained" color="primary">
Create Dashboard
</Button>
<TextField
variant="outlined"
margin="normal"
label="Dashboard URL"
value={this.state.dashboardUrl}
InputProps={{
readOnly: true,
classes: {
root: styles["dashboardUrl"],
focused: styles["dashboardUrl"],
notchedOutline: styles["dashboardUrl"]
},
endAdornment: (
<InputAdornment position="end">
<ClickAwayListener onClickAway={this.closeTooltip}>
<div>
<Tooltip title="Copied!" placement="top" onClose={this.closeTooltip} open={this.state.showTooltip} PopperProps={{ disablePortal: true }} disableFocusListener={true} disableHoverListener={true} disableTouchListener={true}>
<CopyToClipboard text={this.state.dashboardUrl} onCopy={this.copyDashboardUrl}>
<IconButton color="primary" aria-label="Copy dashboard URL" onClick={this.copyDashboardUrl}>
<svg aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 896 1024" height="24"><path d="M128 768h256v64H128v-64z m320-384H128v64h320v-64z m128 192V448L384 640l192 192V704h320V576H576z m-288-64H128v64h160v-64zM128 704h160v-64H128v64z m576 64h64v128c-1 18-7 33-19 45s-27 18-45 19H64c-35 0-64-29-64-64V192c0-35 29-64 64-64h192C256 57 313 0 384 0s128 57 128 128h192c35 0 64 29 64 64v320h-64V320H64v576h640V768zM128 256h512c0-35-29-64-64-64h-64c-35 0-64-29-64-64s-29-64-64-64-64 29-64 64-29 64-64 64h-64c-35 0-64 29-64 64z"/></svg>
</IconButton>
</CopyToClipboard>
</Tooltip>
</div>
</ClickAwayListener>
</InputAdornment>
)
}}
InputLabelProps={{
FormLabelClasses: {
root: styles["dashboardUrl"],
focused: styles["dashboardUrl"]
}
}}
/>
<div className={styles["progress"]} hidden={!this.state.loading}>
<CircularProgress color="secondary" />
</div>
</div>
);
}

private handleChange(selectedOptions: any) {
this.setState({ selectedOptions });
}

private getNames() {
this.setState({ loading: true });
API.getNames((names) => {
nameOptions = this.parseNameData(names);
this.setState({ loading: false });
});
}

private parseNameData(namesData: NameData[]){
return namesData.map(nameData => {
const nameOption: NameOption = {
value: nameData.name,
label: nameData.name
};
return nameOption;
});
}

private createDashboardUrl() {
let url = window.location.origin + "?q=";
for (const option of this.state.selectedOptions) {
url += option.value + "|";
}
this.setState({ dashboardUrl: url.substring(0, url.length - 1) });
}

private copyDashboardUrl() {
this.setState({ showTooltip: true });
}

private closeTooltip() {
this.setState({ showTooltip: false });
}
}

export default DashboardModal;
5 changes: 5 additions & 0 deletions src/item-data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export interface RangeData {
change: number;
}

export interface NameData {
name: string;
type: number;
}

export interface DataPoint {
time: Date;
price: number;
Expand Down
Loading

0 comments on commit 48b2a40

Please sign in to comment.