Skip to content

Commit fab2511

Browse files
committed
Add team support
1 parent 869c7f0 commit fab2511

File tree

9 files changed

+3029
-89
lines changed

9 files changed

+3029
-89
lines changed

.babelrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,12 @@
22
"presets": [
33
"@babel/preset-env",
44
"@babel/preset-react"
5+
],
6+
"plugins": [
7+
["@babel/plugin-transform-runtime",
8+
{
9+
"regenerator": true
10+
}
11+
]
512
]
613
}

dist/main.js

Lines changed: 2352 additions & 41 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 426 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,27 @@
1414
"author": "",
1515
"license": "ISC",
1616
"dependencies": {
17+
"@babel/runtime": "^7.9.2",
1718
"@fortawesome/fontawesome-svg-core": "^1.2.17",
1819
"@fortawesome/free-solid-svg-icons": "^5.8.1",
1920
"@fortawesome/react-fontawesome": "^0.1.4",
21+
"axios": "^0.19.2",
22+
"babel-runtime": "^6.26.0",
2023
"bootstrap": "^4.3.1",
2124
"moment": "^2.22.2",
2225
"moment-timezone": "^0.5.28",
2326
"mongodb": "^3.3.2",
24-
"react-select": "^3.0.4"
27+
"react-router-dom": "^5.1.2",
28+
"react-select": "^3.0.4",
29+
"reactstrap": "^8.4.1"
2530
},
2631
"devDependencies": {
2732
"@babel/core": "^7.4.0",
33+
"@babel/plugin-transform-runtime": "^7.9.0",
2834
"@babel/preset-env": "^7.4.2",
2935
"@babel/preset-react": "^7.0.0",
3036
"babel-loader": "^8.0.5",
37+
"babel-plugin-transform-runtime": "^6.23.0",
3138
"css-loader": "^2.1.1",
3239
"gh-pages": "^2.2.0",
3340
"html-loader": "^0.5.5",

src/app.js

Lines changed: 153 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import React, { Component, Fragment } from "react"
2+
import { Redirect, Link } from 'react-router-dom';
3+
import axios from 'axios'
24

35
import 'bootstrap/dist/css/bootstrap.min.css'
6+
import { Input, Button, Popover, PopoverHeader, PopoverBody } from 'reactstrap';
7+
48
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
59
import { faTimes } from '@fortawesome/free-solid-svg-icons'
610
import './styles/app.css'
@@ -10,23 +14,41 @@ import AsyncSelect from 'react-select/async'
1014
import moment from 'moment-timezone'
1115

1216
import countriesData from '../data/country.json'
13-
14-
// import countriesV2 from '../timezone-data/output.json'
1517
import countriesV2 from '../data/output_15k.json'
1618

17-
const setLocal = (key, value) => {
18-
localStorage.setItem(key, JSON.stringify(value))
19-
}
19+
import { setLocal, makeUnique, isValidName, isNameValid } from './helpers'
2020

21-
const makeUnique = (items) => {
22-
const arr = []
23-
return items.filter((i) => {
24-
if(!arr.includes(i.country)) {
25-
arr.push(i.country)
26-
return true
21+
const saveData = async (teamName, data) => {
22+
const options = {
23+
method: 'POST',
24+
url: `https://f23le7ruh6.execute-api.ap-south-1.amazonaws.com/dev/write-doc?name=${teamName}`,
25+
data: data
2726
}
28-
return false
29-
})
27+
28+
return axios(options).then((res) => {
29+
console.log(res.data)
30+
return true
31+
}).catch((e) => {
32+
console.log('Error in saving ', e)
33+
return false
34+
})
35+
}
36+
37+
const readLists = async (teamName) => {
38+
return axios.get(`https://f23le7ruh6.execute-api.ap-south-1.amazonaws.com/dev/read-doc?name=${teamName}`)
39+
.then((res) => {
40+
if (res.status === 200) {
41+
if(res.data && res.data.response && res.data.response.zones) {
42+
console.log(res.data.response.zones)
43+
return res.data.response.zones
44+
}
45+
return null
46+
}
47+
return null
48+
})
49+
.catch((e) => {
50+
return null
51+
})
3052
}
3153

3254
const countryName = (code = "IN") => countriesData.countries[code.toUpperCase()].name
@@ -35,26 +57,31 @@ const countryName = (code = "IN") => countriesData.countries[code.toUpperCase()]
3557
class App extends Component {
3658
constructor(props) {
3759
super(props)
38-
const listsFromLocal = localStorage.getItem('items')
39-
const lists = listsFromLocal ? JSON.parse(listsFromLocal) : [{"country":"Chennai - India","timeZone":"Asia/Kolkata"}]
60+
61+
const lists = []
4062
const countries = countriesV2
4163
const options = []
42-
// const options = Object.values(countries.countries).map((country) => {
43-
// return {
44-
// value: `${country.abbr} - ${country.code}`,
45-
// label: country.name,
46-
// timeZone: country.zones[0],
47-
// }
48-
// })
4964

5065
this.state = {
5166
options: options,
5267
lists: lists,
53-
value: ""
68+
value: "",
69+
popover: false,
70+
shareButton: false,
71+
shareButtonText: "Share",
72+
shareError: "",
73+
redirect: false,
5474
}
75+
5576
this.handleCountry = this.handleCountry.bind(this)
5677
this.loadOptions = this.loadOptions.bind(this)
5778
this.deleteCountry = this.deleteCountry.bind(this)
79+
this.handleForm = this.handleForm.bind(this)
80+
this.toggle = this.toggle.bind(this)
81+
}
82+
83+
toggle() {
84+
this.setState({popover: !this.state.popover})
5885
}
5986

6087
handleCountry(e) {
@@ -65,17 +92,71 @@ class App extends Component {
6592
lists: [...new Set(this.state.lists)],
6693
value: ""
6794
}, () => {
68-
setLocal('items', this.state.lists)
95+
if (this.props.fromTeam &&
96+
this.props.match &&
97+
this.props.match.params &&
98+
this.props.match.params.id) {
99+
const teamName = this.props.match.params.id
100+
saveData(teamName, this.state.lists)
101+
.catch(e => console.log(e))
102+
} else if(!this.props.fromTeam) {
103+
setLocal('items', this.state.lists)
104+
}
69105
})
70106
}
71107

72108
deleteCountry(name) {
73109
const lists = this.state.lists.filter(l => l.country != name)
74110
this.setState({lists: lists}, () => {
75-
setLocal('items', this.state.lists)
111+
if (this.props.fromTeam &&
112+
this.props.match &&
113+
this.props.match.params &&
114+
this.props.match.params.id) {
115+
const teamName = this.props.match.params.id
116+
saveData(teamName, this.state.lists)
117+
.catch(e => console.log(e))
118+
} else if(!this.props.fromTeam) {
119+
setLocal('items', this.state.lists)
120+
}
76121
})
77122
}
78123

124+
async handleForm(e) {
125+
e.preventDefault()
126+
this.setState({shareButton: true, shareButtonText: "Creating link..!"})
127+
128+
const formData = new FormData(e.target)
129+
const teamName = formData.get('teamName')
130+
131+
const isRegexValid = isNameValid(teamName)
132+
133+
if (!isRegexValid) {
134+
this.setState({shareError: "Name can only be alpha numeric..!", shareButton: false, shareButtonText: "Share"})
135+
return
136+
}
137+
138+
const isValid = await isValidName(teamName)
139+
140+
if (!isValid) {
141+
this.setState({shareError: "Name is already taken. Please try someother name", shareButton: false, shareButtonText: "Share"})
142+
return
143+
}
144+
145+
const listsFromLocal = localStorage.getItem('items')
146+
const lists = listsFromLocal ? JSON.parse(listsFromLocal) : [{"country":"Chennai - India","timeZone":"Asia/Kolkata"}]
147+
148+
console.log('Share with others', teamName)
149+
150+
const isSaveData = await saveData(teamName, lists)
151+
152+
if (!isSaveData) {
153+
this.setState({shareError: "Some other occured. Please try again later..!", shareButton: false, shareButtonText: "Share"})
154+
return
155+
}
156+
157+
this.setState({redirect: true, teamName: teamName})
158+
}
159+
79160
loadOptions(input, callback) {
80161
const countries = countriesV2
81162
const reg = new RegExp(`^${input}`, "gi")
@@ -89,7 +170,18 @@ class App extends Component {
89170
callback(output)
90171
}
91172

92-
componentDidMount() {
173+
async componentDidMount() {
174+
let lists = []
175+
if (this.props.match && this.props.match.params && this.props.match.params.id) {
176+
const savedData = await readLists(this.props.match.params.id)
177+
if (savedData) lists = savedData
178+
} else {
179+
const listsFromLocal = localStorage.getItem('items')
180+
lists = listsFromLocal ? JSON.parse(listsFromLocal) : [{"country":"Chennai - India","timeZone":"Asia/Kolkata"}]
181+
}
182+
183+
this.setState({lists: lists})
184+
93185
this.interval = setInterval(() => {
94186
this.setState({lists: this.state.lists})
95187
}, 1000)
@@ -100,12 +192,34 @@ class App extends Component {
100192
}
101193

102194
render() {
195+
if (this.state.redirect) {
196+
window.location = `/team/${this.state.teamName}`
197+
}
103198
return (
104199
<div className="container">
105200
<div className="row">
106201
<div className="col-md-10 offset-md-1">
107202
<h3 className="text-center" id="title">TimeZone Track </h3>
108-
<AsyncSelect id="country" className="text-center" cacheOptions loadOptions={this.loadOptions} placeholder={"Search for your City"} onChange={this.handleCountry} noOptionsMessage={() => "Search for your City"} value={this.state.value}/>
203+
204+
<div id="country" className="">
205+
<Button id="share" type="button" color="primary" className={`${this.props.fromTeam ? 'd-none' : ''}`}>
206+
Share this page with your team
207+
</Button>
208+
209+
<Popover placement="bottom" isOpen={this.state.popover} target="share" fade={false} toggle={this.toggle}>
210+
<PopoverHeader>Share with your team</PopoverHeader>
211+
<PopoverBody>
212+
<form onSubmit={this.handleForm}>
213+
<Input type="text" placeholder="Enter some unique name" name="teamName" autoFocus={true}/>
214+
<p id="share-error-red">{this.state.shareError}</p>
215+
<Input type="submit" value={this.state.shareButtonText} className="btn btn-outline-primary" disabled={this.state.shareButton}/>
216+
</form>
217+
</PopoverBody>
218+
</Popover>
219+
220+
<AsyncSelect className="text-center" cacheOptions loadOptions={this.loadOptions} placeholder={"Search for your City"} onChange={this.handleCountry} noOptionsMessage={() => "Search for your City"} value={this.state.value}/>
221+
</div>
222+
109223
<List lists={this.state.lists} deleteCountry={this.deleteCountry}/>
110224
</div>
111225
</div>
@@ -128,11 +242,18 @@ class List extends Component {
128242
<Card countryName={list.country} timezone={list.timeZone} deleteCountry={this.props.deleteCountry} key={list.country}/>
129243
)
130244
})
131-
return (
132-
<div className="cards">
133-
{lists}
134-
</div>
135-
)
245+
246+
if (lists.length > 0) {
247+
return (
248+
<div className="cards">
249+
{lists}
250+
</div>
251+
)
252+
} else {
253+
return (
254+
<h5 className="text-center" id="loading">Loading..! </h5>
255+
)
256+
}
136257
}
137258
}
138259

src/helpers.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import axios from 'axios'
2+
3+
const setLocal = (key, value) => {
4+
localStorage.setItem(key, JSON.stringify(value))
5+
}
6+
7+
const makeUnique = (items) => {
8+
const arr = []
9+
return items.filter((i) => {
10+
if(!arr.includes(i.country)) {
11+
arr.push(i.country)
12+
return true
13+
}
14+
return false
15+
})
16+
}
17+
18+
const isValidName = async (teamName) => {
19+
return axios.get(`https://f23le7ruh6.execute-api.ap-south-1.amazonaws.com/dev/exists-doc?name=${teamName}`)
20+
.then((res) => {
21+
if (res.status === 200) {
22+
return true
23+
}
24+
return false
25+
})
26+
.catch((e) => {
27+
return false
28+
})
29+
}
30+
31+
const isNameValid = (teamName) => {
32+
const letters = /^[0-9a-zA-Z]+$/
33+
if (teamName.match(letters)) {
34+
return true
35+
}
36+
return false
37+
}
38+
39+
export {
40+
setLocal,
41+
makeUnique,
42+
isValidName,
43+
isNameValid,
44+
}

src/index.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
import React from "react";
22
import ReactDOM from "react-dom";
3+
import { BrowserRouter, Switch, Route } from 'react-router-dom';
34

45
import App from "./App";
56

6-
ReactDOM.render(<App />, document.getElementById("app"));
7+
ReactDOM.render((
8+
<BrowserRouter>
9+
<React.Fragment>
10+
</React.Fragment>
11+
<Switch>
12+
<Route exact path='/' component={App}/>
13+
14+
<Route exact path='/team/:id' render={(props) => (
15+
<App {...props} fromTeam={true}/>
16+
)}/>
17+
18+
<Route path="*" component={App} />
19+
</Switch>
20+
</BrowserRouter>
21+
), document.getElementById("app"));

0 commit comments

Comments
 (0)