/* Shared UI primitives + Icon set */
const { useState, useEffect, useContext, createContext, useMemo } = React;
/* ===== Icon set ===== */
const I = {
home: ,
grid: <>>,
user: <>>,
users: <>>,
bell: <>>,
logout: <>>,
chevronR: ,
chevronL: ,
arrowR: <>>,
arrowL: <>>,
plus: <>>,
check: ,
x: <>>,
clock: <>>,
calendar: <>>,
mapPin: <>>,
zap: ,
trending: <>>,
wallet: <>>,
cpu: <>>,
receipt: <>>,
shield: ,
coffee: <>>,
thermometer: ,
refresh: <>>,
search: <>>,
menu: <>>,
shoppingBag: <>>,
download: <>>,
copy: <>>,
key: <>>,
edit: <>>,
trash: <>>,
eye: <>>,
lock: <>>,
alertCircle: <>>,
settings: <>>,
};
function Icon({ name, size = 18, className = "", stroke = 1.8, ...rest }) {
const node = I[name];
if (!node) return null;
return (
);
}
/* ===== Context ===== */
const AppCtx = createContext(null);
const useApp = () => useContext(AppCtx);
/* ===== Button ===== */
function Button({ variant = "primary", size = "md", className = "", children, ...rest }) {
const base = "inline-flex items-center justify-center gap-2 font-semibold transition-all duration-150 active:translate-y-px rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-pink-500 disabled:opacity-50 disabled:pointer-events-none";
const sizes = { sm: "text-sm px-4 py-2", md: "text-sm px-5 py-2.5", lg: "text-base px-6 py-3" };
const variants = {
primary: "bg-[#FF2D55] text-white hover:bg-[#E61243] shadow-[0_4px_0_#C2143A] active:shadow-[0_1px_0_#C2143A]",
dark: "bg-[#1A1A1A] text-white hover:bg-black",
ghost: "bg-transparent text-[#1A1A1A] border border-[#1A1A1A] hover:bg-[#1A1A1A] hover:text-white",
soft: "bg-pink-50 text-[#FF2D55] hover:bg-pink-100",
subtle: "bg-stone-100 text-stone-700 hover:bg-stone-200",
danger: "bg-rose-600 text-white hover:bg-rose-700",
};
return (
);
}
/* ===== Badge ===== */
function Badge({ tone = "neutral", children, className = "" }) {
const map = {
neutral: "bg-stone-100 text-stone-700",
success: "bg-emerald-50 text-emerald-700 ring-1 ring-emerald-200",
warning: "bg-amber-50 text-amber-700 ring-1 ring-amber-200",
danger: "bg-rose-50 text-rose-700 ring-1 ring-rose-200",
info: "bg-sky-50 text-sky-700 ring-1 ring-sky-200",
brand: "bg-pink-50 text-[#E61243] ring-1 ring-pink-200",
};
return (
{children}
);
}
/* ===== Card ===== */
function Card({ children, className = "" }) {
return
{children}
;
}
/* ===== Page header ===== */
function PageHeader({ eyebrow, title, jp, sub, actions }) {
return (
{eyebrow &&
{eyebrow}
}
{jp &&
{jp}
}
{title}
{sub &&
{sub}
}
{actions &&
{actions}
}
);
}
/* ===== Toast ===== */
function Toast({ toast, onClose }) {
useEffect(() => {
if (!toast) return;
const t = setTimeout(onClose, 4200);
return () => clearTimeout(t);
}, [toast]);
if (!toast) return null;
const toneMap = { success: "bg-emerald-600", danger: "bg-rose-600", info: "bg-[#1A1A1A]", warning: "bg-amber-500" };
return (
);
}
/* ===== Modal wrapper ===== */
function Modal({ open, onClose, title, children, width = "max-w-lg" }) {
if (!open) return null;
return (
);
}
/* ===== Field input ===== */
function Field({ label, type = "text", value, onChange, full, placeholder, required }) {
return (
onChange(e.target.value)}
placeholder={placeholder}
required={required}
className="mt-1.5 w-full px-4 py-2.5 rounded-xl border border-stone-300 bg-white text-[#1A1A1A] focus:border-[#FF2D55] focus:ring-2 focus:ring-pink-100 outline-none transition"
/>
);
}
/* ===== Stamp ===== */
function Stamp({ children, className = "" }) {
return (
{children}
);
}
Object.assign(window, { Icon, AppCtx, useApp, Button, Badge, Card, PageHeader, Toast, Modal, Field, Stamp });