Complete site refactoring

mx
Letícia Camara 2018-09-22 15:23:51 -03:00
commit 4bebb23794
109 changed files with 20940 additions and 0 deletions

10
.eslintrc Normal file
View 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
View 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
View 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
View 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"
}
}

Binary file not shown.

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

91
public/index.html Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

26
src/assets/css/mdb.min.css vendored Normal file

File diff suppressed because one or more lines are too long

352
src/assets/css/palmpay.css Normal file
View 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

File diff suppressed because one or more lines are too long

101
src/assets/css/tooltip.css Normal file
View 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;
}
*/

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

84
src/components/App.js Normal file
View 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;

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

View 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);

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

View 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
View 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>
&copy; 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;

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

View 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);

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

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

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

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

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

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

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

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

View File

@ -0,0 +1,6 @@
// Export app components
export * from './AmbassadorsPage';
export * from './HomePage';
export * from './MarketingPage';
export * from './MerchantsPage';
export * from './NotFoundPage';

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

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

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

Some files were not shown because too many files have changed in this diff Show More