import { formatDistanceToNow, parseJSON, format, parse } from "date-fns"; export function friendlyDate(date) { return formatDistanceToNow(parseJSON(date), { addSuffix: true }); } export function readableDate(date) { if (!date) { return ""; } return format(parseJSON(date), "dd MMM yyyy"); } export function readableDatetime(date) { if (!date) { return ""; } return format(parseJSON(date), "dd MMM yyyy HH:mm"); } export function stripHtml(html = "") { let tmp = document.createElement("div"); tmp.innerHTML = html; return tmp.textContent || tmp.innerText || ""; } export function randomId(length = 10) { return Math.random() .toString(36) .substring(2, length + 2); } export function clickOutside(node) { const handleClick = (event) => { if (node && !node.contains(event.target) && !event.defaultPrevented) { node.dispatchEvent(new CustomEvent("click_outside", node)); } }; document.addEventListener("click", handleClick, true); return { destroy() { document.removeEventListener("click", handleClick, true); }, }; } export function apiFetch(url, options = {}) { return fetch(url, { ...options, headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').content, ...options.headers, }, }); } export function apiPost(url, body, options = {}) { return fetch(url, { ...options, method: "POST", body: JSON.stringify(body), headers: { "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest", "X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').content, ...options.headers, }, }).then((r) => r.json()); } export function apiGet(url, options = {}) { return fetch(url, { ...options, method: "GET", headers: { "X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').content, "X-Requested-With": "XMLHttpRequest", ...options.headers, }, }).then((r) => r.json()); } export function isEqual(db, ed) { let isObject = (x) => typeof x === "object" && !Array.isArray(x) && x !== null; let isArray = (x) => x?.constructor === Array; let isEmpty = (x) => x === null || x === undefined || x == []; const db_value = db ?? null; const ed_value = ed ?? null; if (isObject(db_value)) { let keys = Object.keys(db_value); return keys.reduce((acc, k) => { if (acc === false) { return false; } return isEqual(db_value?.[k], ed_value?.[k]); }, true); } if (isArray(db_value)) { return db_value.reduce((c, v, i) => { if (c === false) { return false; } return isEqual(v, ed_value?.[i]); }, true); } if (isEmpty(db_value) && isEmpty(ed_value)) { return true; } if (db_value == ed_value) { return true; } return false; // const ok = Object.keys, // tx = typeof x, // ty = typeof y; // return x && y && tx === "object" && tx === ty // ? ok(x).length === ok(y).length && // ok(x).every((key) => isEqual(x[key], y[key])) // : x === y; } export function debounce(fn, delay) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; } export function arrayUnique(array) { return array.filter((value, index) => array.indexOf(value) === index); } export function arrayUniqueBy(items, uniqueBy) { const ids = new Set(items.map((item) => item[uniqueBy])); return [...ids].map((id) => items.find((i) => i[uniqueBy] === id)); } export function arrayUniqueCb(items, aFilter) { const cache = new Set(); return items.filter((item) => { const cacheValue = aFilter(item); if (cache.has(cacheValue)) return false; cache.add(cacheValue); return true; }); }