├── .gitignore
├── README.md
├── build
├── bundle.js
└── main.css
├── client
├── Data.jsx
├── Login.jsx
├── Map.jsx
├── MapContainer.jsx
├── Signup.jsx
├── app.jsx
├── asset
│ ├── LogoSample_ByTailorBrands.jpg
│ ├── logo.JPG
│ └── tabLogo.jpg
├── countries-50m.json
├── countries.js
├── index.js
├── list.jsx
└── styles.scss
├── index.html
├── package-lock.json
├── package.json
├── server
├── db
│ ├── countryCode.csv
│ └── database.js
└── server.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # scratchMap
2 |
3 | #### scratchMap:
4 |
5 | 1. allows users to create account, mark the countries they have been to simply by clicking on the country on the map
6 |
7 | 2. turns clicked coutry gray and add the country name to the visited country list and also increment the country count
8 |
9 | 3. allows users to easily undo an incorrect addition by clicking on the country one more time
10 |
11 | 4. makes it is easy to remember and visualize visited countries
12 |
13 | 5. allows zoom in & out by scrolling up & down, and pan across the map by clicking and dragging
14 |
15 | 6. allows users to hover over coutries to see the name of the country
16 |
17 |
18 | 
19 |
20 |
21 | ### Running your own local version
22 |
23 | Clone this repo: `git clone https://github.com/nydkim/scratchMap.git`
24 |
25 | Install dependencies: `npm i`
26 |
27 | Run the app: `npm run`
28 |
29 |
30 | ### Tools used:
31 |
32 | * D3.js
33 | * React
34 | * React Router
35 | * Express
36 | * PostgreSQL
37 |
38 |
--------------------------------------------------------------------------------
/build/main.css:
--------------------------------------------------------------------------------
1 | body{font-family:'Permanent Marker', cursive;background-color:#f5f4f4;color:#5c6364;margin:0}path{fill:#e5c37b;stroke:#ac6737;stroke-opacity:0.3}img{margin-right:20px;height:148;width:329}input{font-family:'Permanent Marker', cursive;border:grey 1px solid;border-radius:3px;margin-right:2px}button{font-family:'Permanent Marker', cursive}svg{background-color:#f5f4f4}#topBar{display:flex;width:100%}:visited{color:#979797}.visited{fill:#5c6364}.country:hover{fill:red}#list{position:fixed;right:0;top:0;width:15%;background-color:rgba(230,200,119,0.6);height:100vh;margin:0;overflow-y:scroll}
2 |
3 |
--------------------------------------------------------------------------------
/client/Data.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | class Data extends Component {
5 | componentDidMount() {
6 | fetch('/initial')
7 | .then((res) => res.json())
8 | .then((data) => {
9 | console.log('in data', data);
10 | return this.props.loggedIn(data);
11 | });
12 | }
13 |
14 | signout() {
15 | window.location.href = '/';
16 | }
17 |
18 | render() {
19 | let greeting = '';
20 | if (this.props.name) {
21 | const name = this.props.name.charAt(0).toUpperCase() + this.props.name.slice(1);
22 | greeting = `${name}'s `;
23 | }
24 | return (
25 |
26 |
{greeting}Scratch Map
27 |
28 |
29 | );
30 | }
31 | }
32 |
33 | export default Data;
34 |
--------------------------------------------------------------------------------
/client/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link, Redirect } from 'react-router-dom';
3 |
4 | class Login extends Component {
5 | constructor() {
6 | super();
7 | this.state = {
8 | isLoggedIn: false,
9 | };
10 | this.login = this.login.bind(this);
11 | }
12 |
13 | login() {
14 | const username = document.getElementById('loginId').value;
15 | const password = document.getElementById('loginPw').value;
16 | fetch('/login', {
17 | method: 'POST',
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | },
21 | body: JSON.stringify({ username, password }),
22 | })
23 | .then((res) => res.json())
24 | .then((res) => {
25 | if (res.login === true) {
26 | return this.setState({
27 | isLoggedIn: true,
28 | });
29 | }
30 | });
31 | }
32 |
33 | render() {
34 | if (this.state.isLoggedIn) {
35 | console.log('redirecting to data');
36 | return ;
37 | }
38 | return (
39 |
40 |
Welcome to Scratch Map!
41 |
42 |
43 |
44 |
45 | Sign up
46 |
47 | );
48 | }
49 | }
50 |
51 | export default Login;
52 |
--------------------------------------------------------------------------------
/client/Map.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { select, json, geoPath, geoNaturalEarth1, zoom, event, geoOrthographic } from 'd3';
3 | import { feature } from 'topojson';
4 |
5 | class Map extends Component {
6 | constructor() {
7 | super();
8 | this.state;
9 | }
10 |
11 | componentDidMount() {
12 | const projection = geoNaturalEarth1();
13 | const pathGenerator = geoPath().projection(projection);
14 |
15 | // const rect = React.createElement('rect', { id: 'li1', width:"300", height:"100" });
16 | const svg = select(this.refs.vector);
17 | // const svg2 = select('svg')
18 | const g = svg.append('g');
19 |
20 | svg.call(
21 | zoom().on('zoom', () => {
22 | g.attr('transform', event.transform);
23 | })
24 | );
25 |
26 | json('https://cdn.jsdelivr.net/npm/world-atlas@2/countries-50m.json').then((data) => {
27 | const countries = feature(data, data.objects.countries);
28 | // console.log(data);
29 | g.selectAll('path')
30 | .data(countries.features)
31 | .enter()
32 | .append('path')
33 | .attr('class', 'country')
34 | .attr('d', (d) => pathGenerator(d))
35 | .on('click', (d) => {
36 | console.log('clicked', d.properties.name, d.id);
37 | return this.props.clickHandle(d.id);
38 | })
39 | .attr('id', (d) => d.id)
40 | .append('title')
41 | .text((d) => d.properties.name)
42 | .attr('class', 'tooltip');
43 | });
44 | }
45 |
46 | render() {
47 | // return this is map
;
48 | return ;
49 | }
50 | }
51 |
52 | export default Map;
53 |
--------------------------------------------------------------------------------
/client/MapContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Map from './Map.jsx';
3 |
4 | class MapContainer extends Component {
5 | constructor() {
6 | super();
7 | this.state = {
8 | _id: 0,
9 | name: '',
10 | countries: [],
11 | };
12 | }
13 |
14 | // componentDidMount() {
15 | // fetch('/getcountries')
16 | // .then(res => res.json())
17 | // .then(data => {console.log(data)})
18 | // }
19 |
20 |
21 |
22 | render() {
23 | return (
24 |
25 |
this is inside map container
26 |
27 |
28 | );
29 | }
30 |
31 | }
32 |
33 | export default MapContainer;
--------------------------------------------------------------------------------
/client/Signup.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link, Redirect } from 'react-router-dom';
3 |
4 | class Signup extends Component {
5 | constructor() {
6 | super();
7 | this.state = {
8 | isLoggedIn: false,
9 | };
10 |
11 | this.signup = this.signup.bind(this);
12 | }
13 |
14 | signup() {
15 | console.log('in signup');
16 | const name = document.getElementById('signupName').value;
17 | const id = document.getElementById('signupId').value;
18 | const pw = document.getElementById('signupPw').value;
19 | fetch('/signup', {
20 | method: 'POST',
21 | headers: {
22 | 'Content-Type': 'application/json',
23 | },
24 | body: JSON.stringify({ name, id, pw }),
25 | })
26 | .then((res) => res.json())
27 | .then((data) => {
28 | console.log('data back', data);
29 | if (data.login) {
30 | return this.setState({
31 | isLoggedIn: true,
32 | });
33 | }
34 | });
35 | }
36 |
37 | render() {
38 | if (this.state.isLoggedIn) {
39 | console.log('redirecting to data');
40 | return ;
41 | }
42 |
43 | return (
44 |
45 |
Sign up to save your scratch map!
46 |
47 |
48 |
49 |
50 |
51 | Back to log in
52 |
53 | );
54 | }
55 | }
56 |
57 | export default Signup;
58 |
59 | //method="POST" action="/signup"
60 |
--------------------------------------------------------------------------------
/client/app.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { render } from 'react-dom';
3 | import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
4 | import Map from './Map.jsx';
5 | import Login from './Login.jsx';
6 | import Signup from './Signup.jsx';
7 | import Data from './Data.jsx';
8 | import List from './List.jsx';
9 | import './styles.scss';
10 |
11 | class App extends Component {
12 | constructor() {
13 | super();
14 | this.state = { name: '', id: '', countries: [] };
15 | this.clickHandle = this.clickHandle.bind(this);
16 | this.loggedIn = this.loggedIn.bind(this);
17 | }
18 |
19 | clickHandle(code) {
20 | const countries = this.state.countries.slice();
21 | if (!this.state.countries.includes(code)) {
22 | countries.push(code);
23 | this.setState({ ...this.state, countries });
24 | document.getElementById(`${code}`).setAttribute('class', 'visited');
25 | if (this.state.id) {
26 | fetch('/add', {
27 | method: 'POST',
28 | headers: {
29 | 'Content-Type': 'application/json',
30 | },
31 | body: JSON.stringify({ id: this.state.id, code }),
32 | });
33 | }
34 | } else {
35 | const index = this.state.countries.indexOf(code);
36 | countries.splice(index, 1);
37 | this.setState({ ...this.state, countries });
38 | document.getElementById(`${code}`).classList.remove('visited');
39 | if (this.state.id) {
40 | fetch('/del', {
41 | method: 'POST',
42 | headers: {
43 | 'Content-Type': 'application/json',
44 | },
45 | body: JSON.stringify({ id: this.state.id, code }),
46 | });
47 | }
48 | }
49 | }
50 |
51 | loggedIn(data) {
52 | console.log(data);
53 | this.setState({ ...this.state, ...data });
54 | }
55 |
56 | componentDidUpdate() {
57 | console.log('updated!', this.state);
58 | this.state.countries.forEach((code) => {
59 | document.getElementById(`${code}`).setAttribute('class', 'visited');
60 | });
61 | }
62 |
63 | componentDidUpdate() {
64 | this.state.countries.forEach((code) => {
65 | console.log('update', `${code}`);
66 | document.getElementById(`${code}`).setAttribute('class', 'visited');
67 | });
68 | }
69 |
70 | render() {
71 | return (
72 |
73 |
74 |

75 |
76 |
77 |
78 | (
82 |
83 | )}
84 | />
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | );
93 | }
94 | }
95 |
96 | export default App;
97 |
--------------------------------------------------------------------------------
/client/asset/LogoSample_ByTailorBrands.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nydkim/scratchMap/595adf5101ae9b2e9bb3faf55e5ff93a8b534d73/client/asset/LogoSample_ByTailorBrands.jpg
--------------------------------------------------------------------------------
/client/asset/logo.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nydkim/scratchMap/595adf5101ae9b2e9bb3faf55e5ff93a8b534d73/client/asset/logo.JPG
--------------------------------------------------------------------------------
/client/asset/tabLogo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nydkim/scratchMap/595adf5101ae9b2e9bb3faf55e5ff93a8b534d73/client/asset/tabLogo.jpg
--------------------------------------------------------------------------------
/client/countries.js:
--------------------------------------------------------------------------------
1 | const pairs = {
2 | '004': 'Afghanistan',
3 | '008': 'Albania',
4 | '010': 'Antarctica',
5 | '012': 'Algeria',
6 | '016': 'American Samoa',
7 | '020': 'Andorra',
8 | '024': 'Angola',
9 | '028': 'Antigua and Barbuda',
10 | '031': 'Azerbaijan',
11 | '032': 'Argentina',
12 | '036': 'Australia',
13 | '040': 'Austria',
14 | '044': 'Bahamas',
15 | '048': 'Bahrain',
16 | '050': 'Bangladesh',
17 | '051': 'Armenia',
18 | '052': 'Barbados',
19 | '056': 'Belgium',
20 | '060': 'Bermuda',
21 | '064': 'Bhutan',
22 | '068': 'Bolivia (Plurinational State of)',
23 | '070': 'Bosnia and Herzegovina',
24 | '072': 'Botswana',
25 | '074': 'Bouvet Island',
26 | '076': 'Brazil',
27 | '084': 'Belize',
28 | '086': 'British Indian Ocean Territory',
29 | '090': 'Solomon Islands',
30 | '092': 'Virgin Islands (British)',
31 | '096': 'Brunei Darussalam',
32 | '100': 'Bulgaria',
33 | '104': 'Myanmar',
34 | '108': 'Burundi',
35 | '112': 'Belarus',
36 | '116': 'Cambodia',
37 | '120': 'Cameroon',
38 | '124': 'Canada',
39 | '132': 'Cabo Verde',
40 | '136': 'Cayman Islands',
41 | '140': 'Central African Republic',
42 | '144': 'Sri Lanka',
43 | '148': 'Chad',
44 | '152': 'Chile',
45 | '156': 'China',
46 | '158': 'Taiwan, Province of China',
47 | '162': 'Christmas Island',
48 | '166': 'Cocos (Keeling) Islands',
49 | '170': 'Colombia',
50 | '174': 'Comoros',
51 | '175': 'Mayotte',
52 | '178': 'Congo',
53 | '180': 'Congo, Democratic Republic of the',
54 | '184': 'Cook Islands',
55 | '188': 'Costa Rica',
56 | '191': 'Croatia',
57 | '192': 'Cuba',
58 | '196': 'Cyprus',
59 | '203': 'Czechia',
60 | '204': 'Benin',
61 | '208': 'Denmark',
62 | '212': 'Dominica',
63 | '214': 'Dominican Republic',
64 | '218': 'Ecuador',
65 | '222': 'El Salvador',
66 | '226': 'Equatorial Guinea',
67 | '231': 'Ethiopia',
68 | '232': 'Eritrea',
69 | '233': 'Estonia',
70 | '234': 'Faroe Islands',
71 | '238': 'Falkland Islands (Malvinas)',
72 | '239': 'South Georgia and the South Sandwich Islands',
73 | '242': 'Fiji',
74 | '246': 'Finland',
75 | '248': 'Åland Islands',
76 | '250': 'France',
77 | '254': 'French Guiana',
78 | '258': 'French Polynesia',
79 | '260': 'French Southern Territories',
80 | '262': 'Djibouti',
81 | '266': 'Gabon',
82 | '268': 'Georgia',
83 | '270': 'Gambia',
84 | '275': 'Palestine, State of',
85 | '276': 'Germany',
86 | '288': 'Ghana',
87 | '292': 'Gibraltar',
88 | '296': 'Kiribati',
89 | '300': 'Greece',
90 | '304': 'Greenland',
91 | '308': 'Grenada',
92 | '312': 'Guadeloupe',
93 | '316': 'Guam',
94 | '320': 'Guatemala',
95 | '324': 'Guinea',
96 | '328': 'Guyana',
97 | '332': 'Haiti',
98 | '334': 'Heard Island and McDonald Islands',
99 | '336': 'Holy See',
100 | '340': 'Honduras',
101 | '344': 'Hong Kong',
102 | '348': 'Hungary',
103 | '352': 'Iceland',
104 | '356': 'India',
105 | '360': 'Indonesia',
106 | '364': 'Iran (Islamic Republic of)',
107 | '368': 'Iraq',
108 | '372': 'Ireland',
109 | '376': 'Israel',
110 | '380': 'Italy',
111 | '384': "Côte d'Ivoire",
112 | '388': 'Jamaica',
113 | '392': 'Japan',
114 | '398': 'Kazakhstan',
115 | '400': 'Jordan',
116 | '404': 'Kenya',
117 | '408': "Korea (Democratic People's Republic of)",
118 | '410': 'Korea, Republic of',
119 | '414': 'Kuwait',
120 | '417': 'Kyrgyzstan',
121 | '418': "Lao People's Democratic Republic",
122 | '422': 'Lebanon',
123 | '426': 'Lesotho',
124 | '428': 'Latvia',
125 | '430': 'Liberia',
126 | '434': 'Libya',
127 | '438': 'Liechtenstein',
128 | '440': 'Lithuania',
129 | '442': 'Luxembourg',
130 | '446': 'Macao',
131 | '450': 'Madagascar',
132 | '454': 'Malawi',
133 | '458': 'Malaysia',
134 | '462': 'Maldives',
135 | '466': 'Mali',
136 | '470': 'Malta',
137 | '474': 'Martinique',
138 | '478': 'Mauritania',
139 | '480': 'Mauritius',
140 | '484': 'Mexico',
141 | '492': 'Monaco',
142 | '496': 'Mongolia',
143 | '498': 'Moldova, Republic of',
144 | '499': 'Montenegro',
145 | '500': 'Montserrat',
146 | '504': 'Morocco',
147 | '508': 'Mozambique',
148 | '512': 'Oman',
149 | '516': 'Namibia',
150 | '520': 'Nauru',
151 | '524': 'Nepal',
152 | '528': 'Netherlands',
153 | '531': 'Curaçao',
154 | '533': 'Aruba',
155 | '534': 'Sint Maarten (Dutch part)',
156 | '535': 'Bonaire, Sint Eustatius and Saba',
157 | '540': 'New Caledonia',
158 | '548': 'Vanuatu',
159 | '554': 'New Zealand',
160 | '558': 'Nicaragua',
161 | '562': 'Niger',
162 | '566': 'Nigeria',
163 | '570': 'Niue',
164 | '574': 'Norfolk Island',
165 | '578': 'Norway',
166 | '580': 'Northern Mariana Islands',
167 | '581': 'United States Minor Outlying Islands',
168 | '583': 'Micronesia (Federated States of)',
169 | '584': 'Marshall Islands',
170 | '585': 'Palau',
171 | '586': 'Pakistan',
172 | '591': 'Panama',
173 | '598': 'Papua New Guinea',
174 | '600': 'Paraguay',
175 | '604': 'Peru',
176 | '608': 'Philippines',
177 | '612': 'Pitcairn',
178 | '616': 'Poland',
179 | '620': 'Portugal',
180 | '624': 'Guinea-Bissau',
181 | '626': 'Timor-Leste',
182 | '630': 'Puerto Rico',
183 | '634': 'Qatar',
184 | '638': 'Réunion',
185 | '642': 'Romania',
186 | '643': 'Russian Federation',
187 | '646': 'Rwanda',
188 | '652': 'Saint Barthélemy',
189 | '654': 'Saint Helena, Ascension and Tristan da Cunha',
190 | '659': 'Saint Kitts and Nevis',
191 | '660': 'Anguilla',
192 | '662': 'Saint Lucia',
193 | '663': 'Saint Martin (French part)',
194 | '666': 'Saint Pierre and Miquelon',
195 | '670': 'Saint Vincent and the Grenadines',
196 | '674': 'San Marino',
197 | '678': 'Sao Tome and Principe',
198 | '682': 'Saudi Arabia',
199 | '686': 'Senegal',
200 | '688': 'Serbia',
201 | '690': 'Seychelles',
202 | '694': 'Sierra Leone',
203 | '702': 'Singapore',
204 | '703': 'Slovakia',
205 | '704': 'Viet Nam',
206 | '705': 'Slovenia',
207 | '706': 'Somalia',
208 | '710': 'South Africa',
209 | '716': 'Zimbabwe',
210 | '724': 'Spain',
211 | '728': 'South Sudan',
212 | '729': 'Sudan',
213 | '732': 'Western Sahara',
214 | '740': 'Suriname',
215 | '744': 'Svalbard and Jan Mayen',
216 | '748': 'Eswatini',
217 | '752': 'Sweden',
218 | '756': 'Switzerland',
219 | '760': 'Syrian Arab Republic',
220 | '762': 'Tajikistan',
221 | '764': 'Thailand',
222 | '768': 'Togo',
223 | '772': 'Tokelau',
224 | '776': 'Tonga',
225 | '780': 'Trinidad and Tobago',
226 | '784': 'United Arab Emirates',
227 | '788': 'Tunisia',
228 | '792': 'Turkey',
229 | '795': 'Turkmenistan',
230 | '796': 'Turks and Caicos Islands',
231 | '798': 'Tuvalu',
232 | '800': 'Uganda',
233 | '804': 'Ukraine',
234 | '807': 'North Macedonia',
235 | '818': 'Egypt',
236 | '826': 'United Kingdom of Great Britain and Northern Ireland',
237 | '831': 'Guernsey',
238 | '832': 'Jersey',
239 | '833': 'Isle of Man',
240 | '834': 'Tanzania, United Republic of',
241 | '840': 'United States of America',
242 | '850': 'Virgin Islands (U.S.)',
243 | '854': 'Burkina Faso',
244 | '858': 'Uruguay',
245 | '860': 'Uzbekistan',
246 | '862': 'Venezuela (Bolivarian Republic of)',
247 | '876': 'Wallis and Futuna',
248 | '882': 'Samoa',
249 | '887': 'Yemen',
250 | '894': 'Zambia',
251 | };
252 |
253 | export default pairs;
254 |
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import App from './app.jsx';
4 |
5 | render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/client/list.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import pairs from './countries.js';
3 |
4 | class List extends Component {
5 | render() {
6 | const list = this.props.countries.map((code, index) => {
7 | return {pairs[code]} ;
8 | });
9 |
10 | return (
11 |
12 | countries visited: {this.props.countries.length}
13 | {list}
14 |
15 | );
16 | }
17 | }
18 |
19 | export default List;
20 |
--------------------------------------------------------------------------------
/client/styles.scss:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Permanent Marker', cursive;
3 | background-color: #f5f4f4;
4 | color: #5c6364;
5 | margin: 0;
6 | }
7 |
8 | path {
9 | fill: #e5c37b;
10 | stroke: #ac6737;
11 | stroke-opacity: 0.3;
12 | // transition: fill .05s ease-in;
13 | }
14 |
15 | img {
16 | margin-right: 20px;
17 | height: 148;
18 | width: 329;
19 | }
20 |
21 | input {
22 | font-family: 'Permanent Marker', cursive;
23 | border: grey 1px solid;
24 | border-radius: 3px;
25 | margin-right: 2px;
26 | }
27 |
28 | button {
29 | font-family: 'Permanent Marker', cursive;
30 | }
31 |
32 | svg {
33 | background-color: #f5f4f4;
34 | }
35 |
36 | #topBar {
37 | display: flex;
38 | width: 100%;
39 | }
40 |
41 | :visited {
42 | color: #979797;
43 | }
44 |
45 | .visited {
46 | fill: #5c6364;
47 | }
48 |
49 | .country:hover {
50 | fill: red;
51 | }
52 |
53 | #list {
54 | position: fixed;
55 | right: 0;
56 | top: 0;
57 | width: 15%;
58 | background-color: rgba(230, 200, 119, 0.6);
59 |
60 | height: 100vh;
61 | margin: 0;
62 | overflow-y: scroll;
63 | }
64 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Personalized Scratch Map
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "cross-env NODE_ENV=production nodemon server/server.js",
8 | "build": "cross-env NODE_ENV=production webpack",
9 | "dev": "cross-env NODE_ENV=development concurrently \"webpack-dev-server --open\" \"nodemon server/server.js\"",
10 | "gulp-prod": "node_modules/.bin/gulp prod",
11 | "gulp-dev": "node_modules/.bin/gulp dev"
12 | },
13 | "nodemonConfig": {
14 | "ignore": [
15 | "build",
16 | "client"
17 | ]
18 | },
19 | "author": "CodesmithLLC https://github.com/CodesmithLLC ",
20 | "license": "ISC",
21 | "dependencies": {
22 | "browserify": "^10.2.4",
23 | "cookie-parser": "^1.4.5",
24 | "d3": "^5.16.0",
25 | "express": "^4.12.3",
26 | "gulp": "^3.9.0",
27 | "history": "^5.0.0",
28 | "pg": "^8.2.1",
29 | "prop-types": "^15.6.1",
30 | "react": "^16.2.0",
31 | "react-calendar": "^3.1.0",
32 | "react-dom": "^16.3.1",
33 | "react-router-dom": "^5.2.0",
34 | "topojson": "^3.0.2"
35 | },
36 | "devDependencies": {
37 | "@babel/core": "^7.10.3",
38 | "@babel/preset-env": "^7.10.3",
39 | "@babel/preset-react": "^7.10.1",
40 | "babel-loader": "^8.1.0",
41 | "concurrently": "^4.1.2",
42 | "cross-env": "^5.2.1",
43 | "css-loader": "^3.6.0",
44 | "extract-text-webpack-plugin": "^3.0.2",
45 | "isomorphic-fetch": "^2.2.1",
46 | "mini-css-extract-plugin": "^0.9.0",
47 | "node-sass": "^4.14.1",
48 | "nodemon": "^1.18.9",
49 | "sass": "^1.26.8",
50 | "sass-loader": "^8.0.2",
51 | "style-loader": "^1.2.1",
52 | "w": "^1.1.0",
53 | "webpack": "^4.43.0",
54 | "webpack-cli": "^3.3.12",
55 | "webpack-dev-server": "^3.11.0"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/server/db/countryCode.csv:
--------------------------------------------------------------------------------
1 | Code,Country name
2 | 4,Afghanistan
3 | 8,Albania
4 | 10,Antarctica
5 | 12,Algeria
6 | 16,American Samoa
7 | 20,Andorra
8 | 24,Angola
9 | 28,Antigua and Barbuda
10 | 31,Azerbaijan
11 | 32,Argentina
12 | 36,Australia
13 | 40,Austria
14 | 44,Bahamas
15 | 48,Bahrain
16 | 50,Bangladesh
17 | 51,Armenia
18 | 52,Barbados
19 | 56,Belgium
20 | 60,Bermuda
21 | 64,Bhutan
22 | 68,Bolivia (Plurinational State of)
23 | 70,Bosnia and Herzegovina
24 | 72,Botswana
25 | 74,Bouvet Island
26 | 76,Brazil
27 | 84,Belize
28 | 86,British Indian Ocean Territory
29 | 90,Solomon Islands
30 | 92,Virgin Islands (British)
31 | 96,Brunei Darussalam
32 | 100,Bulgaria
33 | 104,Myanmar
34 | 108,Burundi
35 | 112,Belarus
36 | 116,Cambodia
37 | 120,Cameroon
38 | 124,Canada
39 | 132,Cabo Verde
40 | 136,Cayman Islands
41 | 140,Central African Republic
42 | 144,Sri Lanka
43 | 148,Chad
44 | 152,Chile
45 | 156,China
46 | 158,"Taiwan, Province of China"
47 | 162,Christmas Island
48 | 166,Cocos (Keeling) Islands
49 | 170,Colombia
50 | 174,Comoros
51 | 175,Mayotte
52 | 178,Congo
53 | 180,"Congo, Democratic Republic of the"
54 | 184,Cook Islands
55 | 188,Costa Rica
56 | 191,Croatia
57 | 192,Cuba
58 | 196,Cyprus
59 | 203,Czechia
60 | 204,Benin
61 | 208,Denmark
62 | 212,Dominica
63 | 214,Dominican Republic
64 | 218,Ecuador
65 | 222,El Salvador
66 | 226,Equatorial Guinea
67 | 231,Ethiopia
68 | 232,Eritrea
69 | 233,Estonia
70 | 234,Faroe Islands
71 | 238,Falkland Islands (Malvinas)
72 | 239,South Georgia and the South Sandwich Islands
73 | 242,Fiji
74 | 246,Finland
75 | 248,Åland Islands
76 | 250,France
77 | 254,French Guiana
78 | 258,French Polynesia
79 | 260,French Southern Territories
80 | 262,Djibouti
81 | 266,Gabon
82 | 268,Georgia
83 | 270,Gambia
84 | 275,"Palestine, State of"
85 | 276,Germany
86 | 288,Ghana
87 | 292,Gibraltar
88 | 296,Kiribati
89 | 300,Greece
90 | 304,Greenland
91 | 308,Grenada
92 | 312,Guadeloupe
93 | 316,Guam
94 | 320,Guatemala
95 | 324,Guinea
96 | 328,Guyana
97 | 332,Haiti
98 | 334,Heard Island and McDonald Islands
99 | 336,Holy See
100 | 340,Honduras
101 | 344,Hong Kong
102 | 348,Hungary
103 | 352,Iceland
104 | 356,India
105 | 360,Indonesia
106 | 364,Iran (Islamic Republic of)
107 | 368,Iraq
108 | 372,Ireland
109 | 376,Israel
110 | 380,Italy
111 | 384,Côte d'Ivoire
112 | 388,Jamaica
113 | 392,Japan
114 | 398,Kazakhstan
115 | 400,Jordan
116 | 404,Kenya
117 | 408,Korea (Democratic People's Republic of)
118 | 410,"Korea, Republic of"
119 | 414,Kuwait
120 | 417,Kyrgyzstan
121 | 418,Lao People's Democratic Republic
122 | 422,Lebanon
123 | 426,Lesotho
124 | 428,Latvia
125 | 430,Liberia
126 | 434,Libya
127 | 438,Liechtenstein
128 | 440,Lithuania
129 | 442,Luxembourg
130 | 446,Macao
131 | 450,Madagascar
132 | 454,Malawi
133 | 458,Malaysia
134 | 462,Maldives
135 | 466,Mali
136 | 470,Malta
137 | 474,Martinique
138 | 478,Mauritania
139 | 480,Mauritius
140 | 484,Mexico
141 | 492,Monaco
142 | 496,Mongolia
143 | 498,"Moldova, Republic of"
144 | 499,Montenegro
145 | 500,Montserrat
146 | 504,Morocco
147 | 508,Mozambique
148 | 512,Oman
149 | 516,Namibia
150 | 520,Nauru
151 | 524,Nepal
152 | 528,Netherlands
153 | 531,Curaçao
154 | 533,Aruba
155 | 534,Sint Maarten (Dutch part)
156 | 535,"Bonaire, Sint Eustatius and Saba"
157 | 540,New Caledonia
158 | 548,Vanuatu
159 | 554,New Zealand
160 | 558,Nicaragua
161 | 562,Niger
162 | 566,Nigeria
163 | 570,Niue
164 | 574,Norfolk Island
165 | 578,Norway
166 | 580,Northern Mariana Islands
167 | 581,United States Minor Outlying Islands
168 | 583,Micronesia (Federated States of)
169 | 584,Marshall Islands
170 | 585,Palau
171 | 586,Pakistan
172 | 591,Panama
173 | 598,Papua New Guinea
174 | 600,Paraguay
175 | 604,Peru
176 | 608,Philippines
177 | 612,Pitcairn
178 | 616,Poland
179 | 620,Portugal
180 | 624,Guinea-Bissau
181 | 626,Timor-Leste
182 | 630,Puerto Rico
183 | 634,Qatar
184 | 638,Réunion
185 | 642,Romania
186 | 643,Russian Federation
187 | 646,Rwanda
188 | 652,Saint Barthélemy
189 | 654,"Saint Helena, Ascension and Tristan da Cunha"
190 | 659,Saint Kitts and Nevis
191 | 660,Anguilla
192 | 662,Saint Lucia
193 | 663,Saint Martin (French part)
194 | 666,Saint Pierre and Miquelon
195 | 670,Saint Vincent and the Grenadines
196 | 674,San Marino
197 | 678,Sao Tome and Principe
198 | 682,Saudi Arabia
199 | 686,Senegal
200 | 688,Serbia
201 | 690,Seychelles
202 | 694,Sierra Leone
203 | 702,Singapore
204 | 703,Slovakia
205 | 704,Viet Nam
206 | 705,Slovenia
207 | 706,Somalia
208 | 710,South Africa
209 | 716,Zimbabwe
210 | 724,Spain
211 | 728,South Sudan
212 | 729,Sudan
213 | 732,Western Sahara
214 | 740,Suriname
215 | 744,Svalbard and Jan Mayen
216 | 748,Eswatini
217 | 752,Sweden
218 | 756,Switzerland
219 | 760,Syrian Arab Republic
220 | 762,Tajikistan
221 | 764,Thailand
222 | 768,Togo
223 | 772,Tokelau
224 | 776,Tonga
225 | 780,Trinidad and Tobago
226 | 784,United Arab Emirates
227 | 788,Tunisia
228 | 792,Turkey
229 | 795,Turkmenistan
230 | 796,Turks and Caicos Islands
231 | 798,Tuvalu
232 | 800,Uganda
233 | 804,Ukraine
234 | 807,North Macedonia
235 | 818,Egypt
236 | 826,United Kingdom of Great Britain and Northern Ireland
237 | 831,Guernsey
238 | 832,Jersey
239 | 833,Isle of Man
240 | 834,"Tanzania, United Republic of"
241 | 840,United States of America
242 | 850,Virgin Islands (U.S.)
243 | 854,Burkina Faso
244 | 858,Uruguay
245 | 860,Uzbekistan
246 | 862,Venezuela (Bolivarian Republic of)
247 | 876,Wallis and Futuna
248 | 882,Samoa
249 | 887,Yemen
250 | 894,Zambia
251 |
--------------------------------------------------------------------------------
/server/db/database.js:
--------------------------------------------------------------------------------
1 | const { Pool } = require('pg');
2 | const password = '';
3 | const connectionString = `postgressql:postgres:${password}@localhost:5432/scratchmap`;
4 |
5 | const pool = new Pool({
6 | connectionString: connectionString,
7 | });
8 |
9 | module.exports = {
10 | query: (text, params, callback) => {
11 | console.log('executed query', text);
12 | return pool.query(text, params, callback);
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const path = require('path');
4 | const db = require('./db/database.js');
5 | const cookieParser = require('cookie-parser');
6 |
7 | app.use(express.json());
8 | app.use(cookieParser());
9 |
10 | app.use('/asset', express.static(path.join(__dirname, '../client/asset')));
11 |
12 | app.use('/build', () => {
13 | // console.log('inside build');
14 | express.static(path.join(__dirname, '../build'));
15 | });
16 |
17 | app.post('/login', (req, res) => {
18 | const sqlQuery = `select _id, name from people where _id='${req.body.username}' and password='${req.body.password}';`;
19 | db.query(sqlQuery, (err, response) => {
20 | // console.log(response.rows[0]);
21 | if (response.rows[0]) res.cookie('id', response.rows[0]._id).json({ login: true });
22 | else res.status(400).json({ login: false });
23 | });
24 | });
25 |
26 | app.post('/signup', (req, res) => {
27 | // console.log('inside signup');
28 | const sqlQuery = `INSERT INTO people (_id, name, password) VALUES ('${req.body.id}', '${req.body.name}', '${req.body.pw}');`;
29 | db.query(sqlQuery, (err, response) => {
30 | if (err) {
31 | // console.log('in error');
32 | return res.status(400).json({ login: false });
33 | }
34 | // console.log('signup response**', response);
35 | return res.cookie('id', req.body.id).json({ login: true });
36 | });
37 | });
38 |
39 | app.get('/initial', async (req, res) => {
40 | // console.log('in data', req.cookies.id);
41 | let name;
42 | await db.query(`select name from people where _id='${req.cookies.id}'`).then((response) => {
43 | // console.log('first response', response.rows[0].name);
44 | name = response.rows[0].name;
45 | });
46 | // console.log('name', name);
47 | const sqlQuery = `SELECT code FROM visited WHERE _id='${req.cookies.id}';`;
48 | let countries;
49 | await db.query(sqlQuery).then((response) => {
50 | // console.log('countries response**', response.rows);
51 | countries = response.rows.map((row) => {
52 | if (row.code.length === 1) return '00' + row.code;
53 | if (row.code.length === 2) return '0' + row.code;
54 | return row.code;
55 | });
56 | });
57 | console.log('initial fetching done!', name, countries);
58 | res.json({ id: req.cookies.id, name, countries });
59 | });
60 |
61 | app.post('/add', (req, res) => {
62 | console.log(req.body);
63 | const sqlQuery = `INSERT INTO visited (_id, code) VALUES ('${req.body.id}', '${req.body.code}')`;
64 | console.log(sqlQuery);
65 | db.query(sqlQuery, (err, response) => {
66 | if (err) return res.status(400);
67 | else {
68 | console.log('visited added!');
69 | }
70 | });
71 | });
72 |
73 | app.post('/del', (req, res) => {
74 | console.log(req.body);
75 | const sqlQuery = `DELETE FROM visited WHERE _id='${req.body.id}' and code='${req.body.code}'`;
76 | db.query(sqlQuery, (err, response) => {
77 | if (err) return res.status(400);
78 | else {
79 | console.log('visited deleted!');
80 | }
81 | });
82 | });
83 |
84 | app.get('*', (req, res) => {
85 | console.log('inside the *');
86 | res.sendFile(path.join(__dirname, '../index.html'));
87 | });
88 |
89 | app.listen(3000, () => {
90 | console.log('listetning on 3000');
91 | }); //listens on port 3000 -> http://localhost:3000/
92 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
2 |
3 | module.exports = () => {
4 | // console.log(process.env.NODE_ENV);
5 | return {
6 | mode: process.env.NODE_ENV,
7 | entry: './client/index.js',
8 | output: {
9 | path: `${__dirname}/build`,
10 | filename: 'bundle.js',
11 | },
12 |
13 | devServer: {
14 | proxy: {
15 | '/': 'http://localhost:3000',
16 | },
17 | publicPath: 'http://localhost:8080/build/',
18 | },
19 |
20 | plugins: [new MiniCssExtractPlugin()],
21 |
22 | module: {
23 | rules: [
24 | {
25 | test: /\.jsx?/,
26 | exclude: /(node_modules)/,
27 | use: {
28 | loader: 'babel-loader',
29 | options: {
30 | presets: ['@babel/preset-env', '@babel/preset-react'],
31 | },
32 | },
33 | },
34 | {
35 | test: /\.s[ac]ss$/i,
36 | use: [
37 | // Creates `style` nodes from JS strings
38 | MiniCssExtractPlugin.loader,
39 | // Translates CSS into CommonJS
40 | 'css-loader',
41 | // Compiles Sass to CSS
42 | 'sass-loader',
43 | ],
44 | },
45 | ],
46 | },
47 | };
48 | };
49 |
--------------------------------------------------------------------------------