/* HopAIO Dashboard - main app */
const { useState, useEffect, useRef, useMemo, useCallback } = React;
// ---------- Icons (inline SVG) ----------
const I = {
dash: (p) => ,
tasks: (p) => ,
checkout: (p) => ,
monitor: (p) => ,
docs: (p) => ,
settings: (p) => ,
search: (p) => ,
key: (p) => ,
download: (p) => ,
pause: (p) => ,
play: (p) => ,
stop: (p) => ,
refresh: (p) => ,
filter: (p) => ,
copy: (p) => ,
ext: (p) =>
};
// ---------- Site catalog (real data from docs) ----------
const SITES = [
{ code: 'alza', name: 'Alza', region: 'CZ/DE/AT/HU', color: '#ff6900' },
{ code: 'mediaexpert', name: 'Mediaexpert', region: 'PL', color: '#e2061b' },
{ code: 'mediamarkt', name: 'MediaMarkt', region: 'NL', color: '#df0000' },
{ code: 'mediamarkt_de', name: 'MediaMarkt DE', region: 'DE', color: '#df0000' },
{ code: 'empik', name: 'Empik', region: 'PL', color: '#ed1c24' },
{ code: 'elbenwald', name: 'Elbenwald', region: 'DE/EU', color: '#7c5fff' },
{ code: 'basketballemotion', name: 'BasketballEmotion', region: 'ES/EU', color: '#ff8000' },
{ code: 'futbolemotion', name: 'FutbolEmotion', region: 'ES/EU', color: '#0064ff' },
{ code: 'mycomics', name: 'MyComics', region: 'IT', color: '#ffd400' },
{ code: 'fantasiastore', name: 'Fantasiastore', region: 'IT', color: '#9c27b0' },
{ code: 'gamesisland', name: 'Games Island', region: 'DE/EU', color: '#00bcd4' },
{ code: 'proshop', name: 'Proshop', region: 'DE/AT/NL/DK', color: '#27ae60' },
{ code: 'solebox', name: 'Solebox', region: 'DE/EU', color: '#ff4081' },
{ code: 'footshop', name: 'Footshop', region: 'CZ/EU', color: '#1a73e8' },
{ code: 'skatedeluxe', name: 'Skatedeluxe', region: 'DE/EU', color: '#f57c00' },
{ code: 'sportvision', name: 'Sportvision', region: 'CZ', color: '#e91e63' },
{ code: 'sportsshoes', name: 'SportsShoes', region: 'UK/EU', color: '#1976d2' },
{ code: 'frasers', name: 'Frasers (SD/Game/Flannels)', region: 'UK/EU', color: '#444' },
{ code: 'skstore', name: 'SK Store / WSS', region: 'PL/EU', color: '#00897b' },
{ code: 'zalando', name: 'Zalando', region: 'EU', color: '#ff6900' },
{ code: 'secretlair', name: 'Secret Lair', region: 'EU', color: '#e23b3b' },
{ code: 'lowkey', name: 'Lowkey', region: 'HU/CZ', color: '#888888' },
{ code: 'endclothing', name: 'END. Clothing', region: 'UK/EU', color: '#000000' },
{ code: 'travisscott', name: 'Travis Scott (DRAW)', region: 'US', color: '#b59169' }];
const siteByCode = (c) => SITES.find((s) => s.code === c) || { name: c, color: '#444', region: '—' };
// ---------- LOGIN ----------
// Normalize matches license_core.normalize_key() on the Python side: strip
// every non-alnum char and uppercase. The dashboard's JSON files are written
// at data/checkouts/.json so the two halves agree on the path.
// Hosted dashboard backend. Defaults to the production host so the dashboard
// works when opened from disk or any other origin. Override per-deployment by
// setting window.HOP_API_BASE in an inline