Frontend — src/context/CartContext.tsx
import React, { createContext, useContext, useMemo, useState, useEffect } from "react";

export type CartItem =
  | { kind: "stock"; assetId: string; name: string; priceCents: number; qty: number; previewUrl?: string }
  | { kind: "price"; lookupKey: string; name: string; qty: number };

type CartState = {
  items: CartItem[];
  isOpen: boolean;
  open: () => void;
  close: () => void;
  addItem: (item: CartItem) => void;
  removeItem: (index: number) => void;
  clear: () => void;
  subtotalCents: number;
  checkout: (opts?: { successPath?: string; cancelPath?: string }) => Promise<void>;
};

const CartCtx = createContext<CartState | null>(null);
const LS_KEY = "ibrandbiz_cart_v1";

export const CartProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [items, setItems] = useState<CartItem[]>([]);
  const [isOpen, setOpen] = useState(false);

  useEffect(() => {
    try {
      const raw = localStorage.getItem(LS_KEY);
      if (raw) setItems(JSON.parse(raw));
    } catch {}
  }, []);

  useEffect(() => {
    localStorage.setItem(LS_KEY, JSON.stringify(items));
  }, [items]);

  const addItem = (item: CartItem) => setItems((prev) => [...prev, item]);
  const removeItem = (index: number) => setItems((prev) => prev.filter((_, i) => i !== index));
  const clear = () => setItems([]);
  const open = () => setOpen(true);
  const close = () => setOpen(false);

  const subtotalCents = useMemo(
    () =>
      items.reduce((sum, it) => {
        if (it.kind === "stock") return sum + it.priceCents * it.qty;
        return sum; // subscriptions billed by Stripe price, not included here
      }, 0),
    [items]
  );

  const checkout = async (opts?: { successPath?: string; cancelPath?: string }) => {
    const resp = await fetch("/api/checkout/create-session", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        items,
        successPath: opts?.successPath || "/thank-you",
        cancelPath: opts?.cancelPath || "/cart",
      }),
    });
    const data = await resp.json();
    if (!resp.ok) throw new Error(data.error || "Checkout failed");
    window.location.href = data.url; // Redirect to Stripe Checkout
  };

  return (
    <CartCtx.Provider value={{ items, isOpen, open, close, addItem, removeItem, clear, subtotalCents, checkout }}>
      {children}
    </CartCtx.Provider>
  );
};

export const useCart = () => {
  const ctx = useContext(CartCtx);
  if (!ctx) throw new Error("useCart must be used within CartProvider");
  return ctx;
};

Frontend — src/components/CartDrawer.tsx
import React from "react";
import { useCart } from "../context/CartContext";

export default function CartDrawer() {
  const { items, isOpen, close, removeItem, subtotalCents, checkout, clear } = useCart();

  return (
    <div
      className={`fixed top-0 right-0 h-full w-[360px] bg-white shadow-2xl border-l z-50 transition-transform ${
        isOpen ? "translate-x-0" : "translate-x-full"
      }`}
    >
      <div className="p-4 border-b flex items-center justify-between">
        <h2 className="text-lg font-semibold">Your Cart</h2>
        <button onClick={close} className="text-gray-500 hover:text-gray-800">✕</button>
      </div>

      <div className="p-4 space-y-3 overflow-auto h-[calc(100%-160px)]">
        {items.length === 0 ? (
          <p className="text-sm text-gray-500">Your cart is empty.</p>
        ) : (
          items.map((it, i) => (
            <div key={i} className="flex items-center gap-3 border rounded p-2">
              {it.kind === "stock" && it.previewUrl ? (
                <img src={it.previewUrl} alt={it.name} className="w-12 h-12 object-cover rounded" />
              ) : (
                <div className="w-12 h-12 bg-gray-100 rounded flex items-center justify-center text-xs">SUB</div>
              )}
              <div className="flex-1">
                <div className="text-sm font-medium">
                  {it.kind === "stock" ? it.name : `Subscription: ${it.name}`}
                </div>
                <div className="text-xs text-gray-500">
                  Qty {it.qty} —{" "}
                  {it.kind === "stock" ? `$${(it.priceCents / 100).toFixed(2)}` : "Stripe price"}
                </div>
              </div>
              <button onClick={() => removeItem(i)} className="text-red-500 text-sm">Remove</button>
            </div>
          ))
        )}
      </div>

      <div className="p-4 border-t">
        <div className="flex items-center justify-between mb-3">
          <span className="text-sm text-gray-600">Subtotal (one-offs)</span>
          <span className="font-semibold">${(subtotalCents / 100).toFixed(2)}</span>
        </div>
        <button
          onClick={() => checkout({ successPath: "/thank-you", cancelPath: "/cart" })}
          disabled={items.length === 0}
          className="w-full bg-black text-white py-2 rounded hover:opacity-90 disabled:opacity-40"
        >
          Checkout
        </button>
        <button onClick={clear} className="w-full mt-2 text-sm text-gray-500 hover:text-gray-800">Clear cart</button>
      </div>
    </div>
  );
}

Frontend — src/components/Header.tsx
import React from "react";
import { useCart } from "../context/CartContext";
import CartDrawer from "./CartDrawer";

export default function Header() {
  const { open, items } = useCart();

  return (
    <header className="w-full border-b bg-white">
      <div className="max-w-7xl mx-auto h-14 px-4 flex items-center justify-between">
        {/* Left: Logo */}
        <div className="flex items-center gap-2">
          <img src="/logo.svg" alt="IBrandBiz" className="h-7" />
          <span className="font-semibold">IBrandBiz</span>
        </div>

        {/* Right: icons */}
        <div className="flex items-center gap-4">
          {/* Notifications (existing) */}
          <button aria-label="Notifications" className="relative p-2 rounded hover:bg-gray-100">
            🔔
          </button>

          {/* —— INSERT CART HERE —— */}
          <button
            aria-label="Cart"
            onClick={open}
            className="relative p-2 rounded hover:bg-gray-100"
            title="Cart"
          >
            🛒
            {items.length > 0 && (
              <span className="absolute -top-1 -right-1 text-[10px] bg-green-600 text-white rounded-full px-1.5 py-[1px]">
                {items.length}
              </span>
            )}
          </button>

          {/* Settings (existing) */}
          <button aria-label="Settings" className="p-2 rounded hover:bg-gray-100">
            ⚙️
          </button>
        </div>
      </div>

      {/* Drawer lives here so it's always mounted */}
      <CartDrawer />
    </header>
  );
}


Wrap your app root with the provider:

// src/main.tsx or App.tsx
import { CartProvider } from "./context/CartContext";
// ...
<CartProvider>
  <AppRoutes />
</CartProvider>

Frontend — “Add to Cart” example (stock photo card/modal)
// wherever you show a stock photo
import { useCart } from "../context/CartContext";

function StockCard({ asset }: { asset: { id: string; name: string; previewUrl: string } }) {
  const { addItem, open } = useCart();
  const add = () => {
    addItem({ kind: "stock", assetId: asset.id, name: asset.name, priceCents: 699, qty: 1, previewUrl: asset.previewUrl });
    open();
  };
  return (
    <div className="border rounded p-2">
      <img src={asset.previewUrl} className="w-full h-40 object-cover rounded" />
      <div className="mt-2 flex items-center justify-between">
        <div className="text-sm font-medium truncate">{asset.name}</div>
        <button onClick={add} className="text-sm bg-black text-white px-3 py-1 rounded">Add $6.99</button>
      </div>
    </div>
  );
}
