79 |
161 |
{
164 | this.directionsPanel = div;
165 | }}
166 | />
167 |
168 | );
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/view/frontend/preact/src/util/ScriptCache.js:
--------------------------------------------------------------------------------
1 | let counter = 0;
2 | let scriptMap = window._scriptMap || new Map();
3 |
4 | export const ScriptCache = (function(global) {
5 | global._scriptMap = global._scriptMap || scriptMap;
6 | return function ScriptCache(scripts) {
7 | const Cache = {};
8 |
9 | Cache._onLoad = function(key) {
10 | return (cb) => {
11 | let stored = scriptMap.get(key);
12 | if (stored) {
13 | stored.promise.then(() => {
14 | stored.error ? cb(stored.error) : cb(null, stored);
15 | return stored;
16 | });
17 | } else {
18 | // TODO:
19 | }
20 | };
21 | };
22 |
23 | Cache._scriptTag = (key, src) => {
24 | if (!scriptMap.has(key)) {
25 | let tag = document.createElement('script');
26 | let promise = new Promise((resolve, reject) => {
27 | let resolved = false,
28 | errored = false,
29 | body = document.getElementsByTagName('body')[0];
30 |
31 | tag.type = 'text/javascript';
32 | tag.async = false; // Load in order
33 |
34 | const cbName = `loaderCB${counter++}${Date.now()}`;
35 | let cb;
36 |
37 | let handleResult = (state) => {
38 | return (evt) => {
39 | let stored = scriptMap.get(key);
40 | if (state === 'loaded') {
41 | stored.resolved = true;
42 | resolve(src);
43 | // stored.handlers.forEach(h => h.call(null, stored))
44 | // stored.handlers = []
45 | } else if (state === 'error') {
46 | stored.errored = true;
47 | // stored.handlers.forEach(h => h.call(null, stored))
48 | // stored.handlers = [];
49 | reject(evt);
50 | }
51 | stored.loaded = true;
52 |
53 | cleanup();
54 | };
55 | };
56 |
57 | const cleanup = () => {
58 | if (global[cbName] && typeof global[cbName] === 'function') {
59 | global[cbName] = null;
60 | delete global[cbName];
61 | }
62 | };
63 |
64 | tag.onload = handleResult('loaded');
65 | tag.onerror = handleResult('error');
66 | tag.onreadystatechange = () => {
67 | handleResult(tag.readyState);
68 | };
69 |
70 | // Pick off callback, if there is one
71 | if (src.match(/callback=CALLBACK_NAME/)) {
72 | src = src.replace(/(callback=)[^\&]+/, `$1${cbName}`);
73 | cb = window[cbName] = tag.onload;
74 | } else {
75 | tag.addEventListener('load', tag.onload);
76 | }
77 | tag.addEventListener('error', tag.onerror);
78 |
79 | tag.src = src;
80 | body.appendChild(tag);
81 |
82 | return tag;
83 | });
84 | let initialState = {
85 | loaded: false,
86 | error: false,
87 | promise: promise,
88 | tag
89 | };
90 | scriptMap.set(key, initialState);
91 | }
92 | return scriptMap.get(key);
93 | };
94 |
95 | // let scriptTags = document.querySelectorAll('script')
96 | //
97 | // NodeList.prototype.filter = Array.prototype.filter;
98 | // NodeList.prototype.map = Array.prototype.map;
99 | // const initialScripts = scriptTags
100 | // .filter(s => !!s.src)
101 | // .map(s => s.src.split('?')[0])
102 | // .reduce((memo, script) => {
103 | // memo[script] = script;
104 | // return memo;
105 | // }, {});
106 |
107 | Object.keys(scripts).forEach(function(key) {
108 | const script = scripts[key];
109 |
110 | const tag = window._scriptMap.has(key) ?
111 | window._scriptMap.get(key).tag :
112 | Cache._scriptTag(key, script);
113 |
114 | Cache[key] = {
115 | tag: tag,
116 | onLoad: Cache._onLoad(key),
117 | };
118 | });
119 |
120 | return Cache;
121 | };
122 | })(window);
123 |
124 | export default ScriptCache;
125 |
--------------------------------------------------------------------------------
/Model/StoreLocatorRepository.php:
--------------------------------------------------------------------------------
1 | resource = $resource;
36 | $this->storeLocatorFactory = $storeLocatorFactory;
37 | $this->collectionFactory = $collectionFactory;
38 | }
39 |
40 | /**
41 | * Save Store
42 | *
43 | * @param \PandaGroup\StoreLocator\Api\Data\StoreLocatorInterface $store
44 | * @return \PandaGroup\StoreLocator\Api\Data\StoreLocatorInterface
45 | * @throws \Magento\Framework\Exception\CouldNotSaveException
46 | */
47 | public function save(\PandaGroup\StoreLocator\Api\Data\StoreLocatorInterface $store)
48 | {
49 | try {
50 | $this->resource->save($store);
51 | } catch (\Exception $exception) {
52 | throw new \Magento\Framework\Exception\CouldNotSaveException(__($exception->getMessage()));
53 | }
54 |
55 | return $store;
56 | }
57 |
58 | /**
59 | * Retrieve Store
60 | *
61 | * @param $storeId
62 | * @return \PandaGroup\StoreLocator\Model\StoreLocator
63 | * @throws \Magento\Framework\Exception\NoSuchEntityException
64 | */
65 | public function getById($storeId)
66 | {
67 | $store = $this->storeLocatorFactory->create();
68 | $this->resource->load($store, $storeId);
69 | if (!$store->getId()) {
70 | throw new NoSuchEntityException(__('Store with id "%1" does not exist.', $storeId));
71 | }
72 |
73 | return $store;
74 | }
75 |
76 | /**
77 | * Retrieve entity matching the specified criteria.
78 | *
79 | * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
80 | * @return \PandaGroup\StoreLocator\Api\Data\StatesInterface[]
81 | */
82 | public function getList(SearchCriteriaInterface $searchCriteria)
83 | {
84 | $collection = $this->collectionFactory->create();
85 | foreach ($searchCriteria->getFilterGroups() as $filterGroup) {
86 | foreach ($filterGroup->getFilters() as $filter) {
87 | $condition = $filter->getConditionType() ?: 'eq';
88 | $collection->addFieldToFilter($filter->getField(), [$condition => $filter->getValue()]);
89 | }
90 | }
91 |
92 | $sortOrders = $searchCriteria->getSortOrders();
93 | if ($sortOrders) {
94 | foreach ($sortOrders as $sortOrder) {
95 | $collection->addOrder(
96 | $sortOrder->getField(),
97 | ($sortOrder->getDirection() == SortOrder::SORT_ASC) ? 'ASC' : 'DESC'
98 | );
99 | }
100 | }
101 | $collection->setCurPage($searchCriteria->getCurrentPage());
102 | $collection->setPageSize($searchCriteria->getPageSize());
103 |
104 | return $collection->getItems();
105 | }
106 |
107 | /**
108 | * Delete store
109 | *
110 | * @param \PandaGroup\StoreLocator\Api\Data\StoreLocatorInterface $store
111 | * @return bool
112 | * @throws \Magento\Framework\Exception\CouldNotDeleteException
113 | */
114 | public function delete(\PandaGroup\StoreLocator\Api\Data\StoreLocatorInterface $store)
115 | {
116 | try {
117 | $this->resource->delete($store);
118 | } catch (\Exception $exception) {
119 | throw new CouldNotDeleteException(__($exception->getMessage()));
120 | }
121 |
122 | return true;
123 | }
124 |
125 | /**
126 | * Delete entity by ID.
127 | *
128 | * @param int $storeId
129 | * @return bool
130 | * @throws \Magento\Framework\Exception\NoSuchEntityException
131 | * @throws \Magento\Framework\Exception\CouldNotDeleteException
132 | */
133 | public function deleteById($storeId)
134 | {
135 | return $this->delete($this->getById($storeId));
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/view/frontend/preact/src/container/StoreView.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import { connect } from 'mobx-preact';
3 |
4 | import DirectionsTab from './../container/DirectionsTab';
5 | import HoursSpanFill from './../component/HoursSpanFill';
6 |
7 | @connect(['stateStore'])
8 | export default class StoreView extends Component {
9 | constructor(props, { router }) {
10 | super(props);
11 | this.state = {
12 | tab: 'directions',
13 | store: props.stores.find(this.findStore, this.routeId),
14 | };
15 | this.routeId = router.route.match.params.name;
16 | this.state.store = props.stores.find(this.findStore, this.routeId);
17 | }
18 |
19 | componentDidUpdate() {
20 | const currentRoute = this.context.router.route.match.params.name;
21 | if (this.routeId !== currentRoute) {
22 | this.routeId = currentRoute;
23 | this.setState({
24 | store: this.props.stores.find(this.findStore, this.routeId),
25 | });
26 | this.props.stateStore.changeMap(this.state.store.geo, this.state.store.zoom);
27 |
28 | document.title =
29 | "Peter Jackson's " + this.state.store.name + ' ' + this.state.store.addr_cty;
30 | document.getElementsByClassName('storelocator-header__title')[0].innerHTML =
31 | "Peter Jackson's " + this.state.store.name + ' ' + this.state.store.addr_cty;
32 | }
33 | }
34 |
35 | componentDidMount() {
36 | document.title =
37 | "Peter Jackson's " + this.state.store.name + ' ' + this.state.store.addr_cty;
38 | document.getElementsByClassName('storelocator-header__title')[0].innerHTML =
39 | "Peter Jackson's " + this.state.store.name + ' ' + this.state.store.addr_cty;
40 | this.props.stateStore.changeMap(this.state.store.geo, this.state.store.zoom);
41 | }
42 |
43 | findStore(q) {
44 | return (
45 | q.name
46 | .split(' ')
47 | .join('-')
48 | .toLowerCase() == this
49 | );
50 | }
51 |
52 | showTab(active, set) {
53 | return active == set ? 'display: block' : 'display: none';
54 | }
55 |
56 | setTab(selected) {
57 | this.setState({ tab: selected });
58 | }
59 |
60 | setTabClass(selected) {
61 | return this.state.tab == selected ? 'tabs__tab tabs__tab--active' : 'tabs__tab';
62 | }
63 |
64 | render() {
65 | const tab1 = 'hours';
66 | const tab2 = 'directions';
67 | return (
68 |
69 |
70 |
{this.state.store.name}
71 |
88 |
89 |
90 |
91 |
this.setTab(tab1)}>
92 | Store Information
93 |
94 |
this.setTab(tab2)}>
95 | Directions
96 |
97 |
98 |
99 |
100 |
Opening Hours
101 |
102 |
103 |
107 |
112 |
113 |
114 |
115 |
116 | );
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Model/Config/Source/ListState.php:
--------------------------------------------------------------------------------
1 | countriesData = $countriesData;
33 | $this->regionsData = $regionsData;
34 | $this->_catalogConfig = $catalogConfig;
35 | }
36 |
37 | /**
38 | * Retrieve option values array
39 | *
40 | * @return array
41 | */
42 | public function toOptionArray()
43 | {
44 | $options = [];
45 | $options[] = ['label' => __(' '), 'value' => ' '];
46 | foreach ($this->getRegionsAsArray() as $regionId => $regionName) {
47 | $options[] = ['label' => __($regionName), 'value' => $regionId];
48 | }
49 | return $options;
50 | }
51 |
52 | /**
53 | * Retrieve Catalog Config Singleton
54 | *
55 | * @return \Magento\Catalog\Model\Config
56 | */
57 | protected function _getCatalogConfig()
58 | {
59 | return $this->_catalogConfig;
60 | }
61 |
62 | public function getRegionsAsArray($countryCode = '')
63 | {
64 | /** @var \PandaGroup\StoreLocator\Model\ResourceModel\RegionsData\Collection $regionsDataCollection */
65 | $regionsDataCollection = $this->regionsData->getCollection();
66 |
67 | /** @var \PandaGroup\StoreLocator\Model\ResourceModel\CountriesData\Collection $countriesDataCollection */
68 | $countriesDataCollection = $this->countriesData->getCollection();
69 |
70 | if (false === empty($countryCode)) {
71 | $countriesDataCollection->addFilter('code', $countryCode);
72 | $countryId = $countriesDataCollection->getFirstItem()->getId();
73 | if (false === isset($countryId)) return null;
74 | $regionsDataCollection->addFilter('country_id', $countryId);
75 | }
76 |
77 | $regionsByCountry = [];
78 | $emptyRegionId = null;
79 | /*
80 | foreach ($regionsDataCollection as $region) {
81 | // if (false === empty($region->getName())) { // Some rows in the database are empty
82 | // $regionsByCountry[$region->getId()] = $region->getName();
83 | // }
84 |
85 | if (false === empty($region->getName())) { // Some rows in the database are empty
86 | $regionsByCountry[$region->getId()] = $region->getName();
87 | } else {
88 | // $regionsByCountry[$region->getId()] = '-';
89 | $emptyRegionId = $region->getId();
90 | }
91 | }
92 |
93 | if (true === empty($regionsByCountry)) {
94 | $regionsByCountry[$emptyRegionId] = $countriesDataCollection->addFilter('code', $countryCode)->getFirstItem()->getData('name');
95 | $regionsByCountry[''] = ' ';
96 | }
97 | */
98 |
99 | foreach ($regionsDataCollection as $region) {
100 |
101 | if (false === empty($region->getName())) { // Some rows in the database are empty
102 | $regionsByCountry[$region->getId()] = $region->getName();
103 | } else {
104 | $regionsByCountry[$region->getId()] = '-';
105 | $emptyRegionId = $region->getId();
106 | }
107 | }
108 |
109 | $isArrayOnlyOfEmptyRegions = true;
110 | foreach ($regionsByCountry as $simpleRegion) {
111 | if ($simpleRegion != '-') {
112 | $isArrayOnlyOfEmptyRegions = false;
113 | break;
114 | }
115 | }
116 |
117 | // Clear empty regions only if there are some correct regions (nax to empty regions)
118 | if ($countryCode != '') {
119 | foreach ($regionsByCountry as $key => $value) {
120 | if ($value == '-') {
121 | unset($regionsByCountry[$key]);
122 | }
123 | }
124 | }
125 |
126 | // If country has any region show country name on region list witch id form database
127 | // Countries without regions have one empty region
128 | if (true === empty($regionsByCountry) || $isArrayOnlyOfEmptyRegions) {
129 | $regionsByCountry[$emptyRegionId] = $countriesDataCollection->addFilter('code', $countryCode)->getFirstItem()->getData('name');
130 | $regionsByCountry[''] = ' ';
131 | }
132 |
133 | return $regionsByCountry;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/Model/GoogleApi.php:
--------------------------------------------------------------------------------
1 | configProvider = $configProvider;
41 | $this->jsonHelper = $jsonHelper;
42 | $this->logger = $logger;
43 | }
44 |
45 | /**
46 | * @param $addressName
47 | * @return mixed
48 | */
49 | public function getCoordinatesByAddress($addressName)
50 | {
51 | $this->logger->info('Start getting coordinates from Google Api.');
52 | $apiKey = $this->configProvider->getGoogleApiKey();
53 |
54 | $addressName = urlencode($addressName);
55 |
56 | $url = self::GOOGLE_API_ADDRESS_URL . $addressName . '&key=' . $apiKey;
57 |
58 | $ch = curl_init();
59 |
60 | curl_setopt($ch, CURLOPT_URL, $url);
61 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
62 | $output = curl_exec($ch);
63 | curl_close($ch);
64 |
65 | $countryInformation = $this->jsonHelper->jsonDecode($output);
66 |
67 | if (isset($countryInformation['status'])) {
68 | $requestStatus = $countryInformation['status'];
69 | }
70 |
71 | if (isset($countryInformation['error_message'])) {
72 | $requesterrorMessage = $countryInformation['error_message'];
73 | } else $requesterrorMessage = 'Undefined Google Api error';
74 |
75 | if ($requestStatus == 'OK') {
76 | $this->logger->info(' Success getting response from Google Api.');
77 | } else {
78 | $this->logger->error(' Error while getting response from Google Api: '.$requesterrorMessage);
79 | }
80 |
81 | if (isset($countryInformation['results'][0]['geometry']['location']['lat'])
82 | && isset($countryInformation['results'][0]['geometry']['location']['lng'])
83 | ) {
84 | $coordinates['lat'] = $countryInformation['results'][0]['geometry']['location']['lat'];
85 | $coordinates['lng'] = $countryInformation['results'][0]['geometry']['location']['lng'];
86 | $this->logger->info(' Success getting coordinates from Google Api response.');
87 | } else {
88 | $coordinates = null;
89 | $this->logger->error(' Error getting coordinates from Google Api response.');
90 | }
91 |
92 | $this->logger->info('Finish getting coordinates from Google Api.');
93 | return $coordinates;
94 | }
95 |
96 | /**
97 | * @param $regionName
98 | * @return null
99 | */
100 | public function getRegionShortName($regionName)
101 | {
102 | $apiKey = $this->configProvider->getGoogleApiKey();
103 |
104 | $regionName = urlencode($regionName);
105 |
106 | $url = self::GOOGLE_API_ADDRESS_URL . $regionName . '&key=' . $apiKey;
107 |
108 | $ch = curl_init();
109 |
110 | curl_setopt($ch, CURLOPT_URL, $url);
111 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
112 | $output = curl_exec($ch);
113 | curl_close($ch);
114 |
115 | $countryInformation = $this->jsonHelper->jsonDecode($output);
116 |
117 | if (isset($countryInformation['status'])) {
118 | $requestStatus = $countryInformation['status'];
119 | }
120 |
121 | if (isset($countryInformation['error_message'])) {
122 | $requesterrorMessage = $countryInformation['status'];
123 | }
124 |
125 | $shortStateName = null;
126 | foreach ($countryInformation['results'] as $region) {
127 |
128 | if (false === isset($region['address_components'])) continue;
129 |
130 | foreach ($region['address_components'] as $addressComponent) {
131 |
132 | if (false === isset($addressComponent['types'][0])) continue;
133 |
134 | if ($addressComponent['types'][0] === 'administrative_area_level_1') {
135 | $shortStateName = $addressComponent['short_name'];
136 | }
137 | }
138 | }
139 |
140 | return $shortStateName;
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/view/frontend/preact/src/container/StoreHeader.js:
--------------------------------------------------------------------------------
1 | import { h, Component } from 'preact';
2 | import { connect } from 'mobx-preact';
3 | import { Link } from 'react-router-dom';
4 |
5 | import GoogleApiComponent from './../component/GoogleApiComponent';
6 | import HeaderMap from './HeaderMap';
7 | import RegionFilter from './../component/RegionFilter';
8 |
9 | @connect(['stateStore'])
10 | export default class StoreHeader extends Component {
11 | constructor(props) {
12 | super(props);
13 | this.postcodeInput = null;
14 | this.geocode = null;
15 | this.applyFilter = this.applyFilter.bind(this);
16 | this.resetFilters = this.resetFilters.bind(this);
17 | this.searchPostcode = this.searchPostcode.bind(this);
18 | }
19 |
20 | componentWillMount() {
21 | this.props.stateStore.initializeStore(this.context.router);
22 | }
23 |
24 | resetFilters() {
25 | this.props.stateStore.clearFilters();
26 | }
27 |
28 | searchPostcode() {
29 | if (!this.geocode) {
30 | this.geocode = new this.props.google.maps.Geocoder();
31 | }
32 | this.geocode.geocode(
33 | {
34 | componentRestrictions: {
35 | country: this.context.constants.country,
36 | postalCode: this.postcodeInput.value,
37 | },
38 | },
39 | (results, status) => {
40 | if (status === 'OK') {
41 | const newGeo = {
42 | lat: results[0].geometry.location.lat(),
43 | lng: results[0].geometry.location.lng(),
44 | };
45 | this.props.stateStore.setError();
46 | this.props.stateStore.changeMap(newGeo, '11');
47 | } else {
48 | this.props.stateStore.setError('Invalid postcode');
49 | }
50 | }
51 | );
52 | }
53 |
54 | isActiveFilter(region) {
55 | return this.props.stateStore.filters.indexOf(region) > -1
56 | ? 'storelocator-header__filter storelocator-header__filter--active'
57 | : 'storelocator-header__filter';
58 | }
59 |
60 | backButton() {
61 | this.props.stateStore.changeView();
62 | this.props.stateStore.changeMap();
63 | }
64 |
65 | applyFilter(region) {
66 | this.props.stateStore.addFilters(region);
67 | }
68 |
69 | render() {
70 | const { regions } = this.props;
71 | return (
72 |
133 | );
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/Ui/Component/Listing/Column/IndexActions.php:
--------------------------------------------------------------------------------
1 | urlBuilder = $urlBuilder;
40 | parent::__construct($context, $uiComponentFactory, $components, $data);
41 | }
42 |
43 | /**
44 | * Prepare Data Source
45 | *
46 | * @param array $dataSource
47 | *
48 | * @return array
49 | */
50 | public function prepareDataSource(array $dataSource)
51 | {
52 | if (isset($dataSource['data']['items'])) {
53 | foreach ($dataSource['data']['items'] as & $item) {
54 | if (isset($item['storelocator_id'])) {
55 | $item[$this->getData('name')] = [
56 | 'edit' => [
57 | 'href' => $this->urlBuilder->getUrl(
58 | static::URL_PATH_EDIT,
59 | [
60 | 'id' => $item['storelocator_id']
61 | ]
62 | ),
63 | 'label' => __('Edit')
64 | ],
65 | 'delete' => [
66 | 'href' => $this->urlBuilder->getUrl(
67 | static::URL_PATH_DELETE,
68 | [
69 | 'id' => $item['storelocator_id']
70 | ]
71 | ),
72 | 'label' => __('Delete'),
73 | 'confirm' => [
74 | 'title' => __('Delete "${ $.$data.name }"'),
75 | 'message' => __('Are you sure you wan\'t to delete a "${ $.$data.name }" store?')
76 | ]
77 | ]
78 | ];
79 | }
80 | }
81 | }
82 |
83 | return $dataSource;
84 | }
85 |
86 |
87 | // /**
88 | // * Prepare Data Source
89 | // *
90 | // * @param array $dataSource
91 | // * @return array
92 | // */
93 | // public function prepareDataSource(array $dataSource)
94 | // {
95 | ///*
96 | // if (isset($dataSource['data']['items'])) {
97 | // foreach ($dataSource['data']['items'] as & $item) {
98 | // if (isset($item['post_id'])) {
99 | // $item[$this->getData('name')] = [
100 | // 'edit' => [
101 | // 'href' => $this->_urlBuilder->getUrl(
102 | // static::URL_PATH_EDIT,
103 | // [
104 | // 'post_id' => $item['post_id']
105 | // ]
106 | // ),
107 | // 'label' => __('Edit')
108 | // ],
109 | // 'delete' => [
110 | // 'href' => $this->_urlBuilder->getUrl(
111 | // static::URL_PATH_DELETE,
112 | // [
113 | // 'post_id' => $item['post_id']
114 | // ]
115 | // ),
116 | // 'label' => __('Delete'),
117 | // 'confirm' => [
118 | // 'title' => __('Delete "${ $.$data.name }"'),
119 | // 'message' => __('Are you sure you wan\'t to delete the Post "${ $.$data.name }" ?')
120 | // ]
121 | // ]
122 | // ];
123 | // }
124 | // }
125 | // }
126 | //*/
127 | // if (isset($dataSource['data']['items'])) {
128 | // foreach ($dataSource['data']['items'] as &$item) {
129 | //
130 | // $name = $this->getData('name');
131 | // if (isset($item['id']) && $this->isOrderIncrementId($item['id'])) {
132 | // $item[$name]['view_order'] = [
133 | //
134 | //
135 | // 'href' => $this->getRowUrl($item),
136 | //
137 | //
138 | // 'label' => __('View Store'),
139 | // ];
140 | // }
141 | // }
142 | // }
143 | //
144 | // return $dataSource;
145 | // }
146 | //
147 | // public function isOrderIncrementId($orderId)
148 | // {
149 | // $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
150 | // $orderInterface = $objectManager->create('\Magento\Sales\Api\Data\OrderInterface');
151 | // $order = $orderInterface->load($orderId);
152 | // if($order->getId()) {
153 | // return true;
154 | // }
155 | // return false;
156 | // }
157 | //
158 | // public function getRowUrl($item)
159 | // {
160 | // return $this->_urlBuilder->getUrl('storelocator/index/index', ['id' => $item['id']]);
161 | // }
162 | }
163 |
--------------------------------------------------------------------------------
/view/frontend/preact/src/component/Map.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { h, Component } from 'preact';
3 | import camelize from './../util/Camelize';
4 |
5 | const mapStyles = {
6 | container: {
7 | position: 'absolute',
8 | width: '100%',
9 | height: '100%',
10 | },
11 | map: {
12 | position: 'absolute',
13 | left: 0,
14 | right: 0,
15 | bottom: 0,
16 | top: 0,
17 | },
18 | };
19 |
20 | const evtNames = [
21 | 'ready',
22 | 'click',
23 | 'dragend',
24 | 'recenter',
25 | 'bounds_changed',
26 | 'center_changed',
27 | 'dblclick',
28 | 'dragstart',
29 | 'heading_change',
30 | 'idle',
31 | 'maptypeid_changed',
32 | 'mousemove',
33 | 'mouseout',
34 | 'mouseover',
35 | 'projection_changed',
36 | 'resize',
37 | 'rightclick',
38 | 'tilesloaded',
39 | 'tilt_changed',
40 | 'zoom_changed',
41 | ];
42 |
43 | export default class Map extends Component {
44 | constructor(props) {
45 | super(props);
46 | this.listeners = {};
47 | this.state = {
48 | currentLocation: {
49 | lat: this.props.initialCenter.lat,
50 | lng: this.props.initialCenter.lng,
51 | },
52 | };
53 | }
54 |
55 | componentDidMount() {
56 | this.loadMap();
57 | }
58 |
59 | componentDidUpdate(prevProps, prevState) {
60 | if (prevProps.google !== this.props.google) {
61 | this.loadMap();
62 | }
63 | if (this.props.visible !== prevProps.visible) {
64 | this.restyleMap();
65 | }
66 | if (this.props.zoom !== prevProps.zoom) {
67 | this.map.setZoom(Number.parseInt(this.props.zoom));
68 | }
69 | if (this.props.center !== prevProps.center) {
70 | this.setState({
71 | currentLocation: this.props.center,
72 | });
73 | }
74 | if (prevState.currentLocation !== this.state.currentLocation) {
75 | this.recenterMap();
76 | }
77 | }
78 |
79 | componentWillUnmount() {
80 | const { google } = this.props;
81 | if (this.geoPromise) {
82 | this.geoPromise.cancel();
83 | }
84 | Object.keys(this.listeners).forEach(e => {
85 | google.maps.event.removeListener(this.listeners[e]);
86 | });
87 | }
88 |
89 | loadMap() {
90 | if (this.props && this.props.google) {
91 | const { google } = this.props;
92 | const maps = google.maps;
93 |
94 | const node = this.mapDiv;
95 | const curr = this.state.currentLocation;
96 | const center = new maps.LatLng(curr.lat, curr.lng);
97 |
98 | const mapTypeIds = this.props.google.maps.MapTypeId || {};
99 | const mapTypeFromProps = String(this.props.mapType).toUpperCase();
100 |
101 | const mapConfig = Object.assign(
102 | {},
103 | {
104 | mapTypeId: mapTypeIds[mapTypeFromProps],
105 | center: center,
106 | zoom: Number.parseInt(this.props.zoom),
107 | maxZoom: Number.parseInt(this.props.maxZoom),
108 | minZoom: Number.parseInt(this.props.maxZoom),
109 | clickableIcons: this.props.clickableIcons,
110 | disableDefaultUI: this.props.disableDefaultUI,
111 | zoomControl: this.props.zoomControl,
112 | mapTypeControl: this.props.mapTypeControl,
113 | scaleControl: this.props.scaleControl,
114 | streetViewControl: this.props.streetViewControl,
115 | panControl: this.props.panControl,
116 | rotateControl: this.props.rotateControl,
117 | scrollwheel: this.props.scrollwheel,
118 | draggable: this.props.draggable,
119 | keyboardShortcuts: this.props.keyboardShortcuts,
120 | disableDoubleClickZoom: this.props.disableDoubleClickZoom,
121 | noClear: this.props.noClear,
122 | styles: this.props.styles,
123 | gestureHandling: this.props.gestureHandling,
124 | }
125 | );
126 |
127 | Object.keys(mapConfig).forEach(key => {
128 | if (mapConfig[key] === null) {
129 | delete mapConfig[key];
130 | }
131 | });
132 |
133 | this.map = new maps.Map(node, mapConfig);
134 |
135 | evtNames.forEach(e => {
136 | this.listeners[e] = this.map.addListener(e, this.handleEvent(e));
137 | });
138 | maps.event.trigger(this.map, 'ready');
139 | this.forceUpdate();
140 | }
141 | }
142 |
143 | handleEvent(evtName) {
144 | let timeout;
145 | const handlerName = `on${camelize(evtName)}`;
146 |
147 | return e => {
148 | if (timeout) {
149 | clearTimeout(timeout);
150 | timeout = null;
151 | }
152 | timeout = setTimeout(() => {
153 | if (this.props[handlerName]) {
154 | this.props[handlerName](this.props, this.map, e);
155 | }
156 | }, 0);
157 | };
158 | }
159 |
160 | recenterMap() {
161 | if (this.props && this.props.google) {
162 | const map = this.map;
163 |
164 | const { google } = this.props;
165 | const maps = google.maps;
166 |
167 | if (!google) return;
168 |
169 | if (map) {
170 | let center = this.state.currentLocation;
171 | if (!(center instanceof google.maps.LatLng)) {
172 | center = new google.maps.LatLng(center.lat, center.lng);
173 | }
174 | map.setCenter(center);
175 | maps.event.trigger(map, 'recenter');
176 | }
177 | }
178 | }
179 |
180 | restyleMap() {
181 | if (this.map) {
182 | const { google } = this.props;
183 | google.maps.event.trigger(this.map, 'resize');
184 | }
185 | }
186 |
187 | renderChildren() {
188 | const { children } = this.props;
189 |
190 | if (!children) return;
191 |
192 | return React.Children.map(children, c => {
193 | return React.cloneElement(c, {
194 | map: this.map,
195 | google: this.props.google,
196 | mapCenter: this.state.currentLocation,
197 | });
198 | });
199 | }
200 |
201 | render() {
202 | const style = Object.assign({}, mapStyles.map, this.props.style, {
203 | display: this.props.visible ? 'inherit' : 'none',
204 | });
205 |
206 | const containerStyles = Object.assign({}, mapStyles.container, this.props.containerStyle);
207 |
208 | return (
209 |
210 |
{
212 | this.mapDiv = div;
213 | }}
214 | style={style}
215 | >
216 |
217 | {this.renderChildren()}
218 |
219 | );
220 | }
221 | }
222 |
223 | Map.defaultProps = {
224 | zoom: 14,
225 | initialCenter: {
226 | lat: 37.774929,
227 | lng: -122.419416,
228 | },
229 | center: {},
230 | centerAroundCurrentLocation: false,
231 | style: {},
232 | containerStyle: {},
233 | visible: true,
234 | };
235 |
--------------------------------------------------------------------------------
/Helper/ConfigProvider.php:
--------------------------------------------------------------------------------
1 | scopeConfig = $context->getScopeConfig();
55 | $this->storeManager = $storeManager;
56 | parent::__construct($context);
57 | }
58 |
59 | /**
60 | * Retrieve Google API Key
61 | *
62 | * @param null $store
63 | * @return string
64 | */
65 | public function getGoogleApiKey($store = null)
66 | {
67 | return (string) $this->scopeConfig->getValue(
68 | self::STORE_LOCATOR_SECTION . self::STORE_LOCATOR_BASE_SETTINGS_GROUP . self::GOOGLE_API_KEY_FIELD,
69 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
70 | $store
71 | );
72 | }
73 |
74 | /**
75 | * Retrieve Country of stores location
76 | *
77 | * @param null $store
78 | * @return string
79 | */
80 | public function getStoresLocationCountryCode($store = null)
81 | {
82 | return (string) $this->scopeConfig->getValue(
83 | self::STORE_LOCATOR_SECTION . self::STORE_LOCATOR_BASE_SETTINGS_GROUP . self::COUNTRY_FIELD,
84 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
85 | $store
86 | );
87 | }
88 |
89 | /**
90 | * Retrieve Hours Time Format
91 | *
92 | * @param null $store
93 | * @return string
94 | */
95 | public function getHoursTimeFormat($store = null)
96 | {
97 | return (int) $this->scopeConfig->getValue(
98 | self::STORE_LOCATOR_SECTION . self::STORE_LOCATOR_BASE_SETTINGS_GROUP . self::TIME_FORMAT_FIELD,
99 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
100 | $store
101 | );
102 | }
103 |
104 | /**
105 | * Retrieve Latitude of map
106 | *
107 | * @param null $store
108 | * @return string
109 | */
110 | public function getMapLatitude($store = null)
111 | {
112 | return (string) $this->scopeConfig->getValue(
113 | self::STORE_LOCATOR_SECTION . self::STORE_LOCATOR_MAP_SETTINGS_GROUP . self::LATITUDE_FIELD,
114 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
115 | $store
116 | );
117 | }
118 |
119 | /**
120 | * Retrieve Longitude of map
121 | *
122 | * @param null $store
123 | * @return string
124 | */
125 | public function getMapLongitude($store = null)
126 | {
127 | return (string) $this->scopeConfig->getValue(
128 | self::STORE_LOCATOR_SECTION . self::STORE_LOCATOR_MAP_SETTINGS_GROUP . self::LONGITUDE_FIELD,
129 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
130 | $store
131 | );
132 | }
133 |
134 | /**
135 | * Retrieve Zoom Level of map
136 | *
137 | * @param null $store
138 | * @return string
139 | */
140 | public function getMapZoomLevel($store = null)
141 | {
142 | return (int) $this->scopeConfig->getValue(
143 | self::STORE_LOCATOR_SECTION . self::STORE_LOCATOR_MAP_SETTINGS_GROUP . self::ZOOM_LEVEL_FIELD,
144 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
145 | $store
146 | );
147 | }
148 |
149 | /**
150 | * Retrieve Pin image link of map spinner
151 | *
152 | * @param null $store
153 | * @return string
154 | */
155 | public function getPinImageLink($store = null)
156 | {
157 | $filePath = (string) $this->scopeConfig->getValue(
158 | self::STORE_LOCATOR_SECTION . self::STORE_LOCATOR_MAP_SETTINGS_GROUP . self::PIN_IMAGE_LINK_FIELD,
159 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
160 | $store
161 | );
162 |
163 | $dir = \PandaGroup\StoreLocator\Model\Config\Backend\Image::UPLOAD_DIR . DIRECTORY_SEPARATOR;
164 | return $this->getMediaUrl() . $dir . $filePath;
165 | }
166 |
167 | /**
168 | * Retrieve Debug Status
169 | *
170 | * @param null $store
171 | * @return bool
172 | */
173 | public function getDebugStatus($store = null)
174 | {
175 | return (bool) $this->scopeConfig->getValue(
176 | self::STORE_LOCATOR_SECTION . self::STORE_LOCATOR_ADVANCED_GROUP . self::DEBUG_STATUS_FIELD,
177 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
178 | $store
179 | );
180 | }
181 |
182 | /**
183 | * Retrieve MEDIA path
184 | *
185 | * @return string
186 | */
187 | public function getMediaUrl()
188 | {
189 | return $this->_urlBuilder->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]);
190 | }
191 | }
192 |
--------------------------------------------------------------------------------