/* global React */ const { useState, useEffect, useRef, useMemo } = React; /* ---------------- Icons (simple line set) ---------------- */ function Icon({ name, size = 20, stroke = 2.1, color = "currentColor", fill = "none" }) { const p = { width: size, height: size, viewBox: "0 0 24 24", fill, stroke: color, strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round" }; const paths = { qr: <>, scan: <>, pin: <>, phone: , nav: <>, gift: <>, star: , arrow: , chev: , bell: <>, coin: <>, cup: <>, home: <>, grid: <>, user: <>, info: <>, spark: , search: <>, filter: , download: <>, sort: , chevdown: , chevup: , close: , dots: <>, mail: <>, cal: <>, users: <>, receipt: <>, gear: <>, logout: <>, edit: , plus: , minus: , check: , chart: <>, ext: <>, trash: <>, image: <>, eye: <>, eyeoff: <>, box: <>, clock: <>, ticket: <>, copy: <>, alert: <>, percent: <>, sun: <>, rocket: <>, bellpush: <>, target: <>, calrange: <>, multiply: <>, mega: <>, play: , pause: <>, send: <>, sparkle2: , flame: , plant: <>, arrowleft: , shield: <>, lock: <>, key: <>, del: <>, refresh: <>, print: <>, camera: <>, swap: <>, heart: , heartfill: , trash: <>, building: <>, plus2: , share: <>, globe: <>, link: <>, palette: <>, wifi: <>, car: <>, chair: <>, bike: <>, walk: <>, cake: <>, bag: <>, truck: <>, }; return {paths[name] || null}; } /* ---------------- Pseudo-QR generator ---------------- */ function makeQR(seed = 7, n = 29) { // deterministic PRNG let s = seed * 2654435761 % 2147483647; const rnd = () => (s = s * 16807 % 2147483647) / 2147483647; const g = Array.from({ length: n }, () => Array(n).fill(false)); const finder = (r, c) => { for (let i = -1; i <= 7; i++) for (let j = -1; j <= 7; j++) { const rr = r + i, cc = c + j; if (rr < 0 || cc < 0 || rr >= n || cc >= n) continue; const edge = i === 0 || i === 6 || j === 0 || j === 6; const core = i >= 2 && i <= 4 && j >= 2 && j <= 4; const inb = i >= 0 && i <= 6 && j >= 0 && j <= 6; g[rr][cc] = inb ? (edge || core) : false; } }; // random fill for (let r = 0; r < n; r++) for (let c = 0; c < n; c++) g[r][c] = rnd() > 0.52; finder(0, 0); finder(0, n - 7); finder(n - 7, 0); // timing-ish + clear center for logo const m = Math.floor(n / 2); for (let r = m - 3; r <= m + 3; r++) for (let c = m - 3; c <= m + 3; c++) g[r][c] = false; return g; } /* real QR encoding when `value` is given, falls back to the pseudo-QR otherwise */ function realQR(value) { if (!window.qrcode) return null; try { const qr = window.qrcode(0, "H"); qr.addData(value); qr.make(); const n = qr.getModuleCount(); return Array.from({ length: n }, (_, r) => Array.from({ length: n }, (_, c) => qr.isDark(r, c))); } catch (e) { return null; } } function QRCanvas({ value }) { const fallback = useRef(makeQR(42)).current; const grid = useMemo(() => (value ? realQR(value) : null) || fallback, [value]); const n = grid.length; return (
{grid.flatMap((row, r) => row.map((on, c) => ))}
); } /* ---------------- helpers ---------------- */ function fmt(n) { return n.toLocaleString("vi-VN"); } /* ---------------- Shared navigation map ---------------- */ const NAV_URLS = { // customer register: "/register", login: "/login", home: "/", menu: "/menu", orderHistory: "/orders/history", points: "/points", catalog: "/rewards", wallet: "/rewards/wallet", history: "/points", profile: "/profile", store: "/store", pos: "/pos/points", // admin adminHome: "/admin", adminCustomers: "/admin/customers", adminPoints: "/admin/points", adminRewards: "/admin/rewards", adminCampaigns: "/admin/campaigns", adminCheckin: "/admin/checkin", adminOrders: "/admin/orders", adminShipping: "/admin/shipping", adminPromotions: "/admin/promotions", adminMenu: "/admin/menu", adminVariants: "/admin/variants", adminStores: "/admin/stores", adminRoles: "/admin/roles", adminSettings: "/admin/settings", }; const ADMIN_NAV_HREF = { "Tổng quan": NAV_URLS.adminHome, "Khách hàng": NAV_URLS.adminCustomers, "Điểm & giao dịch": NAV_URLS.adminPoints, "Đơn hàng": NAV_URLS.adminOrders, "Phí ship": NAV_URLS.adminShipping, "Đổi quà": NAV_URLS.adminRewards, "Chiến dịch": NAV_URLS.adminCampaigns, "Điểm danh": NAV_URLS.adminCheckin, "Thực đơn": NAV_URLS.adminMenu, "Khuyến mãi": NAV_URLS.adminPromotions, "Variant / Tuỳ chọn": NAV_URLS.adminVariants, "Cửa hàng": NAV_URLS.adminStores, "Phân quyền": NAV_URLS.adminRoles, "Cài đặt": NAV_URLS.adminSettings, }; function adminHref(label) { return ADMIN_NAV_HREF[label] || "#"; } Object.assign(window, { Icon, QRCanvas, makeQR, fmt, NAV_URLS, ADMIN_NAV_HREF, adminHref });