Complete site refactoring
10
.eslintrc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
parser: "babel-eslint",
|
||||||
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"max-len": [1, 120, 2, {ignoreComments: true}]
|
||||||
|
},
|
||||||
|
"extends": ["eslint:recommended", "plugin:react/recommended"]
|
||||||
|
}
|
24
.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.idea
|
||||||
|
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# PalmPay.io Website files
|
||||||
|
|
||||||
|
Feel free to copy any/all of these files if you are creating a clone site such as PalmPay.se, PalmPay.pt, PalmPay.mx, PalmPay.my, PalmPay.ca, PalmPay.pro, etc
|
||||||
|
|
||||||
|
Any questions, feel free to ping us on..
|
||||||
|
Telegram: https://t.me/Agorise
|
||||||
|
Keybase: https://keybase.io/team/Agorise
|
34
package.json
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "client",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@feathersjs/client": "^3.7.2",
|
||||||
|
"@material-ui/core": "^3.1.0",
|
||||||
|
"@material-ui/icons": "^3.0.1",
|
||||||
|
"country-list": "^1.1.0",
|
||||||
|
"mdbreact": "^4.7.1",
|
||||||
|
"react": "^16.5.2",
|
||||||
|
"react-dom": "^16.5.2",
|
||||||
|
"react-google-maps": "^9.4.5",
|
||||||
|
"react-intl": "^2.6.0",
|
||||||
|
"react-modal": "^3.5.1",
|
||||||
|
"react-ripples": "^1.1.2",
|
||||||
|
"react-router-dom": "^4.3.1",
|
||||||
|
"react-scripts": "^1.1.5",
|
||||||
|
"react-scroll": "^1.7.10",
|
||||||
|
"recompose": "^0.30.0",
|
||||||
|
"sort-by": "^1.2.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom --watchAll",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-eslint": "^9.0.0",
|
||||||
|
"eslint": "^5.6.0",
|
||||||
|
"eslint-plugin-react": "^7.11.1"
|
||||||
|
}
|
||||||
|
}
|
BIN
public/docs/Blockchain_My_City_BMC.pdf
Normal file
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 20 KiB |
91
public/index.html
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<title>PalmPay</title>
|
||||||
|
|
||||||
|
<!-- FB Open Graph data -->
|
||||||
|
<meta property="og:title" content="PalmPay">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:image" content="http://palmpay.io/PalmPayIO.png">
|
||||||
|
<meta property="og:description" content="Cryptocurrencies bring Global Sales; Bitcoin Cash, bitUSD, bitEUR, bitRUB, bitXCD, bitSilver, Steem, Litecoin and more.">
|
||||||
|
<meta property="og:url" content="http://palmpay.io/">
|
||||||
|
|
||||||
|
<!-- Twitter Card data -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="PalmPay">
|
||||||
|
<meta name="twitter:description" content="Cryptocurrencies bring Global Sales; Bitcoin Cash, bitUSD, bitEUR, bitRUB, bitXCD, bitSilver, Steem, Litecoin and more.">
|
||||||
|
<meta name="twitter:image" content="http://palmpay.io/PalmPayIO.png">
|
||||||
|
<meta name="twitter:url" content="http://palmpay.io/">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||||
|
|
||||||
|
<meta name="apple-mobile-web-app-title" content="PalmPay">
|
||||||
|
<meta name="application-name" content="PalmPay">
|
||||||
|
<meta name="msapplication-TileColor" content="#128f52">
|
||||||
|
<meta name="msapplication-TileImage" content="/mstile-144x144.png?v4=eE58gaorm5">
|
||||||
|
<meta name="theme-color" content="#29b872">
|
||||||
|
<!--script src="js/react-with-addons.js"></script>
|
||||||
|
<script src="js/react-dom.js"></script>
|
||||||
|
<script src="js/react-simplehtmlparser.min.js"></script>
|
||||||
|
<script src="js/react-language-selector.js"></script-->
|
||||||
|
</head>
|
||||||
|
<body id="home">
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<span id="root"></span>
|
||||||
|
<!-- React Language Selector -->
|
||||||
|
<!--script type="text/javascript">
|
||||||
|
var lang = new React.LS('en_US');
|
||||||
|
|
||||||
|
lang.ready(function () {
|
||||||
|
//List of languages
|
||||||
|
var rlsui_languages = [
|
||||||
|
{
|
||||||
|
id: 'en_US',
|
||||||
|
langfile: 'js/languages/en.json',
|
||||||
|
title: 'EN',
|
||||||
|
name: 'EN',
|
||||||
|
flagImg: 'img/flags/us.png',
|
||||||
|
flagTitle: 'United States'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'el_GR',
|
||||||
|
langfile: 'js/languages/el.json',
|
||||||
|
title: 'GR',
|
||||||
|
name: 'GR',
|
||||||
|
flagImg: 'img/flags/gr.png',
|
||||||
|
flagTitle: 'Greece'
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
//id for loaded language pack also can be 'fr'
|
||||||
|
id: 'fr_FR',
|
||||||
|
//path to language pack
|
||||||
|
langfile: 'languages/fr.json',
|
||||||
|
title: 'French',
|
||||||
|
name: 'French',
|
||||||
|
flagImg: 'img/flags/fr.png',
|
||||||
|
flagTitle: 'French'
|
||||||
|
},*/
|
||||||
|
];
|
||||||
|
//main part for loading language selector ui
|
||||||
|
|
||||||
|
ReactDOM.render(React.createElement(lang.UI, {
|
||||||
|
items: rlsui_languages,
|
||||||
|
selectedLang: window.lang.currentLang(),
|
||||||
|
showFlag: false,
|
||||||
|
onLanguageChanged: function (rlsui_lang) {
|
||||||
|
lang.set_load_lang(rlsui_lang.id, rlsui_lang.langfile);
|
||||||
|
}
|
||||||
|
}), document.getElementById('langs'));
|
||||||
|
});</script-->
|
||||||
|
</body>
|
||||||
|
</html>
|
15
public/manifest.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "PalmPay",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "./index.html",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
229
src/assets/css/csslider.css
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
.csslider {
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
.csslider > input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(10):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -900%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(9):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -800%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(8):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -700%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(7):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -600%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(6):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -500%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(5):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -400%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(4):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -300%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(3):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -200%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(2):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: -100%;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(1):checked ~ ul li:first-of-type {
|
||||||
|
margin-left: 0%;
|
||||||
|
}
|
||||||
|
.csslider > ul {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
z-index: 1;
|
||||||
|
font-size: 0;
|
||||||
|
line-height: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.csslider > ul > li {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 15px;
|
||||||
|
font-size: initial;
|
||||||
|
line-height: normal;
|
||||||
|
-moz-transition: all 0.5s cubic-bezier(0.4, 1.3, 0.65, 1);
|
||||||
|
-o-transition: all 0.5s ease-out;
|
||||||
|
-webkit-transition: all 0.5s cubic-bezier(0.4, 1.3, 0.65, 1);
|
||||||
|
transition: all 0.5s cubic-bezier(0.4, 1.3, 0.65, 1);
|
||||||
|
vertical-align: top;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.csslider > ul > li.scrollable {
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.csslider > .navigation {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -8px;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 10;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
font-size: 0;
|
||||||
|
line-height: 0;
|
||||||
|
text-align: center;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.csslider > .navigation > div {
|
||||||
|
margin-left: -100%;
|
||||||
|
}
|
||||||
|
.csslider > .navigation label {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 0 8px;
|
||||||
|
padding: 8px;
|
||||||
|
background: #3a3a3a;
|
||||||
|
}
|
||||||
|
.csslider > .navigation label:hover:after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.csslider > .navigation label:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
margin-left: -10px;
|
||||||
|
margin-top: -10px;
|
||||||
|
background: #71ad37;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 10px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.csslider > .arrows {
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.csslider.inside .navigation {
|
||||||
|
bottom: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.csslider.inside .navigation label {
|
||||||
|
border: 1px solid #7e7e7e;
|
||||||
|
}
|
||||||
|
.csslider > input:nth-of-type(1):checked ~ .navigation label:nth-of-type(1):after,
|
||||||
|
.csslider > input:nth-of-type(2):checked ~ .navigation label:nth-of-type(2):after,
|
||||||
|
.csslider > input:nth-of-type(3):checked ~ .navigation label:nth-of-type(3):after,
|
||||||
|
.csslider > input:nth-of-type(4):checked ~ .navigation label:nth-of-type(4):after,
|
||||||
|
.csslider > input:nth-of-type(5):checked ~ .navigation label:nth-of-type(5):after,
|
||||||
|
.csslider > input:nth-of-type(6):checked ~ .navigation label:nth-of-type(6):after,
|
||||||
|
.csslider > input:nth-of-type(7):checked ~ .navigation label:nth-of-type(7):after,
|
||||||
|
.csslider > input:nth-of-type(8):checked ~ .navigation label:nth-of-type(8):after,
|
||||||
|
.csslider > input:nth-of-type(9):checked ~ .navigation label:nth-of-type(9):after,
|
||||||
|
.csslider > input:nth-of-type(10):checked ~ .navigation label:nth-of-type(10):after,
|
||||||
|
.csslider > input:nth-of-type(11):checked ~ .navigation label:nth-of-type(11):after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.csslider > .arrows {
|
||||||
|
position: absolute;
|
||||||
|
left: -31px;
|
||||||
|
top: 50%;
|
||||||
|
width: 100%;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 31px;
|
||||||
|
z-index: 0;
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
-webkit-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
.csslider > .arrows label {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
padding: 13px;
|
||||||
|
box-shadow: inset 2px -2px 0 1px #3a3a3a;
|
||||||
|
cursor: pointer;
|
||||||
|
-moz-transition: box-shadow 0.15s, margin 0.15s;
|
||||||
|
-o-transition: box-shadow 0.15s, margin 0.15s;
|
||||||
|
-webkit-transition: box-shadow 0.15s, margin 0.15s;
|
||||||
|
transition: box-shadow 0.15s, margin 0.15s;
|
||||||
|
}
|
||||||
|
.csslider > .arrows label:hover {
|
||||||
|
box-shadow: inset 3px -3px 0 2px #71ad37;
|
||||||
|
margin: 0 0px;
|
||||||
|
}
|
||||||
|
.csslider > .arrows label:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -100%;
|
||||||
|
left: -100%;
|
||||||
|
height: 300%;
|
||||||
|
width: 300%;
|
||||||
|
}
|
||||||
|
.csslider.infinity > input:first-of-type:checked ~ .arrows label.goto-last,
|
||||||
|
.csslider > input:nth-of-type(1):checked ~ .arrows > label:nth-of-type(0),
|
||||||
|
.csslider > input:nth-of-type(2):checked ~ .arrows > label:nth-of-type(1),
|
||||||
|
.csslider > input:nth-of-type(3):checked ~ .arrows > label:nth-of-type(2),
|
||||||
|
.csslider > input:nth-of-type(4):checked ~ .arrows > label:nth-of-type(3),
|
||||||
|
.csslider > input:nth-of-type(5):checked ~ .arrows > label:nth-of-type(4),
|
||||||
|
.csslider > input:nth-of-type(6):checked ~ .arrows > label:nth-of-type(5),
|
||||||
|
.csslider > input:nth-of-type(7):checked ~ .arrows > label:nth-of-type(6),
|
||||||
|
.csslider > input:nth-of-type(8):checked ~ .arrows > label:nth-of-type(7),
|
||||||
|
.csslider > input:nth-of-type(9):checked ~ .arrows > label:nth-of-type(8),
|
||||||
|
.csslider > input:nth-of-type(10):checked ~ .arrows > label:nth-of-type(9),
|
||||||
|
.csslider > input:nth-of-type(11):checked ~ .arrows > label:nth-of-type(10) {
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
right: auto;
|
||||||
|
-moz-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
-o-transform: rotate(45deg);
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
.csslider.infinity > input:last-of-type:checked ~ .arrows label.goto-first,
|
||||||
|
.csslider > input:nth-of-type(1):checked ~ .arrows > label:nth-of-type(2),
|
||||||
|
.csslider > input:nth-of-type(2):checked ~ .arrows > label:nth-of-type(3),
|
||||||
|
.csslider > input:nth-of-type(3):checked ~ .arrows > label:nth-of-type(4),
|
||||||
|
.csslider > input:nth-of-type(4):checked ~ .arrows > label:nth-of-type(5),
|
||||||
|
.csslider > input:nth-of-type(5):checked ~ .arrows > label:nth-of-type(6),
|
||||||
|
.csslider > input:nth-of-type(6):checked ~ .arrows > label:nth-of-type(7),
|
||||||
|
.csslider > input:nth-of-type(7):checked ~ .arrows > label:nth-of-type(8),
|
||||||
|
.csslider > input:nth-of-type(8):checked ~ .arrows > label:nth-of-type(9),
|
||||||
|
.csslider > input:nth-of-type(9):checked ~ .arrows > label:nth-of-type(10),
|
||||||
|
.csslider > input:nth-of-type(10):checked ~ .arrows > label:nth-of-type(11),
|
||||||
|
.csslider > input:nth-of-type(11):checked ~ .arrows > label:nth-of-type(12) {
|
||||||
|
display: block;
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
-moz-transform: rotate(225deg);
|
||||||
|
-ms-transform: rotate(225deg);
|
||||||
|
-o-transform: rotate(225deg);
|
||||||
|
-webkit-transform: rotate(225deg);
|
||||||
|
transform: rotate(225deg);
|
||||||
|
}
|
||||||
|
/*#region MODULES */
|
||||||
|
/*#endregion */
|
14267
src/assets/css/mdb.css
Normal file
26
src/assets/css/mdb.min.css
vendored
Normal file
352
src/assets/css/palmpay.css
Normal file
|
@ -0,0 +1,352 @@
|
||||||
|
/*!
|
||||||
|
* PalmPay - v1.0.0 ()
|
||||||
|
* Copyright 2018 poqdavid
|
||||||
|
* Licensed under GPLv3 (http://palmpay.io/LICENSE.txt)
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: "PFBeauSansPro";
|
||||||
|
src: local("PFBeauSansPro"), local("PFBeauSansPro"), url("../font/pfb/PFBeauSansPro-Reg.ttf");
|
||||||
|
font-weight: 400; }
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "PFBeauSansPro";
|
||||||
|
src: local("PFBeauSansPro SeBold"), local("PFBeauSansPro-SeBold"), url("../font/pfb/PFBeauSansPro-SeBold.ttf");
|
||||||
|
font-weight: 600; }
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "PFBeauSansPro";
|
||||||
|
src: local("PFBeauSansPro Bold"), local("PFBeauSansPro-Bold"), url("../font/pfb/PFBeauSansPro-Bold.ttf");
|
||||||
|
font-weight: 700; }
|
||||||
|
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'PFBeauSansPro', sans-serif !important; }
|
||||||
|
|
||||||
|
hr {
|
||||||
|
max-width: 50px;
|
||||||
|
border-width: 3px;
|
||||||
|
border-color: #128f52; }
|
||||||
|
|
||||||
|
hr.light {
|
||||||
|
border-color: #fff; }
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #128f52;
|
||||||
|
-webkit-transition: all 0.2s;
|
||||||
|
-moz-transition: all 0.2s;
|
||||||
|
transition: all 0.2s; }
|
||||||
|
a:hover {
|
||||||
|
color: #128f52; }
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-family: 'PFBeauSansPro', sans-serif !important; }
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-family: 'PFBeauSansPro', sans-serif !important; }
|
||||||
|
|
||||||
|
.bg-primary {
|
||||||
|
background-color: #128f52 !important; }
|
||||||
|
|
||||||
|
.bg-secondary {
|
||||||
|
background-color: #F0F0F0 !important; }
|
||||||
|
|
||||||
|
.bg-dark {
|
||||||
|
background-color: #212529 !important; }
|
||||||
|
|
||||||
|
.text-faded {
|
||||||
|
color: rgba(255, 255, 255, 0.7); }
|
||||||
|
|
||||||
|
section {
|
||||||
|
padding: 8rem 0; }
|
||||||
|
|
||||||
|
.section-heading {
|
||||||
|
margin-top: 0; }
|
||||||
|
|
||||||
|
::-moz-selection {
|
||||||
|
color: #fff;
|
||||||
|
background: #212529;
|
||||||
|
text-shadow: none; }
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
color: #fff;
|
||||||
|
background: #212529;
|
||||||
|
text-shadow: none; }
|
||||||
|
|
||||||
|
img::selection {
|
||||||
|
color: #fff;
|
||||||
|
background: transparent; }
|
||||||
|
|
||||||
|
img::-moz-selection {
|
||||||
|
color: #fff;
|
||||||
|
background: transparent; }
|
||||||
|
|
||||||
|
.containerfix {
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 0px;
|
||||||
|
padding-left: 0px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
overflow: hidden; }
|
||||||
|
|
||||||
|
#mainNav {
|
||||||
|
border-bottom: 1px solid rgba(33, 37, 41, 0.1);
|
||||||
|
background-color: #128f52;
|
||||||
|
font-family: 'PFBeauSansPro', sans-serif !important;
|
||||||
|
-webkit-transition: all 0.2s;
|
||||||
|
-moz-transition: all 0.2s;
|
||||||
|
transition: all 0.2s;
|
||||||
|
padding-top: 25px;
|
||||||
|
padding-bottom: 25px; }
|
||||||
|
#mainNav .navbar-brand {
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: #128f52;
|
||||||
|
font-family: 'PFBeauSansPro', sans-serif !important; }
|
||||||
|
#mainNav .navbar-brand:focus, #mainNav .navbar-brand:hover {
|
||||||
|
color: #128f52; }
|
||||||
|
#mainNav .navbar-lang {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #128f52;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
cursor: pointer; }
|
||||||
|
#mainNav .navbar-textsmall {
|
||||||
|
font-size: .7rem !important; }
|
||||||
|
#mainNav .expand_caret {
|
||||||
|
position: absolute !important;
|
||||||
|
-webkit-transition: 2s ease-in-out !important;
|
||||||
|
-moz-transition: 2s ease-in-out !important;
|
||||||
|
-o-transition: 2s ease-in-out !important;
|
||||||
|
transition: 2s ease-in-out !important; }
|
||||||
|
#mainNav .rlsui-selected-locale:hover.dropdown-toggle::after {
|
||||||
|
display: inline-block;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
margin-left: .255em;
|
||||||
|
vertical-align: .255em;
|
||||||
|
content: "";
|
||||||
|
border-top: .3em solid;
|
||||||
|
border-right: .3em solid transparent;
|
||||||
|
border-bottom: 0;
|
||||||
|
border-left: .3em solid transparent;
|
||||||
|
-moz-transform: rotate(180deg) !important;
|
||||||
|
-o-transform: rotate(180deg) !important;
|
||||||
|
-ms-transform: rotate(180deg) !important;
|
||||||
|
transform: rotate(180deg) !important; }
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link,
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus {
|
||||||
|
font-size: .9rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: #fff; }
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link:hover,
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus:hover {
|
||||||
|
color: #fec810;
|
||||||
|
cursor: pointer; }
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link.active,
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus.active {
|
||||||
|
color: #fec810 !important;
|
||||||
|
background-color: transparent; }
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link.active:hover,
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus.active:hover {
|
||||||
|
background-color: transparent; }
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
#mainNav {
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 0px; }
|
||||||
|
#mainNav .navbar-brand .logo1 {
|
||||||
|
display: none !important; }
|
||||||
|
#mainNav .navbar-brand .logo2 {
|
||||||
|
display: inline !important; } }
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
#mainNav {
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: transparent; }
|
||||||
|
#mainNav .navbar-brand {
|
||||||
|
color: rgba(18, 143, 82, 0.7); }
|
||||||
|
#mainNav .navbar-brand:focus, #mainNav .navbar-brand:hover {
|
||||||
|
color: #fff; }
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link {
|
||||||
|
padding: 0.5rem 1rem; }
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link,
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus {
|
||||||
|
color: #fff; }
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link:hover,
|
||||||
|
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus:hover {
|
||||||
|
color: #fec810;
|
||||||
|
cursor: pointer; }
|
||||||
|
#mainNav.navbar-shrink {
|
||||||
|
border-bottom: 1px solid rgba(33, 37, 41, 0.1);
|
||||||
|
background-color: #128f52;
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 0px; }
|
||||||
|
#mainNav.navbar-shrink .navbar-brand {
|
||||||
|
color: #fff; }
|
||||||
|
#mainNav.navbar-shrink .navbar-brand:focus, #mainNav.navbar-shrink .navbar-brand:hover {
|
||||||
|
color: #fec810;
|
||||||
|
cursor: pointer; }
|
||||||
|
#mainNav.navbar-shrink .navbar-nav > li.nav-item > a.nav-link,
|
||||||
|
#mainNav.navbar-shrink .navbar-nav > li.nav-item > a.nav-link:focus {
|
||||||
|
color: #fff; }
|
||||||
|
#mainNav.navbar-shrink .navbar-nav > li.nav-item > a.nav-link:hover,
|
||||||
|
#mainNav.navbar-shrink .navbar-nav > li.nav-item > a.nav-link:focus:hover {
|
||||||
|
color: #fec810;
|
||||||
|
cursor: pointer; } }
|
||||||
|
|
||||||
|
header.masthead {
|
||||||
|
padding-top: 10rem;
|
||||||
|
padding-bottom: calc(10rem - 56px);
|
||||||
|
background-image: url("../img/header/header-bg.jpg");
|
||||||
|
background-position: center center;
|
||||||
|
-webkit-background-size: cover;
|
||||||
|
-moz-background-size: cover;
|
||||||
|
-o-background-size: cover;
|
||||||
|
background-size: cover; }
|
||||||
|
header.masthead hr {
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 30px; }
|
||||||
|
.view h1 {
|
||||||
|
font-size: 2rem !important; }
|
||||||
|
.view p {
|
||||||
|
font-weight: 300 !important; }
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.view p {
|
||||||
|
font-size: 1.15rem !important; } }
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
header.masthead {
|
||||||
|
height: 100vh;
|
||||||
|
min-height: 650px;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0; }
|
||||||
|
.view h1 {
|
||||||
|
font-size: 3rem !important; } }
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.view h1 {
|
||||||
|
font-size: 4rem !important; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-box {
|
||||||
|
max-width: 400px; }
|
||||||
|
|
||||||
|
.service-box img {
|
||||||
|
border: 2px solid #128f52;
|
||||||
|
border-radius: 50%; }
|
||||||
|
.service-box img:hover {
|
||||||
|
border: 2px solid #fec810; }
|
||||||
|
|
||||||
|
.text-primary {
|
||||||
|
color: #128f52 !important; }
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border: none;
|
||||||
|
border-radius: 300px;
|
||||||
|
font-family: 'PFBeauSansPro', sans-serif !important; }
|
||||||
|
|
||||||
|
.btn-xl {
|
||||||
|
padding: 1rem 2rem; }
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #128f52 !important;
|
||||||
|
border-color: #128f52 !important; }
|
||||||
|
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #0f7845 !important; }
|
||||||
|
.btn-primary:active, .btn-primary:focus {
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(240, 95, 64, 0.5) !important; }
|
||||||
|
|
||||||
|
.about {
|
||||||
|
font-family: 'PFBeauSansPro'; }
|
||||||
|
.about .about-text {
|
||||||
|
color: black; }
|
||||||
|
.about .about-img {
|
||||||
|
min-height: 30rem;
|
||||||
|
background-size: cover; }
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.about .about-text {
|
||||||
|
color: black; } }
|
||||||
|
.about h1,
|
||||||
|
.about h2,
|
||||||
|
.about h3,
|
||||||
|
.about h4,
|
||||||
|
.about h5,
|
||||||
|
.about h6 {
|
||||||
|
font-family: 'PFBeauSansPro';
|
||||||
|
font-weight: 800 !important; }
|
||||||
|
|
||||||
|
.footerrow {
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
background-color: #128f52;
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
.copyright {
|
||||||
|
background-color: #212529;
|
||||||
|
padding-left: 6rem;
|
||||||
|
padding-right: 6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.social-buttons {
|
||||||
|
margin-bottom: 0; }
|
||||||
|
ul.social-buttons li a {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 40px;
|
||||||
|
display: block;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
-webkit-transition: all 0.3s;
|
||||||
|
-moz-transition: all 0.3s;
|
||||||
|
transition: all 0.3s;
|
||||||
|
color: white;
|
||||||
|
border-radius: 100%;
|
||||||
|
outline: none;
|
||||||
|
background-color: #0c6238; }
|
||||||
|
ul.social-buttons li a:active, ul.social-buttons li a:focus, ul.social-buttons li a:hover {
|
||||||
|
background-color: #fec810; }
|
||||||
|
|
||||||
|
.vertical-align {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row; }
|
||||||
|
|
||||||
|
.vertical-align > [class^="col-"],
|
||||||
|
.vertical-align > [class*=" col-"] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center; }
|
||||||
|
|
||||||
|
.fa-dtube {
|
||||||
|
background-image: url("../img/footer/dtube.png") no-repeat 0 0; }
|
||||||
|
|
||||||
|
.fa-steemit {
|
||||||
|
background-image: url("../img/footer/steemit.png") no-repeat 0 0; }
|
||||||
|
|
||||||
|
.testimonial-item i {
|
||||||
|
font-size: 80px;
|
||||||
|
color: #128f52;
|
||||||
|
padding-bottom: 15px; }
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.pr-xl-6, .px-xl-6 {
|
||||||
|
padding-right: 8rem !important;
|
||||||
|
}
|
||||||
|
.pl-xl-6, .px-xl-6 {
|
||||||
|
padding-left: 8rem !important;
|
||||||
|
}
|
||||||
|
.pr-xl-7, .px-xl-7 {
|
||||||
|
padding-right: 12rem !important;
|
||||||
|
}
|
||||||
|
.pl-xl-7, .px-xl-7 {
|
||||||
|
padding-left: 12rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
5
src/assets/css/palmpay.min.css
vendored
Normal file
101
src/assets/css/tooltip.css
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper {
|
||||||
|
background: #222;
|
||||||
|
color: white;
|
||||||
|
width: 150px;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper .popper__arrow, .popover .popover__arrow {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
position: absolute;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper[data-placement^="top"], .popover[data-placement^="top"] {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper[data-placement^="top"] .popper__arrow, .popover[data-placement^="top"] .popover__arrow {
|
||||||
|
border-width: 5px 5px 0 5px;
|
||||||
|
border-color: #222 transparent transparent transparent;
|
||||||
|
bottom: -5px;
|
||||||
|
left: calc(50% - 5px);
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover[data-placement^="top"] .popover__arrow {
|
||||||
|
border-color: #fff transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper[data-placement^="bottom"], .popover[data-placement^="bottom"] {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper[data-placement^="bottom"] .popper__arrow, .popover[data-placement^="bottom"] .popover__arrow {
|
||||||
|
border-width: 0 5px 5px 5px;
|
||||||
|
border-color: transparent transparent #222 transparent;
|
||||||
|
top: -5px;
|
||||||
|
left: calc(50% - 5px);
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover[data-placement^="bottom"] .popover__arrow {
|
||||||
|
border-color: transparent transparent #fff transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper[data-placement^="right"], .popover[data-placement^="right"] {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper[data-placement^="right"] .popper__arrow, .popover[data-placement^="right"] .popover__arrow {
|
||||||
|
border-width: 5px 5px 5px 0;
|
||||||
|
border-color: transparent #222 transparent transparent;
|
||||||
|
left: -5px;
|
||||||
|
top: calc(50% - 5px);
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover[data-placement^="right"] .popover__arrow {
|
||||||
|
border-color: transparent #fff transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper[data-placement^="left"], .popover[data-placement^="left"] {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper[data-placement^="left"] .popper__arrow, .popover[data-placement^="left"] .popover__arrow {
|
||||||
|
border-width: 5px 0 5px 5px;
|
||||||
|
border-color: transparent transparent transparent #222;
|
||||||
|
right: -5px;
|
||||||
|
top: calc(50% - 5px);
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover[data-placement^="left"] .popover__arrow {
|
||||||
|
border-color: transparent transparent transparent #fff;
|
||||||
|
}
|
||||||
|
*/
|
BIN
src/assets/font/fa/FontAwesome.otf
Normal file
BIN
src/assets/font/fa/fontawesome-webfont.eot
Normal file
2671
src/assets/font/fa/fontawesome-webfont.svg
Normal file
After Width: | Height: | Size: 434 KiB |
BIN
src/assets/font/fa/fontawesome-webfont.ttf
Normal file
BIN
src/assets/font/fa/fontawesome-webfont.woff
Normal file
BIN
src/assets/font/fa/fontawesome-webfont.woff2
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-Black.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-BlackItal.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-Bold.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-BoldItalic.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-Book.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-BookItalic.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-Italic.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-Light.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-LightItal.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-Reg.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-SeBold.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-SeBoldItal.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-Thin.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-ThinItal.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-XThin.ttf
Normal file
BIN
src/assets/font/pfb/PFBeauSansPro-XThinItal.ttf
Normal file
BIN
src/assets/font/roboto/Roboto-Bold.eot
Normal file
BIN
src/assets/font/roboto/Roboto-Bold.ttf
Normal file
BIN
src/assets/font/roboto/Roboto-Bold.woff
Normal file
BIN
src/assets/font/roboto/Roboto-Bold.woff2
Normal file
BIN
src/assets/font/roboto/Roboto-Light.eot
Normal file
BIN
src/assets/font/roboto/Roboto-Light.ttf
Normal file
BIN
src/assets/font/roboto/Roboto-Light.woff
Normal file
BIN
src/assets/font/roboto/Roboto-Light.woff2
Normal file
BIN
src/assets/font/roboto/Roboto-Medium.eot
Normal file
BIN
src/assets/font/roboto/Roboto-Medium.ttf
Normal file
BIN
src/assets/font/roboto/Roboto-Medium.woff
Normal file
BIN
src/assets/font/roboto/Roboto-Medium.woff2
Normal file
BIN
src/assets/font/roboto/Roboto-Regular.eot
Normal file
BIN
src/assets/font/roboto/Roboto-Regular.ttf
Normal file
BIN
src/assets/font/roboto/Roboto-Regular.woff
Normal file
BIN
src/assets/font/roboto/Roboto-Regular.woff2
Normal file
BIN
src/assets/font/roboto/Roboto-Thin.eot
Normal file
BIN
src/assets/font/roboto/Roboto-Thin.ttf
Normal file
BIN
src/assets/font/roboto/Roboto-Thin.woff
Normal file
BIN
src/assets/font/roboto/Roboto-Thin.woff2
Normal file
BIN
src/assets/img/about/ab1.png
Normal file
After Width: | Height: | Size: 190 KiB |
BIN
src/assets/img/about/ab2.png
Normal file
After Width: | Height: | Size: 243 KiB |
BIN
src/assets/img/about/ab3.png
Normal file
After Width: | Height: | Size: 196 KiB |
BIN
src/assets/img/about/ab4.png
Normal file
After Width: | Height: | Size: 250 KiB |
BIN
src/assets/img/about/ab5.png
Normal file
After Width: | Height: | Size: 215 KiB |
BIN
src/assets/img/footer/dtube.png
Normal file
After Width: | Height: | Size: 297 B |
BIN
src/assets/img/footer/footer_logo.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/assets/img/footer/steemit.png
Normal file
After Width: | Height: | Size: 362 B |
BIN
src/assets/img/header/header-bg.jpg
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
src/assets/img/header/logo.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
src/assets/img/loading_icon.gif
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
src/assets/img/map/ambassador_cluster.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
src/assets/img/map/ambassador_pin.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
src/assets/img/map/merchant_cluster.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
src/assets/img/map/merchant_pin.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
src/assets/img/services/bill_pay_counters.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/img/services/cafe_bars.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/img/services/delivery_drivers.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/services/gas_stations.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/img/services/grocery.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/img/services/phone_orders.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/img/services/restaurants.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/services/retail.png
Normal file
After Width: | Height: | Size: 13 KiB |
84
src/components/App.js
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Events, animateScroll as scroll, scrollSpy } from 'react-scroll';
|
||||||
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
|
// Custom components
|
||||||
|
import {
|
||||||
|
AmbassadorsPage,
|
||||||
|
HomePage,
|
||||||
|
MarketingPage,
|
||||||
|
MerchantsPage,
|
||||||
|
NotFoundPage
|
||||||
|
} from './pages';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main App component - Route all the pages.
|
||||||
|
*/
|
||||||
|
class App extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { showmenu: true };
|
||||||
|
this.state = {
|
||||||
|
collapse: false,
|
||||||
|
modal2: false
|
||||||
|
};
|
||||||
|
this.onClick = this.onClick.bind(this);
|
||||||
|
this.handleNavbarClick = this.handleNavbarClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
this.setState({
|
||||||
|
collapse: !this.state.collapse
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNavbarClick() {
|
||||||
|
this.setState({
|
||||||
|
collapse: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
scrollSpy.update();
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
Events.scrollEvent.remove("begin");
|
||||||
|
Events.scrollEvent.remove("end");
|
||||||
|
}
|
||||||
|
scrollToTop() {
|
||||||
|
scroll.scrollToTop();
|
||||||
|
}
|
||||||
|
scrollToBottom() {
|
||||||
|
scroll.scrollToBottom();
|
||||||
|
}
|
||||||
|
scrollTo() {
|
||||||
|
scroll.scrollTo(100);
|
||||||
|
}
|
||||||
|
scrollMore() {
|
||||||
|
scroll.scrollMore(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const collapsed = this.state.collapsed;
|
||||||
|
const overlay = (
|
||||||
|
<div
|
||||||
|
id="sidenav-overlay"
|
||||||
|
style={{ backgroundColor: "transparent" }}
|
||||||
|
onClick={this.handleNavbarClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<span id="apppage">
|
||||||
|
<Switch>
|
||||||
|
<Route exact path="/" title="Palmpay" component={HomePage} />
|
||||||
|
<Route exact path="/ambs" title="PalmPay Ambassadors" component={AmbassadorsPage} />
|
||||||
|
<Route exact path="/marketing" title="PalmPay Marketing" component={MarketingPage} />
|
||||||
|
<Route exact path="/merchants" title="PalmPay Merchants" component={MerchantsPage} />
|
||||||
|
<Route title="PalmPay Page Not Found" component={NotFoundPage} />
|
||||||
|
</Switch>
|
||||||
|
{collapsed && overlay}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
28
src/components/AppHeader.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
//Material UI Components
|
||||||
|
import AppBar from '@material-ui/core/AppBar';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
|
|
||||||
|
import AppLogo from '../assets/img/header/logo.png';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The header of some pages: MerchantsPage, AmbassadorsPage and NotFoundPage.
|
||||||
|
*/
|
||||||
|
function AppHeader() {
|
||||||
|
return (
|
||||||
|
<AppBar position="static" color="inherit" style={{backgroundColor: '#138F52'}}>
|
||||||
|
<Toolbar>
|
||||||
|
<Typography style={{margin: '0 auto'}} variant="title" color="inherit">
|
||||||
|
<Link to="/">
|
||||||
|
<img src={AppLogo} className="app-book-menu-remove-icon" alt="Palmpay logo" />
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppHeader;
|
88
src/components/EnhancedSearch.js
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||||
|
import Checkbox from '@material-ui/core/Checkbox';
|
||||||
|
import TextField from '@material-ui/core/TextField';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||||
|
import {
|
||||||
|
Search
|
||||||
|
} from '@material-ui/icons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is used for type checking the props of the component.
|
||||||
|
*/
|
||||||
|
const propTypes = {
|
||||||
|
query: PropTypes.string.isRequired,
|
||||||
|
columns: PropTypes.array.isRequired,
|
||||||
|
onUpdateQuery: PropTypes.func.isRequired,
|
||||||
|
onColumnChange: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
root: {
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
margin: {
|
||||||
|
margin: theme.spacing.unit,
|
||||||
|
},
|
||||||
|
textField: {
|
||||||
|
flexBasis: 280,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description The Search page component of the table.
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} props - The props that were defined by the caller of this component.
|
||||||
|
*/
|
||||||
|
class EnhancedSearch extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {query, onUpdateQuery, onColumnChange} = this.props;
|
||||||
|
const { classes } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="search-books">
|
||||||
|
<div className="search-books-bar">
|
||||||
|
<div className="search-books-input-wrapper" style={{ textAlign: 'left', marginLeft: 20}}>
|
||||||
|
<TextField
|
||||||
|
style={{ textAlign: 'left', width: 350}}
|
||||||
|
id="outlined-adornment-weight"
|
||||||
|
className={classNames(classes.margin, classes.textField)}
|
||||||
|
variant="outlined"
|
||||||
|
label="Search"
|
||||||
|
placeholder="Enter search term"
|
||||||
|
value={query}
|
||||||
|
onChange={(event) => onUpdateQuery(event.target.value)}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: <InputAdornment position="start"><Search /></InputAdornment>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
{this.props.showSearchColumns && this.props.columns.map(column => (
|
||||||
|
<FormControlLabel
|
||||||
|
key={column.name}
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={column.checked}
|
||||||
|
onChange={onColumnChange(column.name)}
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={column.name}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type checking the props of the component
|
||||||
|
EnhancedSearch.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default withStyles(styles)(EnhancedSearch);
|
304
src/components/EnhancedTable.js
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import sortBy from 'sort-by';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Table from '@material-ui/core/Table';
|
||||||
|
import TableBody from '@material-ui/core/TableBody';
|
||||||
|
import TableCell from '@material-ui/core/TableCell';
|
||||||
|
import TablePagination from '@material-ui/core/TablePagination';
|
||||||
|
import TableRow from '@material-ui/core/TableRow';
|
||||||
|
import Checkbox from '@material-ui/core/Checkbox';
|
||||||
|
import Button from '@material-ui/core/Button';
|
||||||
|
|
||||||
|
// Custom components
|
||||||
|
import EnhancedTableHead from './EnhancedTableHead';
|
||||||
|
import EnhancedSearch from './EnhancedSearch';
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
root: {
|
||||||
|
width: '100%',
|
||||||
|
marginTop: -20,
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
minWidth: 1020,
|
||||||
|
},
|
||||||
|
tableWrapper: {
|
||||||
|
overflowX: 'auto',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is used for type checking the props of the component.
|
||||||
|
*/
|
||||||
|
const propTypes = {
|
||||||
|
columnData: PropTypes.array.isRequired,
|
||||||
|
data: PropTypes.array.isRequired,
|
||||||
|
orderBy: PropTypes.string.isRequired,
|
||||||
|
page: PropTypes.number,
|
||||||
|
rowsPerPage: PropTypes.number,
|
||||||
|
rowsPerPageOptions: PropTypes.array,
|
||||||
|
showSearchColumns: PropTypes.bool,
|
||||||
|
isAdmin: PropTypes.bool,
|
||||||
|
name: PropTypes.string,
|
||||||
|
onEdit: PropTypes.func,
|
||||||
|
onDelete: PropTypes.func,
|
||||||
|
onMultipleDelete: PropTypes.func,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object sets default values to the optional props.
|
||||||
|
*/
|
||||||
|
const defaultProps = {
|
||||||
|
showSearchColumns: true,
|
||||||
|
page: 0,
|
||||||
|
rowsPerPage: 100,
|
||||||
|
rowsPerPageOptions: [5, 10, 25, 50, 100],
|
||||||
|
isAdmin: false,
|
||||||
|
name: 'Default Table',
|
||||||
|
onEdit: () => {},
|
||||||
|
onDelete: () => {},
|
||||||
|
onMultipleDelete: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The custom table used at ambassadors and merchants.
|
||||||
|
*/
|
||||||
|
class EnhancedTable extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
order: 'asc',
|
||||||
|
orderBy: this.props.orderBy,
|
||||||
|
selected: [],
|
||||||
|
data: this.props.data,
|
||||||
|
page: this.props.page,
|
||||||
|
rowsPerPage: this.props.rowsPerPage,
|
||||||
|
searchQuery: '',
|
||||||
|
searchColumns: this.props.columnData.filter(column => !column.disableSearch).map(column => {
|
||||||
|
return {
|
||||||
|
name: column.id,
|
||||||
|
checked: true
|
||||||
|
};
|
||||||
|
}).sort(sortBy('name'))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRequestSort = (event, property) => {
|
||||||
|
const orderBy = property;
|
||||||
|
let order = 'desc';
|
||||||
|
|
||||||
|
if (this.state.orderBy === property && this.state.order === 'desc') {
|
||||||
|
order = 'asc';
|
||||||
|
}
|
||||||
|
|
||||||
|
const data =
|
||||||
|
order === 'desc'
|
||||||
|
? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
|
||||||
|
: this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
|
||||||
|
|
||||||
|
this.setState({ data, order, orderBy });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSelectAllClick = (event, checked) => {
|
||||||
|
if (checked) {
|
||||||
|
this.setState({ selected: this.state.data.map(n => n._id) });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({ selected: [] });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClick = (event, id) => {
|
||||||
|
const { selected } = this.state;
|
||||||
|
const selectedIndex = selected.indexOf(id);
|
||||||
|
let newSelected = [];
|
||||||
|
|
||||||
|
if (selectedIndex === -1) {
|
||||||
|
newSelected = newSelected.concat(selected, id);
|
||||||
|
} else if (selectedIndex === 0) {
|
||||||
|
newSelected = newSelected.concat(selected.slice(1));
|
||||||
|
} else if (selectedIndex === selected.length - 1) {
|
||||||
|
newSelected = newSelected.concat(selected.slice(0, -1));
|
||||||
|
} else if (selectedIndex > 0) {
|
||||||
|
newSelected = newSelected.concat(
|
||||||
|
selected.slice(0, selectedIndex),
|
||||||
|
selected.slice(selectedIndex + 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ selected: newSelected });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChangePage = (event, page) => {
|
||||||
|
this.setState({ page });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChangeRowsPerPage = event => {
|
||||||
|
this.setState({ rowsPerPage: event.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Update the query state and call the search.
|
||||||
|
* @param {string} query - The search term.
|
||||||
|
*/
|
||||||
|
updateQuery = (query) => {
|
||||||
|
query = query.trim();
|
||||||
|
// If query is empty or undefined
|
||||||
|
if (!query) {
|
||||||
|
this.setState({searchQuery: ''});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Update the search field as soon as the character is typed
|
||||||
|
this.setState({searchQuery: query});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the search column select box
|
||||||
|
*/
|
||||||
|
updateSearchColumn = name => () => {
|
||||||
|
const value = !(this.state.searchColumns.filter((c) => c.name === name)[0].checked);
|
||||||
|
const columns = this.state.searchColumns.filter((c) => c.name !== name);
|
||||||
|
columns.push({name: name, checked: value});
|
||||||
|
this.setState({searchColumns: columns.sort(sortBy('name'))});
|
||||||
|
};
|
||||||
|
|
||||||
|
isSelected = id => this.state.selected.indexOf(id) !== -1;
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { data } = this.props;
|
||||||
|
const { columnData, rowsPerPageOptions, showSearchColumns } = this.props;
|
||||||
|
const { order, orderBy, selected, rowsPerPage, page, searchQuery, searchColumns } = this.state;
|
||||||
|
|
||||||
|
// Logic of search query and columns
|
||||||
|
if(searchQuery.length > 0) {
|
||||||
|
data = data.filter((item) => {
|
||||||
|
let insert = false;
|
||||||
|
|
||||||
|
// Iterate over the search column select boxes
|
||||||
|
searchColumns.map(column => {
|
||||||
|
try {
|
||||||
|
if( column.checked &&
|
||||||
|
(item[column.name] !== undefined) &&
|
||||||
|
((item[column.name].toLowerCase()).indexOf(searchQuery.toLowerCase()) !== -1) ){
|
||||||
|
insert = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
//console.error(error);
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(insert){
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={styles.root}>
|
||||||
|
<EnhancedSearch
|
||||||
|
query={searchQuery}
|
||||||
|
columns={searchColumns}
|
||||||
|
showSearchColumns={showSearchColumns}
|
||||||
|
onUpdateQuery={this.updateQuery}
|
||||||
|
onColumnChange={this.updateSearchColumn}
|
||||||
|
/>
|
||||||
|
<div style={styles.tableWrapper}>
|
||||||
|
<Table style={styles.table} aria-labelledby="tableTitle">
|
||||||
|
<EnhancedTableHead
|
||||||
|
columnData={columnData}
|
||||||
|
numSelected={selected.length}
|
||||||
|
order={order}
|
||||||
|
orderBy={orderBy}
|
||||||
|
onSelectAllClick={this.handleSelectAllClick}
|
||||||
|
onRequestSort={this.handleRequestSort}
|
||||||
|
rowCount={data.length}
|
||||||
|
isAdmin={this.props.isAdmin}
|
||||||
|
/>
|
||||||
|
<TableBody>
|
||||||
|
{data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map(n => {
|
||||||
|
const isSelected = this.isSelected(n._id);
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
role="checkbox"
|
||||||
|
aria-checked={false}
|
||||||
|
tabIndex={-1}
|
||||||
|
key={this.props.name + '-trow-' + n._id}
|
||||||
|
selected={false}
|
||||||
|
>
|
||||||
|
{this.props.isAdmin &&
|
||||||
|
<TableCell padding="checkbox" style={{margin: 'auto 0', textAlign: 'center', padding: 0}}>
|
||||||
|
<Checkbox checked={isSelected} onClick={event => this.handleClick(event, n._id)} />
|
||||||
|
</TableCell>
|
||||||
|
}
|
||||||
|
{this.props.columnData.map(column => (
|
||||||
|
<TableCell
|
||||||
|
key={this.props.name + '-data-' + column.id}
|
||||||
|
component="th" scope="row" padding="none"
|
||||||
|
style={{margin: 'auto 0', textAlign: 'center', padding: 0}}
|
||||||
|
>
|
||||||
|
{n[column.id]}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
{this.props.isAdmin &&
|
||||||
|
<TableCell key={this.props.name + '-edit-' + n._id}
|
||||||
|
component="th" scope="row" padding="none"
|
||||||
|
style={{margin: 'auto 0', textAlign: 'center', padding: 0}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="App-button"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => this.props.onEdit(n._id)}
|
||||||
|
>Edit
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
}
|
||||||
|
{this.props.isAdmin &&
|
||||||
|
<TableCell key={this.props.name + '-delete-' + n._id}
|
||||||
|
component="th" scope="row" padding="none"
|
||||||
|
style={{margin: 'auto 0', textAlign: 'center', padding: 0}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="App-button"
|
||||||
|
variant="contained"
|
||||||
|
color="secondary"
|
||||||
|
onClick={() => this.props.onDelete(n._id)}
|
||||||
|
>Delete
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
}
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
<TablePagination
|
||||||
|
component="div"
|
||||||
|
count={data.length}
|
||||||
|
rowsPerPage={rowsPerPage}
|
||||||
|
rowsPerPageOptions={rowsPerPageOptions}
|
||||||
|
page={page}
|
||||||
|
backIconButtonProps={{
|
||||||
|
'aria-label': 'Previous Page',
|
||||||
|
}}
|
||||||
|
nextIconButtonProps={{
|
||||||
|
'aria-label': 'Next Page',
|
||||||
|
}}
|
||||||
|
onChangePage={this.handleChangePage}
|
||||||
|
onChangeRowsPerPage={this.handleChangeRowsPerPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type checking the props of the component
|
||||||
|
EnhancedTable.propTypes = propTypes;
|
||||||
|
// Assign default values to the optional props
|
||||||
|
EnhancedTable.defaultProps = defaultProps;
|
||||||
|
|
||||||
|
export default EnhancedTable;
|
88
src/components/EnhancedTableHead.js
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Checkbox from '@material-ui/core/Checkbox';
|
||||||
|
import TableCell from '@material-ui/core/TableCell';
|
||||||
|
import TableHead from '@material-ui/core/TableHead';
|
||||||
|
import TableRow from '@material-ui/core/TableRow';
|
||||||
|
import TableSortLabel from '@material-ui/core/TableSortLabel';
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is used for type checking the props of the component.
|
||||||
|
*/
|
||||||
|
const propTypes = {
|
||||||
|
columnData: PropTypes.array.isRequired,
|
||||||
|
numSelected: PropTypes.number.isRequired,
|
||||||
|
onRequestSort: PropTypes.func.isRequired,
|
||||||
|
onSelectAllClick: PropTypes.func.isRequired,
|
||||||
|
order: PropTypes.string.isRequired,
|
||||||
|
orderBy: PropTypes.string.isRequired,
|
||||||
|
rowCount: PropTypes.number.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object sets default values to the optional props.
|
||||||
|
*/
|
||||||
|
const defaultProps = {
|
||||||
|
isAdmin: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header of the table.
|
||||||
|
*/
|
||||||
|
class EnhancedTableHead extends React.Component {
|
||||||
|
createSortHandler = property => event => {
|
||||||
|
this.props.onRequestSort(event, property);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { onSelectAllClick, order, orderBy, numSelected, rowCount } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
{this.props.isAdmin &&
|
||||||
|
<TableCell padding="checkbox" style={{margin: 'auto 0', textAlign: 'center', padding: 0}}>
|
||||||
|
<Checkbox
|
||||||
|
indeterminate={numSelected > 0 && numSelected < rowCount}
|
||||||
|
checked={numSelected === rowCount}
|
||||||
|
onChange={onSelectAllClick}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
}
|
||||||
|
{this.props.columnData.map(column => {
|
||||||
|
return (
|
||||||
|
<TableCell
|
||||||
|
key={column.id}
|
||||||
|
numeric={column.numeric}
|
||||||
|
padding={column.disablePadding ? 'none' : 'default'}
|
||||||
|
sortDirection={orderBy === column.id ? order : false}
|
||||||
|
style={{margin: 'auto 0', textAlign: 'center', padding: 0}}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
title="Sort"
|
||||||
|
placement={column.numeric ? 'bottom-end' : 'bottom-start'}
|
||||||
|
enterDelay={300}
|
||||||
|
>
|
||||||
|
<TableSortLabel
|
||||||
|
active={orderBy === column.id}
|
||||||
|
direction={order}
|
||||||
|
onClick={this.createSortHandler(column.id)}
|
||||||
|
>
|
||||||
|
{column.label}
|
||||||
|
</TableSortLabel>
|
||||||
|
</Tooltip>
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
}, this)}
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnhancedTableHead.propTypes = propTypes;
|
||||||
|
EnhancedTableHead.defaultProps = defaultProps;
|
||||||
|
|
||||||
|
|
||||||
|
export default EnhancedTableHead;
|
140
src/components/Footer.js
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import React from "react";
|
||||||
|
import { FormattedMessage, FormattedHTMLMessage } from 'react-intl';
|
||||||
|
import {
|
||||||
|
Footer as FooterBase,
|
||||||
|
Row,
|
||||||
|
Col
|
||||||
|
} from "mdbreact";
|
||||||
|
|
||||||
|
// Images
|
||||||
|
import FooterLogo from '../assets/img/footer/footer_logo.png';
|
||||||
|
import DtubeLogo from '../assets/img/footer/dtube.png';
|
||||||
|
import SteemItLogo from '../assets/img/footer/steemit.png';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The footer of all pages.
|
||||||
|
*/
|
||||||
|
function Footer() {
|
||||||
|
return (
|
||||||
|
<span id="apppage">
|
||||||
|
<FooterBase className="footer text-center">
|
||||||
|
<div className="containerfix">
|
||||||
|
<Row className="vertical-align footerrow">
|
||||||
|
<Col md="4">
|
||||||
|
<img alt="" src={FooterLogo} />
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col md="4">
|
||||||
|
<ul className="list-inline social-buttons">
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://t.me/Agorise">
|
||||||
|
<i className="fa fa-telegram" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://twitter.com/Agorise_world">
|
||||||
|
<i className="fa fa-twitter" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://youtube.com/Agorise">
|
||||||
|
<i className="fa fa-youtube" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://d.tube/c/agorise">
|
||||||
|
<img alt="" src={DtubeLogo} />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://steemit.com/@Agorise">
|
||||||
|
<img alt="" src={SteemItLogo} />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Col>
|
||||||
|
<Col md="4">
|
||||||
|
<p className="mb-0">
|
||||||
|
<span id="ft_lang1">
|
||||||
|
<FormattedMessage id="footer.title" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="copyright py-3 text-center text-white"
|
||||||
|
style={{ fontSize: "0.8em" }}>
|
||||||
|
<div>
|
||||||
|
<div className="row vertical-align">
|
||||||
|
<div className="col-md-10 mx-md-auto">
|
||||||
|
<p>
|
||||||
|
<span id="cp_lang1">
|
||||||
|
<FormattedHTMLMessage id="footer.description" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row vertical-align">
|
||||||
|
<div className="col-md-4" />
|
||||||
|
<div className="col-md-4">
|
||||||
|
<small>
|
||||||
|
© Copyright {new Date().getFullYear()}{" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="http://agorise.world/">
|
||||||
|
{" "}
|
||||||
|
Agorise Ltd.{" "}
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row justify-content-end">
|
||||||
|
<div
|
||||||
|
style={{ fontSize: "0.8em" }}
|
||||||
|
className="text-white col-md-2">
|
||||||
|
<small>
|
||||||
|
Site by:
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://about.me/poqdavid">
|
||||||
|
poqdavid
|
||||||
|
</a>
|
||||||
|
{" "} & {" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="http://leticiacamara.com">
|
||||||
|
Leticia Camara
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FooterBase>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Footer;
|
156
src/components/HomeHeader.js
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Link } from "react-scroll";
|
||||||
|
import {
|
||||||
|
Collapse,
|
||||||
|
Container,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalHeader,
|
||||||
|
Navbar,
|
||||||
|
NavbarNav,
|
||||||
|
NavbarToggler,
|
||||||
|
NavItem,
|
||||||
|
NavLink
|
||||||
|
} from "mdbreact";
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
// Images
|
||||||
|
import HeaderLogo from '../assets/img/header/logo.png';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The header of the home page.
|
||||||
|
*/
|
||||||
|
class HomeHeader extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { showmenu: true };
|
||||||
|
this.state = {
|
||||||
|
collapse: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.toggleModal = this.toggleModal.bind(this);
|
||||||
|
this.onClick = this.onClick.bind(this);
|
||||||
|
this.handleNavbarClick = this.handleNavbarClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
this.setState({
|
||||||
|
collapse: !this.state.collapse
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleModal() {
|
||||||
|
this.setState({
|
||||||
|
modal2: !this.state.modal2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNavbarClick() {
|
||||||
|
this.setState({
|
||||||
|
collapse: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Navbar dark expand="md" fixed="top" id="mainNav" scrolling>
|
||||||
|
<Container className="menuc" hidden={this.state.showmenu}>
|
||||||
|
<Link
|
||||||
|
activeClass="active"
|
||||||
|
className="navbar-brand"
|
||||||
|
to="home"
|
||||||
|
href="/"
|
||||||
|
spy={true}
|
||||||
|
smooth={true}
|
||||||
|
offset={0}
|
||||||
|
duration={500}
|
||||||
|
onSetActive={this.handleSetActive}>
|
||||||
|
<img alt="" className="logo1" src={HeaderLogo} width="100%" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<NavbarToggler onClick={this.onClick} />
|
||||||
|
<Collapse isOpen={this.state.collapse} navbar>
|
||||||
|
<NavbarNav right>
|
||||||
|
<NavItem>
|
||||||
|
<Link
|
||||||
|
activeClass="active"
|
||||||
|
className="nav-link"
|
||||||
|
to="services"
|
||||||
|
spy={true}
|
||||||
|
smooth={true}
|
||||||
|
offset={0}
|
||||||
|
duration={500}
|
||||||
|
onSetActive={this.handleSetActive}>
|
||||||
|
<span id="nav_lang1">
|
||||||
|
<FormattedMessage id="header.services" />
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</NavItem>
|
||||||
|
<NavItem>
|
||||||
|
<Link
|
||||||
|
activeClass="active"
|
||||||
|
className="nav-link"
|
||||||
|
to="about"
|
||||||
|
spy={true}
|
||||||
|
smooth={true}
|
||||||
|
offset={0}
|
||||||
|
duration={500}
|
||||||
|
onSetActive={this.handleSetActive}>
|
||||||
|
<span id="nav_lang2">
|
||||||
|
<FormattedMessage id="header.about" />
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</NavItem>
|
||||||
|
<NavItem>
|
||||||
|
<Link
|
||||||
|
activeClass="active"
|
||||||
|
className="nav-link"
|
||||||
|
href="#testimonies"
|
||||||
|
to="testimonies"
|
||||||
|
spy={true}
|
||||||
|
smooth={true}
|
||||||
|
offset={0}
|
||||||
|
duration={500}
|
||||||
|
onSetActive={this.handleSetActive}>
|
||||||
|
<span id="nav_lang3">
|
||||||
|
<FormattedMessage id="header.testimonies" />
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</NavItem>
|
||||||
|
<NavItem>
|
||||||
|
<NavLink to="#!" onClick={this.toggleModal}>
|
||||||
|
<span id="nav_lang4">
|
||||||
|
<FormattedMessage id="header.downloads" />
|
||||||
|
</span>
|
||||||
|
</NavLink>
|
||||||
|
</NavItem>
|
||||||
|
</NavbarNav>
|
||||||
|
</Collapse>
|
||||||
|
</Container>
|
||||||
|
</Navbar>
|
||||||
|
<Modal isOpen={this.state.modal2} toggle={this.toggleModal}>
|
||||||
|
<ModalHeader toggle={this.toggleModal}>
|
||||||
|
<span id="mdl_lang1">Downloads</span>
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody className="text-center">
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://play.google.com/store/apps/details?id=cy.agorise.palmpay">
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
className="logo1"
|
||||||
|
src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
|
||||||
|
height="60"
|
||||||
|
width="auto"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</ModalBody>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HomeHeader;
|
283
src/components/LayerMap.js
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { compose, withStateHandlers } from 'recompose';
|
||||||
|
import {
|
||||||
|
withScriptjs,
|
||||||
|
withGoogleMap,
|
||||||
|
GoogleMap,
|
||||||
|
Marker,
|
||||||
|
InfoWindow
|
||||||
|
} from 'react-google-maps';
|
||||||
|
import { MarkerClusterer } from 'react-google-maps/lib/components/addons/MarkerClusterer';
|
||||||
|
|
||||||
|
// Custom Components
|
||||||
|
import LayerMapSwitches from './LayerMapSwitches';
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import GOOGLE_MAPS_API from '../utils/constants';
|
||||||
|
import Client from '../utils/feathers';
|
||||||
|
|
||||||
|
// Images
|
||||||
|
import MerchantPin from '../assets/img/map/merchant_pin.png';
|
||||||
|
import AmbassadorPin from '../assets/img/map/ambassador_pin.png';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is used for type checking the props of the component.
|
||||||
|
*/
|
||||||
|
const propTypes = {
|
||||||
|
ambassadors: PropTypes.array,
|
||||||
|
merchants: PropTypes.array,
|
||||||
|
mapCenter: PropTypes.object,
|
||||||
|
mapZoom: PropTypes.number,
|
||||||
|
// Fix google maps modal problem
|
||||||
|
showControls: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object sets default values to the optional props.
|
||||||
|
*/
|
||||||
|
const defaultProps = {
|
||||||
|
mapCenter: { lat: -22.9068, lng: -43.1729 },
|
||||||
|
mapZoom: 12,
|
||||||
|
googleMapURL:
|
||||||
|
`https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API}&v=3.exp&libraries=geometry,drawing,places`,
|
||||||
|
loadingElement: <div style={{ height: `100%` }} />,
|
||||||
|
containerElement: <div style={{ height: `100%` }} />,
|
||||||
|
mapElement: <div style={{ height: `400px` }} />,
|
||||||
|
// Fix google maps modal problem
|
||||||
|
showControls: true,
|
||||||
|
};
|
||||||
|
defaultProps['markers'] = [
|
||||||
|
defaultProps.mapCenter
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map that support Merchant Layer Markers and Ambassadors Layer Markers.
|
||||||
|
*/
|
||||||
|
const CustomLayerMap = compose(
|
||||||
|
withStateHandlers(() => ({
|
||||||
|
isOpenObj: {},
|
||||||
|
isOpenAmbassadorObj:{}
|
||||||
|
}), {
|
||||||
|
onToggleOpen: ({ isOpenObj }) => (index) => {
|
||||||
|
const openObj = isOpenObj;
|
||||||
|
openObj[index] = !openObj[index];
|
||||||
|
return openObj;
|
||||||
|
},
|
||||||
|
onToggleAmbassadorOpen: ({ isOpenAmbassadorObj }) => (index) => {
|
||||||
|
const openObj = isOpenAmbassadorObj;
|
||||||
|
openObj[index] = !openObj[index];
|
||||||
|
return openObj;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
withScriptjs,
|
||||||
|
withGoogleMap
|
||||||
|
)(props =>
|
||||||
|
<GoogleMap
|
||||||
|
defaultZoom={props.mapZoom}
|
||||||
|
defaultCenter={props.mapCenter}
|
||||||
|
>
|
||||||
|
<MarkerClusterer
|
||||||
|
averageCenter
|
||||||
|
enableRetinaIcons
|
||||||
|
gridSize={60}
|
||||||
|
zoomOnClick={true}
|
||||||
|
imagePath='http://www.luminiasoft.com/images/merchant_cluster'
|
||||||
|
>
|
||||||
|
{props.ambassadors.map( (marker, index) => (
|
||||||
|
marker.withInfo ? (
|
||||||
|
<Marker
|
||||||
|
key={index}
|
||||||
|
position={{ lat: marker.lat, lng: marker.lng }}
|
||||||
|
icon={AmbassadorPin}
|
||||||
|
onClick={() => props.onToggleOpen(index)}
|
||||||
|
>
|
||||||
|
{props.isOpenObj[index] && <InfoWindow onCloseClick={() => props.onToggleOpen(index)}>
|
||||||
|
<div>
|
||||||
|
<div style={{ font: "bold 16px Georgia, serif" }}>{marker.infoTitle}</div>
|
||||||
|
<br />
|
||||||
|
<div style={{ font: "14px Georgia, serif" }}>{marker.infoDescription}</div>
|
||||||
|
</div>
|
||||||
|
</InfoWindow>}
|
||||||
|
</Marker>
|
||||||
|
) : (
|
||||||
|
<Marker
|
||||||
|
key={index}
|
||||||
|
position={{ lat: marker.lat, lng: marker.lng }}
|
||||||
|
icon={AmbassadorPin}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</MarkerClusterer>
|
||||||
|
<MarkerClusterer
|
||||||
|
averageCenter
|
||||||
|
enableRetinaIcons
|
||||||
|
gridSize={60}
|
||||||
|
zoomOnClick={true}
|
||||||
|
imagePath='http://www.luminiasoft.com/images/ambassador_cluster'
|
||||||
|
>
|
||||||
|
{props.merchants.map( (marker, index) => (
|
||||||
|
marker.withInfo ? (
|
||||||
|
<Marker
|
||||||
|
key={index}
|
||||||
|
position={{ lat: marker.lat, lng: marker.lng }}
|
||||||
|
icon={MerchantPin}
|
||||||
|
onClick={() => props.onToggleAmbassadorOpen(index)}
|
||||||
|
>
|
||||||
|
{props.isOpenAmbassadorObj[index] && <InfoWindow onCloseClick={() => props.onToggleAmbassadorOpen(index)}>
|
||||||
|
<div>
|
||||||
|
<div style={{ font: "bold 16px Georgia, serif" }}>{marker.infoTitle}</div>
|
||||||
|
<br />
|
||||||
|
<div style={{ font: "14px Georgia, serif" }}>{marker.infoDescription}</div>
|
||||||
|
</div>
|
||||||
|
</InfoWindow>}
|
||||||
|
</Marker>
|
||||||
|
) : (
|
||||||
|
<Marker
|
||||||
|
key={index}
|
||||||
|
position={{ lat: marker.lat, lng: marker.lng }}
|
||||||
|
icon={MerchantPin}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</MarkerClusterer>
|
||||||
|
</GoogleMap>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Type checking the props of the component
|
||||||
|
CustomLayerMap.propTypes = propTypes;
|
||||||
|
// Assign default values to the optional props
|
||||||
|
CustomLayerMap.defaultProps = defaultProps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is used for type checking the props of the component.
|
||||||
|
*/
|
||||||
|
const propTypesLayerMap = {
|
||||||
|
ambassadorsLayer: PropTypes.bool,
|
||||||
|
merchantsLayer: PropTypes.bool,
|
||||||
|
showControls: PropTypes.bool,
|
||||||
|
mapHeight: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
class LayerMap extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
ambassadors: [],
|
||||||
|
merchants: [],
|
||||||
|
ambassadorLayer: this.props.ambassadorsLayer,
|
||||||
|
merchantLayer: this.props.merchantsLayer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Lifecycle event handler called just after the App loads into the DOM.
|
||||||
|
*/
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
|
this.getAmbassadors();
|
||||||
|
this.getMerchants();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Get the ambassadors list from the web service.
|
||||||
|
* @param {string} id - Merchant ID.
|
||||||
|
*/
|
||||||
|
getAmbassadors = () => {
|
||||||
|
const app = this;
|
||||||
|
const ambassadors = Client.service('api/v1/ambassadors');
|
||||||
|
|
||||||
|
this.setState({loading: true});
|
||||||
|
|
||||||
|
ambassadors.find().then( (results) => {
|
||||||
|
const markers = results.data.map(ambassador => {
|
||||||
|
const marker = {
|
||||||
|
lat: ambassador.lat,
|
||||||
|
lng: ambassador.lon,
|
||||||
|
withInfo: true,
|
||||||
|
infoTitle: ambassador.nickname,
|
||||||
|
infoDescription: `${ambassador.city} - ${ambassador.country}`,
|
||||||
|
};
|
||||||
|
return marker;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once both return, update the state
|
||||||
|
app.setState({
|
||||||
|
ambassadors: markers,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
}).catch( error => {
|
||||||
|
app.setState({responseError: error.message, loading: false});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Get the merchants list from the web service.
|
||||||
|
* @param {string} id - Merchant ID.
|
||||||
|
*/
|
||||||
|
getMerchants = () => {
|
||||||
|
const app = this;
|
||||||
|
const merchants = Client.service('api/v1/merchants');
|
||||||
|
|
||||||
|
this.setState({loading: true});
|
||||||
|
|
||||||
|
merchants.find().then( (results) => {
|
||||||
|
const markers = results.data.map(merchant => {
|
||||||
|
const marker = {
|
||||||
|
lat: merchant.lat,
|
||||||
|
lng: merchant.lon,
|
||||||
|
withInfo: true,
|
||||||
|
infoTitle: merchant.name,
|
||||||
|
infoDescription: `${merchant.address}, ${merchant.city} - ${merchant.country}`,
|
||||||
|
};
|
||||||
|
return marker;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once both return, update the state
|
||||||
|
app.setState({
|
||||||
|
merchants: markers,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
}).catch( error => {
|
||||||
|
app.setState({responseError: error.message, loading: false});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleLayerChange = name => event => {
|
||||||
|
this.setState({ [name]: event.target.checked });
|
||||||
|
// Update any time changes
|
||||||
|
this.getAmbassadors();
|
||||||
|
this.getMerchants();
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// create an array with marker components
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{!this.props.showControls ? (
|
||||||
|
<LayerMapSwitches
|
||||||
|
ambassadors={this.state.ambassadorLayer}
|
||||||
|
merchants={this.state.merchantLayer}
|
||||||
|
onChange={this.handleLayerChange}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ height: 56 }}></div>
|
||||||
|
)}
|
||||||
|
<CustomLayerMap
|
||||||
|
ambassadors={this.state.ambassadorLayer ? this.state.ambassadors: []}
|
||||||
|
merchants={this.state.merchantLayer ? this.state.merchants: []}
|
||||||
|
mapZoom={3}
|
||||||
|
mapCenter={{ lat: 0, lng: 0 }}
|
||||||
|
loadingElement={<div style={{ height: `100%` }} />}
|
||||||
|
containerElement={<div style={{ height: `100%` }} />}
|
||||||
|
mapElement={<div style={{ height: this.props.mapHeight ? this.props.mapHeight: '400px' }} />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type checking the props of the component
|
||||||
|
LayerMap.propTypes = propTypesLayerMap;
|
||||||
|
|
||||||
|
export default LayerMap;
|
125
src/components/LayerMapSwitches.js
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import purple from '@material-ui/core/colors/purple';
|
||||||
|
import FormGroup from '@material-ui/core/FormGroup';
|
||||||
|
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||||
|
import Switch from '@material-ui/core/Switch';
|
||||||
|
|
||||||
|
const styles = theme => ({
|
||||||
|
colorSwitchBase: {
|
||||||
|
color: purple[300],
|
||||||
|
'&$colorChecked': {
|
||||||
|
color: purple[500],
|
||||||
|
'& + $colorBar': {
|
||||||
|
backgroundColor: purple[500],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
colorBar: {},
|
||||||
|
colorChecked: {},
|
||||||
|
iOSSwitchBase: {
|
||||||
|
'&$iOSChecked': {
|
||||||
|
color: theme.palette.common.white,
|
||||||
|
'& + $iOSBar': {
|
||||||
|
backgroundColor: '#52d869',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transition: theme.transitions.create('transform', {
|
||||||
|
duration: theme.transitions.duration.shortest,
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
iOSChecked: {
|
||||||
|
transform: 'translateX(15px)',
|
||||||
|
'& + $iOSBar': {
|
||||||
|
opacity: 1,
|
||||||
|
border: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
iOSBar: {
|
||||||
|
borderRadius: 13,
|
||||||
|
width: 42,
|
||||||
|
height: 26,
|
||||||
|
marginTop: -13,
|
||||||
|
marginLeft: -21,
|
||||||
|
border: 'solid 1px',
|
||||||
|
borderColor: theme.palette.grey[400],
|
||||||
|
backgroundColor: theme.palette.grey[50],
|
||||||
|
opacity: 1,
|
||||||
|
transition: theme.transitions.create(['background-color', 'border']),
|
||||||
|
},
|
||||||
|
iOSIcon: {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
},
|
||||||
|
iOSIconChecked: {
|
||||||
|
boxShadow: theme.shadows[1],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The control of the map layers.
|
||||||
|
*/
|
||||||
|
class LayerMapSwitches extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormGroup row>
|
||||||
|
<div style={{ textAlign: 'center', marginLeft: 'auto', marginRight: 'auto'}}>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={this.props.ambassadors}
|
||||||
|
onChange={this.props.onChange('ambassadorLayer')}
|
||||||
|
value="ambassadors"
|
||||||
|
classes={{
|
||||||
|
switchBase: classes.iOSSwitchBase,
|
||||||
|
bar: classes.iOSBar,
|
||||||
|
icon: classes.iOSIcon,
|
||||||
|
iconChecked: classes.iOSIconChecked,
|
||||||
|
checked: classes.iOSChecked,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Ambassadors"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
classes={{
|
||||||
|
switchBase: classes.iOSSwitchBase,
|
||||||
|
bar: classes.iOSBar,
|
||||||
|
icon: classes.iOSIcon,
|
||||||
|
iconChecked: classes.iOSIconChecked,
|
||||||
|
checked: classes.iOSChecked,
|
||||||
|
}}
|
||||||
|
disableRipple
|
||||||
|
checked={this.props.merchants}
|
||||||
|
onChange={this.props.onChange('merchantLayer')}
|
||||||
|
value="merchants"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Merchants"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LayerMapSwitches.propTypes = {
|
||||||
|
classes: PropTypes.object.isRequired,
|
||||||
|
ambassadors: PropTypes.bool,
|
||||||
|
merchants: PropTypes.bool,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
LayerMapSwitches.defaultProps = {
|
||||||
|
ambassadors: true,
|
||||||
|
merchants: true
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(LayerMapSwitches);
|
68
src/components/MarketingHeader.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import {
|
||||||
|
Navbar,
|
||||||
|
NavbarNav,
|
||||||
|
NavbarToggler,
|
||||||
|
Collapse,
|
||||||
|
Container
|
||||||
|
} from "mdbreact";
|
||||||
|
//import { Link } from "react-scroll";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The header of the marketing page.
|
||||||
|
*/
|
||||||
|
class MarketingHeader extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { showmenu: true };
|
||||||
|
this.state = {
|
||||||
|
collapse: false
|
||||||
|
};
|
||||||
|
this.onClick = this.onClick.bind(this);
|
||||||
|
this.handleNavbarClick = this.handleNavbarClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
this.setState({
|
||||||
|
collapse: !this.state.collapse
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNavbarClick() {
|
||||||
|
this.setState({
|
||||||
|
collapse: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Navbar
|
||||||
|
dark
|
||||||
|
expand="md"
|
||||||
|
fixed="top"
|
||||||
|
id="mainNav"
|
||||||
|
scrolling
|
||||||
|
className="top-nav-collapse">
|
||||||
|
<Container hidden={this.state.showmenu}>
|
||||||
|
<a activeClass="active" className="navbar-brand" href="/">
|
||||||
|
<img alt="" className="logo1" src="./img/logo.png" width="100%" />
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
className="logo2"
|
||||||
|
src="./img/logo54.png"
|
||||||
|
width="100%"
|
||||||
|
style={{ display: "none" }}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<NavbarToggler onClick={this.onClick} />
|
||||||
|
<Collapse isOpen={this.state.collapse} navbar>
|
||||||
|
<NavbarNav right />
|
||||||
|
</Collapse>
|
||||||
|
</Container>
|
||||||
|
</Navbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MarketingHeader;
|
133
src/components/PreviewMap.js
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
import React from "react";
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import GOOGLE_MAPS_API from '../utils/constants';
|
||||||
|
import { compose, withStateHandlers } from 'recompose';
|
||||||
|
import {
|
||||||
|
withScriptjs,
|
||||||
|
withGoogleMap,
|
||||||
|
GoogleMap,
|
||||||
|
Marker,
|
||||||
|
InfoWindow
|
||||||
|
} from 'react-google-maps';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object sets default values to the optional props.
|
||||||
|
*/
|
||||||
|
const defaultProps = {
|
||||||
|
mapCenter: { lat: -22.9068, lng: -43.1729 },
|
||||||
|
mapZoom: 12,
|
||||||
|
googleMapURL:
|
||||||
|
`https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API}&v=3.exp&libraries=geometry,drawing,places`,
|
||||||
|
loadingElement: <div style={{ height: `100%` }} />,
|
||||||
|
containerElement: <div style={{ height: `100%` }} />,
|
||||||
|
mapElement: <div style={{ height: `400px` }} />,
|
||||||
|
};
|
||||||
|
defaultProps['markers'] = [
|
||||||
|
defaultProps.mapCenter
|
||||||
|
];
|
||||||
|
|
||||||
|
const CustomGoogleMaps = compose(
|
||||||
|
withStateHandlers(() => ({
|
||||||
|
isOpenObj: {},
|
||||||
|
}), {
|
||||||
|
onToggleOpen: ({ isOpenObj }) => (index) => {
|
||||||
|
const openObj = isOpenObj;
|
||||||
|
openObj[index] = !openObj[index];
|
||||||
|
return openObj;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
withScriptjs,
|
||||||
|
withGoogleMap
|
||||||
|
)(props =>
|
||||||
|
<GoogleMap
|
||||||
|
defaultZoom={props.mapZoom}
|
||||||
|
defaultCenter={props.mapCenter}
|
||||||
|
>
|
||||||
|
{props.markers.map( (marker, index) => (
|
||||||
|
marker.withInfo ? (
|
||||||
|
<Marker
|
||||||
|
key={index}
|
||||||
|
position={{ lat: marker.lat, lng: marker.lng }}
|
||||||
|
icon={marker.icon ? marker.icon:''}
|
||||||
|
onClick={() => props.onToggleOpen(index)}
|
||||||
|
>
|
||||||
|
{props.isOpenObj[index] && <InfoWindow onCloseClick={() => props.onToggleOpen(index)}>
|
||||||
|
<div>
|
||||||
|
<div style={{ font: "bold 16px Georgia, serif" }}>{marker.infoTitle}</div>
|
||||||
|
<br />
|
||||||
|
<div style={{ font: "14px Georgia, serif" }}>{marker.infoDescription}</div>
|
||||||
|
</div>
|
||||||
|
</InfoWindow>}
|
||||||
|
</Marker>
|
||||||
|
) : (
|
||||||
|
<Marker
|
||||||
|
key={index}
|
||||||
|
position={{ lat: marker.lat, lng: marker.lng }}
|
||||||
|
icon={marker.icon ? marker.icon:''}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</GoogleMap>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Type checking the props of the component
|
||||||
|
CustomGoogleMaps.propTypes = {
|
||||||
|
mapCenter: PropTypes.object,
|
||||||
|
mapZoom: PropTypes.number,
|
||||||
|
};
|
||||||
|
// Assign default values to the optional props
|
||||||
|
CustomGoogleMaps.defaultProps = defaultProps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is used for type checking the props of the component.
|
||||||
|
*/
|
||||||
|
const propTypes = {
|
||||||
|
lat: PropTypes.number.isRequired,
|
||||||
|
lng: PropTypes.number.isRequired,
|
||||||
|
icon: PropTypes.string,
|
||||||
|
infoTitle: PropTypes.string,
|
||||||
|
infoDescription: PropTypes.string,
|
||||||
|
width: PropTypes.string,
|
||||||
|
height: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object sets default values to the optional props.
|
||||||
|
*/
|
||||||
|
const defaultPropsPreview = {
|
||||||
|
width: '300px',
|
||||||
|
height: '150px'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A preview map for one element - usually an ambassador or merchant.
|
||||||
|
*/
|
||||||
|
function PreviewMap(props) {
|
||||||
|
|
||||||
|
const marker = {
|
||||||
|
icon: props.icon ? props.icon : '',
|
||||||
|
lat: props.lat,
|
||||||
|
lng: props.lng
|
||||||
|
};
|
||||||
|
if(props.infoTitle && props.infoDescription) {
|
||||||
|
marker.withInfo = true;
|
||||||
|
marker.infoTitle = props.infoTitle;
|
||||||
|
marker.infoDescription = props.infoDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CustomGoogleMaps
|
||||||
|
mapCenter={{ lat: props.lat, lng: props.lng }}
|
||||||
|
mapZoom={12}
|
||||||
|
markers={ [marker] }
|
||||||
|
mapElement={<div style={{ width: props.width, height: props.height }} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type checking the props of the component
|
||||||
|
PreviewMap.propTypes = propTypes;
|
||||||
|
// Assign default values to the optional props
|
||||||
|
PreviewMap.defaultProps = defaultPropsPreview;
|
||||||
|
|
||||||
|
export default PreviewMap;
|
251
src/components/pages/AmbassadorsPage.js
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { FormattedMessage, FormattedHTMLMessage } from 'react-intl';
|
||||||
|
import Modal from 'react-modal';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import Button from '@material-ui/core/Button';
|
||||||
|
|
||||||
|
// Custom components
|
||||||
|
import AppHeader from '../AppHeader';
|
||||||
|
import Footer from '../Footer';
|
||||||
|
import EnhancedTable from '../EnhancedTable';
|
||||||
|
import LayerMap from '../LayerMap';
|
||||||
|
import PreviewMap from '../PreviewMap';
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import Client from '../../utils/feathers';
|
||||||
|
import { stripProtocol } from '../../utils/url';
|
||||||
|
import Countries from 'country-list';
|
||||||
|
|
||||||
|
// Images
|
||||||
|
import AmbassadorPin from '../../assets/img/map/ambassador_pin.png';
|
||||||
|
import LoadingGif from '../../assets/img/loading_icon.gif';
|
||||||
|
|
||||||
|
// List of countries
|
||||||
|
const countries = Countries();
|
||||||
|
|
||||||
|
const centerStyle = {
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadingStyle = {
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20,
|
||||||
|
display: 'block',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapsStyles = {
|
||||||
|
content : {
|
||||||
|
top : '50%',
|
||||||
|
left : '50%',
|
||||||
|
right : 'auto',
|
||||||
|
bottom : 'auto',
|
||||||
|
marginRight : '-50%',
|
||||||
|
transform : 'translate(-50%, -50%)',
|
||||||
|
minWidth : '300px'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const columnData = [
|
||||||
|
{ id: 'nickname', numeric: false, disablePadding: true, label: 'Nickname' },
|
||||||
|
{ id: 'location', numeric: false, disablePadding: true, label: 'BTS Account' },
|
||||||
|
{ id: 'telegram', numeric: false, disablePadding: false, label: 'Telegram Account' },
|
||||||
|
{ id: 'keybase', numeric: false, disablePadding: false, label: 'Keybase' },
|
||||||
|
{ id: 'email', numeric: false, disablePadding: false, label: 'Email' },
|
||||||
|
{ id: 'phone', numeric: false, disablePadding: false, label: 'Phone' },
|
||||||
|
{ id: 'map', numeric: false, disablePadding: false, label: 'Maps', disableSearch: true},
|
||||||
|
{ id: 'link', numeric: false, disablePadding: false, label: 'URL' }
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ambassador page component.
|
||||||
|
*/
|
||||||
|
class AmbassadorsPage extends Component {
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
/** @type {ComponentState} */
|
||||||
|
this.state = {
|
||||||
|
ambassadors: {
|
||||||
|
total: 0,
|
||||||
|
limit: 0,
|
||||||
|
skip: 0,
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
loading: true,
|
||||||
|
rowsPerPage: [100,200,300],
|
||||||
|
numberOfRows: 100,
|
||||||
|
page: 1,
|
||||||
|
total: undefined,
|
||||||
|
mapsModalIsOpen: false,
|
||||||
|
mapsTitle: '',
|
||||||
|
mapsDescription: '',
|
||||||
|
mapsLat: 0,
|
||||||
|
mapsLon: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Lifecycle event handler called just after the App loads into the DOM.
|
||||||
|
*/
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
|
// Get the ambassadors list
|
||||||
|
this.getAmbassadors();
|
||||||
|
}
|
||||||
|
|
||||||
|
fillResults(result) {
|
||||||
|
const data = result;
|
||||||
|
return (item) => data.data.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Get ambassadors from the web service
|
||||||
|
* @param {number} [limit=10] - Max items to be returned.
|
||||||
|
* @param {number} [skip=0] - Start index search
|
||||||
|
*/
|
||||||
|
getAmbassadors = async (limit = 10, skip = 0) => {
|
||||||
|
const app = this;
|
||||||
|
// Initially we don't know how much the total value is, so to make sure we enter the loop
|
||||||
|
// at least once we're just setting it to be 1
|
||||||
|
let total = 1;
|
||||||
|
|
||||||
|
const ambassadors = Client.service('api/v1/ambassadors');
|
||||||
|
this.setState({loading: true});
|
||||||
|
let result;
|
||||||
|
while(skip < total){
|
||||||
|
let partialResponse = await ambassadors.find({
|
||||||
|
query: {
|
||||||
|
$sort: { account: 1 },
|
||||||
|
$limit: limit,
|
||||||
|
$skip: skip
|
||||||
|
}
|
||||||
|
});
|
||||||
|
total = partialResponse.total;
|
||||||
|
result === undefined ? result = partialResponse : partialResponse.data.map(this.fillResults(result));
|
||||||
|
skip = skip + limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.data.forEach(function(ambassador){
|
||||||
|
if(ambassador.city !== undefined) ambassador.city = (ambassador.city).replace(/(^|\s)\S/g, l => l.toUpperCase());
|
||||||
|
if(ambassador.country !== undefined) ambassador.country = countries.getName(ambassador.country);
|
||||||
|
// Setup disabled to be string
|
||||||
|
ambassador.disabled = (ambassador.disabled) ? 'yes': '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once both return, update the state
|
||||||
|
app.setState({loading: false, ambassadors: result});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Close Maps modal.
|
||||||
|
*/
|
||||||
|
closeMapsModal() {
|
||||||
|
this.setState({
|
||||||
|
mapsLat: 0,
|
||||||
|
mapsLon: 0,
|
||||||
|
mapsModalIsOpen: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openMaps(name, address, lat, lon){
|
||||||
|
this.setState({
|
||||||
|
mapsTitle: name,
|
||||||
|
mapsDescription: address,
|
||||||
|
mapsLat: lat,
|
||||||
|
mapsLon: lon,
|
||||||
|
mapsModalIsOpen: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { data } = this.state.ambassadors;
|
||||||
|
|
||||||
|
data.map(ambassador => {
|
||||||
|
ambassador.location = `${ambassador.city} - ${ambassador.country}`;
|
||||||
|
ambassador.link = <a target="_blank" rel="noopener noreferrer"
|
||||||
|
href={ambassador.url}>{stripProtocol(ambassador.url)}</a>;
|
||||||
|
ambassador.map = <Button
|
||||||
|
className="App-button"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => this.openMaps(
|
||||||
|
ambassador.nickname,
|
||||||
|
`${ambassador.city} - ${ambassador.country}`,
|
||||||
|
ambassador.lat,
|
||||||
|
ambassador.lon
|
||||||
|
)}
|
||||||
|
>Show on Map
|
||||||
|
</Button>;
|
||||||
|
return ambassador;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AppHeader />
|
||||||
|
<h2 style={centerStyle}><FormattedMessage id="ambassadors.title" /></h2>
|
||||||
|
{ /* Conditional Rendering */}
|
||||||
|
{(this.state.loading) ? (
|
||||||
|
<img src={LoadingGif} alt="Loading" style={loadingStyle} />
|
||||||
|
): (
|
||||||
|
<div>
|
||||||
|
<p style={{ textAlign: 'left', marginLeft: 20, marginRight: 20 }}>
|
||||||
|
<FormattedHTMLMessage id="ambassadors.description1" />
|
||||||
|
<Link to="/merchants">
|
||||||
|
<FormattedMessage id="ambassadors.merchants_link_description" />
|
||||||
|
</Link>
|
||||||
|
<FormattedHTMLMessage id="ambassadors.description2" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
isOpen={this.state.mapsModalIsOpen}
|
||||||
|
onRequestClose={() => this.closeMapsModal()}
|
||||||
|
style={mapsStyles}
|
||||||
|
ariaHideApp={false}
|
||||||
|
contentLabel={this.state.mapsTitle}
|
||||||
|
>
|
||||||
|
<PreviewMap
|
||||||
|
icon={AmbassadorPin}
|
||||||
|
infoTitle={this.state.mapsTitle}
|
||||||
|
infoDescription={this.state.mapsDescription}
|
||||||
|
lat={this.state.mapsLat}
|
||||||
|
lng={this.state.mapsLon}
|
||||||
|
width="800px"
|
||||||
|
height="600px"
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
{(data.length > 0) ? (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
<EnhancedTable
|
||||||
|
columnData={columnData}
|
||||||
|
data={data}
|
||||||
|
orderBy="nickname"
|
||||||
|
showSearchColumns={false}
|
||||||
|
rowsPerPage={10}
|
||||||
|
isAdmin={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={centerStyle}>No Data found</div>
|
||||||
|
)}
|
||||||
|
<div className="map">
|
||||||
|
<LayerMap
|
||||||
|
ambassadorsLayer={true}
|
||||||
|
merchantsLayer={false}
|
||||||
|
mapHeight={'600px'}
|
||||||
|
showControls={this.state.mapsModalIsOpen}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AmbassadorsPage };
|
7
src/components/pages/HomePage.css
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#apppage .vmain {
|
||||||
|
background-image: url("../../assets/img/header/header-bg.jpg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
37
src/components/pages/HomePage.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
// Custom Components
|
||||||
|
import HomeHeader from '../HomeHeader';
|
||||||
|
import {
|
||||||
|
AboutSection,
|
||||||
|
MainSection,
|
||||||
|
ServicesSection,
|
||||||
|
TestimoniesSection
|
||||||
|
} from '../sections';
|
||||||
|
import Footer from '../Footer';
|
||||||
|
|
||||||
|
import "./HomePage.css";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Home page component.
|
||||||
|
*/
|
||||||
|
function HomePage() {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<HomeHeader />
|
||||||
|
|
||||||
|
<MainSection />
|
||||||
|
|
||||||
|
<ServicesSection />
|
||||||
|
|
||||||
|
<AboutSection />
|
||||||
|
|
||||||
|
<TestimoniesSection />
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { HomePage };
|
176
src/components/pages/MarketingPage.js
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Button, Collapse } from "mdbreact";
|
||||||
|
import { FormattedMessage, FormattedHTMLMessage } from 'react-intl';
|
||||||
|
|
||||||
|
// Custom components
|
||||||
|
import MarketingHeader from '../MarketingHeader';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marketing page component.
|
||||||
|
*/
|
||||||
|
class MarketingPage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.toggle = this.toggle.bind(this);
|
||||||
|
this.onClick1 = this.onClick1.bind(this);
|
||||||
|
this.onClick2 = this.onClick2.bind(this);
|
||||||
|
this.onClick3 = this.onClick3.bind(this);
|
||||||
|
this.onClick4 = this.onClick4.bind(this);
|
||||||
|
this.state = {
|
||||||
|
collapse: false,
|
||||||
|
accordion: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this.setState({ collapse: !this.state.collapse });
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick1() {
|
||||||
|
let state = "";
|
||||||
|
|
||||||
|
if (this.state.accordion !== 1) {
|
||||||
|
state = 1;
|
||||||
|
} else {
|
||||||
|
state = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
accordion: state
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick2() {
|
||||||
|
let state = "";
|
||||||
|
|
||||||
|
if (this.state.accordion !== 2) {
|
||||||
|
state = 2;
|
||||||
|
} else {
|
||||||
|
state = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
accordion: state
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick3() {
|
||||||
|
let state = "";
|
||||||
|
|
||||||
|
if (this.state.accordion !== 3) {
|
||||||
|
state = 3;
|
||||||
|
} else {
|
||||||
|
state = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
accordion: state
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick4() {
|
||||||
|
let state = "";
|
||||||
|
|
||||||
|
if (this.state.accordion !== 4) {
|
||||||
|
state = 4;
|
||||||
|
} else {
|
||||||
|
state = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
accordion: state
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<MarketingHeader />
|
||||||
|
<div id="maincontent">
|
||||||
|
<section data-spy="scroll" data-target="#mainNav" id="services">
|
||||||
|
<div className="containerfix">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-10 mx-md-auto">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
block
|
||||||
|
color="primary"
|
||||||
|
onClick={this.onClick1}
|
||||||
|
style={{ marginBottom: "1rem" }}>
|
||||||
|
<FormattedMessage id="marketing.button.presentations" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Collapse isOpen={this.state.accordion === 1}>
|
||||||
|
<div className="list-group">
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.presentations1" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.presentations2" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.presentations3" />
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
block
|
||||||
|
color="primary"
|
||||||
|
onClick={this.onClick2}
|
||||||
|
style={{ marginBottom: "1rem" }}>
|
||||||
|
<FormattedMessage id="marketing.button.flyers" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Collapse isOpen={this.state.accordion === 2}>
|
||||||
|
<div className="list-group">
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers1" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers2" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers3" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers4" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers5" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers6" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers7" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers8" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers9" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.flyers10" />
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
block
|
||||||
|
color="primary"
|
||||||
|
onClick={this.onClick3}
|
||||||
|
style={{ marginBottom: "1rem" }}>
|
||||||
|
<FormattedMessage id="marketing.button.business_cards" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Collapse isOpen={this.state.accordion === 3}>
|
||||||
|
<div className="list-group">
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.business_cards1" />
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.business_cards2" />
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
block
|
||||||
|
color="primary"
|
||||||
|
onClick={this.onClick4}
|
||||||
|
style={{ marginBottom: "1rem" }}>
|
||||||
|
<FormattedMessage id="marketing.button.promotional_displays" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Collapse isOpen={this.state.accordion === 4}>
|
||||||
|
<div className="list-group">
|
||||||
|
<FormattedHTMLMessage id="marketing.accordion.promotional_displays1" />
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { MarketingPage };
|
246
src/components/pages/MerchantsPage.js
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { FormattedMessage, FormattedHTMLMessage } from 'react-intl';
|
||||||
|
import Modal from 'react-modal';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import Button from '@material-ui/core/Button';
|
||||||
|
|
||||||
|
// Custom components
|
||||||
|
import AppHeader from '../AppHeader';
|
||||||
|
import EnhancedTable from '../EnhancedTable';
|
||||||
|
import Footer from '../Footer';
|
||||||
|
import LayerMap from '../LayerMap';
|
||||||
|
import PreviewMap from '../PreviewMap';
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import Client from '../../utils/feathers';
|
||||||
|
import Countries from 'country-list';
|
||||||
|
|
||||||
|
// Images
|
||||||
|
import LoadingGif from '../../assets/img/loading_icon.gif';
|
||||||
|
import MerchantPin from '../../assets/img/map/merchant_pin.png';
|
||||||
|
|
||||||
|
// List of countries
|
||||||
|
const countries = Countries();
|
||||||
|
|
||||||
|
const centerStyle = {
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadingStyle = {
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20,
|
||||||
|
display: 'block',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapsStyles = {
|
||||||
|
content : {
|
||||||
|
top : '50%',
|
||||||
|
left : '50%',
|
||||||
|
right : 'auto',
|
||||||
|
bottom : 'auto',
|
||||||
|
marginRight : '-50%',
|
||||||
|
transform : 'translate(-50%, -50%)',
|
||||||
|
minWidth : '300px'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const columnData = [
|
||||||
|
{ id: 'name', numeric: false, disablePadding: true, label: 'Name' },
|
||||||
|
{ id: 'address', numeric: false, disablePadding: true, label: 'Address' },
|
||||||
|
{ id: 'city', numeric: false, disablePadding: false, label: 'City' },
|
||||||
|
{ id: 'country', numeric: false, disablePadding: false, label: 'Country' },
|
||||||
|
{ id: 'map', numeric: false, disablePadding: false, label: 'Maps', disableSearch: true}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merchant page component.
|
||||||
|
*/
|
||||||
|
class MerchantsPage extends Component {
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
/** @type {ComponentState} */
|
||||||
|
this.state = {
|
||||||
|
merchants: {
|
||||||
|
total: 0,
|
||||||
|
limit: 0,
|
||||||
|
skip: 0,
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
loading: true,
|
||||||
|
rowsPerPage: [100,200,300],
|
||||||
|
numberOfRows: 100,
|
||||||
|
page: 1,
|
||||||
|
total: undefined,
|
||||||
|
dialogOpen: false,
|
||||||
|
dialogMultipleOpen: false,
|
||||||
|
modalIsOpen: false,
|
||||||
|
mapsModalIsOpen: false,
|
||||||
|
mapsTitle: '',
|
||||||
|
mapsDescription: '',
|
||||||
|
mapsLat: 0,
|
||||||
|
mapsLon: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Lifecycle event handler called just after the App loads into the DOM.
|
||||||
|
*/
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
|
// Get the ambassadors list
|
||||||
|
this.getMerchants();
|
||||||
|
}
|
||||||
|
|
||||||
|
fillResults(result) {
|
||||||
|
const data = result;
|
||||||
|
return (item) => data.data.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Get merchants from the web service
|
||||||
|
* @param {number} [limit=10] - Max items to be returned.
|
||||||
|
* @param {number} [skip=0] - Start index search
|
||||||
|
*/
|
||||||
|
getMerchants = async (limit = 10, skip = 0) => {
|
||||||
|
const app = this;
|
||||||
|
// Initially we don't know how much the total value is, so to make sure we enter the loop
|
||||||
|
// at least once we're just setting it to be 1
|
||||||
|
let total = 1;
|
||||||
|
|
||||||
|
const merchants = Client.service('api/v1/merchants');
|
||||||
|
this.setState({loading: true});
|
||||||
|
let result;
|
||||||
|
while(skip < total){
|
||||||
|
let partialResponse = await merchants.find({
|
||||||
|
query: {
|
||||||
|
$sort: { account: 1 },
|
||||||
|
$limit: limit,
|
||||||
|
$skip: skip
|
||||||
|
}
|
||||||
|
});
|
||||||
|
total = partialResponse.total;
|
||||||
|
result === undefined ? result = partialResponse : partialResponse.data.map(this.fillResults(result));
|
||||||
|
skip = skip + limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.data.forEach(function(merchants){
|
||||||
|
if(merchants.city !== undefined) merchants.city = (merchants.city).replace(/(^|\s)\S/g, l => l.toUpperCase());
|
||||||
|
if(merchants.country !== undefined) merchants.country = countries.getName(merchants.country);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once both return, update the state
|
||||||
|
app.setState({loading: false, merchants: result});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Close Maps modal.
|
||||||
|
*/
|
||||||
|
closeMapsModal() {
|
||||||
|
this.setState({
|
||||||
|
mapsLat: 0,
|
||||||
|
mapsLon: 0,
|
||||||
|
mapsModalIsOpen: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openMaps(name, address, lat, lon){
|
||||||
|
this.setState({
|
||||||
|
mapsTitle: name,
|
||||||
|
mapsDescription: address,
|
||||||
|
mapsLat: lat,
|
||||||
|
mapsLon: lon,
|
||||||
|
mapsModalIsOpen: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { data } = this.state.merchants;
|
||||||
|
|
||||||
|
data.map(merchant => {
|
||||||
|
merchant.map = <Button
|
||||||
|
className="App-button"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => this.openMaps(
|
||||||
|
merchant.name,
|
||||||
|
`${merchant.address}, ${merchant.city} - ${merchant.country}`,
|
||||||
|
merchant.lat,
|
||||||
|
merchant.lon
|
||||||
|
)}
|
||||||
|
>Show on Map
|
||||||
|
</Button>;
|
||||||
|
return merchant;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AppHeader />
|
||||||
|
<h2 style={centerStyle}><FormattedMessage id="merchants.title" /></h2>
|
||||||
|
{ /* Conditional Rendering */}
|
||||||
|
{(this.state.loading) ? (
|
||||||
|
<img src={LoadingGif} alt="Loading" style={loadingStyle} />
|
||||||
|
): (
|
||||||
|
<div>
|
||||||
|
<p style={{ textAlign: 'left', marginLeft: 20, marginRight: 20 }}>
|
||||||
|
<FormattedHTMLMessage id="merchants.description1" />
|
||||||
|
<Link to="/ambs">
|
||||||
|
<FormattedMessage id="merchants.ambassadors_link_description" />
|
||||||
|
</Link>
|
||||||
|
<FormattedHTMLMessage id="merchants.description2" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
isOpen={this.state.mapsModalIsOpen}
|
||||||
|
onRequestClose={() => this.closeMapsModal()}
|
||||||
|
style={mapsStyles}
|
||||||
|
ariaHideApp={false}
|
||||||
|
contentLabel={this.state.mapsTitle}
|
||||||
|
>
|
||||||
|
<PreviewMap
|
||||||
|
icon={MerchantPin}
|
||||||
|
infoTitle={this.state.mapsTitle}
|
||||||
|
infoDescription={this.state.mapsDescription}
|
||||||
|
lat={this.state.mapsLat}
|
||||||
|
lng={this.state.mapsLon}
|
||||||
|
width="800px"
|
||||||
|
height="600px"
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{(data.length > 0) ? (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
<EnhancedTable
|
||||||
|
columnData={columnData}
|
||||||
|
data={data}
|
||||||
|
orderBy="account"
|
||||||
|
rowsPerPage={10}
|
||||||
|
showSearchColumns={false}
|
||||||
|
isAdmin={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={centerStyle}>No Data found</div>
|
||||||
|
)}
|
||||||
|
<div className="map">
|
||||||
|
<LayerMap
|
||||||
|
ambassadorsLayer={false}
|
||||||
|
merchantsLayer={true}
|
||||||
|
mapHeight={'600px'}
|
||||||
|
showControls={this.state.mapsModalIsOpen}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { MerchantsPage };
|
23
src/components/pages/NotFoundPage.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
// Custom components
|
||||||
|
import AppHeader from '../AppHeader';
|
||||||
|
import Footer from '../Footer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page component showed when no route is found.
|
||||||
|
*/
|
||||||
|
function NotFoundPage() {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<AppHeader />
|
||||||
|
|
||||||
|
<h1 style={{ marginTop: 100 }}>Page Not Found</h1>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { NotFoundPage };
|
6
src/components/pages/index.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Export app components
|
||||||
|
export * from './AmbassadorsPage';
|
||||||
|
export * from './HomePage';
|
||||||
|
export * from './MarketingPage';
|
||||||
|
export * from './MerchantsPage';
|
||||||
|
export * from './NotFoundPage';
|
227
src/components/sections/AboutSection.js
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
import React from "react";
|
||||||
|
import { FormattedMessage, FormattedHTMLMessage } from 'react-intl';
|
||||||
|
|
||||||
|
// Images
|
||||||
|
import AboutImage1 from '../../assets/img/about/ab1.png';
|
||||||
|
import AboutImage2 from '../../assets/img/about/ab2.png';
|
||||||
|
import AboutImage3 from '../../assets/img/about/ab3.png';
|
||||||
|
import AboutImage4 from '../../assets/img/about/ab4.png';
|
||||||
|
import AboutImage5 from '../../assets/img/about/ab5.png';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The about section of the home page.
|
||||||
|
*/
|
||||||
|
function AboutSection() {
|
||||||
|
return (
|
||||||
|
<section className="about bg-secondary" id="about">
|
||||||
|
<div className="container">
|
||||||
|
<div className="row align-items-center">
|
||||||
|
<div className="col-lg-6 order-lg-2">
|
||||||
|
<div className="p-5">
|
||||||
|
<img alt="" className="img-fluid" src={AboutImage1} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6 order-lg-1">
|
||||||
|
<div className="p-5">
|
||||||
|
<h2 className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang1">
|
||||||
|
<FormattedMessage id="home.about.about1_title" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang2">
|
||||||
|
<FormattedMessage id="home.about.about1_description1" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang3">
|
||||||
|
<FormattedMessage id="home.about.about1_description2" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang4">
|
||||||
|
<FormattedMessage id="home.about.about1_description3" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang5">
|
||||||
|
<FormattedMessage id="home.about.about1_description4" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row align-items-center">
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<div className="p-5">
|
||||||
|
<img alt="" className="img-fluid" src={AboutImage2} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<div className="p-5">
|
||||||
|
<h2 className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang6">
|
||||||
|
<FormattedMessage id="home.about.about2_title" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang7">
|
||||||
|
<FormattedMessage id="home.about.about2_description1" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang8">
|
||||||
|
<FormattedMessage id="home.about.about2_description2" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang9">
|
||||||
|
<FormattedMessage id="home.about.about2_description3" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row align-items-center">
|
||||||
|
<div className="col-lg-6 order-lg-2">
|
||||||
|
<div className="p-5">
|
||||||
|
<img alt="" className="img-fluid" src={AboutImage3} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6 order-lg-1">
|
||||||
|
<div className="p-5">
|
||||||
|
<h2 className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang10">
|
||||||
|
<FormattedMessage id="home.about.about3_title" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang11">
|
||||||
|
<FormattedMessage id="home.about.about3_description1" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang12">
|
||||||
|
<FormattedMessage id="home.about.about3_description2" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang13">
|
||||||
|
<FormattedMessage id="home.about.about3_description3" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang14">
|
||||||
|
<FormattedHTMLMessage id="home.about.about3_description4" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row align-items-center">
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<div className="p-5">
|
||||||
|
<img alt="" className="img-fluid" src={AboutImage4} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<div className="p-5">
|
||||||
|
<h2 className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang15">
|
||||||
|
<FormattedMessage id="home.about.about4_title" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang16">
|
||||||
|
<FormattedHTMLMessage id="home.about.about4_description1" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang17">
|
||||||
|
<FormattedMessage id="home.about.about4_description2" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row align-items-center">
|
||||||
|
<div className="col-lg-6 order-lg-2">
|
||||||
|
<div className="p-5">
|
||||||
|
<img alt="" className="img-fluid" src={AboutImage5} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6 order-lg-1">
|
||||||
|
<div className="p-5">
|
||||||
|
<h2 className="display-5 about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang18">
|
||||||
|
<FormattedMessage id="home.about.about5_title" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="about-text">
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang19">
|
||||||
|
<FormattedMessage id="home.about.about5_description1" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang20">
|
||||||
|
<FormattedMessage id="home.about.about5_description2" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<span id="ab_lang21">
|
||||||
|
<FormattedMessage id="home.about.about5_description3" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AboutSection };
|
53
src/components/sections/MainSection.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import React from "react";
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import {
|
||||||
|
Mask,
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Button,
|
||||||
|
View,
|
||||||
|
Container
|
||||||
|
} from "mdbreact";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main section of the home page.
|
||||||
|
*/
|
||||||
|
function MainSection() {
|
||||||
|
return (
|
||||||
|
<View className="vmain">
|
||||||
|
<Mask className="d-flex justify-content-center align-items-center gradient">
|
||||||
|
<Container className="px-md-3 px-sm-0">
|
||||||
|
<Row>
|
||||||
|
<Col lg="10" className="mx-auto white-text text-center">
|
||||||
|
<h1 className="text-uppercase">
|
||||||
|
<strong className="important">
|
||||||
|
<span id="header_lang1">
|
||||||
|
<FormattedMessage id="home.main.banner_title" />
|
||||||
|
</span>
|
||||||
|
</strong>
|
||||||
|
</h1>
|
||||||
|
<hr className="hr-light my-4 w-75" />
|
||||||
|
</Col>
|
||||||
|
<Col lg="8" className="mx-auto white-text text-center">
|
||||||
|
<p className="subtext-header mt-2 mb-4 text-faded">
|
||||||
|
<span id="header_lang2">
|
||||||
|
<FormattedMessage id="home.main.banner_description" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
href="#services"
|
||||||
|
className="btn-lg btn-primary"
|
||||||
|
rounded>
|
||||||
|
<span id="header_lang3">
|
||||||
|
<FormattedMessage id="home.main.banner_button" />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Mask>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { MainSection };
|
127
src/components/sections/ServicesSection.js
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import React from "react";
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
// Images
|
||||||
|
import BillPayCounters from '../../assets/img/services/bill_pay_counters.png';
|
||||||
|
import CafeBars from '../../assets/img/services/cafe_bars.png';
|
||||||
|
import DeliveryDrivers from '../../assets/img/services/delivery_drivers.png';
|
||||||
|
import GasStations from '../../assets/img/services/gas_stations.png';
|
||||||
|
import Grocery from '../../assets/img/services/grocery.png';
|
||||||
|
import PhoneOrders from '../../assets/img/services/phone_orders.png';
|
||||||
|
import Restaurants from '../../assets/img/services/restaurants.png';
|
||||||
|
import Retail from '../../assets/img/services/retail.png';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The services section of the home page.
|
||||||
|
*/
|
||||||
|
function ServicesSection() {
|
||||||
|
return (
|
||||||
|
<section data-spy="scroll" data-target="#mainNav" id="services">
|
||||||
|
<div className="containerfix">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-lg-12 text-center">
|
||||||
|
<h2 className="section-heading">
|
||||||
|
<span id="sr_lang1">
|
||||||
|
<FormattedMessage id="home.services.section_title" />
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<hr className="my-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="containerfix">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-lg-3 col-md-6 text-center">
|
||||||
|
<div className="service-box mt-5 mx-auto">
|
||||||
|
<i className="fa fa-4x text-primary mb-3 sr-icons">
|
||||||
|
<img alt="Retail" src={Retail} />
|
||||||
|
</i>
|
||||||
|
<h3 className="mb-3">
|
||||||
|
<span id="sr_lang2"><FormattedMessage id="home.services.retail" /></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-lg-3 col-md-6 text-center">
|
||||||
|
<div className="service-box mt-5 mx-auto">
|
||||||
|
<i className="fa fa-4x text-primary mb-3 sr-icons">
|
||||||
|
<img alt="Cafe/Bars" src={CafeBars} />
|
||||||
|
</i>
|
||||||
|
<h3 className="mb-3">
|
||||||
|
<span id="sr_lang3"><FormattedMessage id="home.services.cafe_bars" /></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-lg-3 col-md-6 text-center">
|
||||||
|
<div className="service-box mt-5 mx-auto">
|
||||||
|
<i className="fa fa-4x text-primary mb-3 sr-icons">
|
||||||
|
<img alt="Restaurants" src={Restaurants} />
|
||||||
|
</i>
|
||||||
|
<h3 className="mb-3">
|
||||||
|
<span id="sr_lang4"><FormattedMessage id="home.services.restaurants" /></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-lg-3 col-md-6 text-center">
|
||||||
|
<div className="service-box mt-5 mx-auto">
|
||||||
|
<i className="fa fa-4x text-primary mb-3 sr-icons">
|
||||||
|
<img alt="Gas Stations" src={GasStations} />
|
||||||
|
</i>
|
||||||
|
<h3 className="mb-3">
|
||||||
|
<span id="sr_lang5"><FormattedMessage id="home.services.gas_stations" /></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-lg-3 col-md-6 text-center">
|
||||||
|
<div className="service-box mt-5 mx-auto">
|
||||||
|
<i className="fa fa-4x text-primary mb-3 sr-icons">
|
||||||
|
<img alt="Bill-Pay Counters" src={BillPayCounters} />
|
||||||
|
</i>
|
||||||
|
<h3 className="mb-3">
|
||||||
|
<span id="sr_lang6"><FormattedMessage id="home.services.bill_pay" /></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-lg-3 col-md-6 text-center">
|
||||||
|
<div className="service-box mt-5 mx-auto">
|
||||||
|
<i className="fa fa-4x text-primary mb-3 sr-icons">
|
||||||
|
<img alt="Grocery" src={Grocery} />
|
||||||
|
</i>
|
||||||
|
<h3 className="mb-3">
|
||||||
|
<span id="sr_lang7"><FormattedMessage id="home.services.grocery" /></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-lg-3 col-md-6 text-center">
|
||||||
|
<div className="service-box mt-5 mx-auto">
|
||||||
|
<i className="fa fa-4x text-primary mb-3 sr-icons">
|
||||||
|
<img alt="Delivery Drivers" src={DeliveryDrivers} />
|
||||||
|
</i>
|
||||||
|
<h3 className="mb-3">
|
||||||
|
<span id="sr_lang8"><FormattedMessage id="home.services.delivery_drivers" /></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-lg-3 col-md-6 text-center">
|
||||||
|
<div className="service-box mt-5 mx-auto">
|
||||||
|
<i className="fa fa-4x text-primary mb-3 sr-icons">
|
||||||
|
<img alt="Phone Orders" src={PhoneOrders} />
|
||||||
|
</i>
|
||||||
|
<h3 className="mb-3">
|
||||||
|
<span id="sr_lang9"><FormattedMessage id="home.services.phone_orders" /></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ServicesSection };
|