,
trash: <>>,
building: <>>,
plus2: ,
share: <>>,
globe: <>>,
link: <>>,
palette: <>>,
wifi: <>>,
car: <>>,
chair: <>>,
bike: <>>,
walk: <>>,
cake: <>>,
bag: <>>,
truck: <>>,
};
return ;
}
/* ---------------- 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 });