/* ============================================================
Shared UI primitives — Evans Web Store
============================================================ */
const { useState, useEffect, useRef, useContext, createContext } = React;
const AppCtx = createContext(null);
/* ---------- Icons (stroke, 1.6) ---------- */
function Icon({ name, size = 20, className = '', style }) {
const s = { width: size, height: size, fill: 'none', stroke: 'currentColor',
strokeWidth: 1.7, strokeLinecap: 'round', strokeLinejoin: 'round', ...style };
const paths = {
cart: <>>,
user: <>>,
chevron: ,
chevronDown: ,
chevronLeft: ,
check: ,
checkCircle: <>>,
plus: ,
minus: ,
search: <>>,
truck: <>>,
box: <>>,
pin: <>>,
coins: <>>,
bell: <>>,
grid: <>>,
users: <>>,
alert: <>>,
logout: <>>,
settings: <>>,
receipt: <>>,
trash: <>>,
edit: <>>,
shield: <>>,
spark: ,
arrowRight: ,
};
return ;
}
/* ---------- Logo (real Evans lockup, locked to 733:231 ratio) ---------- */
function Logo({ size = 34, light = false }) {
const h = size * 1.34;
const w = h * (733 / 231);
return (
);
}
/* ---------- Garment placeholder image ---------- */
function Garment({ product, color, tag = true, logo = true }) {
const c = color || product.colors[0];
const fill = COLORS[c] || '#cfd6d4';
const isWhite = ['white','cream','silver','stone','hivis','tan','khaki'].includes(c);
return (
{logo && product.kind !== 'cap' && (
)}
{product.fr &&
FR
}
{tag &&
Product photo
}
);
}
/* ---------- Swatch row ---------- */
function Swatches({ colors, selected, onSelect, lg = false, max }) {
const shown = max ? colors.slice(0, max) : colors;
const extra = max ? colors.length - max : 0;
return (
{shown.map(c => (
);
}
/* ---------- Points display ---------- */
function Price({ points, size = 16 }) {
return (
{points} pts
(${points})
);
}
function PointsPill() {
const { user } = useContext(AppCtx);
const remaining = user.pointsTotal - user.pointsUsed;
const pct = Math.max(0, Math.min(100, (remaining / user.pointsTotal) * 100));
return (
{remaining} / {user.pointsTotal} pts
);
}
/* ---------- Product card ---------- */
function ProductCard({ product }) {
const { navigate } = useContext(AppCtx);
const [color, setColor] = useState(product.colors[0]);
return (
navigate('product', { id: product.id, color })}>
{product.brand} · {product.sku}
{product.name}
);
}
/* ---------- Storefront header ---------- */
function StoreHeader() {
const { navigate, route, cart, user, logout } = useContext(AppCtx);
const [menu, setMenu] = useState(false);
const cartCount = cart.reduce((n, i) => n + i.qty, 0);
const cats = CATEGORIES.filter(c => user.categories.includes(c.id));
return (
navigate('home')}>
{menu && (
<>
setMenu(false)} />
>
)}
);
}
function NavLink({ label, active, onClick }) {
return (
);
}
function MenuItem({ icon, label, onClick }) {
return (
);
}
/* ---------- Footer ---------- */
function StoreFooter() {
const { navigate } = useContext(AppCtx);
return (
);
}
Object.assign(window, { AppCtx, Icon, Logo, Garment, Swatches, Price, PointsPill, ProductCard, StoreHeader, NavLink, MenuItem, StoreFooter });