import React, { useEffect, useMemo, useState } from "react";
import {
LayoutDashboard,
ShoppingCart,
BarChart3,
Wallet,
Table2,
Settings,
LogOut,
RefreshCw,
Search,
ChevronDown,
Check,
X,
Pencil,
Trash2,
Percent,
BadgeCheck,
} from "lucide-react";
/**
* iStore POS (Apple-only) — single-file UI mock inspired by the provided POS layout.
* - Product grid with category tabs + search
* - Cart/order summary with totals, discount, and simple order meta
* - Clean, responsive layout
*/
// ---------- Helpers
const money = (n) =>
new Intl.NumberFormat(undefined, {
style: "currency",
currency: "USD",
}).format(n);
const nowLabel = () => {
const d = new Date();
const weekday = d.toLocaleDateString(undefined, { weekday: "long" });
const date = d.toLocaleDateString(undefined, { day: "2-digit", month: "short", year: "numeric" });
const time = d.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" });
return `${weekday}, ${date} • ${time}`;
};
const pill = (active) =>
`inline-flex items-center gap-2 rounded-full px-3 py-1 text-sm transition ${
active
? "bg-slate-900 text-white shadow-sm"
: "bg-white text-slate-700 hover:bg-slate-50 border border-slate-200"
}`;
function dataUriCard({ title, subtitle, glyph = "" }) {
// Simple “photo-like” SVG card so images work offline.
const svg = `
`;
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg.trim())}`;
}
function escapeXml(s) {
return String(s)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
// ---------- Data
const PRODUCTS = [
{
id: "iphone-15-pro",
name: "iPhone 15 Pro",
price: 999,
category: "iPhone",
variant: "128GB",
available: true,
glyph: "📱",
},
{
id: "iphone-15",
name: "iPhone 15",
price: 799,
category: "iPhone",
variant: "128GB",
available: true,
glyph: "📱",
},
{
id: "macbook-air-m3",
name: "MacBook Air",
price: 1099,
category: "Mac",
variant: "M3 • 13-inch",
available: true,
glyph: "💻",
},
{
id: "macbook-pro-m3",
name: "MacBook Pro",
price: 1599,
category: "Mac",
variant: "M3 • 14-inch",
available: true,
glyph: "💻",
},
{
id: "imac-m3",
name: "iMac",
price: 1299,
category: "Mac",
variant: "M3 • 24-inch",
available: true,
glyph: "🖥️",
},
{
id: "mac-mini",
name: "Mac mini",
price: 599,
category: "Mac",
variant: "M2",
available: true,
glyph: "🧊",
},
{
id: "ipad-pro",
name: "iPad Pro",
price: 999,
category: "iPad",
variant: "11-inch",
available: true,
glyph: "📟",
},
{
id: "ipad-air",
name: "iPad Air",
price: 599,
category: "iPad",
variant: "10.9-inch",
available: true,
glyph: "📟",
},
{
id: "apple-watch-ultra",
name: "Apple Watch Ultra",
price: 799,
category: "Watch",
variant: "49mm",
available: true,
glyph: "⌚",
},
{
id: "apple-watch-series",
name: "Apple Watch Series",
price: 399,
category: "Watch",
variant: "41mm",
available: true,
glyph: "⌚",
},
{
id: "airpods-pro",
name: "AirPods Pro",
price: 249,
category: "Audio",
variant: "2nd gen",
available: true,
glyph: "🎧",
},
{
id: "airpods-max",
name: "AirPods Max",
price: 549,
category: "Audio",
variant: "Over-ear",
available: true,
glyph: "🎧",
},
{
id: "homepod",
name: "HomePod",
price: 299,
category: "Audio",
variant: "2nd gen",
available: true,
glyph: "🔊",
},
{
id: "airtag-4pack",
name: "AirTag (4‑pack)",
price: 99,
category: "Accessories",
variant: "4 pack",
available: true,
glyph: "🏷️",
},
{
id: "magic-keyboard",
name: "Magic Keyboard",
price: 99,
category: "Accessories",
variant: "Wireless",
available: false,
glyph: "⌨️",
},
];
const CATEGORIES = ["All", "iPhone", "Mac", "iPad", "Watch", "Audio", "Accessories"];
const SEED_ORDER_ID = () => `#I${Math.floor(100000 + Math.random() * 900000)}`;
// ---------- UI bits
function Chip({ children, active, onClick }) {
return (
);
}
function IconDot({ ok }) {
return (
{ok ? "Available" : "Out of stock"}
);
}
function Divider() {
return
;
}
function SmallField({ label, children }) {
return (
);
}
// ---------- Main
export default function IStorePOS() {
const [activeNav, setActiveNav] = useState("Menu Order");
const [category, setCategory] = useState("All");
const [query, setQuery] = useState("");
const [cart, setCart] = useState(() => {
// Prefill like the screenshot (optional):
const pre = [
{ id: "airpods-pro", qty: 1, note: "—" },
{ id: "macbook-air-m3", qty: 1, note: "Silver" },
{ id: "iphone-15-pro", qty: 1, note: "Natural Titanium" },
];
return pre;
});
const [applyDiscount, setApplyDiscount] = useState(true);
const [orderId, setOrderId] = useState(SEED_ORDER_ID);
const [clock, setClock] = useState(nowLabel());
const [orderType, setOrderType] = useState("In-store");
const [counter, setCounter] = useState("Counter A");
useEffect(() => {
const t = setInterval(() => setClock(nowLabel()), 15_000);
return () => clearInterval(t);
}, []);
const productsWithImages = useMemo(() => {
return PRODUCTS.map((p) => ({
...p,
image:
p.image ||
dataUriCard({
title: p.name,
subtitle: `${p.variant} • ${money(p.price)}`,
glyph: p.glyph,
}),
}));
}, []);
const counts = useMemo(() => {
const c = { All: productsWithImages.length };
for (const cat of CATEGORIES.slice(1)) {
c[cat] = productsWithImages.filter((p) => p.category === cat).length;
}
return c;
}, [productsWithImages]);
const filtered = useMemo(() => {
const q = query.trim().toLowerCase();
return productsWithImages
.filter((p) => (category === "All" ? true : p.category === category))
.filter((p) =>
q
? `${p.name} ${p.variant} ${p.category}`.toLowerCase().includes(q)
: true
);
}, [productsWithImages, category, query]);
const cartLines = useMemo(() => {
return cart
.map((line) => {
const p = productsWithImages.find((x) => x.id === line.id);
if (!p) return null;
return {
...line,
product: p,
lineTotal: p.price * line.qty,
};
})
.filter(Boolean);
}, [cart, productsWithImages]);
const subtotal = useMemo(() => cartLines.reduce((s, l) => s + l.lineTotal, 0), [cartLines]);
const taxRate = 0.1;
const taxes = useMemo(() => subtotal * taxRate, [subtotal]);
const discountEligible = subtotal >= 500;
const discount = useMemo(() => (applyDiscount && discountEligible ? subtotal * 0.1 : 0), [applyDiscount, discountEligible, subtotal]);
const total = useMemo(() => Math.max(0, subtotal + taxes - discount), [subtotal, taxes, discount]);
const addToCart = (id) => {
setCart((prev) => {
const idx = prev.findIndex((x) => x.id === id);
if (idx >= 0) {
const next = [...prev];
next[idx] = { ...next[idx], qty: next[idx].qty + 1 };
return next;
}
return [...prev, { id, qty: 1, note: "—" }];
});
};
const setQty = (id, qty) => {
setCart((prev) =>
prev
.map((x) => (x.id === id ? { ...x, qty } : x))
.filter((x) => x.qty > 0)
);
};
const removeLine = (id) => setCart((prev) => prev.filter((x) => x.id !== id));
const randomizeOrder = () => {
setOrderId(SEED_ORDER_ID());
setQuery("");
setCategory("All");
};
const navItems = [
{ label: "Dashboard", icon: LayoutDashboard },
{ label: "Menu Order", icon: ShoppingCart },
{ label: "Analytics", icon: BarChart3 },
{ label: "Withdrawal", icon: Wallet },
{ label: "Manage Table", icon: Table2 },
];
return (
{/* Sidebar */}
{/* Main */}
{/* Top bar */}
{/* Content */}
{/* Left: menu */}
{CATEGORIES.map((c) => (
setCategory(c)}>
{c}
{counts[c] ?? 0}
))}
Showing {filtered.length} items
{/* Grid */}
{filtered.map((p) => {
const inStock = p.available;
return (
);
})}
{/* Right: order summary */}
iStore POS UI mock • Frontend only (no backend)
);
}