first prefix lookup
This commit is contained in:
parent
5d134e18b9
commit
116fbcb18c
@ -1,105 +1,56 @@
|
||||
|
||||
/*
|
||||
* Prefix lookup actions
|
||||
*/
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
export const SET_QUERY_INPUT_VALUE = "@lookup/SET_QUERY_INPUT_VALUE";
|
||||
export const SET_QUERY_VALUE = "@lookup/SET_QUERY_VALUE";
|
||||
export const SET_QUERY_TYPE = "@lookup/SET_QUERY_TYPE";
|
||||
export const LOAD_RESULTS_REQUEST = '@lookup/LOAD_RESULTS_REQUEST';
|
||||
export const LOAD_RESULTS_SUCCESS = '@lookup/LOAD_RESULTS_SUCCESS';
|
||||
export const LOAD_RESULTS_ERROR = '@lookup/LOAD_RESULTS_ERROR';
|
||||
|
||||
export const RESET = "@lookup/RESET";
|
||||
export const EXECUTE = "@lookup/EXECUTE";
|
||||
|
||||
export const LOOKUP_STARTED = "@lookup/LOOKUP_STARTED";
|
||||
export const LOOKUP_RESULTS = "@lookup/LOOKUP_RESULTS";
|
||||
|
||||
|
||||
/*
|
||||
* Action Creators
|
||||
*/
|
||||
|
||||
export function setQueryInputValue(q) {
|
||||
if(!q) { q = ''; }
|
||||
return {
|
||||
type: SET_QUERY_INPUT_VALUE,
|
||||
payload: {
|
||||
queryInput: q
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setQueryValue(q) {
|
||||
return {
|
||||
type: SET_QUERY_VALUE,
|
||||
payload: {
|
||||
query: q
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setQueryType(type) {
|
||||
return {
|
||||
type: SET_QUERY_TYPE,
|
||||
payload: {
|
||||
queryType: type
|
||||
}
|
||||
// Action creators
|
||||
export function loadResultsRequest(query) {
|
||||
return {
|
||||
type: LOAD_RESULTS_REQUEST,
|
||||
payload: {
|
||||
query: query
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
return {
|
||||
type: RESET
|
||||
export function loadResultsSuccess(query, results) {
|
||||
return {
|
||||
type: LOAD_RESULTS_SUCCESS,
|
||||
payload: {
|
||||
query: query,
|
||||
results: results
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function execute() {
|
||||
return {
|
||||
type: EXECUTE
|
||||
export function loadResultsError(query, error) {
|
||||
return {
|
||||
type: LOAD_RESULTS_ERROR,
|
||||
payload: {
|
||||
query: query,
|
||||
error: error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function loadResults(query) {
|
||||
return (dispatch) => {
|
||||
dispatch(loadResultsRequest(query));
|
||||
|
||||
axios.get(`/api/lookup/prefix?q=${query}`)
|
||||
.then((res) => {
|
||||
dispatch(loadResultsSuccess(query, res.data));
|
||||
})
|
||||
.catch((error) => {
|
||||
dispatch(loadResultsError(query, error));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function lookupStarted(routeserverId, query) {
|
||||
return {
|
||||
type: LOOKUP_STARTED,
|
||||
payload: {
|
||||
routeserverId: routeserverId,
|
||||
query: query
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function lookupResults(routeserverId, query, results) {
|
||||
return {
|
||||
type: LOOKUP_RESULTS,
|
||||
payload: {
|
||||
routeserverId: routeserverId,
|
||||
query: query,
|
||||
results: results
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function routesSearch(routeserverId, q) {
|
||||
return (dispatch) => {
|
||||
dispatch(lookupStarted(routeserverId, q));
|
||||
axios.get(`/birdseye/api/routeserver/${routeserverId}/routes/lookup?q=${q}`)
|
||||
.then((result) => {
|
||||
let routes = result.data.result.routes;
|
||||
dispatch(lookupResults(
|
||||
routeserverId,
|
||||
q,
|
||||
routes
|
||||
));
|
||||
})
|
||||
.catch((error) => {
|
||||
dispatch(lookupResults(
|
||||
routeserverId,
|
||||
q,
|
||||
[]
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,87 +1,43 @@
|
||||
|
||||
/*
|
||||
* Alice (Prefix-)Lookup
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import SearchInput
|
||||
from 'components/search-input'
|
||||
import {loadResults} from './actions'
|
||||
|
||||
import LoadingIndicator
|
||||
from 'components/loading-indicator/small'
|
||||
import LookupResults from './results'
|
||||
import SearchInput from 'components/search-input/debounced'
|
||||
|
||||
import {setQueryInputValue,
|
||||
execute,
|
||||
routesSearch}
|
||||
from './actions'
|
||||
class Lookup extends React.Component {
|
||||
doLookup(q) {
|
||||
this.props.dispatch(loadResults(q));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="lookup-container">
|
||||
<div className="card">
|
||||
<SearchInput
|
||||
placeholder="Search for prefixes by entering a network address"
|
||||
onChange={(e) => this.doLookup(e.target.value)} />
|
||||
</div>
|
||||
|
||||
import QueryDispatcher
|
||||
from './query-dispatcher'
|
||||
|
||||
import LookupResults
|
||||
from './results'
|
||||
|
||||
import {queryParams}
|
||||
from 'components/utils/query'
|
||||
|
||||
|
||||
class LookupView extends React.Component {
|
||||
|
||||
setQuery(q) {
|
||||
this.props.dispatch(
|
||||
setQueryInputValue(q)
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Initial mount: keep query from querystring
|
||||
let params = queryParams();
|
||||
this.props.dispatch(
|
||||
setQueryInputValue(params.q)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
handleFormSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.props.dispatch(execute());
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="routes-lookup">
|
||||
|
||||
<div className="card lookup-header">
|
||||
<form className="form-lookup" onSubmit={(e) => this.handleFormSubmit(e)}>
|
||||
<SearchInput placeholder="Search for routes by entering a network address"
|
||||
name="q"
|
||||
onChange={(e) => this.setQuery(e.target.value)}
|
||||
disabled={this.props.isSearching}
|
||||
value={this.props.queryInput} />
|
||||
<QueryDispatcher />
|
||||
</form>
|
||||
</div>
|
||||
<LoadingIndicator show={this.props.isRunning} />
|
||||
<div className="lookup-results">
|
||||
<LookupResults results={this.props.results}
|
||||
finished={this.props.isFinished} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<LookupResults />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state) => {
|
||||
return {
|
||||
isRunning: state.lookup.queryRunning,
|
||||
isFinished: state.lookup.queryFinished,
|
||||
(state) => {
|
||||
return {
|
||||
isLoading: state.lookup.isLoading,
|
||||
error: state.lookup.error
|
||||
}
|
||||
}
|
||||
)(Lookup);
|
||||
|
||||
queryInput: state.lookup.queryInput,
|
||||
|
||||
results: state.lookup.results,
|
||||
search: state.lookup.search,
|
||||
}
|
||||
}
|
||||
)(LookupView);
|
||||
|
||||
|
@ -1,112 +0,0 @@
|
||||
|
||||
import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import {QUERY_TYPE_UNKNOWN,
|
||||
QUERY_TYPE_PREFIX}
|
||||
from './query'
|
||||
|
||||
import {setQueryType,
|
||||
routesSearch}
|
||||
from './actions'
|
||||
|
||||
|
||||
class QueryDispatcher extends React.Component {
|
||||
/*
|
||||
* Check if given query is a valid network address
|
||||
* with a lame regex if format resembles a network address.
|
||||
*/
|
||||
isNetwork(query) {
|
||||
// IPv4:
|
||||
if (query.match(/(\d+\.)(\d+\.)(\d+\.)(\d+)\/(\d+)/)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// IPv6:
|
||||
if (query.match(/([0-9a-fA-F]+:+)+\/\d+/)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if our query is ready
|
||||
*/
|
||||
isQueryReady() {
|
||||
if (this.props.isRunning ||
|
||||
this.props.queryType == QUERY_TYPE_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
executeQuery() {
|
||||
// Check if we should dispatch this query now
|
||||
for (let rs of this.props.routeservers) {
|
||||
// Debug: limit to rs20
|
||||
if (rs.id != 20) { continue; }
|
||||
switch (this.props.queryType) {
|
||||
case QUERY_TYPE_PREFIX:
|
||||
this.props.dispatch(
|
||||
routesSearch(rs.id, this.props.input)
|
||||
);
|
||||
default:
|
||||
this.props.dispatch(
|
||||
dummySearch(rs.id, this.props.input)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* handle query input, dispatches queryies to
|
||||
* all routeservers.
|
||||
*/
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.isRunning) {
|
||||
return null; // Do nothing while a query is being processed
|
||||
}
|
||||
|
||||
if (nextProps.shouldExecute) {
|
||||
this.executeQuery();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine query type
|
||||
let queryType = QUERY_TYPE_UNKNOWN;
|
||||
if (this.isNetwork(nextProps.input)) {
|
||||
queryType = QUERY_TYPE_PREFIX;
|
||||
}
|
||||
|
||||
this.props.dispatch(setQueryType(queryType));
|
||||
}
|
||||
|
||||
/*
|
||||
* Render anything? Nope.
|
||||
*/
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default connect(
|
||||
(state) => {
|
||||
return {
|
||||
input: state.lookup.queryInput,
|
||||
|
||||
queryType: state.lookup.queryType,
|
||||
|
||||
isRunning: state.lookup.queryRunning,
|
||||
isFinished: state.lookup.queryFinished,
|
||||
|
||||
shouldExecute: state.lookup.queryDispatch,
|
||||
|
||||
routeserversQueue: state.lookup.routeserversQueue,
|
||||
routeservers: state.routeservers.all
|
||||
};
|
||||
}
|
||||
)(QueryDispatcher);
|
||||
|
@ -1,6 +0,0 @@
|
||||
|
||||
|
||||
export const QUERY_TYPE_UNKNOWN = 'unknown';
|
||||
export const QUERY_TYPE_PREFIX = 'prefix';
|
||||
export const QUERY_TYPE_ASN = 'asn';
|
||||
|
@ -1,119 +1,41 @@
|
||||
/*
|
||||
* Prefix Lookup Reducer
|
||||
*/
|
||||
|
||||
import {SET_QUERY_TYPE,
|
||||
SET_QUERY_VALUE,
|
||||
SET_QUERY_INPUT_VALUE,
|
||||
|
||||
LOOKUP_STARTED,
|
||||
LOOKUP_RESULTS,
|
||||
|
||||
RESET,
|
||||
EXECUTE}
|
||||
from './actions'
|
||||
|
||||
import {QUERY_TYPE_UNKNOWN} from './query'
|
||||
import {LOAD_RESULTS_REQUEST,
|
||||
LOAD_RESULTS_SUCCESS,
|
||||
LOAD_RESULTS_ERROR}
|
||||
from './actions'
|
||||
|
||||
const initialState = {
|
||||
results: {},
|
||||
query: '',
|
||||
|
||||
queue: new Set(),
|
||||
results: [],
|
||||
error: null,
|
||||
queryDurationMs: 0.0,
|
||||
|
||||
queryInput: "",
|
||||
|
||||
query: "",
|
||||
queryType: QUERY_TYPE_UNKNOWN,
|
||||
|
||||
queryRunning: false,
|
||||
queryFinished: false,
|
||||
queryDispatch: false,
|
||||
};
|
||||
|
||||
|
||||
// Action handlers:
|
||||
|
||||
// Handle lookup start
|
||||
function _lookupStarted(state, lookup) {
|
||||
// Enqueue Routeserver
|
||||
let queue = new Set(state.queue);
|
||||
queue.add(lookup.routeserverId);
|
||||
|
||||
// Clear results
|
||||
let results = Object.assign({}, state.results, {
|
||||
[lookup.routeserverId]: []
|
||||
});
|
||||
|
||||
// Make state update
|
||||
return {
|
||||
queue: queue,
|
||||
results: results,
|
||||
|
||||
queryRunning: true,
|
||||
queryFinished: false,
|
||||
};
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
|
||||
// Handle a finished lookup
|
||||
function _lookupResults(state, lookup) {
|
||||
// Dequeue routeserver
|
||||
let queue = new Set(state.queue);
|
||||
let currentQueueSize = queue.size;
|
||||
queue.delete(lookup.routeserverId);
|
||||
|
||||
// Any routeservers left in the queue?
|
||||
let isRunning = true;
|
||||
if (queue.size == 0) {
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
let isFinished = false;
|
||||
if (queue.size == 0 && currentQueueSize > 0) {
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
|
||||
// Update results set
|
||||
let results = Object.assign({}, state.results, {
|
||||
[lookup.routeserverId]: lookup.results,
|
||||
});
|
||||
|
||||
// Make state update
|
||||
return {
|
||||
results: results,
|
||||
queue: queue,
|
||||
queryRunning: isRunning,
|
||||
queryFinished: isFinished
|
||||
}
|
||||
}
|
||||
|
||||
// Reducer
|
||||
export default function reducer(state=initialState, action) {
|
||||
let payload = action.payload;
|
||||
switch(action.type) {
|
||||
// Setup
|
||||
case SET_QUERY_TYPE:
|
||||
case SET_QUERY_VALUE:
|
||||
case SET_QUERY_INPUT_VALUE:
|
||||
return Object.assign({}, state, payload);
|
||||
|
||||
// Search
|
||||
case LOOKUP_STARTED:
|
||||
// Update state on lookup started
|
||||
return Object.assign({}, state, _lookupStarted(state, payload), {
|
||||
queryDispatch: false,
|
||||
});
|
||||
|
||||
case LOOKUP_RESULTS:
|
||||
// Update state when we receive results
|
||||
return Object.assign({}, state, _lookupResults(state, payload));
|
||||
|
||||
case EXECUTE:
|
||||
return Object.assign({}, state, {
|
||||
queryDispatch: true,
|
||||
});
|
||||
|
||||
case RESET:
|
||||
return Object.assign({}, state, initialState);
|
||||
}
|
||||
return state;
|
||||
switch(action.type) {
|
||||
case LOAD_RESULTS_REQUEST:
|
||||
return Object.assign({}, state, initialState, {
|
||||
isLoading: true,
|
||||
});
|
||||
case LOAD_RESULTS_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
isLoading: false,
|
||||
queryDurationMs: action.payload.results.query_duration_ms,
|
||||
results: action.payload.results.routes,
|
||||
error: null,
|
||||
});
|
||||
case LOAD_RESULTS_ERROR:
|
||||
return Object.assign({}, state, initialState, {
|
||||
error: action.payload.error,
|
||||
});
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,61 +1,100 @@
|
||||
|
||||
import _ from 'underscore'
|
||||
|
||||
import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
export default class LookupResults extends React.Component {
|
||||
import FilterReason
|
||||
from 'components/routeservers/large-communities/filter-reason'
|
||||
|
||||
_countResults() {
|
||||
let count = 0;
|
||||
for (let rs in this.props.results) {
|
||||
let set = this.props.results[rs];
|
||||
count += set.length;
|
||||
}
|
||||
return count;
|
||||
import NoexportReason
|
||||
from 'components/routeservers/large-communities/noexport-reason'
|
||||
|
||||
|
||||
class ResultsTable extends React.Component {
|
||||
|
||||
render() {
|
||||
if (this.props.routes.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_resultSetEmpty() {
|
||||
let resultCount = this._countResults();
|
||||
if (this.props.finished && resultCount == 0){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const routes = this.props.routes.map((route) => (
|
||||
<tr key={route.id + route.routeserver.id}>
|
||||
<td>{route.network}</td>
|
||||
<td>{route.bgp.as_path.join(" ")}</td>
|
||||
<td>{route.gateway}</td>
|
||||
<td>{route.neighbour.description}</td>
|
||||
<td>{route.neighbour.asn}</td>
|
||||
<td>{route.routeserver.name}</td>
|
||||
</tr>
|
||||
));
|
||||
|
||||
_awaitingResults() {
|
||||
let resultCount = this._countResults();
|
||||
if (!this.props.finished && resultCount == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* No Results */
|
||||
renderEmpty() {
|
||||
return (
|
||||
<div className="card card-results card-no-results">
|
||||
The prefix could not be found.
|
||||
Did you specify a network address?
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this._resultSetEmpty()) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
|
||||
if (this._awaitingResults) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Render Results table
|
||||
return (
|
||||
<div className="card card-results">
|
||||
ROUTES INCOMING!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="card">
|
||||
{this.props.header}
|
||||
<table className="table table-striped table-routes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Network</th>
|
||||
<th>AS Path</th>
|
||||
<th>Gateway</th>
|
||||
<th>Neighbour</th>
|
||||
<th>ASN</th>
|
||||
<th>RS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{routes}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class LookupResults extends React.Component {
|
||||
|
||||
render() {
|
||||
const mkHeader = (color, action) => (
|
||||
<p style={{"color": color, "textTransform": "uppercase"}}>
|
||||
Routes {action}
|
||||
</p>
|
||||
);
|
||||
|
||||
const filtdHeader = mkHeader("orange", "filtered");
|
||||
const recvdHeader = mkHeader("green", "accepted");
|
||||
const noexHeader = mkHeader("red", "not exported");
|
||||
|
||||
let filteredRoutes = this.props.routes.filtered;
|
||||
let importedRoutes = this.props.routes.imported;
|
||||
|
||||
return (
|
||||
<div className="lookup-results">
|
||||
<ResultsTable header={filtdHeader} routes={filteredRoutes} />
|
||||
<ResultsTable header={recvdHeader} routes={importedRoutes} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function selectRoutes(routes, state) {
|
||||
return _.where(routes, {state: state});
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state) => {
|
||||
let routes = state.lookup.results;
|
||||
let filteredRoutes = selectRoutes(routes, 'filtered');
|
||||
let importedRoutes = selectRoutes(routes, 'imported');
|
||||
return {
|
||||
routes: {
|
||||
filtered: filteredRoutes,
|
||||
imported: importedRoutes
|
||||
}
|
||||
}
|
||||
}
|
||||
)(LookupResults);
|
||||
|
||||
|
@ -1,191 +0,0 @@
|
||||
import _ from 'underscore'
|
||||
|
||||
import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
|
||||
import {loadRouteserverRoutes, loadRouteserverRoutesFiltered} from '../actions'
|
||||
import {showBgpAttributes} from './bgp-attributes-modal-actions'
|
||||
|
||||
import LoadingIndicator
|
||||
from 'components/loading-indicator/small'
|
||||
|
||||
|
||||
class FilterReason extends React.Component {
|
||||
render() {
|
||||
const route = this.props.route;
|
||||
|
||||
if (!this.props.reject_reasons || !route || !route.bgp ||
|
||||
!route.bgp.large_communities) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const reason = route.bgp.large_communities.filter(elem =>
|
||||
elem[0] == this.props.asn && elem[1] == this.props.reject_id
|
||||
);
|
||||
if (!reason.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <p className="reject-reason">{this.props.reject_reasons[reason[0][2]]}</p>;
|
||||
}
|
||||
}
|
||||
|
||||
FilterReason = connect(
|
||||
state => {
|
||||
return {
|
||||
reject_reasons: state.routeservers.reject_reasons,
|
||||
asn: state.routeservers.asn,
|
||||
reject_id: state.routeservers.reject_id,
|
||||
}
|
||||
}
|
||||
)(FilterReason);
|
||||
|
||||
|
||||
function _filteredRoutes(routes, filter) {
|
||||
let filtered = [];
|
||||
if(filter == "") {
|
||||
return routes; // nothing to do here
|
||||
}
|
||||
|
||||
filter = filter.toLowerCase();
|
||||
|
||||
// Filter protocols
|
||||
filtered = _.filter(routes, (r) => {
|
||||
return (r.network.toLowerCase().indexOf(filter) != -1 ||
|
||||
r.gateway.toLowerCase().indexOf(filter) != -1 ||
|
||||
r.interface.toLowerCase().indexOf(filter) != -1);
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
class RoutesTable extends React.Component {
|
||||
showAttributesModal(route) {
|
||||
this.props.dispatch(
|
||||
showBgpAttributes(route)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let routes = this.props.routes;
|
||||
const routes_columns = this.props.routes_columns;
|
||||
|
||||
routes = _filteredRoutes(routes, this.props.filter);
|
||||
if (!routes || !routes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const _lookup = (r, path) => {
|
||||
const split = path.split(".").reduce((acc, elem) => acc[elem], r);
|
||||
|
||||
if (Array.isArray(split)) {
|
||||
return split.join(" ");
|
||||
}
|
||||
return split;
|
||||
}
|
||||
|
||||
let routesView = routes.map((r) => {
|
||||
return (
|
||||
<tr key={r.network} onClick={() => this.showAttributesModal(r)}>
|
||||
<td>{r.network}{this.props.display_filter && <FilterReason route={r}/>}</td>
|
||||
{Object.keys(routes_columns).map(col => <td key={col}>{_lookup(r, col)}</td>)}
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
{this.props.header}
|
||||
<table className="table table-striped table-routes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Network</th>
|
||||
{Object.values(routes_columns).map(col => <th key={col}>{col}</th>)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{routesView}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RoutesTable = connect(
|
||||
(state) => {
|
||||
return {
|
||||
filter: state.routeservers.routesFilterValue,
|
||||
reject_reasons: state.routeservers.reject_reasons,
|
||||
routes_columns: state.config.routes_columns,
|
||||
}
|
||||
}
|
||||
)(RoutesTable);
|
||||
|
||||
|
||||
class RoutesTables extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.dispatch(
|
||||
loadRouteserverRoutes(this.props.routeserverId, this.props.protocolId)
|
||||
);
|
||||
this.props.dispatch(
|
||||
loadRouteserverRoutesFiltered(this.props.routeserverId,
|
||||
this.props.protocolId)
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if(this.props.isLoading) {
|
||||
return (
|
||||
<LoadingIndicator />
|
||||
);
|
||||
}
|
||||
|
||||
const routes = this.props.routes[this.props.protocolId];
|
||||
const filtered = this.props.filtered[this.props.protocolId] || [];
|
||||
|
||||
if((!routes || routes.length == 0) &&
|
||||
(!filtered || filtered.length == 0)) {
|
||||
return(
|
||||
<p className="help-block">
|
||||
No routes matched your filter.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const received = routes.filter(r => filtered.indexOf(r) < 0);
|
||||
|
||||
const mkHeader = (color, action) => (
|
||||
<p style={{"color": color, "textTransform": "uppercase"}}>
|
||||
Routes {action}
|
||||
</p>
|
||||
);
|
||||
|
||||
const filtdHeader = mkHeader("orange", "filtered");
|
||||
const recvdHeader = mkHeader("green", "accepted");
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RoutesTable header={filtdHeader} routes={filtered} display_filter={true}/>
|
||||
<RoutesTable header={recvdHeader} routes={received} display_filter={false}/>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default connect(
|
||||
(state) => {
|
||||
return {
|
||||
isLoading: state.routeservers.routesAreLoading,
|
||||
routes: state.routeservers.routes,
|
||||
filtered: state.routeservers.filtered,
|
||||
}
|
||||
}
|
||||
)(RoutesTables);
|
23
client/components/search-input/debounced.jsx
Normal file
23
client/components/search-input/debounced.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react'
|
||||
import DebounceInput from 'react-debounce-input'
|
||||
|
||||
|
||||
export default class DebouncedSearchInput extends React.Component {
|
||||
render() {
|
||||
return(
|
||||
<div className="input-group">
|
||||
<span className="input-group-addon">
|
||||
<i className="fa fa-search"></i>
|
||||
</span>
|
||||
<DebounceInput
|
||||
minLength={2}
|
||||
debounceTimeout={250}
|
||||
className="form-control"
|
||||
{...this.props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,14 +16,12 @@ export default class Welcome extends React.Component {
|
||||
<p>Your friendly bird looking glass</p>
|
||||
</div>
|
||||
|
||||
<div className="col-md-8">
|
||||
<Lookup />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
/*
|
||||
<div className="col-md-8">
|
||||
<Lookup />
|
||||
</div>
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user