push uri list now works

pull/30/head
unknown 2018-11-15 18:28:11 -05:00
parent 852b44701d
commit 397ceff833
9 changed files with 123 additions and 20 deletions

View File

@ -28,6 +28,34 @@ export const addUrl = url => ({
data: url
})
export const changeUrlField = (id, value) => ({
type: ACTIONS.edit_url,
data: {
id,
value
}
})
export const removeUrl = id => async (dispatch, getState) => {
dispatch(setWorking(true))
try {
await Ajax.delete({
url: '/api/targets/' + id
})
dispatch({
type: ACTIONS.delete_url,
data: id
})
} catch (e) {
dispatch({
type: ACTIONS.error,
data: e
})
} finally {
dispatch(setWorking(false))
}
}
export const fetchUrls = () => async (dispatch, getState) => {
dispatch(setWorking(true))
try {
@ -61,3 +89,22 @@ export const createNewUrl = () => async (dispatch, getState) => {
dispatch(setWorking(false))
}
}
export const setUrl = (id, value) => async (dispatch, getState) => {
dispatch(setWorking(true))
try {
await Ajax.patch({
url: '/api/targets/' + id,
data: {
url: value
}
})
} catch (e) {
dispatch({
type: ACTIONS.error,
data: e
})
} finally {
dispatch(setWorking(false))
}
}

View File

@ -5,14 +5,17 @@ import React from 'react'
import '../../styles/shared/underlineinput.scss'
const UnderlineInput = props => (
<div className='underlined-input'>
<div className={'underlined-input ' + (props.className ? props.className : '')}>
<input
type={props.type}
name={props.name}
value={props.value}
className={(props.value.length ? 'has-content' : '')}
className={(props.value.length ? 'has-content' : '') + (props.pattern
? (props.value.length && !props.pattern.test(props.value) ? ' invalid' : '')
: '')}
autoComplete='nothing'
onChange={props.onChange} />
onChange={props.onChange}
onBlur={props.onBlur} />
<div className='reacts-to'>
<label className='placeholder'>{props.placeholder}</label>
<div className='underline' />

View File

@ -2,7 +2,11 @@
import React from 'react'
import IconButton from '../components/IconButton'
import UnderlineInput from '../components/UnderlineInput'
import '../../styles/shared/urilistitem.scss'
import { changeUrlField, setUrl, removeUrl } from '../actions/targets'
const uriRegex = /(.+:\/\/)?(.+\.)*(.+\.).{1,}(:\d+)?(.+)?/i
class UriListItem extends React.Component {
constructor () {
@ -11,8 +15,16 @@ class UriListItem extends React.Component {
render () {
return (
<li className='uri-list-item flex-container'>
<span className='uri flex'>uri.uri</span>
<IconButton icon='delete' />
<UnderlineInput
className='uri flex'
type='text'
name={'url-' + this.props.id}
placeholder='Destination URL'
value={'' + this.props.url}
pattern={uriRegex}
onChange={(e) => this.props.dispatch(changeUrlField(this.props.id, e.target.value))}
onBlur={(e) => this.props.dispatch(setUrl(this.props.id, e.target.value))} />
<IconButton icon='delete' onClick={() => this.props.dispatch(removeUrl(this.props.id))}/>
</li>
)
}

View File

@ -81,7 +81,7 @@ export default class Ajax {
}
xhr.onload = () => {
if (xhr.status !== 200) { return xhr.onerror() }
if (!('' + xhr.status).startsWith('2')) { return xhr.onerror() }
var data = xhr.response
try {
data = JSON.parse(data)

View File

@ -4,6 +4,7 @@ import Actions from '../actions/targets'
const reducer = (state = {}, action) => {
const { type, data } = action
let urls
switch (type) {
case Actions.set_working:
return {
@ -15,7 +16,23 @@ const reducer = (state = {}, action) => {
}
case Actions.add_url:
return {
urls: state.urls.concat(data)
urls: state.urls.concat(data),
error: ''
}
case Actions.delete_url:
return {
urls: state.urls.filter(x => x.id !== data),
error: ''
}
case Actions.edit_url:
urls = state.urls
urls.find(x => x.id === data.id).url = data.value
return {
urls: urls
}
case Actions.error:
return {
error: data.message
}
default: return {}
}

View File

@ -17,7 +17,10 @@ class App extends React.Component {
email: '',
password: ''
},
urls: [1, 2],
urls: [{
id: 1,
url: 'http'
}],
working: false
}
@ -41,7 +44,11 @@ class App extends React.Component {
}
getRegisteredUris () {
return this.state.urls.map((item, i) => {
return (<UriListItem key={i} />)
return (<UriListItem
key={i}
dispatch={this.dispatch}
id={item.id}
url={item.url} />)
})
}
render () {
@ -54,6 +61,7 @@ class App extends React.Component {
</aside>
<section className={'content flex' + (this.state.working ? ' working' : '')}>
<Progress bound />
{this.state.error && <div className='error-box'>{this.state.error}</div>}
<header className='flex-container'>
<div className='flex'>
<h1>Push URIs</h1>

View File

@ -71,6 +71,9 @@
left: 0;
}
}
&.invalid {
color: $red;
}
&.invalid:focus + .reacts-to,
&.invalid:active + .reacts-to,
&.invalid.has-content + .reacts-to {
@ -79,7 +82,7 @@
}
}
&.invalid + .reacts-to {
.underline {
.underline:before {
background: $red;
}
}

View File

@ -1,16 +1,12 @@
@import '../lib/vars';
.uri-list-item {
height: 50px;
line-height: 50px;
height: 60px;
line-height: 60px;
background: $background-1;
box-shadow: $shadow-0;
.uri {
display: inline-block;
font-size: 16px;
}
& > .button.icon {
margin: 5px;
margin: 7px 5px 3px 5px;
}
}
}

View File

@ -9,9 +9,20 @@
}
}
.content {
padding: 14px 14px 42px 14px;
padding: 14px 0 42px 0;
position: relative;
.error-box {
height: 30px;
line-height: 30px;
background: $red;
color: white;
padding: 0 14px;
margin: -14px 0 8px 0;
}
header {
padding: 0 14px;
h1 {
text-shadow: 1px 1px 2px $black-3;
}
@ -26,9 +37,15 @@
}
}
.list {
margin: 14px 0;
margin: 14px;
padding: 0;
list-style: none;
overflow: hidden;
}
&.working {
& > .progress {
top: 0;
height: 4px;
}
}
}