Making widget.js
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
// ==== DB CONFIG ====
|
// ==== DB CONFIG ====
|
||||||
|
|||||||
6
salony/README.md
Normal file
6
salony/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
For using "widget.js"
|
||||||
|
past this code inside page:
|
||||||
|
|
||||||
|
<div id="salony-widget"></div>
|
||||||
|
<script src="https://serwer1852487.home.pl/Geo/salony/widget.js"></script>
|
||||||
|
<script async src="https://maps.googleapis.com/maps/api/js?key=KEY"></script>
|
||||||
@@ -3,7 +3,6 @@ require __DIR__ . '/../db.php';
|
|||||||
|
|
||||||
|
|
||||||
if ($_POST) {
|
if ($_POST) {
|
||||||
|
|
||||||
// === Dodajemy miejsce ===
|
// === Dodajemy miejsce ===
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
INSERT INTO salon_places (name, woj)
|
INSERT INTO salon_places (name, woj)
|
||||||
@@ -13,13 +12,12 @@ if ($_POST) {
|
|||||||
$_POST['name'],
|
$_POST['name'],
|
||||||
$_POST['woj'] ?: null
|
$_POST['woj'] ?: null
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$placeId = $pdo->lastInsertId();
|
$placeId = $pdo->lastInsertId();
|
||||||
|
|
||||||
// === Dodajemy sklep (shop) ===
|
// === Dodajemy sklep (shop) ===
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
INSERT INTO shops (place_id, address, open_hours, url_address, url_shop, lat, lng)
|
INSERT INTO shops (place_id, address, open_hours, url_address, url_shop, lat, lng)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
");
|
");
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$placeId,
|
$placeId,
|
||||||
|
|||||||
429
salony/widget.js
Normal file
429
salony/widget.js
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
(function () {
|
||||||
|
const API_URL = 'https://serwer1852487.home.pl/Geo/api/salony/';
|
||||||
|
const root = document.getElementById('salony-widget');
|
||||||
|
if (!root) return;
|
||||||
|
|
||||||
|
root.innerHTML = `
|
||||||
|
<div class="mapFull">
|
||||||
|
<div id="map"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mapResults">
|
||||||
|
<h1>ZNAJDŹ SALON</h1>
|
||||||
|
|
||||||
|
<div class="searchCont">
|
||||||
|
<input id="tinp" type="text" placeholder="ZNAJDŹ SKLEP">
|
||||||
|
<ul class="suggestList">
|
||||||
|
<li class="firstSuggest"></li>
|
||||||
|
</ul>
|
||||||
|
<label for="tinp">Wpisz Miasto bądź województwo</label>
|
||||||
|
<button id="search">szukaj
|
||||||
|
<i class="fas fa-search" style="min-height: 14px;"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="resultQ">
|
||||||
|
WYNIKI: <span class="dynamic">0</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul id="shopList">
|
||||||
|
<li class="forCloning place">
|
||||||
|
<input type="radio" name="shopPlace">
|
||||||
|
<div class="shopHiddenInfo">
|
||||||
|
<p class="lat"></p>
|
||||||
|
<p class="lng"></p>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<h2 class="shopAddress"></h2>
|
||||||
|
<p>GODZINY OTWARCIA:</p>
|
||||||
|
<p class="shopHours"></p>
|
||||||
|
<a class="link" target="_blank">Wskazówki dojazdu</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
injectStyles();
|
||||||
|
|
||||||
|
let map;
|
||||||
|
let markers = [];
|
||||||
|
let places = [];
|
||||||
|
var iconB = 'https://moodo.pl/data/include/cms/salony-1/mark2.png'
|
||||||
|
var iconW = 'https://moodo.pl/data/include/cms/salony-1/markw2.png'
|
||||||
|
|
||||||
|
fetch(API_URL)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
places = data;
|
||||||
|
initMap();
|
||||||
|
buildSuggestList();
|
||||||
|
renderList(data);
|
||||||
|
renderMarkers(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function prepareToCompare(str) {
|
||||||
|
return convertSpecialsCharacters(str.toString().toLowerCase().trim())
|
||||||
|
}
|
||||||
|
function convertSpecialsCharacters(str) {
|
||||||
|
const conversions = {}
|
||||||
|
conversions.a = 'ą'
|
||||||
|
conversions.e = 'ę'
|
||||||
|
conversions.z = 'ż|ź'
|
||||||
|
conversions.s = 'ś'
|
||||||
|
conversions.c = 'ć'
|
||||||
|
conversions.l = 'ł'
|
||||||
|
conversions.o = 'ó|ò'
|
||||||
|
conversions.A = 'Ą'
|
||||||
|
conversions.E = 'Ę'
|
||||||
|
conversions.Z = 'Ż|Ź'
|
||||||
|
conversions.S = 'Ś'
|
||||||
|
conversions.C = 'Ć'
|
||||||
|
conversions.L = 'Ł'
|
||||||
|
conversions.O = 'Ó|Ò'
|
||||||
|
|
||||||
|
for (const i in conversions) {
|
||||||
|
const re = new RegExp(conversions[i], 'g')
|
||||||
|
str = str.replace(re, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- MAP ----------------
|
||||||
|
function initMap() {
|
||||||
|
map = new google.maps.Map(document.getElementById('map'), {
|
||||||
|
zoom: 6,
|
||||||
|
center: { lat: 52.1, lng: 19.4 },
|
||||||
|
mapTypeId: 'roadmap'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMarkers(data) {
|
||||||
|
data.forEach(place => {
|
||||||
|
place.shops.forEach(shop => {
|
||||||
|
if (!shop.lat || !shop.lng) return;
|
||||||
|
|
||||||
|
const marker = new google.maps.Marker({
|
||||||
|
map,
|
||||||
|
icon: iconB,
|
||||||
|
position: { lat: +shop.lat, lng: +shop.lng }
|
||||||
|
});
|
||||||
|
|
||||||
|
marker.addListener('click', () => {
|
||||||
|
map.setZoom(16);
|
||||||
|
map.panTo(marker.getPosition());
|
||||||
|
filterList(place.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
markers.push(marker);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- LIST ----------------
|
||||||
|
function renderList(data) {
|
||||||
|
const list = document.getElementById('shopList');
|
||||||
|
list.querySelectorAll('.place:not(.forCloning)').forEach(el => el.remove());
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
data.forEach(place => {
|
||||||
|
place.shops.forEach(shop => {
|
||||||
|
const clone = list.querySelector('.forCloning').cloneNode(true);
|
||||||
|
clone.classList.remove('forCloning');
|
||||||
|
|
||||||
|
clone.querySelector('.lat').textContent = shop.lat;
|
||||||
|
clone.querySelector('.lng').textContent = shop.lng;
|
||||||
|
clone.querySelector('.shopAddress').textContent =
|
||||||
|
`${place.name} - ${shop.address || ''}`;
|
||||||
|
clone.querySelector('.shopHours').textContent =
|
||||||
|
shop.open_hours || '—';
|
||||||
|
|
||||||
|
clone.querySelector('.link').href = shop.map_url || '#';
|
||||||
|
|
||||||
|
clone.addEventListener('click', (e) => {
|
||||||
|
map.setZoom(16);
|
||||||
|
map.panTo({ lat: +shop.lat, lng: +shop.lng });
|
||||||
|
|
||||||
|
const ancestor = e.target.closest('.place')
|
||||||
|
ancestor.classList.add('active')
|
||||||
|
ancestor.getElementsByTagName('INPUT')[0].checked = true
|
||||||
|
});
|
||||||
|
|
||||||
|
list.appendChild(clone);
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('.dynamic').textContent = count;
|
||||||
|
document.querySelector('.resultQ').classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- SEARCH ----------------
|
||||||
|
function buildSuggestList() {
|
||||||
|
const ul = document.querySelector('.suggestList');
|
||||||
|
|
||||||
|
places.forEach(p => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = p.name;
|
||||||
|
li.onclick = () => {
|
||||||
|
document.getElementById('tinp').value = p.name;
|
||||||
|
filterList(p.name);
|
||||||
|
};
|
||||||
|
ul.appendChild(li);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterList(val) {
|
||||||
|
renderList(
|
||||||
|
places.filter(p =>
|
||||||
|
p.name.toLowerCase().includes(val.toLowerCase())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function filterAvailablePlaces(actualVal) {
|
||||||
|
let listElements = document.querySelectorAll('.suggestList li')
|
||||||
|
for (const element of listElements) {
|
||||||
|
if (
|
||||||
|
!prepareToCompare(element.innerText).includes(
|
||||||
|
prepareToCompare(actualVal)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
element.style.display = 'none'
|
||||||
|
} else {
|
||||||
|
element.style.display = 'block'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchInp = document.getElementById('tinp')
|
||||||
|
searchInp.onfocus = function (e) {
|
||||||
|
e.target.closest('.searchCont').classList.add('active')
|
||||||
|
}
|
||||||
|
searchInp.addEventListener('focusout', function (e) {
|
||||||
|
let targ = e.target
|
||||||
|
setTimeout(function () {
|
||||||
|
targ.closest('.searchCont').classList.remove('active')
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
searchInp.onkeyup = function (e) {
|
||||||
|
if (e.keyCode == 13) {
|
||||||
|
document.getElementById('search').click()
|
||||||
|
} else {
|
||||||
|
filterAvailablePlaces(searchInp.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('search').onclick = () => {
|
||||||
|
filterList(document.getElementById('tinp').value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------- STYLES ----------------
|
||||||
|
|
||||||
|
function injectStyles() {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.innerHTML = `
|
||||||
|
#salony-widget *{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#salony-widget{
|
||||||
|
min-height: 40vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
#salony-widget .mapFull {
|
||||||
|
flex-basis:65%;
|
||||||
|
min-height:40vw;
|
||||||
|
}
|
||||||
|
#salony-widget .mapFull #map{
|
||||||
|
filter: grayscale(100%);
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#salony-widget .mapResults {
|
||||||
|
padding: 0 16px 0 48px;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-basis: 35%;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults h1 {
|
||||||
|
color: #333;
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
font-size: 29px;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#salony-widget .mapResults .searchCont {
|
||||||
|
margin: 18px 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
max-width: 340px;
|
||||||
|
border: 2px solid rgba(0, 0, 0, .7);
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont input{
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
padding: 10px;
|
||||||
|
color: #333;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
height: 40px;
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont label{
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
position: absolute;
|
||||||
|
color: rgba(0, 0, 0, .6);
|
||||||
|
top: -10px;
|
||||||
|
padding: 2px;
|
||||||
|
background: #fff;
|
||||||
|
left: 4px;
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.5s;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont button{
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
outline: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont .suggestList {
|
||||||
|
position: absolute;
|
||||||
|
box-shadow: 2px 3px 10px rgba(0, 0, 0, .3);
|
||||||
|
top: 20px;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0 !important;
|
||||||
|
max-height: 100px;
|
||||||
|
z-index: 10;
|
||||||
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
margin: 20px 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont.active .suggestList {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont .suggestList li {
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px;
|
||||||
|
transition: 0.3s;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont .suggestList li:hover {
|
||||||
|
background: #f2f2f2;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont .suggestList li.firstSuggest {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont label {
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
position: absolute;
|
||||||
|
color: rgba(0, 0, 0, .6);
|
||||||
|
top: -10px;
|
||||||
|
padding: 2px;
|
||||||
|
background: #fff;
|
||||||
|
left: 4px;
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.5s;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .searchCont.active label {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#salony-widget .mapResults .resultQ {
|
||||||
|
visibility: hidden;
|
||||||
|
color: #333;
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults .resultQ.active {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList {
|
||||||
|
padding: 0 5px;
|
||||||
|
margin: 20px 0;
|
||||||
|
list-style-type: none;
|
||||||
|
max-height: 360px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList li.place.forCloning {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList li.place {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 10px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList li.place a {
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
margin-top: 8px;
|
||||||
|
display: none;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList li.place.active a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList li.place input{
|
||||||
|
margin-right: 10px;
|
||||||
|
position: relative;
|
||||||
|
top: 6px;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList li.place .shopHiddenInfo{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList li.place .content h2{
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#salony-widget .mapResults #shopList li.place .content p{
|
||||||
|
color: #333;
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user