diff --git a/.buildpacks b/.buildpacks index 9fb12425..96d0a9dd 100644 --- a/.buildpacks +++ b/.buildpacks @@ -1,2 +1 @@ https://github.com/heroku/heroku-buildpack-nodejs.git -https://github.com/heroku/heroku-buildpack-ruby.git diff --git a/.codeclimate.yml b/.codeclimate.yml index a187069d..4aeeffb8 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,16 +1,10 @@ --- engines: - brakeman: - enabled: true - bundler-audit: - enabled: true duplication: enabled: true config: languages: count_threshold: 3 # rule of three - ruby: - mass_threshold: 36 # default: 18 javascript: mass_threshold: 80 # default: 40 eslint: @@ -18,21 +12,11 @@ engines: channel: "eslint-3" fixme: enabled: true - rubocop: - enabled: true - exclude_fingerprints: - - 74f18007b920e8d81148d2f6a2756534 ratings: paths: - - 'Gemfile.lock' - - '**.erb' - - '**.rb' - '**.js' - '**.jsx' exclude_paths: -- app/assets/images/ -- app/assets/javascripts/lib/ -- frontend/src/patched/ -- db/ -- script/ -- spec/ +- public/images +- public/lib +- src/patched/ diff --git a/.nvmrc b/.nvmrc index ca063943..e3f76f34 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -6.2.2 +8.9.4 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..8ccb5030 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +addons: + code_climate: + repo_token: 479d3bf56798fbc7fff3fc8151a5ed09e8ac368fd5af332c437b9e07dbebb44e \ No newline at end of file diff --git a/README.md b/README.md index 27235033..4b9ae84f 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,28 @@ -Make sure you have `nodemon` and `node-sass` installed -`$ npm install -g nodemon node-sass` +Make sure you're running a good up to date LTS version of `node`, like 8.9.4 +Make sure you have `node-sass` installed +`$ npm install -g node-sass` -Run the following at the same time, in two terminals - +Run the following at the same time, in TWO SEPARATE terminals. We tell the server where the backend process is running with the API environment variable +JS files, and CSS will rebuild automatically, just refresh the page +If coding the server itself, you will have to use nodemon, or kill and restart the server process manually ``` -$ API=http://localhost:3001 nodemon server.js +$ API=http://localhost:3001 node server.js $ node-sass -w sass/application.scss public/css/application.css ``` -To run the server as a daemon that will be re-run if it crashes, you can -use the forever node package. +To make sure the css files get built, use the following in another terminal ``` -$ npm install -g forever -$ forever start server.js +touch sass/application.scss ``` -Run the metamaps api in another terminal using +Run the metamaps api in another terminal using (on port 3001, so the UI can talk to it) +For now, make sure you are running on the `add-user-route` branch of Metamaps, and that it's up to date with the latest on that branch `$ rails s -p 3001` +open up http://localhost:3000 and start coding! + Checklist - [x] Get the Import lightbox working, and not conflicting on screen - [x] Handling CSRF @@ -27,32 +30,52 @@ Checklist - [x] Figure out how authentication of requests from the frontend to the API works - [x] Figure out how to combine the nodejs realtime server into server.js - [x] Notifications: make sure loading states are working for popup and page +- [x] Request unreadNotificationCount +- [x] Request invite code +- [x] Request user object itself +- [x] Load the metacodes +- [x] move ImportDialog lightbox into main app +- [x] create topic form +- [x] Fork map lightbox / component + +- [ ] fix other places where metacode sets are used +- [ ] make newtopic form load metacodes from users selected ones +- [ ] create synapse form +- [ ] replace old loader with react loader +- [ ] ensure exports of maps work - [ ] Notifications: make sure notifications either look nice, or redirect - [ ] Notifications: pagination - [ ] Notifications: Request unreadNotificationCount - [ ] Notifications: CSS fixes related to 'controller-x' in body classes - [ ] Make sure loading state for explore maps pages work - [ ] Get actioncable working -- [ ] Request invite code -- [x] Request user object itself +- [ ] lightboxes +- [ ] About lightbox +- [ ] Switch Metacodes lightbox / component +- [ ] break up index.html into parts - [ ] Handle CSS metacode colors - [ ] Fix Request An Invite page - [ ] Make 'new map' action work - [ ] Modify the remaining rails templates into JSX templates - [x] notifications list - [x] notification page - - [ ] list metacodes - - [ ] new metacode - - [ ] edit metacode - - [ ] list metacode_sets - - [ ] new metacode set - - [ ] edit metacode set + - [x] list metacodes + - [x] new metacode + - [x] edit metacode + - [x] list metacode_sets + - [x] new metacode set + - [x] edit metacode set - [ ] authorized apps - [ ] registered apps - [ ] authorize - [ ] user passwords - [ ] Modify the RubyOnRails app to only serve JSON responses, no HTML pages anymore -- [ ] Modify the RubyOnRails app to include an endpoint that responds with basic data the front end needs to display (such as the invite code for the user, and the current metamaps build) (a bunch of the data found here: https://github.com/metamaps/metamaps/blob/frontendonly/Metamaps.ServerData.js.erb) - [ ] Modify the frontend to request that data from the API which is necessary at first to load the page -- [x] Load the metacodes -- [ ] Load the metacode sets + - [x] Load the metacode sets + +To run the server as a daemon that will be re-run if it crashes, you can +use the forever node package. +``` +$ npm install -g forever +$ forever start server.js +``` \ No newline at end of file diff --git a/apiProxyMiddleware.js b/apiProxyMiddleware.js index 47c5752a..bd380368 100644 --- a/apiProxyMiddleware.js +++ b/apiProxyMiddleware.js @@ -1,7 +1,8 @@ const request = require('request') function apiProxyMiddleware (req, res, next) { - if (!(req.xhr || req.originalUrl.indexOf('.json') > -1 || req.method !== 'GET')) { + // TODO: tidy this up! + if (!(req.xhr || req.headers['content-type'] === 'application/json' || req.originalUrl.indexOf('.json') > -1 || req.method !== 'GET')) { return next() } const method = req.method.toLowerCase() diff --git a/package.json b/package.json index 2606b210..5f87ace8 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "server": "node server.js", "build": "webpack", "build:watch": "webpack --watch", - "test": "mocha-webpack --webpack-config webpack.test.config.js --require frontend/test_support/dom.js --recursive frontend/test", - "eslint": "eslint frontend", - "eslint:fix": "eslint --fix frontend" + "test": "mocha-webpack --webpack-config webpack.test.config.js --require test_support/dom.js --recursive test", + "eslint": "eslint src", + "eslint:fix": "eslint --fix src" }, "repository": { "type": "git", diff --git a/public/index.html b/public/index.html index a0b79386..f7496952 100644 --- a/public/index.html +++ b/public/index.html @@ -11,17 +11,18 @@ + - - - - Metamaps + + + + Metamaps - + - + - + - + + -
-
-
-
- - - - - - - - - - - - - - - - - - - + +
+ -
- - - - - - - - - - - - - +
+
+ - diff --git a/src/components/NewSynapse.js b/src/components/NewSynapse.js new file mode 100644 index 00000000..10e926f8 --- /dev/null +++ b/src/components/NewSynapse.js @@ -0,0 +1,18 @@ +import React, { Component } from 'react' + +class NewSynapse extends Component { + componentDidMount() { + this.props.initNewSynapse() + } + + render = () => { + return ( +
+ + +
+ ) + } +} + +export default NewSynapse \ No newline at end of file diff --git a/src/components/NewTopic.js b/src/components/NewTopic.js new file mode 100644 index 00000000..d837ea6b --- /dev/null +++ b/src/components/NewTopic.js @@ -0,0 +1,54 @@ +import React, { Component } from 'react' + +class NewTopic extends Component { + componentDidMount() { + this.props.initNewTopic() + } + + render = () => { + const metacodes = [ + { + "id": 1, + "name": "Action", + "created_at": "2017-03-04T17:33:07.394Z", + "updated_at": "2017-03-04T17:33:07.394Z", + "color": "#BD6C85", + "icon": "https://s3.amazonaws.com/metamaps-assets/metacodes/blueprint/96px/bp_action.png" + } + ] + return ( +
+ +
this.props.openMetacodeSwitcher()}> +
Switch Metacodes
+
+ +
+
Pin Open
+
Unpin
+
+ +
+ {metacodes.map(m => {m.name})} +
+ + + +
+
+
+ ) + } +} + +export default NewTopic + +/* +TODO: +{ @metacodes.each do |metacode| } + Metamaps.Create.selectedMetacodes.push("{ metacode.id }"); + Metamaps.Create.newSelectedMetacodes.push("{ metacode.id }"); + Metamaps.Create.selectedMetacodeNames.push("{ metacode.name }"); + Metamaps.Create.newSelectedMetacodeNames.push("{ metacode.name }"); + { end } +*/ \ No newline at end of file diff --git a/src/components/NotificationBox.js b/src/components/NotificationBox.js index fdb836f6..b3669de4 100644 --- a/src/components/NotificationBox.js +++ b/src/components/NotificationBox.js @@ -61,11 +61,11 @@ class NotificationBox extends Component { } render = () => { - const { loading } = this.props + const { notifications, loading } = this.props return
} diff --git a/src/routes/Admin.js b/src/routes/Admin/AdminHeader.js similarity index 70% rename from src/routes/Admin.js rename to src/routes/Admin/AdminHeader.js index f715aa1c..8e1510c4 100644 --- a/src/routes/Admin.js +++ b/src/routes/Admin/AdminHeader.js @@ -1,8 +1,8 @@ import React, { Component } from 'react' -import NavBar from '../components/NavBar' -import NavBarLink from '../components/NavBarLink' +import NavBar from '../../components/NavBar' +import NavBarLink from '../../components/NavBarLink' -class Admin extends Component { +class AdminHeader extends Component { render = () => { return ( @@ -15,4 +15,4 @@ class Admin extends Component { } } -export default Admin +export default AdminHeader diff --git a/src/routes/Admin/EditMetacode.js b/src/routes/Admin/EditMetacode.js new file mode 100644 index 00000000..e7797cc2 --- /dev/null +++ b/src/routes/Admin/EditMetacode.js @@ -0,0 +1,98 @@ +import React, { Component } from 'react' +import { Link, browserHistory } from 'react-router' +import AdminHeader from './AdminHeader' + +class EditMetacode extends Component { + constructor(props) { + super(props) + this.state = { + existingIcon: null, + icon: null, + name: '', + color: '' + } + } + + componentDidMount() { + const { metacodes } = this.props + const id = parseInt(this.props.params.id, 10) + const metacode = metacodes.find(m => m.id === id) + this.setState({ + existingIcon: metacode.icon, + name: metacode.name, + color: metacode.color + }) + } + + validate = (event) => { + if (this.state.name.length === 0) { + event.preventDefault() + window.alert('A name must be provided') + } else if (!this.state.color.startsWith('#')) { + event.preventDefault() + window.alert('Please begin color with a # symbol') + } + } + + updateForKey = (key) => event => this.setState({[key]: event.target.value}) + + handleFile = (event) => { + this.setState({ + icon: event.target.files[0] + }) + } + + onSubmit = async (event) => { + event.preventDefault() + const { name, color, icon } = this.state + const { updateMetacode, params: { id } } = this.props + try { + const result = await updateMetacode(id, name, color, icon) + browserHistory.push(`/metacodes`) + } catch (e) { + console.log(e) + window.alert('There was an error updating the metacode, check the console') + } + } + + render = () => { + return ( +
+
+
+
+ +
+ + +
+
+
+ + {this.state.name} +
+
+ + + +
+
+
+ + +
+
+
+ Cancel + +
+
+
+
+ +
+ ) + } +} + +export default EditMetacode \ No newline at end of file diff --git a/src/routes/Admin/EditMetacodeSet.js b/src/routes/Admin/EditMetacodeSet.js new file mode 100644 index 00000000..08b11324 --- /dev/null +++ b/src/routes/Admin/EditMetacodeSet.js @@ -0,0 +1,41 @@ +import React, { Component } from 'react' +import { browserHistory } from 'react-router' + +import AdminHeader from './AdminHeader' +import MetacodeSetEditor from './MetacodeSetEditor' + +/* +TODO: + get the data actually updating after the network response +*/ + +class EditMetacodeSet extends Component { + onSubmit = async (metacodes, name, desc) => { + const { updateMetacodeSet, params: { id } } = this.props + try { + const result = await updateMetacodeSet(id, metacodes, name, desc) + browserHistory.push(`/metacode_sets`) + } catch (e) { + console.log(e) + window.alert('There was an error updating the metacode set') + } + } + + render = () => { + const { metacodeSets, metacodes } = this.props + const id = parseInt(this.props.params.id, 10) + const metacodeSet = metacodeSets.find(m => m.id === id) + return ( +
+
+
+ +
+
+ +
+ ) + } +} + +export default EditMetacodeSet \ No newline at end of file diff --git a/src/routes/Admin/MetacodeSetEditor.js b/src/routes/Admin/MetacodeSetEditor.js new file mode 100644 index 00000000..bf2f840a --- /dev/null +++ b/src/routes/Admin/MetacodeSetEditor.js @@ -0,0 +1,135 @@ +import React, { Component } from 'react' +import { Link } from 'react-router' + +class MetacodeSetEditor extends Component { + constructor(props) { + super(props) + this.state = { + selectMetacodes: [], + name: '', + desc: '' + } + } + + componentDidMount() { + const { forEdit, metacodeSet } = this.props + if (forEdit) { + this.setState({ + selectMetacodes: metacodeSet.metacodes, + name: metacodeSet.name, + desc: metacodeSet.desc + }) + } + } + + selectAll = () => { + this.setState({ + selectMetacodes: this.props.metacodes.map(m => m.id) + }) + } + + deselectAll = () => { + this.setState({ selectMetacodes: [] }) + } + + liClickHandler = (metacodeId) => { + const { selectMetacodes } = this.state + if (selectMetacodes.indexOf(metacodeId) > -1) { + this.setState({ + selectMetacodes: selectMetacodes.filter(id => id !== metacodeId) + }) + } else { + this.setState({ + selectMetacodes: selectMetacodes.concat([metacodeId]) + }) + } + } + + updateForKey = (key) => event => this.setState({[key]: event.target.value}) + + validate = (event) => { + if (this.state.selectMetacodes.length === 0) { + event.preventDefault() + window.alert('Please select at least one metacode for the set') + } else if (this.state.name.length === 0) { + event.preventDefault() + window.alert('A name must be provided') + } + } + + onSubmit = (event) => { + event.preventDefault() + const { selectMetacodes, name, desc } = this.state + this.props.onSubmit(selectMetacodes, name, desc) + } + + render = () => { + const { selectMetacodes } = this.state + const { metacodes, forNew, forEdit } = this.props + const { length } = metacodes + return ( +
+ +
+ + +
+
+
+ +