push uri list now works
parent
852b44701d
commit
397ceff833
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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' />
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue