var PHARMACIES_DATA = [
{id:'А01',name:'Аптека №1 — Центральна', addr:'вул. Дерибасівська, 12', city:'Одеса', lat:46.4825,lng:30.7233,head:'1/1',pharm:'3/3',cashier:'2/3',schedule:'Пн–Нд 8–22',status:'ok',enterprise:'Одесская аптечная сеть'},
{id:'А07',name:'Аптека №7 — Черемушки', addr:'вул. Маршала Жукова, 54', city:'Одеса', lat:46.4191,lng:30.7058,head:'1/1',pharm:'1/2',cashier:'2/2',schedule:'Пн–Сб 9–20',status:'warn',enterprise:'Одесская аптечная сеть'},
{id:'А12',name:'Аптека №12 — Таїрова', addr:'вул. Академіка Корольова, 112',city:'Одеса', lat:46.4311,lng:30.7601,head:'1/1',pharm:'2/2',cashier:'1/1',schedule:'Пн–Нд 9–21',status:'ok',enterprise:'Одесская аптечная сеть'},
{id:'А23',name:'Аптека №23 — Котовський', addr:'пр. Добровольського, 133', city:'Одеса', lat:46.3952,lng:30.6487,head:'0/1',pharm:'1/2',cashier:'1/1',schedule:'Пн–Нд 8–20',status:'crit',enterprise:'Одесская аптечная сеть'},
{id:'А31',name:'Аптека №31 — Пересип', addr:'вул. Балківська, 74', city:'Одеса', lat:46.5089,lng:30.7412,head:'1/1',pharm:'2/2',cashier:'2/2',schedule:'Пн–Пт 9–18',status:'ok',enterprise:'Одесская аптечная сеть'},
{id:'А44',name:'Аптека №44 — Київ Центр', addr:'вул. Хрещатик, 15', city:'Київ', lat:50.4501,lng:30.5234,head:'1/1',pharm:'3/3',cashier:'2/3',schedule:'Пн–Нд 8–22',status:'ok',enterprise:'Киевская аптечная сеть'},
{id:'А51',name:'Аптека №51 — Лівобережна',addr:'пр. Броварський, 23', city:'Київ', lat:50.4721,lng:30.6115,head:'1/1',pharm:'1/2',cashier:'1/1',schedule:'Пн–Сб 8–21',status:'warn',enterprise:'Киевская аптечная сеть'},
{id:'А67',name:'Аптека №67 — Харків', addr:'пр. Науки, 44', city:'Харків',lat:49.9935,lng:36.2304,head:'0/1',pharm:'2/3',cashier:'1/2',schedule:'Пн–Нд 9–21',status:'crit',enterprise:'Харьковская аптечная сеть'},
];
var PHARMA_SC = {
ok: {label:'Норма', cls:'badge-success',color:'#22c55e',border:'#16a34a'},
warn:{label:'Увага', cls:'badge-warning',color:'#f59e0b',border:'#d97706'},
crit:{label:'Нестача',cls:'badge-danger', color:'#ef4444',border:'#dc2626'},
};
var pharmaState = {
cur: 'table',
lmap: null,
markers: [],
filtered: PHARMACIES_DATA.slice()
};
function pharmaInit() {
if (!document.getElementById('pharma-tbody')) return;
pharmaState.filtered = PHARMACIES_DATA.slice();
pharmaRenderTable(pharmaState.filtered);
// Предзагружаем Leaflet в фоне
pharmaLoadLeaflet(function(){});
renderEnterpriseFilter();
}
function pharmaLoadLeaflet(cb) {
if (window.L) { cb(); return; }
if (document.getElementById('leaflet-js')) {
document.getElementById('leaflet-js').addEventListener('load', cb);
return;
}
var lc = document.createElement('link');
lc.rel = 'stylesheet';
lc.href = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css';
document.head.appendChild(lc);
var ls = document.createElement('script');
ls.id = 'leaflet-js';
ls.src = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js';
ls.onload = cb;
document.head.appendChild(ls);
}
function pharmaScell(v) {
var p = v.split('/'), c = +p[0], m = +p[1];
var col = c < m ? (c === 0 ? 'var(--color-destructive)' : 'var(--color-warning)') : 'var(--color-foreground)';
var fw = c < m ? '500' : '400';
return '
' + v + ' ';
}
function pharmaRenderTable(data) {
var countEl = document.getElementById('pharma-count');
var tbody = document.getElementById('pharma-tbody');
if (!tbody) return;
if (countEl) countEl.textContent = 'Показано ' + data.length + ' з ' + PHARMACIES_DATA.length + ' аптек';
tbody.innerHTML = data.map(function(p) {
var c = PHARMA_SC[p.status];
return '' +
'' +
'' +
'
' + p.id + '
' +
'
' + p.name + '
' +
'
' + p.addr + '
' +
'
' +
' ' +
'' + p.city + ' ' +
pharmaScell(p.head) + pharmaScell(p.pharm) + pharmaScell(p.cashier) +
'' + p.schedule + ' ' +
'' + c.label + ' ' +
' ';
}).join('');
}
function pharmaMakeIcon(status) {
var c = PHARMA_SC[status];
var svg = '' +
' ' +
' ' +
'Rx ';
return L.divIcon({html:svg, iconSize:[28,36], iconAnchor:[14,36], popupAnchor:[0,-36], className:''});
}
function pharmaRenderMarkers(data) {
pharmaState.markers.forEach(function(m){ pharmaState.lmap.removeLayer(m); });
pharmaState.markers = [];
data.forEach(function(p, i) {
var c = PHARMA_SC[p.status];
var popup = '' +
'
' + p.name + '
' +
'
' + p.addr + '
' +
'
' +
'Завідуючий ' + p.head + ' ' +
'Фармацевт ' + p.pharm + ' ' +
'Касир ' + p.cashier + ' ' +
'Режим ' + p.schedule + ' ' +
'
' + c.label + '
';
var mk = L.marker([p.lat, p.lng], {icon: pharmaMakeIcon(p.status)}).addTo(pharmaState.lmap).bindPopup(popup);
pharmaState.markers.push(mk);
});
}
function pharmaRenderSidebar(data) {
var el = document.getElementById('pharma-sidebar');
if (!el) return;
el.innerHTML = data.map(function(p, i) {
var c = PHARMA_SC[p.status];
return '' +
'
' +
'
' +
'
' + p.id + '
' +
'
' + p.name.replace(/Аптека №\d+ — /, '') + ' ' +
'
' +
'
' + c.label + ' ' +
'
' +
'
' + p.addr + '
' +
'
';
}).join('');
}
function pharmaFocus(i) {
if (pharmaState.lmap && pharmaState.markers[i]) {
pharmaState.lmap.setView(pharmaState.markers[i].getLatLng(), 15);
pharmaState.markers[i].openPopup();
}
}
function pharmaAddBaseLayer(map) {
if (typeof acoreAddMapBaseLayer === 'function') {
acoreAddMapBaseLayer(map);
return;
}
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap ',
maxZoom: 19
}).addTo(map);
}
function pharmaInitMap() {
requestAnimationFrame(function() {
requestAnimationFrame(function() {
var mapEl = document.getElementById('pharma-map');
if (!mapEl) return;
if (!pharmaState.lmap) {
pharmaState.lmap = L.map('pharma-map', {center:[48.3794,31.1656], zoom:6});
pharmaAddBaseLayer(pharmaState.lmap);
}
pharmaState.lmap.invalidateSize();
pharmaRenderMarkers(pharmaState.filtered);
pharmaRenderSidebar(pharmaState.filtered);
});
});
}
function pharmaView(v) {
pharmaState.cur = v;
var isTable = v === 'table';
var pvTable = document.getElementById('pv-table');
var pvMap = document.getElementById('pv-map');
var btnT = document.getElementById('btn-table');
var btnM = document.getElementById('btn-map');
if (!pvTable) return;
pvTable.style.display = isTable ? '' : 'none';
pvMap.style.display = isTable ? 'none' : '';
btnT.style.background = isTable ? 'var(--color-card)' : 'transparent';
btnT.style.color = isTable ? 'var(--color-foreground)' : 'var(--color-muted-foreground)';
btnM.style.background = isTable ? 'transparent' : 'var(--color-card)';
btnM.style.color = isTable ? 'var(--color-muted-foreground)' : 'var(--color-foreground)';
if (!isTable) {
pharmaLoadLeaflet(pharmaInitMap);
}
}
function pharmaFilter() {
var s = (document.getElementById('pharma-search') || {}).value || '';
var city = (document.getElementById('filter-city') || {}).value || '';
var st = (document.getElementById('filter-status') || {}).value || '';
var enterprise = (document.getElementById('filter-enterprise') || {}).value || '';
s = s.toLowerCase();
pharmaState.filtered = PHARMACIES_DATA.filter(function(p) {
return (!s || p.name.toLowerCase().indexOf(s) > -1 || p.addr.toLowerCase().indexOf(s) > -1) &&
(!city || p.city === city) &&
(!st || p.status === st) &&
(!enterprise || p.enterprise === enterprise);
});
pharmaRenderTable(pharmaState.filtered);
if (pharmaState.cur === 'map' && pharmaState.lmap) {
pharmaRenderMarkers(pharmaState.filtered);
pharmaRenderSidebar(pharmaState.filtered);
}
}
function renderEnterpriseFilter() {
var select = document.getElementById('filter-enterprise');
if (!select) return;
var enterprises = PHARMACIES_DATA
.map(function(p) { return p.enterprise; })
.filter(Boolean)
.filter(function(value, index, array) { return array.indexOf(value) === index; });
select.innerHTML = 'Все предприятия ' +
enterprises.map(function(e) {
return '' + e + ' ';
}).join('');
}