got metacode sets working

This commit is contained in:
Connor Turland 2018-03-09 15:53:46 -05:00
parent 84e9a67332
commit f8506e9763
15 changed files with 325 additions and 215 deletions

View file

@ -60,8 +60,8 @@ Checklist
- [ ] new metacode - [ ] new metacode
- [ ] edit metacode - [ ] edit metacode
- [x] list metacode_sets - [x] list metacode_sets
- [ ] new metacode set - [x] new metacode set
- [ ] edit metacode set - [x] edit metacode set
- [ ] authorized apps - [ ] authorized apps
- [ ] registered apps - [ ] registered apps
- [ ] authorize - [ ] authorize

View file

@ -1,7 +1,8 @@
const request = require('request') const request = require('request')
function apiProxyMiddleware (req, res, next) { 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() return next()
} }
const method = req.method.toLowerCase() const method = req.method.toLowerCase()

View file

@ -1,49 +0,0 @@
/* global $ */
const Admin = {
selectMetacodes: [],
allMetacodes: [],
init: function() {
var self = Admin
$('#metacodes_value').val(self.selectMetacodes.toString())
},
selectAll: function() {
var self = Admin
$('.editMetacodes li').removeClass('toggledOff')
self.selectMetacodes = self.allMetacodes.slice(0)
$('#metacodes_value').val(self.selectMetacodes.toString())
},
deselectAll: function() {
var self = Admin
$('.editMetacodes li').addClass('toggledOff')
self.selectMetacodes = []
$('#metacodes_value').val(0)
},
liClickHandler: function() {
var self = Admin
if ($(this).attr('class') !== 'toggledOff') {
$(this).addClass('toggledOff')
const valueToRemove = $(this).attr('id')
self.selectMetacodes.splice(self.selectMetacodes.indexOf(valueToRemove), 1)
$('#metacodes_value').val(self.selectMetacodes.toString())
} else if ($(this).attr('class') === 'toggledOff') {
$(this).removeClass('toggledOff')
self.selectMetacodes.push($(this).attr('id'))
$('#metacodes_value').val(self.selectMetacodes.toString())
}
},
validate: function() {
var self = Admin
if (self.selectMetacodes.length === 0) {
window.alert('Would you pretty please select at least one metacode for the set?')
return false
}
}
}
export default Admin

View file

