176 lines
8.9 KiB
JavaScript
176 lines
8.9 KiB
JavaScript
(()=>{
|
||
const F=FuelTrack;
|
||
let searchController=null,searchSequence=0;
|
||
const normalize=value=>String(value??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toLowerCase().replace(/[^a-z0-9]+/g,' ').trim();
|
||
const matches=(haystack,query)=>{const source=normalize(haystack),terms=normalize(query).split(/\s+/).filter(Boolean);return terms.every(term=>source.includes(term))};
|
||
|
||
F.bindStationLiveSearch=()=>{
|
||
const form=F.qs('#station-search-form');
|
||
if(!form||form.dataset.liveBound)return;
|
||
form.dataset.liveBound='1';
|
||
const run=F.debounce(async()=>{
|
||
const seq=++searchSequence;
|
||
searchController?.abort();
|
||
searchController=new AbortController();
|
||
const p=new URLSearchParams(new FormData(form));p.delete('page');
|
||
const url=`${form.action}?${p}`;
|
||
try{
|
||
const r=await fetch(url,{headers:{'X-Requested-With':'XMLHttpRequest'},signal:searchController.signal});
|
||
if(!r.ok)throw new Error('Nie udało się wyszukać stacji.');
|
||
const doc=new DOMParser().parseFromString(await r.text(),'text/html');
|
||
if(seq!==searchSequence)return;
|
||
const incoming=F.qs('main',doc),current=F.qs('main');
|
||
if(!incoming||!current)return;
|
||
bootstrap.Modal.getInstance(F.qs('#stationPointsModal'))?.hide();
|
||
current.replaceWith(incoming);
|
||
document.querySelectorAll('.modal-backdrop').forEach(x=>x.remove());
|
||
document.body.classList.remove('modal-open');
|
||
document.body.style.removeProperty('padding-right');
|
||
history.replaceState({},'',url);
|
||
F.initPage();
|
||
}catch(e){if(e.name!=='AbortError')F.notify(e.message,'danger')}
|
||
},300);
|
||
F.qs('[data-live-search]',form)?.addEventListener('input',run);
|
||
F.qsa('[data-live-sort]',form).forEach(x=>x.addEventListener('change',run));
|
||
};
|
||
|
||
F.bindFavoriteStations=()=>{
|
||
F.qsa('.favorite-station-form').forEach(form=>{
|
||
if(form.dataset.favoriteSearchBound)return;
|
||
form.dataset.favoriteSearchBound='1';
|
||
const input=F.qs('.station-modal-search',form),tbody=F.qs('tbody',form);
|
||
const pagination=F.qs('.favorite-pagination',form),summary=F.qs('.favorite-pagination-summary',form);
|
||
if(!input||!tbody)return;
|
||
const pageSize=Math.max(1,Number(form.dataset.pageSize||10));
|
||
let currentPage=1;
|
||
let empty=F.qs('.favorite-search-empty',tbody);
|
||
if(!empty){
|
||
empty=document.createElement('tr');empty.className='favorite-search-empty d-none';
|
||
empty.innerHTML='<td colspan="3" class="text-center text-body-secondary py-4">Brak pasujących stacji.</td>';
|
||
tbody.append(empty);
|
||
}
|
||
const rows=()=>F.qsa('tr[data-station-name]',tbody);
|
||
const filteredRows=()=>rows().filter(row=>matches(row.dataset.stationName||row.textContent,input.value));
|
||
const renderPagination=(totalPages,totalItems)=>{
|
||
if(summary)summary.textContent=totalItems?`Wyniki: ${totalItems} · strona ${currentPage} z ${totalPages}`:'Brak wyników';
|
||
if(!pagination)return;
|
||
pagination.innerHTML='';
|
||
if(totalPages<=1)return;
|
||
const add=(label,page,disabled=false,active=false)=>{
|
||
const li=document.createElement('li');li.className=`page-item${disabled?' disabled':''}${active?' active':''}`;
|
||
const button=document.createElement('button');button.type='button';button.className='page-link';button.textContent=label;
|
||
button.disabled=disabled;button.addEventListener('click',()=>{currentPage=page;render()});
|
||
li.append(button);pagination.append(li);
|
||
};
|
||
add('‹',Math.max(1,currentPage-1),currentPage===1);
|
||
const first=Math.max(1,currentPage-2),last=Math.min(totalPages,first+4);
|
||
for(let page=Math.max(1,last-4);page<=last;page++)add(String(page),page,false,page===currentPage);
|
||
add('›',Math.min(totalPages,currentPage+1),currentPage===totalPages);
|
||
};
|
||
const render=()=>{
|
||
const matching=filteredRows();
|
||
const totalPages=Math.max(1,Math.ceil(matching.length/pageSize));
|
||
currentPage=Math.min(currentPage,totalPages);
|
||
const visibleSet=new Set(matching.slice((currentPage-1)*pageSize,currentPage*pageSize));
|
||
rows().forEach(row=>{
|
||
const show=visibleSet.has(row);
|
||
row.hidden=!show;row.classList.toggle('d-none',!show);
|
||
});
|
||
empty.hidden=matching.length!==0;empty.classList.toggle('d-none',matching.length!==0);
|
||
renderPagination(totalPages,matching.length);
|
||
};
|
||
input.addEventListener('input',()=>{currentPage=1;render()});
|
||
input.addEventListener('search',()=>{currentPage=1;render()});
|
||
const modal=form.closest('.modal');
|
||
modal?.addEventListener('shown.bs.modal',()=>{input.focus();render()});
|
||
modal?.addEventListener('hidden.bs.modal',()=>{input.value='';currentPage=1;render()});
|
||
form.addEventListener('change',event=>{
|
||
if(!event.target.matches('input[name="station_ids"]'))return;
|
||
const checked=F.qsa('input[name="station_ids"]:checked',form).length;
|
||
const max=Number(form.dataset.maxFavorites||25);
|
||
if(checked>max){event.target.checked=false;F.notify(`Możesz wybrać maksymalnie ${max} stacji.`,'danger')}
|
||
});
|
||
render();
|
||
});
|
||
};
|
||
|
||
F.bindStationFavoriteToggles=()=>{
|
||
F.qsa('.station-favorite-toggle').forEach(button=>{
|
||
if(button.dataset.bound)return;
|
||
button.dataset.bound='1';
|
||
button.addEventListener('click',async()=>{
|
||
const original=button.textContent;
|
||
button.disabled=true;
|
||
button.textContent='Zapisywanie…';
|
||
try{
|
||
const r=await fetch(button.dataset.url,{method:'POST',headers:{Accept:'application/json','X-Requested-With':'XMLHttpRequest'}});
|
||
const d=await r.json();
|
||
if(!r.ok||!d.ok)throw new Error(d.message||d.error||'Nie udało się zmienić ulubionych.');
|
||
const favorite=Boolean(d.data&&d.data.favorite);
|
||
button.textContent=favorite?'Usuń z ulubionych':'Dodaj do ulubionych';
|
||
F.notify(d.message||'Zapisano');
|
||
}catch(e){button.textContent=original;F.notify(e.message,'danger')}
|
||
finally{button.disabled=false}
|
||
});
|
||
});
|
||
};
|
||
|
||
// Zachowany jako no-op: formularz tankowania używa natywnego selecta z optgroup.
|
||
F.bindStationProposalSelect=()=>{};
|
||
|
||
F.bindStationPoints=()=>{
|
||
const modalEl=F.qs('#stationPointsModal');
|
||
if(!modalEl)return;
|
||
const search=F.qs('#stationPointsSearch',modalEl);
|
||
if(search&&!search.dataset.bound){
|
||
search.dataset.bound='1';
|
||
search.addEventListener('input',F.debounce(()=>F.loadStationPoints?.(),250));
|
||
}
|
||
};
|
||
|
||
F.loadStationPoints=async()=>{
|
||
const modalEl=F.qs('#stationPointsModal');
|
||
if(!modalEl||!modalEl.dataset.stationId)return;
|
||
const rows=F.qs('#stationPointsRows',modalEl),empty=F.qs('#stationPointsEmpty',modalEl),loading=F.qs('#stationPointsLoading',modalEl),count=F.qs('#stationPointsCount',modalEl),search=F.qs('#stationPointsSearch',modalEl);
|
||
const esc=v=>String(v??'').replace(/[&<>'"]/g,c=>({'&':'&','<':'<','>':'>',"'":''','"':'"'}[c]));
|
||
loading.classList.remove('d-none');empty.classList.add('d-none');rows.innerHTML='';
|
||
try{
|
||
const r=await fetch(`/api/stations/${modalEl.dataset.stationId}/points?q=${encodeURIComponent(search.value.trim())}&per_page=100`,{headers:{Accept:'application/json','X-Requested-With':'XMLHttpRequest'}}),d=await r.json();
|
||
if(!r.ok||!d.ok)throw new Error(d.message||'Nie udało się pobrać punktów');
|
||
count.textContent=`Liczba punktów: ${d.data.total}`;
|
||
rows.innerHTML=d.data.items.map(x=>`<tr><td><strong>${esc(x.name)}</strong>${x.dkn?`<div class="small text-body-secondary">DKN: ${esc(x.dkn)}</div>`:''}</td><td>${esc(x.address)}</td><td>${esc(x.region||'—')}</td><td>${x.has_petrol?'<span class="badge text-bg-secondary me-1">benzyna</span>':''}${x.has_diesel?'<span class="badge text-bg-secondary me-1">diesel</span>':''}${x.has_lpg?'<span class="badge text-bg-secondary">LPG</span>':''}</td></tr>`).join('');
|
||
empty.classList.toggle('d-none',d.data.items.length>0);
|
||
}catch(e){empty.textContent=e.message;empty.classList.remove('d-none')}
|
||
finally{loading.classList.add('d-none')}
|
||
};
|
||
|
||
if(!document.documentElement.dataset.stationPointsDelegated){
|
||
document.documentElement.dataset.stationPointsDelegated='1';
|
||
document.addEventListener('click',e=>{
|
||
const b=e.target.closest('.station-points-open');
|
||
if(!b)return;
|
||
const modalEl=F.qs('#stationPointsModal');
|
||
if(!modalEl)return;
|
||
modalEl.dataset.stationId=b.dataset.stationId;
|
||
F.qs('#stationPointsTitle',modalEl).textContent=`Punkty: ${b.dataset.stationName}`;
|
||
F.qs('#stationPointsSearch',modalEl).value='';
|
||
F.qs('#stationPointsCount',modalEl).textContent='';
|
||
bootstrap.Modal.getOrCreateInstance(modalEl).show();
|
||
F.loadStationPoints();
|
||
});
|
||
}
|
||
|
||
F.bindStationInlineCompany=()=>{
|
||
F.qsa('.station-inline-company-select').forEach(select=>{
|
||
if(select.dataset.bound)return;
|
||
select.dataset.bound='1';
|
||
select.addEventListener('change',()=>{
|
||
const url=new URL(location.href);
|
||
url.searchParams.set('company_id',select.value);
|
||
url.searchParams.delete('page');
|
||
F.replaceMain(url.toString(),{push:true}).catch(e=>F.notify(e.message,'danger'));
|
||
});
|
||
});
|
||
};
|
||
})();
|