palmpay.mx-website/src/components/pages/MerchantsPage.js

505 lines
16 KiB
JavaScript

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';
import sortBy from 'sort-by';
// Custom components
import AppHeader from '../AppHeader';
import EnhancedTable from '../EnhancedTable';
import Footer from '../Footer';
import LayerMap from '../LayerMap';
import PreviewMap from '../PreviewMap';
import { stripProtocol } from '../../utils/url';
// 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';
import { WHICH_OPTIONS } from '../../utils/constants';
import "./MerchantsPage.css";
// List of countries
const countries = Countries();
const centerStyle = {
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: 'phone', numeric: false, disablePadding: true, label: 'Phone' },
{ id: 'telegram', numeric: false, disablePadding: true, label: 'Telegram' },
{ id: 'link', numeric: false, disablePadding: false, label: 'Website' },
{ 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: []
},
merchantsSearch: [],
ambassadorsMarkers: [],
tellersMarkers: [],
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() {
this.getAmbassadors();
this.getMerchants();
}
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 = 50, 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/v2/ambassadors');
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;
}
const markers = [];
result.data.forEach(ambassador => {
ambassador.cities.forEach(function(city) {
const infoDescription = <div>
<div><b>Location</b>: {(city.name).replace(/(^|\s)\S/g, l => l.toUpperCase())} - {countries.getName(city.country)}</div>
{(ambassador.nickname) && (<div><b>Nickname</b>: {ambassador.nickname}</div>)}
{(ambassador.telegram) && (<div><b>Telegram</b>:
<a
href={`https://t.me/${(ambassador.telegram.trim().charAt(0) === '@') ? ambassador.telegram.trim().slice(1): ambassador.telegram.trim()}`}
target="_blank"
rel="noopener noreferrer"
>{ambassador.telegram}</a>
</div>)}
{(ambassador.keybase) && (<div><b>Keybase</b>: {ambassador.keybase}</div>)}
{(ambassador.email) && (<div><b>Email</b>: {ambassador.email}</div>)}
{(ambassador.phone) && (<div><b>Phone</b>: {ambassador.phone}</div>)}
{(ambassador.url) && (<div><b>URL:</b>: <a target="_blank" rel="noopener noreferrer"
href={ambassador.url}>{stripProtocol(ambassador.url)}</a></div>)}
</div>;
const marker = {
lat: city.lat,
lng: city.lon,
withInfo: true,
infoTitle: ambassador.nickname,
infoDescription: infoDescription,
};
markers.push(marker);
});
});
// Once both return, update the state
app.setState({ ambassadorsMarkers: markers });
};
/**
* @description Get tellers from the web service
* @param {number} [limit=10] - Max items to be returned.
* @param {number} [skip=0] - Start index search
*/
getTellers = async (limit = 50, 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 tellers = Client.service('api/v2/tellers');
this.setState({loading: true});
let result;
while(skip < total){
let partialResponse = await tellers.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(tellers){
if(tellers.city !== undefined) tellers.city = (tellers.city).replace(/(^|\s)\S/g, l => l.toUpperCase());
if(tellers.country !== undefined) tellers.country = countries.getName(tellers.country);
});
result.data.map(teller => {
teller.link = {
searchText: stripProtocol(teller.url),
value: (
<a target="_blank" rel="noopener noreferrer"
href={teller.url}>{stripProtocol(teller.url)}</a>
)
};
teller.location = {
searchText: `${teller.country} - ${teller.city}`,
value: (teller.city) ? `${teller.city} - ${teller.country}`: teller.country
}
teller.map = <Button
className="App-button"
variant="contained"
style={{
backgroundColor: "#fdcf09",
color: 'white',
whiteSpace: 'nowrap'
}}
onClick={() => this.openMaps(teller.gt_name, app.getTellerMarker(teller))}
>Show on Map
</Button>;
return teller;
});
// Once both return, update the state
app.setState({
loading: false,
tellersMarkers: result.data
});
};
getTellerMarker = (teller) => {
const which = WHICH_OPTIONS.filter(w => w.id.toLowerCase() === teller.which.toLowerCase());
teller.which_value = ( teller.which && which.length > 0) ?
which[0].value :
'';
const infoDescription = (
<div>
<div><b>Address</b>: {teller.address}</div>
{(teller.which) && (<div><b>Which:</b>: {teller.which}</div>)}
{(teller.bitshares_address) && (<div><b>BTS Account:</b>: {teller.bitshares_address}</div>)}
{(teller.address) && (<div><b>Address:</b>: {teller.address}</div>)}
{(teller.telegram_original) && (<div><b>Telegram</b>:
<a
href={`https://t.me/${(teller.telegram_original.trim().charAt(0) === '@') ? teller.telegram_original.trim().slice(1): teller.telegram_original.trim()}`}
target="_blank"
rel="noopener noreferrer"
>{teller.telegram_original}</a>
</div>)
}
{(teller.keybase) && (<div><b>Keybase</b>: {teller.keybase}</div>)}
{(teller.whatsapp) && (<div><b>Whatsapp</b>: {teller.whatsapp}</div>)}
{(teller.viber) && (<div><b>Viber</b>: {teller.viber}</div>)}
{(teller.email) && (<div><b>Email</b>: {teller.email}</div>)}
{(teller.phone) && (<div><b>Phone</b>: {teller.phone}</div>)}
{(teller.url) && (<div><b>URL:</b>: <a target="_blank" rel="noopener noreferrer"
href={teller.url}>{stripProtocol(teller.url)}</a></div>)}
</div>
);
const marker = {
lat: teller.lat,
lng: teller.lon,
withInfo: true,
infoTitle: teller.gt_name,
infoDescription: infoDescription,
};
return marker;
};
/**
* @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 = 50, 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);
});
result.data.map(merchant => {
if(merchant.telegram){
merchant.telegram_original = merchant.telegram;
merchant.telegram = {
searchText: merchant.telegram,
value: (
<a
href={`https://t.me/${(merchant.telegram.trim().charAt(0) === '@') ?
merchant.telegram.trim().slice(1): merchant.telegram.trim()}`}
target="_blank"
rel="noopener noreferrer"
>{merchant.telegram}</a>
)
};
}
else{
}
merchant.link = {
searchText: stripProtocol(merchant.website),
value: (
<a target="_blank" rel="noopener noreferrer"
href={merchant.website}>{stripProtocol(merchant.website)}</a>
)
};
merchant.location = {
searchText: `${merchant.country} - ${merchant.city}`,
value: (merchant.city) ? `${merchant.city} - ${merchant.country}`: merchant.country
}
merchant.map = <Button
className="App-button"
variant="contained"
style={{
backgroundColor: "#139657",
color: 'white',
whiteSpace: 'nowrap'
}}
onClick={() => this.openMaps(merchant.name, app.getMerchantMarker(merchant))}
>Show on Map
</Button>;
return merchant;
});
// Once both return, update the state
app.setState({
loading: false,
merchants: result,
merchantsSearch: result.data
});
};
/**
* @description Close Maps modal.
*/
closeMapsModal() {
this.setState({
mapsLat: 0,
mapsLon: 0,
mapsModalIsOpen: false
});
}
openMaps(name, marker){
this.setState({
mapsTitle: name,
mapsDescription: marker.infoDescription,
mapsLat: marker.lat,
mapsLon: marker.lng,
mapsModalIsOpen: true
});
}
getMerchantMarker = (merchant) => {
const infoDescription = <div>
<div><b>Address</b>: {merchant.address}</div>
{(merchant.phone) && (<div><b>Phone</b>: {merchant.phone}</div>)}
{(merchant.telegram_original) && (<div><b>Telegram</b>:
<a
href={`https://t.me/${(merchant.telegram_original.trim().charAt(0) === '@') ? merchant.telegram_original.trim().slice(1): merchant.telegram_original.trim()}`}
target="_blank"
rel="noopener noreferrer"
>{merchant.telegram_original}</a>
</div>)}
{(merchant.website) && (<div><b>Website:</b>: <a target="_blank" rel="noopener noreferrer"
href={merchant.website}>{stripProtocol(merchant.website)}</a></div>)}
</div>;
const marker = {
lat: merchant.lat,
lng: merchant.lon,
withInfo: true,
infoTitle: merchant.name,
infoDescription: infoDescription,
};
return marker;
};
handleSearchChange(data){
this.setState({ merchantsSearch: data });
}
render() {
let { data: merchantsData } = this.state.merchants;
const { ambassadorsMarkers, merchantsSearch } = this.state;
const merchantMarkers = merchantsSearch.map(merchant => {
return this.getMerchantMarker(merchant);
});
merchantsData = merchantsData.sort(sortBy('name'));
const textComponent = (
<span>
<FormattedHTMLMessage id="merchants.description1" />
<Link to="/ambs">
<FormattedMessage id="merchants.ambassadors_link_description" />
</Link>
<FormattedHTMLMessage id="merchants.description2" />
</span>
);
return (
<div>
<AppHeader />
<section data-spy="scroll" data-target="#mainNav" id="services" className="merchants_services" >
<div className="containerfix">
<div className="row">
<div className="col-md-10 mx-md-auto">
<h2 className="ambassadorsTitle merchantsMargin" style={centerStyle}><FormattedMessage id="merchants.title" /></h2>
{ /* Conditional Rendering */}
{(this.state.loading) ? (
<img src={LoadingGif} alt="Loading" style={loadingStyle} />
): (
<div>
<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>
{(merchantsData.length > 0) ? (
<div>
<br />
<EnhancedTable
description={textComponent}
columnData={columnData}
data={merchantsData}
orderBy="account"
rowsPerPage={10}
showSearchColumns={false}
isAdmin={false}
onSearchChange={(data) => this.handleSearchChange(data)}
/>
</div>
) : (
<div style={centerStyle}>No Data found</div>
)}
<div className="map">
<LayerMap
ambassadors={ambassadorsMarkers}
merchants={merchantMarkers}
ambassadorsLayer={false}
merchantsLayer={true}
tellersLayer={false}
mapHeight={'600px'}
showControls={this.state.mapsModalIsOpen}
/>
</div>
</div>
)}
</div>
</div>
</div>
</section>
<Footer />
</div>
);
}
}
export { MerchantsPage };