Commit 0365ba8e by Barnabás Czémán

Add initial forms and pages

parent d1490b28
DANGEROUSLY_DISABLE_HOST_CHECK=true
......@@ -2,6 +2,7 @@
"extends": "airbnb",
"rules": {
"react/jsx-filename-extension": "off",
"react/prop-types": "off",
"import/extensions": off,
"jsx-quotes": ["error", "prefer-single"],
"max-len": ["error", 125],
......@@ -11,4 +12,3 @@
"document": "false"
}
}
......@@ -2,22 +2,34 @@
"name": "network-frontend",
"version": "0.1.0",
"private": true,
"proxy": "http://127.0.0.1:8080",
"dependencies": {
"axios": "^0.17.0",
"moment": "^2.19.2",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-redux": "^5.0.6",
"react-router-dom": "^4.2.2",
"react-scripts": "1.0.14",
"redux": "^3.7.2",
"redux-form": "^7.1.2",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.2.0",
"semantic-ui-css": "^2.2.12",
"semantic-ui-react": "^0.75.1"
"semantic-ui-react": "^0.75.1",
"underscore": "^1.8.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"eslint": "^4.10.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.4.0"
}
}
import React from 'react'
import { Container, Divider, Dropdown, Grid, Header, Image, List, Menu, Segment } from 'semantic-ui-react'
import { Container, Dropdown, List, Menu, Segment } from 'semantic-ui-react'
import { NavLink, Link, Route } from 'react-router-dom'
import {
HostList,
VlanList,
DomainList,
RecordList,
BlacklistList,
RuleList,
FirewallList,
SwitchPortList,
VlanGroupList,
HostGroupList,
} from './containers'
import DomainFormContainer from './containers/DomainFormContainer'
import VlanFormContainer from './containers/VlanFormContainer'
import HostFormContainer from './containers/HostFormContainer'
import BlacklistFormContainer from './containers/BlacklistFormContainer'
import RuleFormContainer from './containers/RuleFormContainer'
import FirewallFormContainer from './containers/FirewallFormContainer'
import RecordFormContainer from './containers/RecordFormContainer'
import SwitchPortFormContainer from './containers/SwitchPortFormContainer'
import VlanGroupFormContainer from './containers/VlanGroupFormContainer'
import HostGroupFormContainer from './containers/HostGroupFormContainer'
const App = () => (
<div>
<Menu fixed='top' inverted>
......@@ -25,23 +49,33 @@ const App = () => (
</Container>
</Menu>
<Container text style={{ marginTop: '7em' }}>
<Container text style={{ marginTop: '7em', marginBottom: '7em' }}>
<Route path='/' exact />
<Route path='/hosts' />
<Route path='/vlans' />
<Route path='/doamins' />
<Route path='/records' />
<Route path='/blacklist' />
<Route path='/rules' />
<Route path='/switchports' />
<Route path='/firewalls' />
<Route path='/vlangroups' />
<Route path='/hostgroups' />
<Route path='/hosts' exact component={HostList} />
<Route path='/hosts/:id' component={HostFormContainer} />
<Route path='/vlans' exact component={VlanList} />
<Route path='/vlans/:id' component={VlanFormContainer} />
<Route path='/domains' exact component={DomainList} />
<Route path='/domains/:id' component={DomainFormContainer} />
<Route path='/records' exact component={RecordList} />
<Route path='/records/:id' component={RecordFormContainer} />
<Route path='/blacklist' exact component={BlacklistList} />
<Route path='/blacklist/:id' component={BlacklistFormContainer} />
<Route path='/rules' exact component={RuleList} />
<Route path='/rules/:id' component={RuleFormContainer} />
<Route path='/switchports' exact component={SwitchPortList} />
<Route path='/switchports/:id' component={SwitchPortFormContainer} />
<Route path='/firewalls' exact component={FirewallList} />
<Route path='/firewalls/:id' component={FirewallFormContainer} />
<Route path='/vlangroups' exact component={VlanGroupList} />
<Route path='/vlangroups/:id' component={VlanGroupFormContainer} />
<Route path='/hostgroups' exact component={HostGroupList} />
<Route path='/hostgroups/:id' exact component={HostGroupFormContainer} />
</Container>
<Segment inverted vertical >
<Container textAlign='center'>
<List horizontal inverted divided link>
<List horizontal inverted divided link>
<List.Item as='a' href='#'>Legal Notice</List.Item>
<List.Item as='a' href='#'>Policy</List.Item>
<List.Item as='a' href='#'>Help</List.Item>
......
import React from 'react'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
const Root = ({ store }) => (
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
)
export default Root
import axios from 'axios'
export const REQUEST_DATA = 'REQUEST_DATA'
export const RECEIVE_DATA = 'RECEIVE_DATA'
export const REQUEST_OPTIONS = 'REQUEST_OPTIONS'
export const RECEIVE_OPTIONS = 'RECEIVE_OPTIONS'
export const receiveData = (name, { data }) => ({
type: RECEIVE_DATA,
payload: {
data,
name,
isFetching: false,
},
})
export const requestData = name => ({
type: REQUEST_DATA,
payload: {
data: [],
name,
isFetching: true,
},
})
export const fetchData = (name, url) => (dispatch) => {
dispatch(requestData(name))
axios.get(url).then(response => dispatch(receiveData(name, response)))
}
const config = {
xsrfCookieName: 'csrftoken',
xsrfHeaderName: 'X-CSRFToken',
}
export const postData = (url, data) => () => axios.post(url, data, config)
export const putData = (url, data) => () => axios.put(url, data, config)
export const deleteData = url => () => axios.delete(url, config)
export const receiveOptions = (name, { data }) => ({
type: RECEIVE_OPTIONS,
payload: {
options: data,
name,
isFetching: false,
},
})
export const requestOptions = name => ({
type: REQUEST_OPTIONS,
payload: {
options: {},
name,
isFetching: true,
},
})
export const fetchOptions = (name, url) => (dispatch) => {
dispatch(requestOptions(name))
return axios.options(url).then(response => dispatch(receiveOptions(name, response)))
}
/* export const logMe = data => (
async (dispatch) => {
console.log(data)
try {
const response = await axios.post('url', data)
} catch (err) {
console.error(err)
}
}
) */
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
import SemanticReduxFormField from '../components/SemanticReduxFormField'
const BlacklistForm = ({
pristine,
submitting,
hostOptions,
handleSubmit,
}) => (
<Form onSubmit={handleSubmit}>
<Field component={Form.Input} type='text' placeholder='IPv4' name='ipv4' />
<Field
options={hostOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='host'
type='text'
search
/>
<Field component={Form.Input} type='datetime-local' name='expires_at' />
<Field component={Form.Checkbox} label='Whitelisted' name='whitelisted' />
<Field component={Form.TextArea} label='Reason' name='reason' />
<Field component={Form.TextArea} label='Short Message' name='short_message' />
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
export default reduxForm({ form: 'blacklist' })(BlacklistForm)
import React from 'react'
import { Image } from 'semantic-ui-react'
const Dashboard = () => (
<Image src='' alt='graphit' />
)
export default Dashboard
import React, { Component } from 'react'
import { Table, Icon, Menu } from 'semantic-ui-react'
import moment from 'moment'
import _ from 'underscore'
export default class DataTable extends Component {
constructor(props) {
super(props)
this.state = {
column: null,
data: this.props.data,
direction: null,
}
}
handleSort(clickedColumn) {
const { column, data, direction } = this.state
if (column !== clickedColumn) {
this.setState({
column: clickedColumn,
data: { ...data, results: _.sortBy(data.results, clickedColumn) },
direction: 'ascending',
})
} else {
this.setState({
data: { results: data.results.reverse() },
direction: direction === 'ascending' ? 'descending' : 'ascending',
})
}
}
handlePagination(url) {
this.props.onPagination(url).then(data => this.setState(data))
}
handleRowClick(item) {
const { options } = this.props
this.props.history.push(`${this.props.match.path}/edit`, { item, options })
}
renderField(item, field) {
const { POST } = this.props.options.actions
if (item[field] !== null) {
switch (POST[field].type) {
case 'datetime':
return moment(item[field]).format('YYYY.MM.DD hh:mm')
case 'boolean':
if (item[field] === true) {
return <Icon color='green' name='checkmark' size='large' />
}
return <Icon color='red' name='close' size='large' />
case 'field':
case 'choices':
case 'integer':
case 'string':
default:
return item[field]
}
}
return ''
}
renderItems() {
const { fields } = this.props
const { data } = this.state
return data.results.map(item => (
<Table.Row key={item.url} onClick={() => this.handleRowClick(item)}>
{fields.map(field => (
<Table.Cell key={field}>{this.renderField(item, field)}</Table.Cell>
))}
</Table.Row>
))
}
render() {
if (this.props.options !== undefined) {
const { options: { actions: { POST } }, data, fields } = this.props
const { column, direction } = this.state
return (
<Table sortable selectable singleLine>
<Table.Header>
<Table.Row>
{fields.map(field => (
<Table.HeaderCell
key={field}
sorted={column === field ? direction : null}
onClick={() => this.handleSort(field)}
>
{POST[field].label}
</Table.HeaderCell>
))}
</Table.Row>
</Table.Header>
<Table.Body>
{ this.renderItems() }
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan={fields.length}>
{ data.previous || data.next ?
<Menu floated='right' pagination>
{ data.previous ?
<Menu.Item onClick={() => this.handlePagination(data.previous)}>Previous</Menu.Item> : ''
}
{ data.next ?
<Menu.Item onClick={() => this.handlePagination(data.next)}>Next</Menu.Item> : ''
}
</Menu> : ''
}
</Table.HeaderCell>
</Table.Row>
</Table.Footer>
</Table>
)
}
return <div />
}
}
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
import SemanticReduxFormField from '../components/SemanticReduxFormField'
const DomainForm = ({
pristine,
submitting,
handleSubmit,
ownerOptions,
}) => (
<Form onSubmit={handleSubmit}>
<Field component={Form.Input} type='text' placeholder='Name' name='name' />
<Field component={Form.Input} type='number' placeholder='Ttl' name='ttl' />
<Field
options={ownerOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='owner'
type='text'
search
/>
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
export default reduxForm({ form: 'domain' })(DomainForm)
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
const FirewallForm = ({ pristine, submitting, handleSubmit }) => (
<Form onSubmit={handleSubmit}>
<Field component={Form.Input} type='text' placeholder='Name' name='name' />
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
export default reduxForm({ form: 'domain' })(FirewallForm)
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
import SemanticReduxFormField from '../components/SemanticReduxFormField'
const HostForm = ({
pristine,
submitting,
handleSubmit,
ownerOptions,
}) => (
<Form onSubmit={handleSubmit}>
<Field component={Form.Input} type='text' placeholder='Name' name='name' />
<Field component={Form.Input} type='number' placeholder='Ttl' name='ttl' />
<Field
options={ownerOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='owner'
type='text'
search
/>
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
export default reduxForm({ form: 'host' })(HostForm)
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
import SemanticReduxFormField from '../components/SemanticReduxFormField'
const HostGroupForm = ({
pristine,
submitting,
handleSubmit,
ownerOptions,
}) => (
<Form onSubmit={handleSubmit}>
<Field component={Form.Input} type='text' placeholder='Name' name='name' />
<Field component={Form.TextArea} type='text' label='Description' name='description' />
<Field
options={ownerOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='owner'
type='text'
search
/>
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
export default reduxForm({ form: 'domain' })(HostGroupForm)
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
import SemanticReduxFormField from '../components/SemanticReduxFormField'
const RecordForm = ({
pristine,
submitting,
handleSubmit,
hostOptions,
domainOptions,
ownerOptions,
}) => (
<Form onSubmit={handleSubmit}>
<Field
options={[
{ key: 'A', value: 'A', text: 'A' },
{ key: 'CNAME', value: 'CNAME', text: 'CNAME' },
{ key: 'AAAA', value: 'AAAA', text: 'AAAA' },
{ key: 'MX', value: 'MX', text: 'MX' },
{ key: 'NS', value: 'NS', text: 'NS' },
{ key: 'PTR', value: 'PTR', text: 'PTR' },
{ key: 'TXT', value: 'TXT', text: 'TXT' },
]}
component={SemanticReduxFormField}
as={Form.Select}
name='type'
type='text'
/>
<Field
options={hostOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='host'
type='text'
search
/>
<Field component={Form.Input} type='text' placeholder='Name' name='name' />
<Field
options={domainOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='domain'
type='text'
search
/>
<Field component={Form.Input} type='text' placeholder='Address' name='address' />
<Field component={Form.Input} type='number' placeholder='Ttl' name='ttl' />
<Field component={Form.TextArea} name='description' label='Description' />
<Field
options={ownerOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='owner'
type='text'
search
/>
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
export default reduxForm({ form: 'record' })(RecordForm)
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
const HostForm = ({ pristine, submitting, handleSubmit }) => (
<Form onSubmit={handleSubmit}>
<Field component={Form.Input} type='text' placeholder='Name' name='name' />
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
export default reduxForm({ form: 'domain' })(HostForm)
import React, { Component } from 'react'
import { Input } from 'semantic-ui-react'
export default class SemanticReduxFormField extends Component {
handleChange(value) {
const { input } = this.props
return input.onChange(value)
}
render() {
const { input, meta: { touched, error }, as: As = Input } = this.props
return (
<As
{...input}
value={input.value || []}
{...this.props}
onChange={(e, { value }) => this.handleChange(value)}
error={touched && error}
/>
)
}
}
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
import SemanticReduxFormField from '../components/SemanticReduxFormField'
const SwitchPortForm = ({
pristine,
submitting,
handleSubmit,
taggedVlansOptions,
unTaggedVlanOptions,
}) => (
<Form onSubmit={handleSubmit}>
<Field
options={taggedVlansOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='tagged_vlans'
label='Tagged Vlans'
type='text'
search
/>
<Field
options={unTaggedVlanOptions()}
component={SemanticReduxFormField}
as={Form.Select}
name='untagged_vlan'
label='Untagged Vlan'
type='text'
search
/>
<Field component={Form.TextArea} label='Description' name='description' />
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
export default reduxForm({ form: 'domain' })(SwitchPortForm)
import React from 'react'
import { reduxForm, formValueSelector, Field } from 'redux-form'
import { connect } from 'react-redux'
import { Form, Segment } from 'semantic-ui-react'
import SemanticReduxFormField from './SemanticReduxFormField'
const VlanForm = ({
pristine,
submitting,
handleSubmit,
networkType,
natToOptions,
domainOptions,
ownerOptions,
}) => (
<Form onSubmit={handleSubmit}>
<Field component={Form.Input} type='text' placeholder='Name' name='name' />
<Field component={Form.Input} type='number' placeholder='VID' name='vid' />
<Field
component={SemanticReduxFormField}
as={Form.Select}
type='text'
options={[
{ key: 'public', value: 'public', text: 'Public' },
{ key: 'portforward', value: 'portforward', text: 'Portforward' },
]}
name='network_type'
/>
<Field component={Form.Input} type='text' placeholder='IPv4 address/prefix' name='network4' />
<Field component={Form.Checkbox} name='managed' label='Managed' />
{ networkType === 'portforward' && (
<Segment basic>
<Field
component={Form.Select}
as={Form.Select}
name='snat_to'
options={natToOptions()}
multiple
search
/>
<Field component={Form.Input} type='text' placeholder='Snat IP' name='snat_ip' />
<Field component={Form.TextArea} name='dhcp_pool' />
</Segment>
)}
<Field component={Form.Input} type='text' placeholder='IPv6 address/prefix' name='network6' />
<Field component={Form.Input} type='text' placeholder='IPv6 template' name='ipv6_template' />
<Field component={Form.Input} type='number' placeholder='IPv6 prefixlen/host' name='host_ipv6_prefixlen' />
<Field component={Form.Input} type='text' placeholder='IPv6 template' name='ipv6_template' />
<Field
component={SemanticReduxFormField}
as={Form.Select}
type='text'
name='domain'
options={domainOptions()}
search
/>
<Field component={Form.Input} type='text' placeholder='Reverse Domain' name='reverse_domain' />
<Field component={Form.TextArea} label='Description' name='description' />
<Field component={Form.TextArea} label='Comment' name='comment' />
<Field
component={SemanticReduxFormField}
as={Form.Select}
type='text'
name='owner'
options={ownerOptions()}
search
/>
<Form.Button disabled={pristine || submitting} type='submit'>Submit</Form.Button>
</Form>
)
const formName = 'vlan'
const selector = formValueSelector(formName)
const mapStateToProps = state => ({
networkType: selector(state, 'network_type'),
})
export default connect(mapStateToProps)(reduxForm({ form: formName })(VlanForm))
import React from 'react'
import { reduxForm, Field } from 'redux-form'
import { Form } from 'semantic-ui-react'
import SemanticReduxFormField from '../components/SemanticReduxFormField'
const VlanGroupForm = ({
pristine,
submitting,
handleSubmit,
vlansOptions,
ownerOptions,
}) => (
<Form onSubmit={handleSubmit}>
<Field component={Form.Input} type='text' placeholder='Name' name='name' />