Add Polaris frontend: storefront + admin console (React 18/Vite/TS/Tailwind)

- Shared src/lib/api.ts fetch wrapper -> /api with Cache-Control: no-store
- Storefront: home hero, shop grid (category filter + search), product detail,
  cart, checkout (POST /api/orders -> confirmation + order number), order tracking
- Admin: JWT login, today KPI dashboard (revenue/orders/gross/net/margin/aov/refunds),
  top products, supplier performance, product import, lifecycle view, order list
  with tracking simulation
- Multi-stage Dockerfile (Vite build -> nginx static + /api proxy -> api:8000)
- nginx.conf with SPA fallback and no-store HTML caching
- mock/mock_api.py for local verification (excluded from image)
This commit is contained in:
drjones
2026-08-25 20:28:26 -07:00
parent 035bb3a2e9
commit b09dcfd929
39 changed files with 6067 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
import { Link, NavLink, Outlet } from 'react-router-dom';
import { useCart } from '../store/cart';
import { IconCart, IconCoffee, IconExternalLink, IconStar, IconTruck } from './Icons';
const navLink = ({ isActive }: { isActive: boolean }) =>
`rounded-lg px-3 py-2 text-sm font-medium transition ${
isActive ? 'bg-white/[0.08] text-white' : 'text-slate-400 hover:text-white'
}`;
export default function StoreLayout() {
const { count } = useCart();
return (
<div className="bg-app flex min-h-screen flex-col">
<header className="sticky top-0 z-40 border-b border-white/[0.06] bg-night-950/70 backdrop-blur-xl">
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6">
<Link to="/" className="flex items-center gap-2.5">
<span className="flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 shadow-glow">
<IconStar className="h-5 w-5 text-white" />
</span>
<span className="text-lg font-bold tracking-tight text-white">
Polaris<span className="text-indigo-400">.</span>
</span>
</Link>
<nav className="hidden items-center gap-1 md:flex">
<NavLink to="/" end className={navLink}>
Home
</NavLink>
<NavLink to="/shop" className={navLink}>
Shop
</NavLink>
<NavLink to="/track" className={navLink}>
Track order
</NavLink>
</nav>
<div className="flex items-center gap-2">
<Link to="/track" className="btn-ghost !px-3 !py-2 md:hidden" title="Track order">
<IconTruck className="h-4 w-4" />
</Link>
<Link to="/cart" className="btn-ghost relative !px-3.5 !py-2">
<IconCart className="h-4 w-4" />
<span className="hidden sm:inline">Cart</span>
{count > 0 && (
<span className="absolute -right-1.5 -top-1.5 flex h-5 min-w-[1.25rem] items-center justify-center rounded-full bg-gradient-to-r from-indigo-500 to-violet-500 px-1 text-[11px] font-bold text-white shadow-glow">
{count}
</span>
)}
</Link>
</div>
</div>
</header>
<main className="flex-1">
<Outlet />
</main>
<footer className="border-t border-white/[0.06] bg-night-900/60">
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-4 px-4 py-8 sm:flex-row sm:px-6">
<div className="flex items-center gap-2 text-sm text-slate-400">
<IconStar className="h-4 w-4 text-indigo-400" />
<span>Polaris — automated commerce, by starlight.</span>
</div>
<div className="flex items-center gap-4 text-sm">
<Link to="/admin" className="text-slate-500 transition hover:text-indigo-300">
Admin
</Link>
<a
href="https://buymeacoffee.com/r26xrthzttg"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 rounded-lg border border-white/10 bg-white/[0.04] px-3 py-1.5 text-amber-200/90 transition hover:bg-white/[0.08]"
>
<IconCoffee className="h-4 w-4" />
Buy me a coffee
<IconExternalLink className="h-3.5 w-3.5 opacity-60" />
</a>
</div>
</div>
</footer>
</div>
);
}