Start
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
/* ═══════════════════════════════════════════════════════════
|
||||
1. HELPERS
|
||||
═══════════════════════════════════════════════════════════ */
|
||||
const H = {
|
||||
uid: () => Math.random().toString(36).slice(2,9) + Date.now().toString(36),
|
||||
esc: s => String(s||'').replace(/[&<>"']/g, c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])),
|
||||
fmDist: m => m==null?'-': m>=1000? (m/1000).toFixed(2)+' km': Math.round(m).toLocaleString('id-ID')+' m',
|
||||
fmArea: m => m==null?'-': m>=10000? (m/10000).toFixed(4)+' ha': Math.round(m).toLocaleString('id-ID')+' m²',
|
||||
fmNum: n => (n||0).toLocaleString('id-ID'),
|
||||
today: () => new Date().toISOString().slice(0,10),
|
||||
geoLen: lls => { let t=0; for(let i=0;i<lls.length-1;i++) t+=lls[i].distanceTo(lls[i+1]); return Math.round(t); },
|
||||
geoArea: lls => {
|
||||
const R=6371008.8; let a=0,n=lls.length;
|
||||
for(let i=0;i<n;i++){const j=(i+1)%n,φ1=lls[i].lat*Math.PI/180,φ2=lls[j].lat*Math.PI/180,dλ=(lls[j].lng-lls[i].lng)*Math.PI/180;a+=dλ*(2+Math.sin(φ1)+Math.sin(φ2));}
|
||||
return Math.round(Math.abs(a*R*R/2));
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
/* ═══════════════════════════════════════════════════════════
|
||||
2. STORAGE
|
||||
═══════════════════════════════════════════════════════════ */
|
||||
const Store = (() => {
|
||||
const KEYS = { spbu:'wg2_spbu', jalan:'wg2_jalan', parsil:'wg2_parsil', warga:'wg2_warga', rumahIbadah:'wg2_ri' };
|
||||
const _r = k => { try{ return JSON.parse(localStorage.getItem(k)||'[]') }catch{return[]} };
|
||||
const _w = (k,d) => { try{ localStorage.setItem(k,JSON.stringify(d)) }catch{} };
|
||||
let db = Object.fromEntries(Object.entries(KEYS).map(([k,v])=>[k,_r(v)]));
|
||||
return {
|
||||
all: ln => db[ln]||[],
|
||||
find:(ln,id) => (db[ln]||[]).find(r=>r.id===id)||null,
|
||||
add:(ln,rec) => { db[ln].push(rec); _w(KEYS[ln],db[ln]); },
|
||||
update:(ln,id,patch) => {
|
||||
const i=db[ln].findIndex(r=>r.id===id); if(i<0)return;
|
||||
db[ln][i]={...db[ln][i],...patch}; _w(KEYS[ln],db[ln]);
|
||||
},
|
||||
remove:(ln,id) => { db[ln]=db[ln].filter(r=>r.id!==id); _w(KEYS[ln],db[ln]); },
|
||||
allDb: () => db,
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,9 @@
|
||||
/* ═══════════════════════════════════════════════════════════
|
||||
3. TOAST
|
||||
═══════════════════════════════════════════════════════════ */
|
||||
const Toast = {
|
||||
_show(msg,type){ const c=document.getElementById('toasts'),el=document.createElement('div');
|
||||
el.className=`toast ${type}`;el.innerHTML=`<span>${{success:'✓',error:'✕',info:'ℹ'}[type]||'ℹ'}</span>${H.esc(msg)}`;
|
||||
c.appendChild(el);setTimeout(()=>{el.classList.add('out');el.addEventListener('animationend',()=>el.remove(),{once:true})},2800); },
|
||||
success: m=>Toast._show(m,'success'), error: m=>Toast._show(m,'error'), info: m=>Toast._show(m,'info'),
|
||||
};
|
||||
Reference in New Issue
Block a user