diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2020-06-02 16:09:59 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2020-06-02 16:09:59 +0200 |
| commit | 2db55c3d261ddee52019bbd06dc5f6545db39c16 (patch) | |
| tree | 4afe164982b81ca8cbd239e9f08567e8ae7fb2cf /frontend | |
| parent | 42d5e59335d97aa0cf165f2aedf8d04f60db9310 (diff) | |
form for making a new graph. add username field to db
Diffstat (limited to 'frontend')
| -rw-r--r-- | frontend/app.js | 20 | ||||
| -rw-r--r-- | frontend/common/app.css | 37 | ||||
| -rw-r--r-- | frontend/common/form.component.js | 8 | ||||
| -rw-r--r-- | frontend/common/form.css | 61 | ||||
| -rw-r--r-- | frontend/common/header.component.js | 14 | ||||
| -rw-r--r-- | frontend/common/index.js | 1 | ||||
| -rw-r--r-- | frontend/index.js | 1 | ||||
| -rw-r--r-- | frontend/views/index.js | 3 | ||||
| -rw-r--r-- | frontend/views/index/components/graph.form.js | 149 | ||||
| -rw-r--r-- | frontend/views/index/containers/graph.edit.js | 53 | ||||
| -rw-r--r-- | frontend/views/index/containers/graph.index.js | 28 | ||||
| -rw-r--r-- | frontend/views/index/containers/graph.new.js | 44 | ||||
| -rw-r--r-- | frontend/views/index/index.container.js | 33 | ||||
| -rw-r--r-- | frontend/views/index/index.css | 18 | ||||
| -rw-r--r-- | frontend/views/upload/index.js | 3 | ||||
| -rw-r--r-- | frontend/views/upload/upload.container.js | 2 |
16 files changed, 422 insertions, 53 deletions
diff --git a/frontend/app.js b/frontend/app.js index 38a50c6..da8dccb 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -4,7 +4,7 @@ import { Route } from 'react-router' import { Header } from './common' -import actions from './actions' +// import actions from './actions' import * as views from './views' @@ -22,17 +22,15 @@ export default class App extends Component { render() { return ( <ConnectedRouter history={this.props.history}> - <div> + <div className='app'> <Header /> - <div className='app'> - <div className='body'> - {viewList} - <Route exact key='root' path='/' render={() => { - // redirect to search!! - setTimeout(() => this.props.history.push('/search/'), 10) - return null - }} /> - </div> + <div className='body'> + {viewList} + <Route exact key='root' path='/' render={() => { + // redirect to index!! + setTimeout(() => this.props.history.push('/index'), 10) + return null + }} /> </div> </div> </ConnectedRouter> diff --git a/frontend/common/app.css b/frontend/common/app.css index a235507..41aa54e 100644 --- a/frontend/common/app.css +++ b/frontend/common/app.css @@ -2,15 +2,17 @@ html, body { margin: 0; padding: 0; - min-width: 100%; - min-height: 100%; + width: 100%; + height: 100%; } body { - background: #fff; - color: #000; + background: #000; + color: #ddd; overflow-y: scroll; font-family: 'Roboto', sans-serif; font-size: 0.875rem; + height: 100%; + width: 100%; } .gray { color: #888; @@ -18,6 +20,21 @@ body { /* layout */ +.container { + height: 100%; + width: 100%; +} +.app { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; +} +.app .body { + display: flex; + flex-grow: 1; +} + .row { display: flex; flex-direction: row; @@ -30,9 +47,7 @@ body { .row > div:last-child { margin-right: 0; } -.body section > div:last-child { - flex-grow: 1; -} + .row.menubar { justify-content: flex-end; @@ -59,7 +74,7 @@ header { flex-direction: row; justify-content: space-between; align-items: center; - background: #11f; + background: rgba(64,64,64,0.5); color: white; } header b { @@ -151,8 +166,12 @@ p { /* links */ +b { + color: #fff; +} a { - color: #11f; + text-decoration: underline; + color: #8df; } /* menu button */ diff --git a/frontend/common/form.component.js b/frontend/common/form.component.js index fb8acbe..36369b5 100644 --- a/frontend/common/form.component.js +++ b/frontend/common/form.component.js @@ -10,10 +10,18 @@ export const TextInput = props => ( onChange={props.onChange} name={props.name} value={props.data[props.name]} + autoComplete={props.autoComplete} /> </label> ) +export const LabelDescription = props => ( + <label className={'text description'}> + <span>{props.title}</span> + <span>{props.children}</span> + </label> +) + export const NumberInput = props => ( <label className={props.error ? 'error' : 'text'}> <span>{props.title}</span> diff --git a/frontend/common/form.css b/frontend/common/form.css index f3ba85d..5b8f1e3 100644 --- a/frontend/common/form.css +++ b/frontend/common/form.css @@ -49,13 +49,24 @@ form .error input[type=password] { border-color: #f11; } +/* form field descriptions */ + +form label.description { + cursor: normal; + font-size: small; + color: #ddd; +} + /* text input */ input[type=text], input[type=number], input[type=password] { padding: 0.5rem; - border: 1px solid #888; + border: 1px solid #ddd; + color: #fff; + background: #111; + font-family: 'Roboto', sans-serif; font-size: 0.875rem; width: 20rem; border-radius: 0.125rem; @@ -64,19 +75,24 @@ input[type=password] { input[type=text]:focus, input[type=number]:focus, input[type=password]:focus { - border: 1px solid #000; + border: 1px solid #8ff; + background: #000; } textarea { width: 35rem; height: 20rem; padding: 0.5rem; - border: 1px solid #888; + border: 1px solid #ddd; + font-family: 'Roboto', sans-serif; + background: #111; + color: #fff; font-size: 0.875rem; border-radius: 0.125rem; } textarea:focus { - border: 1px solid #000; + border: 1px solid #8ff; + background: #000; } /* checkbox */ @@ -114,7 +130,7 @@ input[type="checkbox"]:after { left: 0; width: 0.75rem; height: 0.75rem; - border: 0.0625rem solid #888; + border: 0.0625rem solid #ddd; content: ""; background-color: #fff; background-repeat: no-repeat; @@ -141,7 +157,7 @@ input[type="checkbox"]:checked:after { min-width: auto; background: #fff; border-radius: 0.125rem; - border: 1px solid #888; + border: 1px solid #ddd; padding: 0.5rem; overflow: hidden; text-overflow: ellipsis; @@ -165,14 +181,14 @@ input[type="checkbox"]:checked:after { height: 0; border-left: 0.375rem solid transparent; border-right: 0.375rem solid transparent; - border-top: 0.375rem solid #888; + border-top: 0.375rem solid #ddd; } .select.focus { - border-color: #11f; + border-color: #fff; background: #f4f4ff; } .select.focus:after { - border-top-color: #11f; + border-top-color: #fff; } .select:hover { background-color: #f4f4ff; @@ -192,21 +208,22 @@ input[type="checkbox"]:checked:after { button { position: relative; - background: #fff; + background: #333; border-radius: 0.125rem; - border: 1px solid #888; + color: #ddd; + border: 1px solid; padding: 0.5rem; overflow: hidden; text-overflow: ellipsis; font-family: 'Roboto', sans-serif; font-size: 0.875rem; cursor: pointer; - text-transform: uppercase; + /*text-transform: uppercase;*/ transition: all 0.1s; } button:hover { - background-color: #f0f0ff; - border-color: #1111ff; + background-color: #000; + border-color: #fff; } button.process { padding-left: 1.5rem; @@ -227,9 +244,9 @@ button.process:focus:after { border-left-color: #11f; } button:focus { - background: #f4f4ff; - border-color: #11f; - color: #11f; + background: #000; + border-color: #fff; + color: #fff; outline: 0; } button:disabled { @@ -245,13 +262,15 @@ button:disabled:after { margin-right: 0.75rem; } button.submit { - color: #1111ff; - border-color: #1111ff; + border-color: #d8f; + color: #fff; + background: #111; } +button.submit:focus, button.submit:hover { + border-color: #fff; color: #fff; - background: #1111ff; - border-color: #1111ff; + background: #222; } /* file upload, should always be inside a container */ diff --git a/frontend/common/header.component.js b/frontend/common/header.component.js index a4777dc..5cb15b5 100644 --- a/frontend/common/header.component.js +++ b/frontend/common/header.component.js @@ -1,6 +1,6 @@ -import React from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; +import React from 'react' +// import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' import { Link } from 'react-router-dom' import { session } from '../session' @@ -11,8 +11,6 @@ function Header(props) { <Link to="/" className="logo"><b>swimmer</b></Link> </div> <div> - <Link to="/collection/">Collections</Link> - <Link to="/dashboard/">Dashboard</Link> <span className='username' onClick={() => changeUsername()}> {' → '}{props.username} </span> @@ -34,9 +32,9 @@ const mapStateToProps = (state) => ({ auth: state.auth, username: session.get('username'), isAuthenticated: state.auth.isAuthenticated, -}); +}) const mapDispatchToProps = (dispatch) => ({ -}); +}) -export default connect(mapStateToProps, mapDispatchToProps)(Header); +export default connect(mapStateToProps, mapDispatchToProps)(Header) diff --git a/frontend/common/index.js b/frontend/common/index.js index 9e81c99..9a11eba 100644 --- a/frontend/common/index.js +++ b/frontend/common/index.js @@ -5,6 +5,7 @@ export { export { Select, Checkbox, FileInput, FileInputField, TextInput, NumberInput, TextArea, SubmitButton, + LabelDescription, } from './form.component' export { Loader, Swatch, Dot, Columns, Statistic, Detections, Progress diff --git a/frontend/index.js b/frontend/index.js index 94cac35..6f1a0a5 100644 --- a/frontend/index.js +++ b/frontend/index.js @@ -7,6 +7,7 @@ import App from './app' import { store, history } from './store' const container = document.createElement('div') +container.classList.add('container') document.body.appendChild(container) ReactDOM.render( diff --git a/frontend/views/index.js b/frontend/views/index.js index 5f88c96..69bb70e 100644 --- a/frontend/views/index.js +++ b/frontend/views/index.js @@ -1 +1,2 @@ -export { Container as upload } from './upload' +export { default as index } from './index/index.container' +export { default as upload } from './upload/upload.container' diff --git a/frontend/views/index/components/graph.form.js b/frontend/views/index/components/graph.form.js new file mode 100644 index 0000000..ef546ec --- /dev/null +++ b/frontend/views/index/components/graph.form.js @@ -0,0 +1,149 @@ +import React, { Component } from 'react' +import { Link } from 'react-router-dom' + +import { session } from '../../../session' + +import { TextInput, LabelDescription, TextArea, Checkbox, SubmitButton, Loader } from '../../../common' + +const newGraph = () => ({ + path: '', + title: '', + username: session('username'), + description: '', +}) + +export default class GraphForm extends Component { + state = { + title: "", + submitTitle: "", + data: { ...newGraph() }, + errorFields: new Set([]), + } + + componentDidMount() { + const { data, isNew } = this.props + const title = isNew ? 'new graph' : 'editing ' + data.title + const submitTitle = isNew ? "Create Graph" : "Save Changes" + this.setState({ + title, + submitTitle, + errorFields: new Set([]), + data: { + ...newGraph(), + ...data + }, + }) + } + + handleChange(e) { + const { errorFields } = this.state + const { name, value } = e.target + if (errorFields.has(name)) { + errorFields.delete(name) + } + this.setState({ + errorFields, + data: { + ...this.state.data, + [name]: value, + } + }) + } + + handleSelect(name, value) { + const { errorFields } = this.state + if (errorFields.has(name)) { + errorFields.delete(name) + } + this.setState({ + errorFields, + data: { + ...this.state.data, + [name]: value, + } + }) + } + + handleSubmit(e) { + e.preventDefault() + const { isNew, onSubmit } = this.props + const { data } = this.state + const requiredKeys = "title username".split(" ") + const validKeys = "title username notes archived".split(" ") + const validData = validKeys.reduce((a,b) => { a[b] = data[b]; return a }, {}) + const errorFields = requiredKeys.filter(key => !validData[key]) + if (errorFields.length) { + console.log('error', errorFields, validData) + this.setState({ errorFields: new Set(errorFields) }) + } else { + if (isNew) { + // side effect: set username if we're creating a new graph + session.set('username', data.username) + } else { + validData.id = data.id + } + console.log('submit', validData) + onSubmit(validData) + } + } + + render() { + const { isNew } = this.props + const { title, submitTitle, errorFields, data } = this.state + return ( + <div className='form'> + <h1>{title}</h1> + <form onSubmit={this.handleSubmit.bind(this)}> + <TextInput + title="Path" + name="path" + required + data={data} + error={errorFields.has('path')} + onChange={this.handleChange.bind(this)} + autoComplete="off" + /> + <LabelDescription> + {data.path + ? 'Project URLs will be: /' + data.path + '/example' + : 'Enter the base path for this project.'} + </LabelDescription> + <TextInput + title="Title" + name="title" + required + data={data} + error={errorFields.has('title')} + onChange={this.handleChange.bind(this)} + autoComplete="off" + /> + <TextInput + title="Author" + name="username" + required + data={data} + error={errorFields.has('username')} + onChange={this.handleChange.bind(this)} + autoComplete="off" + /> + <TextArea + title="Description" + name="description" + data={data} + onChange={this.handleChange.bind(this)} + /> + <SubmitButton + title={submitTitle} + onClick={this.handleSubmit.bind(this)} + /> + {!!errorFields.size && + <label> + <span></span> + <span>Please complete the required fields =)</span> + </label> + } + </form> + </div> + ) + } +} diff --git a/frontend/views/index/containers/graph.edit.js b/frontend/views/index/containers/graph.edit.js new file mode 100644 index 0000000..2f8c7fb --- /dev/null +++ b/frontend/views/index/containers/graph.edit.js @@ -0,0 +1,53 @@ +import React, { Component } from 'react' +import { Link } from 'react-router-dom' +import { connect } from 'react-redux' + +import { history } from '../../../store' +import actions from '../../../actions' + +import { Loader } from '../../../common' + +import GraphForm from '../components/graph.form' + +class GraphEdit extends Component { + componentDidMount() { + actions.graph.show(this.props.match.params.id) + } + + handleSubmit(data) { + actions.graph.update(data) + .then(response => { + // response + console.log(response) + history.push('/graph/' + data.id + '/show/') + }) + } + + render() { + const { show } = this.props.graph + if (show.loading || !show.res) { + return ( + <div className='form'> + <h1>Loading...</h1> + <Loader /> + </div> + ) + } + return ( + <GraphForm + data={show.res} + onSubmit={this.handleSubmit.bind(this)} + /> + ) + } +} + +const mapStateToProps = state => ({ + graph: state.graph, +}) + +const mapDispatchToProps = dispatch => ({ + // searchActions: bindActionCreators({ ...searchActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(GraphEdit) diff --git a/frontend/views/index/containers/graph.index.js b/frontend/views/index/containers/graph.index.js new file mode 100644 index 0000000..b18c768 --- /dev/null +++ b/frontend/views/index/containers/graph.index.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react' +import { Link } from 'react-router-dom' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +// import actions from '../../actions' +// import * as uploadActions from './upload.actions' + +class GraphIndex extends Component { + render() { + return ( + <div className='graphIndex'> + <b>welcome, swimmer</b> + <Link to='/index/new'>+ new project</Link> + </div> + ) + } +} + +const mapStateToProps = state => ({ + // upload: state.upload, +}) + +const mapDispatchToProps = dispatch => ({ + // uploadActions: bindActionCreators({ ...uploadActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(GraphIndex) diff --git a/frontend/views/index/containers/graph.new.js b/frontend/views/index/containers/graph.new.js new file mode 100644 index 0000000..186f8f7 --- /dev/null +++ b/frontend/views/index/containers/graph.new.js @@ -0,0 +1,44 @@ +import React, { Component } from 'react' +import { Link } from 'react-router-dom' +import { connect } from 'react-redux' + +import { history } from '../../../store' +import actions from '../../../actions' + +import GraphForm from '../components/graph.form' + +class GraphNew extends Component { + handleSubmit(data) { + console.log(data) + actions.graph.create(data) + .then(res => { + console.log(res) + if (res.res && res.res.id) { + history.push('/graph/' + res.res.name) + } + }) + .catch(err => { + console.error('error') + }) + } + + render() { + return ( + <GraphForm + isNew + data={{}} + onSubmit={this.handleSubmit.bind(this)} + /> + ) + } +} + +const mapStateToProps = state => ({ + graph: state.graph, +}) + +const mapDispatchToProps = dispatch => ({ + // searchActions: bindActionCreators({ ...searchActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(GraphNew) diff --git a/frontend/views/index/index.container.js b/frontend/views/index/index.container.js new file mode 100644 index 0000000..7805e83 --- /dev/null +++ b/frontend/views/index/index.container.js @@ -0,0 +1,33 @@ +import React, { Component } from 'react' +import { Route } from 'react-router-dom' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import './index.css' + +// import actions from '../../actions' +// import * as uploadActions from './upload.actions' + +import GraphIndex from './containers/graph.index' +import GraphNew from './containers/graph.new' + +class Container extends Component { + render() { + return ( + <div className='index'> + <Route exact path='/index/new' component={GraphNew} /> + <Route exact path='/index' component={GraphIndex} /> + </div> + ) + } +} + +const mapStateToProps = state => ({ + // upload: state.upload, +}) + +const mapDispatchToProps = dispatch => ({ + // uploadActions: bindActionCreators({ ...uploadActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(Container) diff --git a/frontend/views/index/index.css b/frontend/views/index/index.css new file mode 100644 index 0000000..48c5abc --- /dev/null +++ b/frontend/views/index/index.css @@ -0,0 +1,18 @@ +* { + +} + +.index > div { + margin: 1rem; + padding: 1rem; + max-height: calc(100% - 2rem); + overflow: scroll; + background: rgba(64,12,64,0.9); +} +.graphIndex { + display: flex; + flex-direction: column; +} +.graphIndex > * { + margin-bottom: 0.5rem; +}
\ No newline at end of file diff --git a/frontend/views/upload/index.js b/frontend/views/upload/index.js deleted file mode 100644 index b1e9ebe..0000000 --- a/frontend/views/upload/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as Container } from './upload.container' - -import './upload.css' diff --git a/frontend/views/upload/upload.container.js b/frontend/views/upload/upload.container.js index c163b92..0096b4f 100644 --- a/frontend/views/upload/upload.container.js +++ b/frontend/views/upload/upload.container.js @@ -3,6 +3,8 @@ import { Route, Link } from 'react-router-dom' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' +import './upload.css' + import actions from '../../actions' import * as uploadActions from './upload.actions' |
