Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
"start": "webpack-dev-server --mode development --open --history-api-fallback"
},
"dependencies": {
"bootstrap": "^4.6.0",
"promise-retry": "^2.0.1",
"prop-types": "^15.6.2",
"react": "^16.5.0",
"react-bootstrap": "^1.4.3",
"react-dom": "^16.5.0",
"react-redux": "^5.0.7",
"redux": "^4.0.0",
"shortid": "^2.2.16",
"styled-components": "^3.4.6"
},
"devDependencies": {
Expand All @@ -35,6 +39,7 @@
"eslint-plugin-react": "^7.11.1",
"html-webpack-plugin": "^3.2.0",
"react-hot-loader": "^4.3.6",
"redux-devtools": "^3.7.0",
"webpack": "^4.18.0",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.8"
Expand Down
243 changes: 243 additions & 0 deletions src/components/app/Countries.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import React, { Component } from "react";
import getCountry from "../../api/country";
import { Button, Form, Spinner, Jumbotron } from "react-bootstrap";
import shortid from "shortid";
import promiseRetry from "promise-retry";
import CsTable from "./Table";
import styled from "styled-components";
import {
addCountry,
selectCountry,
updateCountry,
deleteCountry,
} from "../../store/actions";
import { connect } from "react-redux";

class Countries extends Component {
constructor(props) {
super(props);

this.state = {
headerData: ["", "Country", "Population"],
population: "",
countryDdData: [],
country: "AFGHANISTAN",
sort: "ascending",
data: [],
selected: null,
isLoading: false,
};
}

componentDidMount() {
promiseRetry((retry, number) => {
if (number !== 1) {
this.setState({
isLoading: true,
});
}

return getCountry()
.then((data) => {
this.setState({
countryDdData: data,
isLoading: false,
});
})
.catch(retry);
});
}

save = (event) => {
event.preventDefault();
const { population, country, data } = this.state;
const newData = {
population: population,
country: country,
id: data.length + 1,
};

this.props.addCountry(newData);
};

changeHandler = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};

delete = (e) => {
this.props.deleteCountry(parseInt(e));
};

select = (e) => {
this.props.selectCountry(parseInt(e));
};

componentWillReceiveProps(nextProps) {
if (nextProps.countries.selected !== this.state.selected) {
const { population, country } = nextProps.countries.selected;
const newState = {
...this.state,
population,
country,
selected: nextProps.countries.selected,
};
this.setState(newState);
}

if (nextProps.countries.data !== this.state.data) {
const { data } = nextProps.countries;
const newState = {
...this.state,
data,
selected: null,
};
this.setState(newState);
}
}

update = () => {
const { population, country, selected } = this.state;
this.props.updateCountry({
data: { population, country },
id: selected.id,
});
};

cancel = () => {
this.setState({
selected: null,
country: "",
population: "",
});
};

sortByPopulation = () => {
const { data, sort } = this.state;
let sortData, sortVal;

if (sort === "ascending") {
sortData = data.sort((a, b) => {
return a.population - b.population;
});
sortVal = "descending";
} else {
sortData = data.sort((a, b) => {
return b.population - a.population;
});
sortVal = "ascending";
}

this.setState({
data: sortData,
sort: sortVal,
});
};

render() {
const { countryDdData, isLoading, sort } = this.state;

const ButtonGroup = styled.div`
padding: 10px 15px;
`;
const Margin = styled.span`
margin: 10px 15px;
`;

return (
<React.Fragment>
<Jumbotron>
<div className="container">
<form onSubmit={this.save}>
<div className="row">
<div className="col-md-3">
<Form.Group>
<Form.Label>Select Country</Form.Label>
<Form.Control
as="select"
value={this.state.country}
name="country"
onChange={this.changeHandler}
>
{countryDdData.length
? countryDdData.map((c) => (
<option key={shortid.generate()}>{c.name}</option>
))
: null}
</Form.Control>
{isLoading ? (
<Form.Text className="text-muted">
Fetching Data...
<Spinner animation="border" role="status" size="sm">
<span className="sr-only">Loading...</span>
</Spinner>
</Form.Text>
) : null}
</Form.Group>
</div>
<div className="col-md-3">
<Form.Group>
<Form.Label>Population</Form.Label>
<Form.Control
type="number"
placeholder="Enter Population"
name="population"
onChange={this.changeHandler}
value={this.state.population}
/>
</Form.Group>
</div>
<div className="col-md-6">
{!this.state.selected ? (
<ButtonGroup>
<Button variant="primary" type="submit">
Enter Data
</Button>
<Button variant="primary" onClick={this.sortByPopulation}>
Sort Population by {sort}
</Button>
</ButtonGroup>
) : null}
{this.state.selected ? (
<ButtonGroup>
<Button variant="primary" onClick={this.update}>
Update
</Button>
</ButtonGroup>
) : null}
</div>
</div>
</form>
<Margin>
<h1>List of Countries</h1>
<Margin>
<CsTable
striped
bordered
hover
data={this.state}
selectEmitted={this.select}
cancelEmitted={this.cancel}
deleteEmitted={this.delete}
></CsTable>
</Margin>
</Margin>
</div>
</Jumbotron>
</React.Fragment>
);
}
}

