Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/user feedback #8

Merged
merged 12 commits into from
Nov 13, 2017
14 changes: 14 additions & 0 deletions server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,20 @@ app.post('/logout', (req, res) => {
res.sendStatus(200);
});

app.post('/logout', (req, res) => {
logger.info('Logging user out');
const token = req.body.token;
try {
// Remove user from storage
delete sessions[token];
logger.info('Successful logout');
res.sendStatus(200);
} catch (e) {
logger.error(`Unable to log user out: ${e}`);
res.sendStatus(500);
}
});

app.post('/api', (req, res) => {
// re route api requests with API key
const method = req.body.method;
Expand Down
7 changes: 5 additions & 2 deletions src/actions/LoginActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,20 @@ export function logout() {
return (dispatch) => {
dispatch(sendingRequest(true));
auth.logout(sessionStorage.accessToken, (success) => {
dispatch(sendingRequest(false));
if (success) {
dispatch(sendingRequest(false));
dispatch(setAuthState(false));
localStorage.clear();
sessionStorage.clear();
browserHistory.push('/');
// This needs to go at the end, or else if we logout whilst on a page
// that uses the redux store, an error will occur before the user
// is redirected to '/'.
dispatch(resetState(undefined));
} else {
dispatch(setErrorMessage(errorMessages.GENERAL_ERROR));
sessionStorage.clear();
browserHistory.push('/');
dispatch(resetState(undefined));
}
});
};
Expand Down
90 changes: 90 additions & 0 deletions src/components/ChildRefTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import PropTypes from 'prop-types';
import ReactTable from 'react-table';
import ErrorModal from '../components/ErrorModal';
import industryCodeDescription from '../utils/siccode';
import config from '../config/api-urls';
import { formatData } from '../utils/helperMethods';

const { REROUTE_URL, API_VERSION, BUSINESS_ENDPOINT } = config;

class ChildRefTable extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
data: [],
error: false,
errorMessage: '',
};
this.closeModal = this.closeModal.bind(this);
this.fetchData = this.fetchData.bind(this);
}
componentDidMount() {
this.fetchData(this.props.row);
}
fetchData(row) {
fetch(`${REROUTE_URL}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': sessionStorage.getItem('accessToken'),
},
body: JSON.stringify({
method: 'GET',
endpoint: `${API_VERSION}/${BUSINESS_ENDPOINT}/${row.original.id}`,
}),
})
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error(`Error: ${response.status} ${response.statusText}`);
})
.then(data => this.setState({ data: formatData(data), isLoading: false }))
.catch(error => this.setState({ errorMessage: error.message, error: true, isLoading: false }));
}
closeModal() {
this.setState({ error: false, errorMessage: '' });
}
render() {
return (
<div style={{ padding: '20px' }}>
<ReactTable
data={this.state.data}
columns={[
{
Header: 'Company Number',
accessor: 'companyNo',
},
{
Header: 'VAT References',
accessor: 'vatRefs',
},
{
Header: 'PAYE References',
accessor: 'payeRefs',
},
]}
defaultPageSize={1}
loading={this.state.isLoading}
className="-striped -highlight"
showPaginationTop={false}
showPaginationBottom={false}
/>
<h3>Industry Code: {industryCodeDescription[this.state.data.industryCode]}</h3>
<ErrorModal
show={this.state.error}
message={this.state.errorMessage}
close={this.closeModal}
/>
</div>
);
}
}

ChildRefTable.propTypes = {
row: PropTypes.object.isRequired,
};

export default ChildRefTable;
2 changes: 1 addition & 1 deletion src/components/MatchForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class MatchForm extends React.Component {
<SelectInput value={this.props.initialValues.LegalStatus} label="Legal Status" id="LegalStatus" onChange={this.props.onChange} bands={legalStatusBands} /><br />
<SelectInput value={this.props.initialValues.Turnover} label="Turnover" id="Turnover" onChange={this.props.onChange} bands={turnoverBands} /><br />
<SelectInput value={this.props.initialValues.TradingStatus} label="Trading Status" id="TradingStatus" onChange={this.props.onChange} bands={tradingStatusBands} /><br />
<TextInput label="Post Code" id="PostCode" onChange={this.props.onChange} /><br />
<TextInput value={this.props.initialValues.PostCode} label="Post Code" id="PostCode" onChange={this.props.onChange} /><br />
<Button id="loginButton" size="wide" text="Search" onClick={!this.props.currentlySending ? this.props.onSubmit : null} ariaLabel="Login Button" type="submit" loading={this.props.currentlySending} />
&nbsp;
<Button id="clearButton" size="wide" text="Clear" onClick={this.props.onClear} ariaLabel="Clear Button" type="reset" />
Expand Down
88 changes: 88 additions & 0 deletions src/components/ResultsTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react';
import PropTypes from 'prop-types';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import SummaryTable from '../components/SummaryTable';
import ChildRefTable from '../components/ChildRefTable';

const ResultsTable = ({ results, showFilter, showPagination, defaultPageSize }) => {
return (
<div id="react-table">
<ReactTable
showPagination={showPagination}
data={results}
filterable={showFilter}
columns={[
{
Header: 'UBRN',
id: 'id',
accessor: d => d.id,
},
{
Header: 'Business Name',
id: 'businessName',
accessor: d => d.businessName,
},
{
Header: 'PostCode',
id: 'postCode',
accessor: d => d.postCode,
},
{
Header: 'Industry Code',
id: 'industryCode',
accessor: d => d.industryCode,
},
{
Header: 'Legal Status',
id: 'legalStatus',
accessor: d => d.legalStatus,
},
{
Header: 'Trading Status',
id: 'tradingStatus',
accessor: d => d.tradingStatus,
},
{
Header: 'Turnover',
id: 'turnover',
accessor: d => d.turnover,
},
{
Header: 'Employment Bands',
id: 'employmentBands',
accessor: d => d.employmentBands,
},
]}
defaultPageSize={defaultPageSize}
className="-striped -highlight"
SubComponent={row => {
return (
<ChildRefTable row={row} />
);
}}
/>
<br /><br />
<SummaryTable
title="Useful Information"
items={[
{ key: 'Number of results', value: results.length },
{ key: 'Results capped at 10,000', value: (results.length === 10000) ? 'true' : 'false' },
]}
/>
</div>
);
};

ResultsTable.defaultProps = {
defaultPageSize: 10,
};

ResultsTable.propTypes = {
results: PropTypes.array.isRequired,
showFilter: PropTypes.bool.isRequired,
showPagination: PropTypes.bool.isRequired,
defaultPageSize: PropTypes.number,
};

export default ResultsTable;
33 changes: 33 additions & 0 deletions src/components/SummaryTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';

const SummaryTable = ({ title, items }) => {
return (
<div className="sdc-isolation">
<div className="summary">
<h2 className="summary__title saturn" id="">{title}</h2>
{
items.map((item) => {
return (
<div className="summary__items">
<div className="summary__question" id="">
{item.key}
</div>
<div className="summary__answer">
<div className="summary__answer-text" id="">{item.value}</div>
</div>
</div>
);
})
}
</div>
</div>
);
};

SummaryTable.propTypes = {
title: PropTypes.string.isRequired,
items: PropTypes.array.isRequired,
};

export default SummaryTable;
20 changes: 9 additions & 11 deletions src/components/UBRNForm.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import Loader from 'halogen/PulseLoader';
import { Button } from 'registers-react-library';
import TextInput from './TextInput';

class UBRNForm extends React.Component {
render() {
const spinner = (<Loader color="#FFFFFF" size="10px" margin="0px" />);
const icon = (<span className="icon icon-search--light"></span>);
const buttonContent = (this.props.currentlySending) ? spinner : icon;
return (
<form className="col-wrap search__form" action="/search" method="get">
<label className="search__label col col--md-5 col--lg-6" htmlFor="nav-search">UBRN</label>
<input ref={ip => (this.myInput = ip)} placeholder="Enter UBRN to search..." autoFocus onChange={this.props.onChange} type="search" autoComplete="on" className="search__input col col--md-21 col--lg-32" id="nav-search" name="q" value={this.props.value} />
<button onClick={!this.props.currentlySending ? this.props.onSubmit : null} aria-label="Search UBRN button" type="submit" className={`search__button col--md-3 col--lg-3 ${this.props.valid}`} id="nav-search-submit">
{buttonContent}
</button>
<form>
<TextInput ref={ip => (this.childTextInput = ip)} value={this.props.value} label="UBRN" id="UBRN" autoFocus onChange={this.props.onChange} /><br />
<Button id="loginButton" size="wide" text="Search" onClick={!this.props.currentlySending ? this.props.onSubmit : null} ariaLabel="Login Button" type="submit" loading={this.props.currentlySending} />
&nbsp;
<Button id="clearButton" size="wide" text="Clear" onClick={this.props.onClear} ariaLabel="Clear Button" type="reset" />
<br /><br />
</form>
);
}
Expand All @@ -23,7 +21,7 @@ UBRNForm.propTypes = {
currentlySending: PropTypes.bool.isRequired,
onSubmit: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
valid: PropTypes.string.isRequired,
onClear: PropTypes.func.isRequired,
value: PropTypes.string.isRequired,
};

Expand Down
1 change: 1 addition & 0 deletions src/config/api-urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const apiUrls = {
AUTH_URL: 'http://localhost:3001',
REROUTE_URL: 'http://localhost:3001/api',
SEARCH_ENDPOINT: 'search/',
BUSINESS_ENDPOINT: 'business',
API_VERSION: 'v1',
};

Expand Down
25 changes: 25 additions & 0 deletions src/utils/helperMethods.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,28 @@ export function getLegalStatusDescription(status: string) {
return 'Not Allocated';
}
}

export function maxSize(...args) {
return args.reduce((a, b) => (a > b ? a.length : b.length), 0);
}

export function formatData(business: {}) {
const largestRef = maxSize(business.vatRefs, business.payeRefs);
const formattedData = [];
for (let i = 0; i <= largestRef; i += 1) {
if (i === 0) {
formattedData.push({
companyNo: business.companyNo,
vatRefs: business.vatRefs[i],
payeRefs: business.payeRefs[i],
});
} else {
formattedData.push({
companyNo: '',
vatRefs: business.vatRefs[i],
payeRefs: business.payeRefs[i],
});
}
}
return formattedData;
}
Loading