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 };
|