Tuesday, 28 August 2018
HOW TO GRAPHQL
reference:
https://www.howtographql.com/
https://www.youtube.com/watch?v=_kYUzNVyj-0
https://www.youtube.com/watch?v=lQDrREfapow&list=PLn2e1F9Rfr6kwLZy5BkIde_Gvm0ytB61L
graphcool vs prisma
https://www.prisma.io/forum/t/graphcool-framework-vs-graphcool-v1-beta-vs-prisma/2174
https://www.prisma.io/
pagination
https://graphql.org/learn/pagination/
connections
https://blog.apollographql.com/explaining-graphql-connections-c48b7c3d6976
prisma resolvers
https://www.prisma.io/docs/tutorials/build-graphql-servers/development/build-a-graphql-server-with-prisma-ohdaiyoo6c
data model (user, post...) -> schema (query, mutation...) -> resolver (implement schema) action:(_, args, ...){return context.prisma.mutation/query.action{...} }->
Sunday, 26 August 2018
JWT authentication
get token from JWT.sign
if token is not included in authorization, post request is denied
if token is validated by JWT.verify, post request gets data back
toke expires after the period in JWT.sign expireIn, post request is rejected.
1. install postman https://www.getpostman.com/
2. npm init, entry point app.js
3. npm install express jsonwebtoken
4. npm install -g nodemon
5. create app.js
6. send post request to localhost:5000/api/login with postman to get token
7. send post request to localhost:5000/api/posts with token in authorization header
app.js
const express = require('express');const jwt = require('jsonwebtoken');
const app = express();
app.get('/api', (req, res) => {
res.json({
message: 'welcome to the api'
});
});
app.post('/api/posts', verifyToken, (req, res) => {
jwt.verify(req.token, 'secretkey', (err, authData) => {
if (err) {
res.sendStatus(403);
} else {
res.json({
message: 'post created.',
authData
});
}
});
res.json({
message: 'post created.'
});
});
app.post('/api/login', (req, res) => {
//mock user
const user = {
id: 1,
username: 'brad',
email: 'brad@gmail.com'
}
jwt.sign({ user }, 'secretkey', { expiresIn:'30s' }, (err, token) => {
res.json({ token });
});
});
//format of token
//authorization: bearer <access_token>
function verifyToken(req, res, next) {
//get auth header value
const bearerHeader = req.headers['authorization'];
//check if bearer is undefined
if (typeof bearerHeader !== 'undefined') {
//split at the space
const bearer = bearerHeader.split(' ');
//get token from array
const bearerToken = bearer[1];
//set the token
req.token = bearerToken;
//next middleware
next();
}
else {
//forbidden
res.sendStatus(403);
}
}
app.listen(5000, () => console.log('server started on port 5000'));
reference:
https://www.youtube.com/watch?v=7nafaH9SddU
https://jwt.io/introduction/
Friday, 24 August 2018
css collection
project link: http://chuanshuoge1-css.surge.sh/
import './App.css';
import 'antd/dist/antd.css';
import { Card, Row, Col } from 'antd';
import banner from './img/banner.png';
import horizontalScroll from './img/horizontalScroll.png';
import parallax from './img/parallax.png';
import sticky from './img/sticky.png'
import cube from './img/3d.png'
class App extends Component {
render() {
return (
<Row gutter={8}>
<Col xs={24} sm={12} lg={8} xl={6} >
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/02/css-3d.html'>3D</a>} style={{ width: 300, height: 350 }}>
<img src={cube} style={{ width: '100%' }}></img>
</Card>
</Col>
<Col xs={24} sm={12} lg={8} xl={6}>
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/03/css-parallax_30.html'>Parallax</a>} style={{ width: 300, height: 350 }}>
<img src={parallax} style={{ width: '100%', marginTop: 50 }}></img>
</Card>
</Col>
<Col xs={24} sm={12} lg={8} xl={6}>
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/01/sliding-banner-html-css-jquery.html'>Sliding Banner</a>} style={{ width: 300, height: 350 }}>
<img src={banner} style={{width:'100%'}}></img>
</Card>
</Col>
<Col xs={24} sm={12} lg={8} xl={6} >
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/03/css-sticky-header.html'>Parallax</a>} style={{ width: 300, height: 350 }}>
<img src={sticky} style={{ width: '100%' }}></img>
</Card>
</Col>
<Col xs={24} sm={12} lg={8} xl={6}>
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/04/css-scroll-snap.html'>Horizontal Scroll</a>} style={{ width: 300, height: 350 }}>
<img src={horizontalScroll} style={{ width: '100%', marginTop: 50 }}></img>
</Card>
</Col>
</Row>
);
}
}
export default App;
reference:
https://chuanshuoge2.blogspot.com/2017/06/css-image-animation.html
app.js
import React, { Component } from 'react';import './App.css';
import 'antd/dist/antd.css';
import { Card, Row, Col } from 'antd';
import banner from './img/banner.png';
import horizontalScroll from './img/horizontalScroll.png';
import parallax from './img/parallax.png';
import sticky from './img/sticky.png'
import cube from './img/3d.png'
class App extends Component {
render() {
return (
<Row gutter={8}>
<Col xs={24} sm={12} lg={8} xl={6} >
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/02/css-3d.html'>3D</a>} style={{ width: 300, height: 350 }}>
<img src={cube} style={{ width: '100%' }}></img>
</Card>
</Col>
<Col xs={24} sm={12} lg={8} xl={6}>
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/03/css-parallax_30.html'>Parallax</a>} style={{ width: 300, height: 350 }}>
<img src={parallax} style={{ width: '100%', marginTop: 50 }}></img>
</Card>
</Col>
<Col xs={24} sm={12} lg={8} xl={6}>
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/01/sliding-banner-html-css-jquery.html'>Sliding Banner</a>} style={{ width: 300, height: 350 }}>
<img src={banner} style={{width:'100%'}}></img>
</Card>
</Col>
<Col xs={24} sm={12} lg={8} xl={6} >
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/03/css-sticky-header.html'>Parallax</a>} style={{ width: 300, height: 350 }}>
<img src={sticky} style={{ width: '100%' }}></img>
</Card>
</Col>
<Col xs={24} sm={12} lg={8} xl={6}>
<Card title={<a href='https://chuanshuoge2.blogspot.com/2018/04/css-scroll-snap.html'>Horizontal Scroll</a>} style={{ width: 300, height: 350 }}>
<img src={horizontalScroll} style={{ width: '100%', marginTop: 50 }}></img>
</Card>
</Col>
</Row>
);
}
}
export default App;
https://chuanshuoge2.blogspot.com/2017/06/css-image-animation.html
Tuesday, 21 August 2018
react vs react native
React is UI library for the view of your web app, using javascript and JSX, React native is an extra library on the top of React, to make native app for iOS and Android devices.
reference:
https://medium.com/@alexmngn/from-reactjs-to-react-native-what-are-the-main-differences-between-both-d6e8e88ebf24
https://stackoverflow.com/questions/34641582/what-is-the-difference-between-react-native-and-react
https://facebook.github.io/react-native/
reference:
https://medium.com/@alexmngn/from-reactjs-to-react-native-what-are-the-main-differences-between-both-d6e8e88ebf24
https://stackoverflow.com/questions/34641582/what-is-the-difference-between-react-native-and-react
https://facebook.github.io/react-native/
Monday, 20 August 2018
Microsoft SQL Design & Implementation final
Part 3
vendor: Australia Bike Retailer
order # date employee total
9 14/12/2011 259 Buyer 694.17
88 09/02/2012 252 Buyer 656.59
167 30/05/2012 255 Buyer 710.96
246 21/09/2012 253 Buyer 564.20
325 25/04/2013 251 Buyer 1497.51
404 25/06/2013 261 Purchasing Assistant 278.84
434 05/08/2013 253 Buyer 285.36
513 11/08/2013 251 Buyer 402.73
596 18/08/2013 261 Purchasing Assistant 501.20
683 25/08/2013 257 Buyer 358.63
766 01/09/2013 254 Buyer 234.96
849 08/09/2013 258 Buyer 694.17
932 18/09/2013 260 Purchasing Assistant 272.76
1031 13/10/2013 259 Buyer 501.20
1095 06/11/2013 257 Buyer 593.59
1174 13/11/2013 254 Buyer 278.84
1253 25/11/2013 258 Buyer 415.33
1332 04/12/2013 260 Purchasing Assistant 272.76
1411 12/12/2013 259 Buyer 501.20
1490 20/12/2013 256 Buyer 358.63
1569 30/12/2013 255 Buyer 234.96
1648 06/01/2014 253 Buyer 694.17
1727 16/01/2014 251 Buyer 773.96
1806 24/01/2014 261 Purchasing Assistant 482.52
1885 05/02/2014 257 Buyer 111.07
1964 12/02/2014 254 Buyer 278.84
2043 20/02/2014 258 Buyer 415.33
2122 27/02/2014 250 Purchasing Manager 272.76
2201 05/03/2014 259 Buyer 501.20
2280 12/03/2014 256 Buyer 358.63
2359 20/03/2014 255 Buyer 234.96
2438 28/03/2014 253 Buyer 694.17
2517 07/04/2014 251 Buyer 773.96
2596 14/04/2014 261 Purchasing Assistant 482.52
2675 21/04/2014 257 Buyer 111.07
2754 28/04/2014 254 Buyer 278.84
2833 05/05/2014 250 Purchasing Manager 415.33
2912 13/05/2014 260 Purchasing Assistant 272.76
2991 20/05/2014 259 Buyer 501.20
3070 26/05/2014 256 Buyer 358.63
3149 02/06/2014 255 Buyer 234.96
3228 07/06/2014 253 Buyer 694.17
3307 13/06/2014 251 Buyer 773.96
3386 20/06/2014 261 Purchasing Assistant 482.52
3465 26/06/2014 252 Buyer 111.07
3544 02/07/2014 254 Buyer 278.84
3623 09/07/2014 258 Buyer 415.33
3702 15/07/2014 260 Purchasing Assistant 272.76
3781 21/07/2014 259 Buyer 501.20
3860 26/07/2014 256 Buyer 358.63
3939 31/07/2014 255 Buyer 234.96
sum 22678.91
vendor: Allenson Cycles
order # date employee total
3 16/04/2011 257 Buyer 8847.30
82 09/02/2012 254 Buyer 8847.30
161 30/05/2012 258 Buyer 8847.30
240 05/09/2012 260 Purchasing Assistant 8847.30
319 24/04/2013 259 Buyer 8847.30
398 25/06/2013 256 Buyer 8847.30
428 04/08/2013 260 Purchasing Assistant 8847.30
507 11/08/2013 259 Buyer 8847.30
590 17/08/2013 256 Buyer 8847.30
677 25/08/2013 255 Buyer 8847.30
760 01/09/2013 253 Buyer 8847.30
843 08/09/2013 251 Buyer 8847.30
926 17/09/2013 261 Purchasing Assistant 8847.30
1025 13/10/2013 252 Buyer 8847.30
1089 30/10/2013 252 Buyer 8847.30
1168 13/11/2013 253 Buyer 8847.30
1247 25/11/2013 251 Buyer 8847.30
1326 04/12/2013 261 Purchasing Assistant 8847.30
1405 12/12/2013 257 Buyer 8847.30
1484 19/12/2013 254 Buyer 8847.30
1563 30/12/2013 258 Buyer 8847.30
1642 06/01/2014 260 Purchasing Assistant 8847.30
1721 16/01/2014 259 Buyer 8847.30
1800 23/01/2014 252 Buyer 8847.30
1879 04/02/2014 255 Buyer 8847.30
1958 11/02/2014 253 Buyer 8847.30
2037 19/02/2014 251 Buyer 8847.30
2116 26/02/2014 261 Purchasing Assistant 8847.30
2195 04/03/2014 257 Buyer 8847.30
2274 12/03/2014 254 Buyer 8847.30
2353 20/03/2014 258 Buyer 8847.30
2432 28/03/2014 260 Purchasing Assistant 8847.30
2511 07/04/2014 259 Buyer 8847.30
2590 14/04/2014 256 Buyer 8847.30
2669 21/04/2014 255 Buyer 8847.30
2748 25/04/2014 253 Buyer 8847.30
2827 02/05/2014 251 Buyer 8847.30
2906 13/05/2014 261 Purchasing Assistant 8847.30
2985 20/05/2014 257 Buyer 8847.30
3064 26/05/2014 254 Buyer 8847.30
3143 31/05/2014 258 Buyer 8847.30
3222 06/06/2014 250 Purchasing Manager 8847.30
3301 13/06/2014 259 Buyer 8847.30
3380 20/06/2014 256 Buyer 8847.30
3459 25/06/2014 255 Buyer 8847.30
3538 02/07/2014 253 Buyer 8847.30
3617 09/07/2014 251 Buyer 8847.30
3696 14/07/2014 261 Purchasing Assistant 8847.30
3775 19/07/2014 257 Buyer 8847.30
3854 25/07/2014 254 Buyer 8847.30
3933 30/07/2014 250 Purchasing Manager 8847.30
sum 451212.30
...
...
vendor: Chicago City Saddles
order # date employee total
19 15/12/2011 259 Buyer 79204.13
98 08/03/2012 256 Buyer 73377.15
177 22/06/2012 252 Buyer 101720.85
256 21/09/2012 253 Buyer 50860.43
335 25/04/2013 251 Buyer 152581.28
414 30/07/2013 261 Purchasing Assistant 28343.70
444 05/08/2013 253 Buyer 34118.70
523 12/08/2013 251 Buyer 50860.43
606 18/08/2013 261 Purchasing Assistant 39258.45
693 26/08/2013 252 Buyer 50860.43
776 02/09/2013 254 Buyer 62462.40
859 09/09/2013 258 Buyer 39258.45
942 18/09/2013 260 Purchasing Assistant 28343.70
1041 14/10/2013 259 Buyer 73377.15
1105 07/11/2013 257 Buyer 50860.43
1184 15/11/2013 254 Buyer 28343.70
1263 26/11/2013 258 Buyer 50860.43
1342 05/12/2013 260 Purchasing Assistant 34118.70
1421 13/12/2013 259 Buyer 39258.45
1500 20/12/2013 252 Buyer 50860.43
1579 31/12/2013 255 Buyer 79204.13
1658 08/01/2014 253 Buyer 22516.73
1737 17/01/2014 251 Buyer 101720.85
1816 24/01/2014 261 Purchasing Assistant 50860.43
1895 05/02/2014 257 Buyer 152581.28
1974 12/02/2014 254 Buyer 28343.70
2053 20/02/2014 258 Buyer 50860.43
2132 27/02/2014 260 Purchasing Assistant 34118.70
2211 05/03/2014 259 Buyer 39258.45
2290 13/03/2014 256 Buyer 50860.43
2369 21/03/2014 255 Buyer 79204.13
2448 31/03/2014 253 Buyer 22516.73
2527 08/04/2014 251 Buyer 101720.85
2606 15/04/2014 261 Purchasing Assistant 50860.43
2685 22/04/2014 257 Buyer 152581.28
2764 28/04/2014 254 Buyer 28343.70
2843 05/05/2014 258 Buyer 50860.43
2922 14/05/2014 250 Purchasing Manager 34118.70
3001 21/05/2014 259 Buyer 39258.45
3080 27/05/2014 256 Buyer 50860.43
3159 02/06/2014 255 Buyer 79204.13
3238 09/06/2014 253 Buyer 22516.73
3317 16/06/2014 251 Buyer 101720.85
3396 21/06/2014 261 Purchasing Assistant 50860.43
3475 26/06/2014 257 Buyer 152581.28
3554 03/07/2014 254 Buyer 28343.70
3633 10/07/2014 250 Purchasing Manager 50860.43
3712 15/07/2014 260 Purchasing Assistant 34118.70
3791 21/07/2014 259 Buyer 39258.45
3870 26/07/2014 256 Buyer 50860.43
3949 31/07/2014 255 Buyer 79204.13
sum 3029108.91
vendor: Business Equipment Center
order # date employee total
16 14/12/2011 253 Buyer 150.79
95 27/02/2012 251 Buyer 150.79
174 11/06/2012 261 Purchasing Assistant 150.79
253 21/09/2012 252 Buyer 150.79
332 25/04/2013 254 Buyer 150.79
411 22/07/2013 258 Buyer 150.79
441 05/08/2013 257 Buyer 150.79
520 11/08/2013 250 Purchasing Manager 150.79
603 18/08/2013 258 Buyer 150.79
690 26/08/2013 260 Purchasing Assistant 150.79
773 02/09/2013 259 Buyer 150.79
856 08/09/2013 256 Buyer 150.79
939 18/09/2013 255 Buyer 150.79
1038 13/10/2013 253 Buyer 150.79
1102 07/11/2013 260 Purchasing Assistant 150.79
1181 15/11/2013 259 Buyer 150.79
1260 26/11/2013 256 Buyer 150.79
1339 04/12/2013 255 Buyer 150.79
1418 13/12/2013 253 Buyer 150.79
1497 20/12/2013 251 Buyer 150.79
1576 31/12/2013 261 Purchasing Assistant 150.79
1655 08/01/2014 257 Buyer 150.79
1734 17/01/2014 254 Buyer 150.79
1813 24/01/2014 258 Buyer 150.79
1892 05/02/2014 260 Purchasing Assistant 150.79
1971 12/02/2014 259 Buyer 150.79
2050 20/02/2014 256 Buyer 150.79
2129 27/02/2014 255 Buyer 150.79
2208 05/03/2014 253 Buyer 150.79
2287 13/03/2014 251 Buyer 150.79
2366 21/03/2014 261 Purchasing Assistant 150.79
2445 31/03/2014 257 Buyer 150.79
2524 07/04/2014 254 Buyer 150.79
2603 14/04/2014 258 Buyer 150.79
2682 21/04/2014 260 Purchasing Assistant 150.79
2761 28/04/2014 259 Buyer 150.79
2840 05/05/2014 256 Buyer 150.79
2919 14/05/2014 255 Buyer 150.79
2998 21/05/2014 253 Buyer 150.79
3077 27/05/2014 251 Buyer 150.79
3156 02/06/2014 261 Purchasing Assistant 150.79
3235 09/06/2014 257 Buyer 150.79
3314 16/06/2014 254 Buyer 150.79
3393 21/06/2014 258 Buyer 150.79
3472 26/06/2014 260 Purchasing Assistant 150.79
3551 02/07/2014 252 Buyer 150.79
3630 09/07/2014 256 Buyer 150.79
3709 15/07/2014 255 Buyer 150.79
3788 21/07/2014 253 Buyer 150.79
3867 26/07/2014 251 Buyer 150.79
3946 31/07/2014 261 Purchasing Assistant 150.79
sum 7690.29
final total >>> 63792002.21
---------------------------------------------
SQL query
select * from Purchasing.Vendorselect * from HumanResources.Employee
select * from Purchasing.PurchaseOrderHeader
select * from Purchasing.PurchaseOrderDetail
alter view my_view as
select detail.PurchaseOrderID, convert(varchar, header.OrderDate, 103) as date, e.BusinessEntityID, e.JobTitle, sum(detail.LineTotal) as total from Production.Product as p inner join
Purchasing.PurchaseOrderDetail as detail on p.ProductID=detail.ProductID inner join
Purchasing.PurchaseOrderHeader as header on detail.PurchaseOrderID=header.PurchaseOrderID inner join
HumanResources.Employee as e on header.EmployeeID = e.BusinessEntityID
group by detail.PurchaseOrderID, header.OrderDate, e.BusinessEntityID, e.JobTitle
select * from my_view
select purchaseorderid, count(*) from my_view
group by purchaseorderid
having count(*)>1
alter procedure my_report as
begin
declare @company varchar(40)
declare @vendor_id numeric(8)
declare @order# numeric(10)
declare @date varchar(20)
declare @employee_id numeric(10)
declare @employee_title varchar(20)
declare @total1 numeric(20,2)=0
declare @total2 numeric(20,2)=0
declare @total3 numeric(20,2)=0
declare cursor1 cursor for
select Name, BusinessEntityID from Purchasing.Vendor
open cursor1
fetch next from cursor1 into @company, @vendor_id
while(@@FETCH_STATUS=0)
begin
print 'vendor: ' + convert(varchar, @company)
declare cursor2 cursor for
select detail.PurchaseOrderID, convert(varchar, header.OrderDate, 103), e.BusinessEntityID, e.JobTitle, sum(detail.LineTotal) as total from Production.Product as p inner join
Purchasing.PurchaseOrderDetail as detail on p.ProductID=detail.ProductID inner join
Purchasing.PurchaseOrderHeader as header on detail.PurchaseOrderID=header.PurchaseOrderID inner join
HumanResources.Employee as e on header.EmployeeID = e.BusinessEntityID
where header.VendorID = @vendor_id
group by detail.PurchaseOrderID, header.OrderDate, e.BusinessEntityID, e.JobTitle
order by PurchaseOrderID
open cursor2
fetch next from cursor2 into @order#, @date, @employee_id, @employee_title,@total1
print char(9)+'order #'+char(9)+'date'+char(9)+'employee'+char(9)+'total'
while(@@FETCH_STATUS=0)
begin
print char(9)+convert(varchar, @order#)+char(9)+ @date+char(9)+convert(varchar, @employee_id)+char(9)+ @employee_title+char(9)+ convert(varchar,@total1)
set @total2=@total2+@total1
fetch next from cursor2 into @order#, @date, @employee_id, @employee_title,@total1
end
close cursor2
deallocate cursor2
print char(9) +'sum'+char(9)+convert(varchar,@total2)
set @total3=@total3+@total2
set @total2=0
fetch next from cursor1 into @company, @vendor_id
end
close cursor1
deallocate cursor1
print 'final total >>> '+convert(varchar,@total3)
end
exec my_report
------------------------------
reference:
http://chuanshuoge2.blogspot.com/2018/02/sql-function-procedure-trigger.html
Sunday, 19 August 2018
react flight
project site: http://chuanshuoge1-react-flight.surge.sh/
--src
--app.js
--app.css
--airports.js
--package.json
"name": "react-flight",
"version": "0.1.0",
"private": true,
"dependencies": {
"antd": "^3.8.0",
"react": "^16.4.2",
"react-dom": "^16.4.2",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
import './App.css';
import airports from './airports';
import 'antd/dist/antd.css';
import {
Steps, Drawer, Button, Radio, Icon, Tooltip, Badge,
Timeline, DatePicker, Input, Affix, Row, Col, Select,
Checkbox, message, Modal, Popover, Menu, Dropdown,
Collapse, Avatar,
} from 'antd';
const Step = Steps.Step;
const { RangePicker } = DatePicker;
const Option = Select.Option;
const Panel = Collapse.Panel;
class App extends Component {
constructor(props) {
super(props);
this.state = {
currentStep: 0,
drawerOpen: false,
badgeCount: 0,
roundTrip: 'round',
departingDate: '',
returningDate: '',
translate: false,
dayRange: 0,
adult: 1,
children: 0,
infants: 0,
cabin: 'Economy/Coach',
airline: 'Search All Airlines',
stopOptions: 'All stops',
layover: 'Any layover hours',
checkBox: false,
fromComplete: true,
toComplete: true,
departingComplete: true,
timeLineArray: [],
modalVisible: false,
popoverVisible: false,
airportFrom: '',
airportTo: '',
searchResults: [],
itinerary: '',
panelHeader:[],
};
}
componentDidMount() {
const timeNow = new Date();
const newTimeLine = <Timeline.Item>Webpage opens {timeNow.toLocaleString()}<br />
</Timeline.Item>
//store webpage open event
this.setState({
timeLineArray: this.state.timeLineArray.concat(newTimeLine),
badgeCount: this.state.badgeCount + 1,
});
//fake itinerary panel header
this.setState({
panelHeader: [
<div>
<Avatar style={{ backgroundColor: 'green' }} icon="dingding" />
{' '}Air X <span className='ticketPrice'>$1000</span>
</div>,
<div>
<Avatar style={{ backgroundColor: 'blue' }} icon="twitter" />
{' '}Air Y <span className='ticketPrice'>$900</span>
</div>,
<div>
<Avatar style={{ backgroundColor: 'gold' }} icon="qq" />
{' '}Air Z <span className='ticketPrice'>$800</span>
</div>,
<div>
<Avatar style={{ backgroundColor: 'red' }} icon="github" />
{' '}Air W <span className='ticketPrice'>$750</span>
</div>,
]
})
}
openDrawer() {
this.setState({ drawerOpen: true })
}
closeDrawer() {
this.setState({ drawerOpen: false })
}
handleChange_roundTrip(trip) {
this.setState({ roundTrip: trip });
}
handleChange_date(e) {
console.log(e);
const departDate = e.length > 1 ? e[0]._d.toDateString() : e._d.toDateString();
const returnDate = e.length > 1 ? e[1]._d.toDateString() : null;
this.setState({ departingDate: departDate })
this.state.roundTrip === 'round' ? this.setState({ returningDate: returnDate }) : null;
}
handleClick_translate() {
this.setState(prevState => { return { translate: !prevState.translate } });
}
handleChange_dayRange(value) {
this.setState({ dayRange: value });
}
handleChange_passenger(value, name) {
this.setState({ [name]: value })
}
handleChange_checkBox(value) {
this.setState({ checkBox: value })
}
async handleClick_search() {
let searchFormComplete = true;
//check if from input is complete
if (this.state.airportFrom.length !== 3) {
message.error('Please select from airport');
this.setState({ fromComplete: false });
searchFormComplete = false;
} else {
this.setState({ fromComplete: true });
}
//check if to input is complete
if (this.state.airportTo.length !== 3) {
message.error('Please select to airport');
this.setState({ toComplete: false });
searchFormComplete = false;
} else {
this.setState({ toComplete: true });
}
//check if departure calendar is complete
if (this.state.departingDate === '') {
message.error('Departing Date is not filled');
this.setState({ departingComplete: false });
searchFormComplete = false;
} else {
this.setState({ departingComplete: true });
}
if (!searchFormComplete) { return }
//form complete
else {
//add search history to event
const dropdown_menu = <Menu>
<Menu.Item>
{this.state.roundTrip} trip<br />
From: {this.state.airportFrom}<br />
To: {this.state.airportTo}<br />
Departing: {this.state.departingDate}<br />
Returning:{this.state.returningDate}<br />
Date Range: {this.state.dayRange}<br />
Adult: {this.state.adult}<br />
Children: {this.state.children}<br />
Infants: {this.state.infants}<br />
Class: {this.state.cabin}<br />
Airline:{this.state.airline}<br />
Stop: {this.state.stopOptions}<br />
Layover: {this.state.layover}<br />
Airports within 70 miles: {this.state.checkBox}<br />
</Menu.Item>
</Menu>
const timeNow = new Date();
const newTimeLine = <Timeline.Item>Flight searched {timeNow.toLocaleString()}<br />
{/*store search details in dropdown menu*/}
<Dropdown overlay={dropdown_menu} trigger={['click']}>
<a >Show detail... <Icon type="down" /></a>
</Dropdown>
</Timeline.Item>
//increase event counter, show search result
await this.setState({
currentStep: 1,
timeLineArray: this.state.timeLineArray.concat(newTimeLine),
badgeCount: this.state.badgeCount + 1,
modalVisible: true,
searchResults: [],
});
//start flight search (mock)
this.searchFlights();
}
}
searchFlights() {
//fake itineraries
const itinerary = [<div>
<p className='flightInfo'>9:30a-5:50p</p>
<p>Connections... Layovers...</p>
<p>Air X MU 5712 - {this.state.cabin} - Boeing 737-800</p>
</div>,
<div>
<p className='flightInfo'>3:25p - 10:50a</p>
<p>Connections... Layovers...</p>
<p>Air Y CA 991 - {this.state.cabin} - Boeing 777-300ER</p>
</div>,
<div>
<p className='flightInfo'> 12:40p - 4:00p+1day</p>
<p>Connections... Layovers...</p>
<p>Air Z AC 201 - {this.state.cabin} - Airbus A321</p>
</div>,
<div>
<p className='flightInfo'> 4:05p - 6:30p</p>
<p>Connections... Layovers...</p>
<p>Air W NH 5702 - {this.state.cabin} - Airbus A319</p>
</div>,
]
//insert faked new itinerary into searchResult
const generateItinerary = (newItinerary, header) => this.setState({
searchResults: this.state.searchResults.concat(
<div>
<Row>
<Button size='small' type="primary" block
onClick={() => this.handleClick_itinerary(
<div>
{header}
{this.state.departingDate}
{newItinerary}
</div>
)}
>
Book this itinerary</Button>
</Row>
<Row gutter={8}>
<Col span={4}>
<span className='flightInfo'>{this.state.departingDate}</span>
</Col>
<Col span={20}>
{newItinerary}
</Col>
</Row>
</div>
)
});
//add a new itinerary every second
generateItinerary(itinerary[0], this.state.panelHeader[0]);
setTimeout(() => {
generateItinerary(itinerary[1], this.state.panelHeader[1])
}, 1000);
setTimeout(() => {
generateItinerary(itinerary[2], this.state.panelHeader[2])
}, 2000);
setTimeout(() => {
generateItinerary(itinerary[3], this.state.panelHeader[3])
}, 3000);
}
handleClick_modalCancel = () => {
this.setState({
modalVisible: false,
});
}
handleChange_airport(value, name) {
this.setState({ [name]: value.toUpperCase() });
}
//when itinerary is selected, event is logged, seat selection window opens
handleClick_itinerary(newItinerary) {
const dropdown_menu = <Menu>
<Menu.Item>
{newItinerary}
</Menu.Item>
</Menu>
const timeNow = new Date();
const newTimeLine = <Timeline.Item>Itinerary selected {timeNow.toLocaleString()}<br />
{/*hide itinerary in dropdown menu*/}
<Dropdown overlay={dropdown_menu} trigger={['click']}>
<a >Show detail... <Icon type="down" /></a>
</Dropdown>
</Timeline.Item>
this.setState({
itinerary: newItinerary,
currentStep: 2,
timeLineArray: this.state.timeLineArray.concat(newTimeLine),
badgeCount: this.state.badgeCount + 1,
modalVisible: false,
});
}
//open itinerary window if step>0
handleClick_gotoStep2() {
this.state.currentStep > 0 ?
this.setState({ modalVisible: true }) :
message.warning('Please search flight first');
}
render() {
{/*********popover will not display if filtered airports are too many*******/ }
const modalContent_from =
airports
.filter(x => x.airport.toUpperCase().includes(this.state.airportFrom)).length < 6 ?
<div>
{
airports
.filter(x => (x.airport.toUpperCase().includes(this.state.airportFrom)
|| x.code.toUpperCase().includes(this.state.airportFrom))
&& x.code.toUpperCase() !== this.state.airportTo)
.map((item, index) =>
<p className='popover-item' key={item.id}
onClick={() => this.handleChange_airport(item.code, 'airportFrom')}>
{item.airport} ({item.code})</p>
)
}
</div> :
<div>
too many...
</div>
const modalContent_to =
airports
.filter(x => x.airport.toUpperCase().includes(this.state.airportTo)).length < 6 ?
<div>
{
airports
.filter(x => (x.airport.toUpperCase().includes(this.state.airportTo)
|| x.code.toUpperCase().includes(this.state.airportTo))
&& x.code.toUpperCase() !== this.state.airportFrom)
.map((item, index) =>
<p className='popover-item' key={item.id}
onClick={() => this.handleChange_airport(item.code, 'airportTo')}>
{item.airport} ({item.code})</p>
)
}
</div> :
<div>
too many...
</div>
return (
<div>
{/**********header, steps**********/}
<Affix offsetTop={0}>
<div className="my-header">
<h2 className="my-title">FlyWorld.com</h2>
<Steps size="small" current={this.state.currentStep}>
<Step title="Search flights" />
<Step title={
<Button type="primary" size='small'
onClick={()=>this.handleClick_gotoStep2()}
>Select itinerary</Button>} />
<Step title="Select seat" />
<Step title="Pay" />
</Steps>
</div>
</Affix>
{/**********timeLine button, badge, tooltip**********/}
<div className="timeLine-button">
<Tooltip placement="left" title="TimeLine">
<Badge count={this.state.badgeCount}>
<Button type="primary" shape="circle" size="large"
onClick={() => this.openDrawer()}>
<Icon type="clock-circle-o" />
</Button>
</Badge>
</Tooltip>
</div>
{/**********drawer, timeLine**********/}
<Drawer
title="Events"
placement="right"
closable={false}
onClose={() => this.closeDrawer()}
visible={this.state.drawerOpen}
>
<Timeline>
{this.state.timeLineArray}
</Timeline>,
</Drawer>
{/**********form1, transluscent background**********/}
<div className="form-background">
<div className="form-background2">
{/**********radio group**********/}
<Radio.Group value={this.state.roundTrip} size='small' buttonStyle="solid"
onChange={(e) => this.handleChange_roundTrip(e.target.value)}>
<Radio.Button value='round'>Round Trip</Radio.Button>
<Radio.Button value='single'>Single Trip</Radio.Button>
</Radio.Group>
{/**********language translator button**********/}
<Button className="translate-button" size='small'
style={{ backgroundColor: 'rgba(0, 0, 0, 0)', color: 'white', border: 0, }}
onClick={() => this.handleClick_translate()}>
{this.state.translate ? 'EN' : '中文'}
</Button>
{/**********popover departure arrival airport**********/}
<Row gutter={8}>
<Col span={12}>
<h4 className="my-label">From</h4>
{/**********from airport**********/}
<Popover content={modalContent_from} placement='bottom'
trigger='click' title="Select Airports">
<Input size='small' placeholder={'airport name'}
value={this.state.airportFrom}
style={this.state.fromComplete ? null : { border: '2px solid red' }}
onChange={(e) => this.handleChange_airport(e.target.value, 'airportFrom')} />
</Popover>
</Col>
<Col span={12}>
<h4 className="my-label">To</h4>
{/**********to airport**********/}
<Popover content={modalContent_to} placement='bottom'
trigger='click' title="Select Airports">
<Input size='small' placeholder={'airport name'}
value={this.state.airportTo}
style={this.state.toComplete ? null : { border: '2px solid red' }}
onChange={(e) => this.handleChange_airport(e.target.value, 'airportTo')} />
</Popover>
</Col>
</Row>
{/**********date picker**********/}
{this.state.roundTrip === 'round' ?
<h4 className="my-label">Departing - Returning</h4> :
<h4 className="my-label">Departing</h4>
}
{this.state.roundTrip === 'round' ?
<RangePicker size='small' placeholder={['Departing', 'Returning']}
style={this.state.departingComplete ? null : { border: '2px solid red' }}
onChange={(e) => this.handleChange_date(e)} /> :
<DatePicker size='small' placeholder='Departing'
onChange={(e) => this.handleChange_date(e)} />
}
<Row>
<Col span={10}>
{/**********+/- days**********/}
<h4 className="my-label">Date Range</h4>
<Radio.Group value={this.state.dayRange} size='small' buttonStyle="solid"
onChange={(e) => this.handleChange_dayRange(e.target.value)}>
<Radio.Button value={0}>0</Radio.Button>
<Radio.Button value={1}>{'\u00B1'}1</Radio.Button>
<Radio.Button value={3}>{'\u00B1'}3</Radio.Button>
<Radio.Button value={7}>{'\u00B1'}7</Radio.Button>
</Radio.Group>
<span className="my-label">{' '}days</span>
</Col>
{/**********Adults, Children, Infants**********/}
<Col span={14}>
<Row gutter={8}>
<Col span={7}>
<p className="my-label2">Adults(12+)</p>
<Select defaultValue={1} size='small' style={{ width: 50 }}
onChange={(e) => this.handleChange_passenger(e, 'adult')}>
{passengers(10)}
</Select>
</Col>
<Col span={9}>
<p className="my-label2">Children(2-11)</p>
<Select defaultValue={0} size='small' style={{ width: 50, marginLeft: '10px' }}
onChange={(e) => this.handleChange_passenger(e, 'children')}>
{passengers(7)}
</Select>
</Col>
<Col span={8}>
<p className="my-label2">infants in laps</p>
<Select defaultValue={0} size='small' style={{ width: 50 }}
onChange={(e) => this.handleChange_passenger(e, 'infants')}>
{passengers(5)}
</Select>
</Col>
</Row>
</Col>
</Row>
<Row gutter={8}>
{/**********Cabin/Class**********/}
<Col span={12}>
<h4 className="my-label">Select cabin/class</h4>
<Select defaultValue='Economy/Coach' size='small' style={{ width: '100%' }}
onChange={(e) => this.handleChange_passenger(e, 'cabin')}>
<Option value='Economy/Coach'>Economy/Coach</Option>
<Option value='Premium Economy'>Premium Economy</Option>
<Option value='Business Class'>Business Class</Option>
<Option value='First Class'>First Class</Option>
</Select>
</Col>
{/**********airline**********/}
<Col span={12}>
<h4 className="my-label">Select airline</h4>
<Select defaultValue='Search All Airlines' size='small' style={{ width: '100%' }}
onChange={(e) => this.handleChange_passenger(e, 'airline')}>
<Option value='US & China Carriers'>US & China Carriers</Option>
<Option value='Chinese Carriers'>Chinese Carriers</Option>
<Option value='Star Alliance'>Star Alliance</Option>
<Option value='Sky Team'>Sky Team</Option>
<Option value='One World'>One World</Option>
</Select>
</Col>
</Row>
<Row gutter={8}>
{/**********stop options**********/}
<Col span={12}>
<h4 className="my-label">Select Stop Options</h4>
<Select defaultValue='All stops' size='small' style={{ width: '100%' }}
onChange={(e) => this.handleChange_passenger(e, 'stopOptions')}>
<Option value='All stops'>All stops</Option>
<Option value='Non-stop only'>Non-stop only</Option>
<Option value='1-stop only'>1-stop only</Option>
<Option value='2-stop only'>2-stop only</Option>
</Select>
</Col>
{/**********layover**********/}
<Col span={12}>
<h4 className="my-label">Max Layover Hours</h4>
<Select defaultValue='Any layover hours' size='small' style={{ width: '100%' }}
onChange={(e) => this.handleChange_passenger(e, 'layover')}>
<Option value='Any layover hours'>Any layover hours</Option>
<Option value='2 hours less'>2 hours less</Option>
<Option value='4 hours less'>4 hours less</Option>
<Option value='8 hours less'>8 hours less</Option>
<Option value='12 hours less'>12 hours less</Option>
</Select>
</Col>
</Row>
{/**********airpot distance**********/}
<div style={{ marginTop: '10px' }}>
<Checkbox onChange={(e) => this.handleChange_checkBox(e.target.checked)}>
<span style={{ color: 'white' }}>
Include airports within 70 miles</span>
</Checkbox>
</div>
{/**********search button**********/}
<Button style={{ marginTop: '10px' }} type="primary" icon="search"
onClick={() => this.handleClick_search()}>Search</Button>
</div>
</div>
{/**********modal select flight**********/}
<Modal
visible={this.state.modalVisible}
title={<span>{this.state.airportFrom}
{this.state.roundTrip === 'round' ? < Icon type="swap" /> : <Icon type="swap-right" />}
{this.state.airportTo}
</span>}
onCancel={() => this.handleClick_modalCancel()}
footer={[
<Button key="back" onClick={() => this.handleClick_modalCancel()}>Cancel</Button>,
]}
>
{/*search results are collapsed*/}
<Collapse defaultActiveKey={['0']} >
{this.state.searchResults.map((item, index) =>
<Panel header={this.state.panelHeader[index]}
key={index}>
{item}
</Panel>
)}
</Collapse>
</Modal>
</div>
);
}
}
const passengers = (num) => {
const optionArray = [];
for (let i = 0; i < num; i++) {
optionArray.push(<Option key={i} value={i}>{i}</Option>)
}
return optionArray;
}
export default App;
---------------------------------------------
App.css
.my-title {
text-align: center;
}
.form-background {
height: 450px;
width: 100%;
background: url(flight-background.jpg);
background-repeat: no-repeat;
background-size: 100% 100%;
}
.form-background2 {
position: absolute;
height: 430px;
width: 400px;
margin-top: 10px;
margin-left: 10px;
padding: 10px;
background-color: rgba(0, 0, 0, 0.6);
}
.my-label {
margin-top: 10px;
color: white;
clear: both;
}
.my-label2 {
margin-top: 10px;
color: white;
font-size: 11px;
}
.my-header {
background-color: white;
}
.translate-button {
float: right;
}
.popover-item {
cursor: pointer;
}
.popover-item:nth-child(2n) {
background-color: lightcyan;
}
.ticketPrice {
float: right;
font: bold;
font-size: 18px;
}
.flightInfo {
font: bold;
}
reference:
http://chuanshuoge2.blogspot.com/2018/08/react-flight-reference.html
https://ant.design/
top steps, form, bottom right timeline
click timeline button, drawer open right, timeline events logged
popover opens when input entered
select departure return from calendar
filter by other dropdown list
form not complete, error message displays
search history is logged
modal opens when search button clicked and form is complete
book an itinerary form collapsed menu, itinerary is logged
to reselect itinerary, click button on the step, window reopens, change itinerary booking
--src
--app.js
--app.css
--airports.js
--package.json
package.json
{"name": "react-flight",
"version": "0.1.0",
"private": true,
"dependencies": {
"antd": "^3.8.0",
"react": "^16.4.2",
"react-dom": "^16.4.2",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
----------------------------------------------
App.js
import React, { Component } from 'react';import './App.css';
import airports from './airports';
import 'antd/dist/antd.css';
import {
Steps, Drawer, Button, Radio, Icon, Tooltip, Badge,
Timeline, DatePicker, Input, Affix, Row, Col, Select,
Checkbox, message, Modal, Popover, Menu, Dropdown,
Collapse, Avatar,
} from 'antd';
const Step = Steps.Step;
const { RangePicker } = DatePicker;
const Option = Select.Option;
const Panel = Collapse.Panel;
class App extends Component {
constructor(props) {
super(props);
this.state = {
currentStep: 0,
drawerOpen: false,
badgeCount: 0,
roundTrip: 'round',
departingDate: '',
returningDate: '',
translate: false,
dayRange: 0,
adult: 1,
children: 0,
infants: 0,
cabin: 'Economy/Coach',
airline: 'Search All Airlines',
stopOptions: 'All stops',
layover: 'Any layover hours',
checkBox: false,
fromComplete: true,
toComplete: true,
departingComplete: true,
timeLineArray: [],
modalVisible: false,
popoverVisible: false,
airportFrom: '',
airportTo: '',
searchResults: [],
itinerary: '',
panelHeader:[],
};
}
componentDidMount() {
const timeNow = new Date();
const newTimeLine = <Timeline.Item>Webpage opens {timeNow.toLocaleString()}<br />
</Timeline.Item>
//store webpage open event
this.setState({
timeLineArray: this.state.timeLineArray.concat(newTimeLine),
badgeCount: this.state.badgeCount + 1,
});
//fake itinerary panel header
this.setState({
panelHeader: [
<div>
<Avatar style={{ backgroundColor: 'green' }} icon="dingding" />
{' '}Air X <span className='ticketPrice'>$1000</span>
</div>,
<div>
<Avatar style={{ backgroundColor: 'blue' }} icon="twitter" />
{' '}Air Y <span className='ticketPrice'>$900</span>
</div>,
<div>
<Avatar style={{ backgroundColor: 'gold' }} icon="qq" />
{' '}Air Z <span className='ticketPrice'>$800</span>
</div>,
<div>
<Avatar style={{ backgroundColor: 'red' }} icon="github" />
{' '}Air W <span className='ticketPrice'>$750</span>
</div>,
]
})
}
openDrawer() {
this.setState({ drawerOpen: true })
}
closeDrawer() {
this.setState({ drawerOpen: false })
}
handleChange_roundTrip(trip) {
this.setState({ roundTrip: trip });
}
handleChange_date(e) {
console.log(e);
const departDate = e.length > 1 ? e[0]._d.toDateString() : e._d.toDateString();
const returnDate = e.length > 1 ? e[1]._d.toDateString() : null;
this.setState({ departingDate: departDate })
this.state.roundTrip === 'round' ? this.setState({ returningDate: returnDate }) : null;
}
handleClick_translate() {
this.setState(prevState => { return { translate: !prevState.translate } });
}
handleChange_dayRange(value) {
this.setState({ dayRange: value });
}
handleChange_passenger(value, name) {
this.setState({ [name]: value })
}
handleChange_checkBox(value) {
this.setState({ checkBox: value })
}
async handleClick_search() {
let searchFormComplete = true;
//check if from input is complete
if (this.state.airportFrom.length !== 3) {
message.error('Please select from airport');
this.setState({ fromComplete: false });
searchFormComplete = false;
} else {
this.setState({ fromComplete: true });
}
//check if to input is complete
if (this.state.airportTo.length !== 3) {
message.error('Please select to airport');
this.setState({ toComplete: false });
searchFormComplete = false;
} else {
this.setState({ toComplete: true });
}
//check if departure calendar is complete
if (this.state.departingDate === '') {
message.error('Departing Date is not filled');
this.setState({ departingComplete: false });
searchFormComplete = false;
} else {
this.setState({ departingComplete: true });
}
if (!searchFormComplete) { return }
//form complete
else {
//add search history to event
const dropdown_menu = <Menu>
<Menu.Item>
{this.state.roundTrip} trip<br />
From: {this.state.airportFrom}<br />
To: {this.state.airportTo}<br />
Departing: {this.state.departingDate}<br />
Returning:{this.state.returningDate}<br />
Date Range: {this.state.dayRange}<br />
Adult: {this.state.adult}<br />
Children: {this.state.children}<br />
Infants: {this.state.infants}<br />
Class: {this.state.cabin}<br />
Airline:{this.state.airline}<br />
Stop: {this.state.stopOptions}<br />
Layover: {this.state.layover}<br />
Airports within 70 miles: {this.state.checkBox}<br />
</Menu.Item>
</Menu>
const timeNow = new Date();
const newTimeLine = <Timeline.Item>Flight searched {timeNow.toLocaleString()}<br />
{/*store search details in dropdown menu*/}
<Dropdown overlay={dropdown_menu} trigger={['click']}>
<a >Show detail... <Icon type="down" /></a>
</Dropdown>
</Timeline.Item>
//increase event counter, show search result
await this.setState({
currentStep: 1,
timeLineArray: this.state.timeLineArray.concat(newTimeLine),
badgeCount: this.state.badgeCount + 1,
modalVisible: true,
searchResults: [],
});
//start flight search (mock)
this.searchFlights();
}
}
searchFlights() {
//fake itineraries
const itinerary = [<div>
<p className='flightInfo'>9:30a-5:50p</p>
<p>Connections... Layovers...</p>
<p>Air X MU 5712 - {this.state.cabin} - Boeing 737-800</p>
</div>,
<div>
<p className='flightInfo'>3:25p - 10:50a</p>
<p>Connections... Layovers...</p>
<p>Air Y CA 991 - {this.state.cabin} - Boeing 777-300ER</p>
</div>,
<div>
<p className='flightInfo'> 12:40p - 4:00p+1day</p>
<p>Connections... Layovers...</p>
<p>Air Z AC 201 - {this.state.cabin} - Airbus A321</p>
</div>,
<div>
<p className='flightInfo'> 4:05p - 6:30p</p>
<p>Connections... Layovers...</p>
<p>Air W NH 5702 - {this.state.cabin} - Airbus A319</p>
</div>,
]
//insert faked new itinerary into searchResult
const generateItinerary = (newItinerary, header) => this.setState({
searchResults: this.state.searchResults.concat(
<div>
<Row>
<Button size='small' type="primary" block
onClick={() => this.handleClick_itinerary(
<div>
{header}
{this.state.departingDate}
{newItinerary}
</div>
)}
>
Book this itinerary</Button>
</Row>
<Row gutter={8}>
<Col span={4}>
<span className='flightInfo'>{this.state.departingDate}</span>
</Col>
<Col span={20}>
{newItinerary}
</Col>
</Row>
</div>
)
});
//add a new itinerary every second
generateItinerary(itinerary[0], this.state.panelHeader[0]);
setTimeout(() => {
generateItinerary(itinerary[1], this.state.panelHeader[1])
}, 1000);
setTimeout(() => {
generateItinerary(itinerary[2], this.state.panelHeader[2])
}, 2000);
setTimeout(() => {
generateItinerary(itinerary[3], this.state.panelHeader[3])
}, 3000);
}
handleClick_modalCancel = () => {
this.setState({
modalVisible: false,
});
}
handleChange_airport(value, name) {
this.setState({ [name]: value.toUpperCase() });
}
//when itinerary is selected, event is logged, seat selection window opens
handleClick_itinerary(newItinerary) {
const dropdown_menu = <Menu>
<Menu.Item>
{newItinerary}
</Menu.Item>
</Menu>
const timeNow = new Date();
const newTimeLine = <Timeline.Item>Itinerary selected {timeNow.toLocaleString()}<br />
{/*hide itinerary in dropdown menu*/}
<Dropdown overlay={dropdown_menu} trigger={['click']}>
<a >Show detail... <Icon type="down" /></a>
</Dropdown>
</Timeline.Item>
this.setState({
itinerary: newItinerary,
currentStep: 2,
timeLineArray: this.state.timeLineArray.concat(newTimeLine),
badgeCount: this.state.badgeCount + 1,
modalVisible: false,
});
}
//open itinerary window if step>0
handleClick_gotoStep2() {
this.state.currentStep > 0 ?
this.setState({ modalVisible: true }) :
message.warning('Please search flight first');
}
render() {
{/*********popover will not display if filtered airports are too many*******/ }
const modalContent_from =
airports
.filter(x => x.airport.toUpperCase().includes(this.state.airportFrom)).length < 6 ?
<div>
{
airports
.filter(x => (x.airport.toUpperCase().includes(this.state.airportFrom)
|| x.code.toUpperCase().includes(this.state.airportFrom))
&& x.code.toUpperCase() !== this.state.airportTo)
.map((item, index) =>
<p className='popover-item' key={item.id}
onClick={() => this.handleChange_airport(item.code, 'airportFrom')}>
{item.airport} ({item.code})</p>
)
}
</div> :
<div>
too many...
</div>
const modalContent_to =
airports
.filter(x => x.airport.toUpperCase().includes(this.state.airportTo)).length < 6 ?
<div>
{
airports
.filter(x => (x.airport.toUpperCase().includes(this.state.airportTo)
|| x.code.toUpperCase().includes(this.state.airportTo))
&& x.code.toUpperCase() !== this.state.airportFrom)
.map((item, index) =>
<p className='popover-item' key={item.id}
onClick={() => this.handleChange_airport(item.code, 'airportTo')}>
{item.airport} ({item.code})</p>
)
}
</div> :
<div>
too many...
</div>
return (
<div>
{/**********header, steps**********/}
<Affix offsetTop={0}>
<div className="my-header">
<h2 className="my-title">FlyWorld.com</h2>
<Steps size="small" current={this.state.currentStep}>
<Step title="Search flights" />
<Step title={
<Button type="primary" size='small'
onClick={()=>this.handleClick_gotoStep2()}
>Select itinerary</Button>} />
<Step title="Select seat" />
<Step title="Pay" />
</Steps>
</div>
</Affix>
{/**********timeLine button, badge, tooltip**********/}
<div className="timeLine-button">
<Tooltip placement="left" title="TimeLine">
<Badge count={this.state.badgeCount}>
<Button type="primary" shape="circle" size="large"
onClick={() => this.openDrawer()}>
<Icon type="clock-circle-o" />
</Button>
</Badge>
</Tooltip>
</div>
{/**********drawer, timeLine**********/}
<Drawer
title="Events"
placement="right"
closable={false}
onClose={() => this.closeDrawer()}
visible={this.state.drawerOpen}
>
<Timeline>
{this.state.timeLineArray}
</Timeline>,
</Drawer>
{/**********form1, transluscent background**********/}
<div className="form-background">
<div className="form-background2">
{/**********radio group**********/}
<Radio.Group value={this.state.roundTrip} size='small' buttonStyle="solid"
onChange={(e) => this.handleChange_roundTrip(e.target.value)}>
<Radio.Button value='round'>Round Trip</Radio.Button>
<Radio.Button value='single'>Single Trip</Radio.Button>
</Radio.Group>
{/**********language translator button**********/}
<Button className="translate-button" size='small'
style={{ backgroundColor: 'rgba(0, 0, 0, 0)', color: 'white', border: 0, }}
onClick={() => this.handleClick_translate()}>
{this.state.translate ? 'EN' : '中文'}
</Button>
{/**********popover departure arrival airport**********/}
<Row gutter={8}>
<Col span={12}>
<h4 className="my-label">From</h4>
{/**********from airport**********/}
<Popover content={modalContent_from} placement='bottom'
trigger='click' title="Select Airports">
<Input size='small' placeholder={'airport name'}
value={this.state.airportFrom}
style={this.state.fromComplete ? null : { border: '2px solid red' }}
onChange={(e) => this.handleChange_airport(e.target.value, 'airportFrom')} />
</Popover>
</Col>
<Col span={12}>
<h4 className="my-label">To</h4>
{/**********to airport**********/}
<Popover content={modalContent_to} placement='bottom'
trigger='click' title="Select Airports">
<Input size='small' placeholder={'airport name'}
value={this.state.airportTo}
style={this.state.toComplete ? null : { border: '2px solid red' }}
onChange={(e) => this.handleChange_airport(e.target.value, 'airportTo')} />
</Popover>
</Col>
</Row>
{/**********date picker**********/}
{this.state.roundTrip === 'round' ?
<h4 className="my-label">Departing - Returning</h4> :
<h4 className="my-label">Departing</h4>
}
{this.state.roundTrip === 'round' ?
<RangePicker size='small' placeholder={['Departing', 'Returning']}
style={this.state.departingComplete ? null : { border: '2px solid red' }}
onChange={(e) => this.handleChange_date(e)} /> :
<DatePicker size='small' placeholder='Departing'
onChange={(e) => this.handleChange_date(e)} />
}
<Row>
<Col span={10}>
{/**********+/- days**********/}
<h4 className="my-label">Date Range</h4>
<Radio.Group value={this.state.dayRange} size='small' buttonStyle="solid"
onChange={(e) => this.handleChange_dayRange(e.target.value)}>
<Radio.Button value={0}>0</Radio.Button>
<Radio.Button value={1}>{'\u00B1'}1</Radio.Button>
<Radio.Button value={3}>{'\u00B1'}3</Radio.Button>
<Radio.Button value={7}>{'\u00B1'}7</Radio.Button>
</Radio.Group>
<span className="my-label">{' '}days</span>
</Col>
{/**********Adults, Children, Infants**********/}
<Col span={14}>
<Row gutter={8}>
<Col span={7}>
<p className="my-label2">Adults(12+)</p>
<Select defaultValue={1} size='small' style={{ width: 50 }}
onChange={(e) => this.handleChange_passenger(e, 'adult')}>
{passengers(10)}
</Select>
</Col>
<Col span={9}>
<p className="my-label2">Children(2-11)</p>
<Select defaultValue={0} size='small' style={{ width: 50, marginLeft: '10px' }}
onChange={(e) => this.handleChange_passenger(e, 'children')}>
{passengers(7)}
</Select>
</Col>
<Col span={8}>
<p className="my-label2">infants in laps</p>
<Select defaultValue={0} size='small' style={{ width: 50 }}
onChange={(e) => this.handleChange_passenger(e, 'infants')}>
{passengers(5)}
</Select>
</Col>
</Row>
</Col>
</Row>
<Row gutter={8}>
{/**********Cabin/Class**********/}
<Col span={12}>
<h4 className="my-label">Select cabin/class</h4>
<Select defaultValue='Economy/Coach' size='small' style={{ width: '100%' }}
onChange={(e) => this.handleChange_passenger(e, 'cabin')}>
<Option value='Economy/Coach'>Economy/Coach</Option>
<Option value='Premium Economy'>Premium Economy</Option>
<Option value='Business Class'>Business Class</Option>
<Option value='First Class'>First Class</Option>
</Select>
</Col>
{/**********airline**********/}
<Col span={12}>
<h4 className="my-label">Select airline</h4>
<Select defaultValue='Search All Airlines' size='small' style={{ width: '100%' }}
onChange={(e) => this.handleChange_passenger(e, 'airline')}>
<Option value='US & China Carriers'>US & China Carriers</Option>
<Option value='Chinese Carriers'>Chinese Carriers</Option>
<Option value='Star Alliance'>Star Alliance</Option>
<Option value='Sky Team'>Sky Team</Option>
<Option value='One World'>One World</Option>
</Select>
</Col>
</Row>
<Row gutter={8}>
{/**********stop options**********/}
<Col span={12}>
<h4 className="my-label">Select Stop Options</h4>
<Select defaultValue='All stops' size='small' style={{ width: '100%' }}
onChange={(e) => this.handleChange_passenger(e, 'stopOptions')}>
<Option value='All stops'>All stops</Option>
<Option value='Non-stop only'>Non-stop only</Option>
<Option value='1-stop only'>1-stop only</Option>
<Option value='2-stop only'>2-stop only</Option>
</Select>
</Col>
{/**********layover**********/}
<Col span={12}>
<h4 className="my-label">Max Layover Hours</h4>
<Select defaultValue='Any layover hours' size='small' style={{ width: '100%' }}
onChange={(e) => this.handleChange_passenger(e, 'layover')}>
<Option value='Any layover hours'>Any layover hours</Option>
<Option value='2 hours less'>2 hours less</Option>
<Option value='4 hours less'>4 hours less</Option>
<Option value='8 hours less'>8 hours less</Option>
<Option value='12 hours less'>12 hours less</Option>
</Select>
</Col>
</Row>
{/**********airpot distance**********/}
<div style={{ marginTop: '10px' }}>
<Checkbox onChange={(e) => this.handleChange_checkBox(e.target.checked)}>
<span style={{ color: 'white' }}>
Include airports within 70 miles</span>
</Checkbox>
</div>
{/**********search button**********/}
<Button style={{ marginTop: '10px' }} type="primary" icon="search"
onClick={() => this.handleClick_search()}>Search</Button>
</div>
</div>
{/**********modal select flight**********/}
<Modal
visible={this.state.modalVisible}
title={<span>{this.state.airportFrom}
{this.state.roundTrip === 'round' ? < Icon type="swap" /> : <Icon type="swap-right" />}
{this.state.airportTo}
</span>}
onCancel={() => this.handleClick_modalCancel()}
footer={[
<Button key="back" onClick={() => this.handleClick_modalCancel()}>Cancel</Button>,
]}
>
{/*search results are collapsed*/}
<Collapse defaultActiveKey={['0']} >
{this.state.searchResults.map((item, index) =>
<Panel header={this.state.panelHeader[index]}
key={index}>
{item}
</Panel>
)}
</Collapse>
</Modal>
</div>
);
}
}
const passengers = (num) => {
const optionArray = [];
for (let i = 0; i < num; i++) {
optionArray.push(<Option key={i} value={i}>{i}</Option>)
}
return optionArray;
}
export default App;
App.css
.my-title {
text-align: center;
}
.form-background {
height: 450px;
width: 100%;
background: url(flight-background.jpg);
background-repeat: no-repeat;
background-size: 100% 100%;
}
.form-background2 {
position: absolute;
height: 430px;
width: 400px;
margin-top: 10px;
margin-left: 10px;
padding: 10px;
background-color: rgba(0, 0, 0, 0.6);
}
.my-label {
margin-top: 10px;
color: white;
clear: both;
}
.my-label2 {
margin-top: 10px;
color: white;
font-size: 11px;
}
.my-header {
background-color: white;
}
.translate-button {
float: right;
}
.popover-item {
cursor: pointer;
}
.popover-item:nth-child(2n) {
background-color: lightcyan;
}
.ticketPrice {
float: right;
font: bold;
font-size: 18px;
}
.flightInfo {
font: bold;
}
-----------------------------------------------
airport.js
const airports = [
{
id: 1,
airport: 'Hartsfield Jackson Atlanta International',
code: 'ATL'
},
{
id: 2,
airport: 'Beijing Capital International',
code: 'PEK'
},
{
id: 3,
airport: 'Dubai International ',
code: 'DXB'
},
{
id: 4,
airport: 'Tokyo Haneda',
code: 'HND'
},
{
id: 5,
airport: 'Los Angeles International ',
code: 'LAX'
},
{
id: 6,
airport: "O'Hare International",
code: 'ORD'
},
{
id: 7,
airport: 'London Heathrow',
code: 'LHR'
},
{
id: 8,
airport: 'Hong Kong International',
code: 'HKG'
},
{
id: 9,
airport: 'Shanghai Pudong International',
code: 'PVG'
},
{
id: 10,
airport: 'Paris Charles de Gaulle',
code: 'CDG'
},
{
id: 11,
airport: 'Amsterdam Schiphol ',
code: 'AMS'
},
{
id: 12,
airport: "Dallas/Fort Worth International",
code: 'DFW'
},
{
id: 13,
airport: 'Guangzhou Baiyun International',
code: 'CAN'
},
{
id: 14,
airport: 'Frankfurt',
code: 'FRA'
},
{
id: 15,
airport: 'Istanbul Ataturk Airport ',
code: 'IST'
},
{
id: 16,
airport: 'Indira Gandhi International Airport',
code: 'DEL'
},
{
id: 17,
airport: 'Soekarno-Hatta International Airport',
code: 'CGK'
},
{
id: 18,
airport: "Singapore Changi Airport",
code: 'SIN'
},
];
export default airports;
---------------------------------------
reference:
http://chuanshuoge2.blogspot.com/2018/08/react-flight-reference.html
https://ant.design/
Subscribe to:
Posts (Atom)