├── .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 | ![ezgif-4-b5e6887b9806](https://user-images.githubusercontent.com/63560710/85976336-5b8f8c80-b9a8-11ea-8b95-cda8009e6eb2.gif) 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 | --------------------------------------------------------------------------------