@ -1,51 +1,145 @@
function fetchWithCookies(url) { function get(url) {
return fetch(url, { credentials: 'same-origin' }) return fetch(url, {
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
}
})
} }
function postWithCookies(url, data = {}) { function post(url, data = {}) {
return fetch(url, { return fetch(url, {
credentials: 'same-origin', credentials: 'same-origin',
method: 'POST', method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data) body: JSON.stringify(data)
}) })
} }
function put(url, data = {}) {
return fetch(url, {
credentials: 'same-origin',
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
}
function deleteReq(url) {
return fetch(url, {
credentials: 'same-origin',
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
})
}
async function getMetacodes() { async function getMetacodes() {
const res = await fetchWithCookies('/metacodes.json') const res = await get('/metacodes')
const data = await res.json() const data = await res.json()
return data return data
} }
async function getMetacodeSets() { async function getMetacodeSets() {
const res = await fetchWithCookies('/metacode_sets.json') const res = await get('/metacode_sets')
const data = await res.json() const data = await res.json()
return data return data
} }
async function createMetacodeSet(metacodes, name, desc) {
const res = await post(`/metacode_sets`, {
metacodes: {
value: metacodes.toString()
},
metacode_set: {
name,
desc
}
})
if (!res.ok) {
throw new Error()
return
}
const data = await res.json()
return data
}
async function updateMetacodeSet(id, metacodes, name, desc) {
const res = await put(`/metacode_sets/${id}`, {
metacodes: {
value: metacodes.toString()
},
metacode_set: {
name,
desc
}
})
if (!res.ok) {
throw new Error()
return
}
return true
}
async function deleteMetacodeSet(id) {
const res = await deleteReq(`/metacode_sets/${id}`)
return res.ok
}
async function createMetacode(name, color, icon) {
const res = await post(`/metacodes`, {
metacode: {
name,
color
}
})
if (!res.ok) {
throw new Error()
return
}
const data = await res.json()
return data
}
async function updateMetacode(id, name, color, icon) {
const res = await put(`/metacodes/${id}`)
return res.ok
}
async function getCurrentUser() { async function getCurrentUser() {
const res = await fetchWithCookies('/users/current.json') const res = await get('/users/current')
const data = await res.json() const data = await res.json()
return data return data
} }
async function approveAccessRequest(mapId, requestId) { async function approveAccessRequest(mapId, requestId) {
const res = await postWithCookies(`/maps/${mapId}/approve_access/${requestId}`) const res = await post(`/maps/${mapId}/approve_access/${requestId}`)
return res.status === 200 return res.ok
} }
async function denyAccessRequest(mapId, requestId) { async function denyAccessRequest(mapId, requestId) {
const res = await postWithCookies(`/maps/${mapId}/deny_access/${requestId}`) const res = await post(`/maps/${mapId}/deny_access/${requestId}`)
return res.status === 200 return res.ok
} }
async function requestAccess(mapId) { async function requestAccess(mapId) {
const res = await postWithCookies(`/maps/${mapId}/access_request`) const res = await post(`/maps/${mapId}/access_request`)
return res.status === 200 return res.ok
} }
module.exports = { module.exports = {
getMetacodes, getMetacodes,
getMetacodeSets, getMetacodeSets,
createMetacodeSet,
updateMetacodeSet,
deleteMetacodeSet,
createMetacode,
updateMetacode,
getCurrentUser, getCurrentUser,
approveAccessRequest, approveAccessRequest,
denyAccessRequest, denyAccessRequest,

View file

@ -122,7 +122,8 @@ const ReactApp = {
self.getMapsProps(), self.getMapsProps(),
self.getContextMenuProps(), self.getContextMenuProps(),
self.getTopicCardProps(), self.getTopicCardProps(),
self.getChatProps()) self.getChatProps(),
self.getAdminProps())
}, },
getMapProps: function() { getMapProps: function() {
const self = ReactApp const self = ReactApp
@ -255,6 +256,16 @@ const ReactApp = {
filterAllSynapses: Filter.filterAllSynapses filterAllSynapses: Filter.filterAllSynapses
} }
}, },
getAdminProps: function() {
const self = ReactApp
return {
createMetacodeSet: DataFetcher.createMetacodeSet,
updateMetacodeSet: DataFetcher.updateMetacodeSet,
deleteMetacodeSet: DataFetcher.deleteMetacodeSet,
createMetacode: DataFetcher.createMetacode,
updateMetacode: DataFetcher.updateMetacode
}
},
resize: function() { resize: function() {
const self = ReactApp const self = ReactApp
const maps = ExploreMaps.collection const maps = ExploreMaps.collection

View file

@ -1,5 +1,4 @@
import Active from './Active' import Active from './Active'
import Admin from './Admin'
import AutoLayout from './AutoLayout' import AutoLayout from './AutoLayout'
import Cable from './Cable' import Cable from './Cable'
import Control from './Control' import Control from './Control'
@ -32,7 +31,6 @@ import Visualize from './Visualize'
const Metamaps = window.Metamaps || {} const Metamaps = window.Metamaps || {}
Metamaps.Active = Active Metamaps.Active = Active
Metamaps.Admin = Admin
Metamaps.AutoLayout = AutoLayout Metamaps.AutoLayout = AutoLayout
Metamaps.Cable = Cable Metamaps.Cable = Cable
Metamaps.Control = Control Metamaps.Control = Control
@ -86,6 +84,7 @@ function runInitFunctions(serverData) {
document.addEventListener('DOMContentLoaded', async function() { document.addEventListener('DOMContentLoaded', async function() {
Metamaps.ServerData = Metamaps.ServerData || {} Metamaps.ServerData = Metamaps.ServerData || {}
try { try {
// TODO: do these in parallel (Promise.all)
const metacodes = await DataFetcher.getMetacodes() const metacodes = await DataFetcher.getMetacodes()
Metamaps.ServerData.Metacodes = metacodes Metamaps.ServerData.Metacodes = metacodes

View file

@ -1,7 +1,7 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import AdminHeader from './AdminHeader' import AdminHeader from './AdminHeader'
class Metacodes extends Component { class EditMetacode extends Component {
render = () => { render = () => {
return ( return (
<div> <div>
@ -11,4 +11,4 @@ class Metacodes extends Component {
} }
} }
export default Metacodes export default EditMetacode

View file

@ -1,14 +1,41 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import AdminHeader from './AdminHeader' 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')
}
}
class Metacodes extends Component {
render = () => { render = () => {
const { metacodeSets, metacodes } = this.props
const id = parseInt(this.props.params.id, 10)
const metacodeSet = metacodeSets.find(m => m.id === id)
return ( return (
<div> <div>
<div id="yield">
<div className="centerContent">
<MetacodeSetEditor metacodeSet={metacodeSet} metacodes={metacodes} onSubmit={this.onSubmit} forEdit />
</div>
</div>
<AdminHeader /> <AdminHeader />
</div> </div>
) )
} }
} }
export default Metacodes export default EditMetacodeSet

View file

@ -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 (
<form className={forNew ? "new_metacode_set" : "edit_metacode_set"} id={forNew ? "new_metacode_set" : "edit_metacode_set"} onSubmit={this.onSubmit} acceptCharset="UTF-8">
<input name="utf8" type="hidden" value="✓" />
<div className="field">
<label htmlFor="metacode_set_name">Name</label>
<input value={this.state.name} onChange={this.updateForKey('name')} type="text" name="metacode_set[name]" id="metacode_set_name" />
<div className="clearfloat"></div>
</div>
<div className="field">
<label htmlFor="metacode_set_desc">Description</label>
<textarea value={this.state.desc} onChange={this.updateForKey('desc')} cols="40" rows="4" name="metacode_set[desc]" id="metacode_set_desc" />
<div className="clearfloat"></div>
</div>
<br />
<p>Choose Metacodes</p>
<div className="allMetacodes">
<span id="showAll" onClick={this.selectAll}>Select All</span>
<span id="hideAll" onClick={this.deselectAll}>Unselect All</span>
</div>
<div className="clearfloat"></div>
<div className="editMetacodes">
<ul id="filters-one">
{metacodes.filter((m, i) => i < length/4).map((m, i) => {
return <MetacodeListItem selected={selectMetacodes.indexOf(m.id) > -1} metacode={m} key={i} onClick={() => this.liClickHandler(m.id)} />
})}
</ul>
<ul id="filters-two">
{metacodes.filter((m, i) => i >= length/4 && i < length/4*2).map((m, i) => {
return <MetacodeListItem selected={selectMetacodes.indexOf(m.id) > -1} metacode={m} key={i} onClick={() => this.liClickHandler(m.id)} />
})}
</ul>
<ul id="filters-three">
{metacodes.filter((m, i) => i >= length/4*2 && i < length/4*3).map((m, i) => {
return <MetacodeListItem selected={selectMetacodes.indexOf(m.id) > -1} metacode={m} key={i} onClick={() => this.liClickHandler(m.id)} />
})}
</ul>
<ul id="filters-four">
{metacodes.filter((m, i) => i >= length/4*3 && i < length).map((m, i) => {
return <MetacodeListItem selected={selectMetacodes.indexOf(m.id) > -1} metacode={m} key={i} onClick={() => this.liClickHandler(m.id)} />
})}
</ul>
</div>
<div className="clearfloat"></div>
<div className="actions">
<Link className="button" to="/metacode_sets">Cancel</Link>
<input onClick={this.validate} type="submit" name="commit" value={forNew ? "Create Metacode Set" : "Update Metacode Set"} className="add" />
</div>
</form>
)
}
}
class MetacodeListItem extends Component {
render = () => {
const { selected, onClick, metacode } = this.props
return (
<li id={metacode.id} className={selected ? "" : "toggledOff"} onClick={onClick}>
<img src={metacode.icon} alt={metacode.name} />
<p>{metacode.name.toLowerCase()}</p>
<div className="clearfloat"></div>
</li>
)
}
}
export default MetacodeSetEditor

View file

@ -2,6 +2,11 @@ import React, { Component } from 'react'
import { Link } from 'react-router' import { Link } from 'react-router'
import AdminHeader from './AdminHeader' import AdminHeader from './AdminHeader'
/*
TODO:
make the delete metacode set button work
*/
class MetacodeSets extends Component { class MetacodeSets extends Component {
render = () => { render = () => {
const { metacodeSets, metacodes } = this.props const { metacodeSets, metacodes } = this.props
@ -43,7 +48,7 @@ class MetacodeSetRow extends Component {
<a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href={`/metacode_sets/${metacodeSet.id}`}>Delete</a> <a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href={`/metacode_sets/${metacodeSet.id}`}>Delete</a>
</td> </td>
<td className="metacodeSetDesc"> <td className="metacodeSetDesc">
{metacodeSet.description} {metacodeSet.desc}
</td> </td>
<td> <td>
{metacodeSet.metacodes.map((mId, index) => { {metacodeSet.metacodes.map((mId, index) => {

View file

@ -1,7 +1,7 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import AdminHeader from './AdminHeader' import AdminHeader from './AdminHeader'
class Metacodes extends Component { class NewMetacode extends Component {
render = () => { render = () => {
return ( return (
<div> <div>
@ -11,4 +11,4 @@ class Metacodes extends Component {
} }
} }
export default Metacodes export default NewMetacode

View file

@ -1,14 +1,38 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import AdminHeader from './AdminHeader' import { browserHistory } from 'react-router'
import AdminHeader from './AdminHeader'
import MetacodeSetEditor from './MetacodeSetEditor'
/*
TODO:
get the data actually updating after the network response
*/
class NewMetacodeSet extends Component {
onSubmit = async (metacodes, name, desc) => {
const { createMetacodeSet } = this.props
try {
const result = await createMetacodeSet(metacodes, name, desc)
browserHistory.push(`/metacode_sets`)
} catch (e) {
console.log(e)
window.alert('There was an error creating the metacode set')
}
}
class Metacodes extends Component {
render = () => { render = () => {
const { metacodes } = this.props
return ( return (
<div> <div>
<div id="yield">
<div className="centerContent">
<MetacodeSetEditor metacodes = {metacodes} onSubmit={this.onSubmit} forNew />
</div>
</div>
<AdminHeader /> <AdminHeader />
</div> </div>
) )
} }
} }
export default Metacodes export default NewMetacodeSet

View file

@ -1,89 +0,0 @@
{ form_for(@metacode_set) do |f| }
{ if @metacode_set.errors.any? }
<div id="error_explanation">
<h2>{ pluralize(@metacode_set.errors.count, "error") } prohibited this metacode set from being saved:</h2>
<ul>
{ @metacode_set.errors.full_messages.each do |msg| }
<li>{ msg }</li>
{ end }
</ul>
</div>
{ end }
<div className="field">
{ f.label :name }
{ f.text_field :name }
<div className="clearfloat"></div>
</div>
<div className="field">
{ f.label :desc, "Description" }
{ f.text_area :desc, :cols => "40", :rows => "4" }
<div className="clearfloat"></div>
</div>
<br />
<p>Choose Metacodes</p>
<div className="allMetacodes">
<span id="showAll" onclick="Metamaps.Admin.selectAll();">Select All</span>
<span id="hideAll" onclick="Metamaps.Admin.deselectAll();">Unselect All</span>
</div>
<div className="clearfloat"></div>
<div className="editMetacodes">
<ul id="filters-one">
{ $i = 0 }
{ @m = Metacode.order("name").all }
{ while $i < (Metacode.all.length / 4) do }
<li id="{ @m[$i].id }" { if not @m[$i].in_metacode_set(@metacode_set) }className="toggledOff"{ end }
onclick="Metamaps.Admin.liClickHandler.call(this);">
<img src="{ asset_path @m[$i].icon }" alt="{ @m[$i].name }" />
<p>{ @m[$i].name.downcase }</p>
<div className="clearfloat"></div>
</li>
{ $i += 1 }
{ end }
</ul>
<ul id="filters-two">
{ while $i < (Metacode.all.length / 4 * 2) do }
<li id="{ @m[$i].id }" { if not @m[$i].in_metacode_set(@metacode_set) }className="toggledOff"{ end }
onclick="Metamaps.Admin.liClickHandler.call(this);">
<img src="{ asset_path @m[$i].icon }" alt="{ @m[$i].name }" />
<p>{ @m[$i].name.downcase }</p>
<div className="clearfloat"></div>
</li>
{ $i += 1 }
{ end }
</ul>
<ul id="filters-three">
{ while $i < (Metacode.all.length / 4 * 3) do }
<li id="{ @m[$i].id }" { if not @m[$i].in_metacode_set(@metacode_set) }className="toggledOff"{ end }
onclick="Metamaps.Admin.liClickHandler.call(this);">
<img src="{ asset_path @m[$i].icon }" alt="{ @m[$i].name }" />
<p>{ @m[$i].name.downcase }</p>
<div className="clearfloat"></div>
</li>
{ $i += 1 }
{ end }
</ul>
<ul id="filters-four">
{ while $i < Metacode.all.length do }
<li id="{ @m[$i].id }" { if not @m[$i].in_metacode_set(@metacode_set) }className="toggledOff"{ end }
onclick="Metamaps.Admin.liClickHandler.call(this);">
<img src="{ asset_path @m[$i].icon }" alt="{ @m[$i].name }" />
<p>{ @m[$i].name.downcase }</p>
<div className="clearfloat"></div>
</li>
{ $i += 1 }
{ end }
</ul>
</div>
{ hidden_field(:metacodes, :value, {:value => 0}) }
<div className="clearfloat"></div>
<div className="actions">
{ link_to 'Cancel', metacode_sets_path,
{ :className => 'button' } }
{ f.submit :className => 'add', :onclick => "return Metamaps.Admin.validate();" }
</div>
{ end }

View file

@ -1,24 +0,0 @@
import React, { Component } from react
class MyComponent extends Component {
render = () => {
return (
<div id="yield">
<div className='centerContent'>
{ render 'form' }
</div>
</div>
)
}
}
export default MyComponent
<script>
{ Metacode.all.each do |m| }
{ if m.in_metacode_set(@metacode_set) }
Metamaps.Admin.selectMetacodes.push("{ m.id }");
{ end }
Metamaps.Admin.allMetacodes.push("{ m.id }");
{ end }
</script>

View file

@ -1,24 +0,0 @@
import React, { Component } from react
class MyComponent extends Component {
render = () => {
return (
<div id="yield">
<div className='centerContent'>
{ render 'form' }
</div>
</div>
)
}
}
export default MyComponent
<script>
{ Metacode.all.each do |m| }
{ if m.in_metacode_set(@metacode_set) }
Metamaps.Admin.selectMetacodes.push("{ m.id }");
{ end }
Metamaps.Admin.allMetacodes.push("{ m.id }");
{ end }
</script>