export default connect(
(state) => ({
countries: state.countries,
}),
{
addCountry,
selectCountry,
updateCountry,
deleteCountry,
}
)(Countries);
81 changes: 81 additions & 0 deletions src/components/app/Table.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { Component } from "react";
import { Button, Table } from "react-bootstrap";
import shortid from "shortid";
import { connect } from "react-redux";

class CsTable extends Component {
constructor(props) {
super(props);
}

selectEvent = (e) => {
this.props.selectEmitted(e.target.id);
};

cancelEvent = (e) => {
this.props.cancelEmitted(e.target.id);
};

deleteEvent = (e) => {
this.props.deleteEmitted(e.target.id);
};

tableBody() {
const { data, selected } = this.props.data;
return data && data.length
? data.map((d) => (
<tr key={shortid.generate()}>
<td>
{!selected ? (
<Button variant="primary" id={d.id} onClick={this.selectEvent}>
Select
</Button>
) : null}
{selected && selected.id === d.id ? (
<Button
variant="secondary"
id={d.id}
onClick={this.cancelEvent}
>
Cancel
</Button>
) : null}
{!selected ? (
<Button variant="danger" id={d.id} onClick={this.deleteEvent}>
Delete
</Button>
) : null}
</td>
<td key={shortid.generate()}>{d.country}</td>
<td key={shortid.generate()}>{d.population}</td>
</tr>
))
: null;
}

tableHeader() {
const { headerData } = this.props.data;
return headerData.length
? headerData.map((d) => <th key={shortid.generate()}>{d}</th>)
: null;
}

renderTable() {
return (
<React.Fragment>
<Table striped bordered hover size="sm">
<thead>
<tr>{this.tableHeader()}</tr>
</thead>
<tbody>{this.tableBody()}</tbody>
</Table>
</React.Fragment>
);
}

render() {
return this.renderTable();
}
}

export default connect(null, {})(CsTable);
23 changes: 7 additions & 16 deletions src/components/app/index.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@

import React from 'react';
import { Provider } from 'react-redux';
import Theme from '../theme';
import createStore from '../../store';
import Layout from '../layout';
import H1 from './H1';

import React from "react";
import { Provider } from "react-redux";
import createStore from "../../store";
import Countries from "./Countries";
import rootReducer from "../../store/reducers";
// create the redux store
const store = createStore();
const store = createStore(rootReducer);

export default () => (
<Provider store={store}>
<Theme>
<Layout>
<H1>
Good luck!
</H1>
</Layout>
</Theme>
<Countries />
</Provider>
);
7 changes: 6 additions & 1 deletion src/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Country Populations</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous" />
</head>

<body>
<div id="app"></div>
</body>
</html>

</html>
Loading