Sunday 26 August 2018

1 Hour of the Most Emotional Music

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/


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;

reference:
https://chuanshuoge2.blogspot.com/2017/06/css-image-animation.html

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.Vendor
select * 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/

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/