4) NEW FILE client/src/pages/Creator/MyMarketplacePage.tsx

Shows Active Listings, a Cancelled Assets panel, a New Upload shortcut, and Stats (Payouts/Performance).

Uses assumed endpoints; if not present, Replit can wire the server to match:

GET /api/creator/my-assets?status=active|cancelled&page=&limit=

POST /api/creator/assets/:id/cancel

GET /api/creator/payouts/summary

import { useEffect, useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useAuth } from "@/contexts/AuthContext";
import { Link, useLocation } from "wouter";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Skeleton } from "@/components/ui/skeleton";
import { toast } from "sonner";
import { DollarSign, Upload, X, Eye, BarChart3, ShoppingBag, Archive } from "lucide-react";

interface CreatorAsset {
  id: string;
  title: string;
  description?: string;
  price: number;
  createdAt: string;
  salesCount: number;
  previewUrl?: string;
  status: "active" | "cancelled";
}

interface PayoutSummary {
  lifetimeEarningsCents: number;
  pendingPayoutCents: number;
  lastPayoutAt?: string;
}

export default function MyMarketplacePage() {
  const { currentUser } = useAuth();
  const [, setLocation] = useLocation();
  const qc = useQueryClient();

  // Data: active + cancelled + payouts
  const { data: active, isLoading: loadingActive } = useQuery<CreatorAsset[]>({
    queryKey: ["creator-my-assets", "active"],
    queryFn: async () => {
      const res = await fetch(`/api/creator/my-assets?status=active`, { credentials: "include" });
      if (!res.ok) throw new Error("Failed to load active assets");
      return res.json();
    }
  });

  const { data: cancelled, isLoading: loadingCancelled } = useQuery<CreatorAsset[]>({
    queryKey: ["creator-my-assets", "cancelled"],
    queryFn: async () => {
      const res = await fetch(`/api/creator/my-assets?status=cancelled`, { credentials: "include" });
      if (!res.ok) throw new Error("Failed to load cancelled assets");
      return res.json();
    }
  });

  const { data: payouts } = useQuery<PayoutSummary>({
    queryKey: ["creator-payouts-summary"],
    queryFn: async () => {
      const res = await fetch(`/api/creator/payouts/summary`, { credentials: "include" });
      if (!res.ok) throw new Error("Failed to load payouts summary");
      return res.json();
    }
  });

  const cancelMutation = useMutation({
    mutationFn: async (assetId: string) => {
      const res = await fetch(`/api/creator/assets/${assetId}/cancel`, { method: "POST", credentials: "include" });
      if (!res.ok) throw new Error("Failed to cancel asset");
      return res.json();
    },
    onSuccess: () => {
      toast.success("Asset cancelled and moved to Cancelled Assets.");
      qc.invalidateQueries({ queryKey: ["creator-my-assets"] });
    },
    onError: () => toast.error("Could not cancel asset. Please try again.")
  });

  const fmt = (cents: number) => `$${(cents / 100).toFixed(2)}`;

  return (
    <div className="space-y-8">
      {/* Stats */}
      <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
        <Card>
          <CardContent className="p-4 flex items-center">
            <DollarSign className="h-8 w-8 text-primary mr-3" />
            <div>
              <div className="text-2xl font-bold">{fmt(payouts?.lifetimeEarningsCents || 0)}</div>
              <div className="text-sm text-muted-foreground">Lifetime Earnings</div>
            </div>
          </CardContent>
        </Card>
        <Card>
          <CardContent className="p-4 flex items-center">
            <ShoppingBag className="h-8 w-8 text-primary mr-3" />
            <div>
              <div className="text-2xl font-bold">
                {(active || []).reduce((sum, a) => sum + (a.salesCount || 0), 0).toLocaleString()}
              </div>
              <div className="text-sm text-muted-foreground">Total Sales (Active)</div>
            </div>
          </CardContent>
        </Card>
        <Card>
          <CardContent className="p-4 flex items-center">
            <BarChart3 className="h-8 w-8 text-primary mr-3" />
            <div>
              <div className="text-2xl font-bold">{fmt(payouts?.pendingPayoutCents || 0)}</div>
              <div className="text-sm text-muted-foreground">Pending Payout</div>
            </div>
          </CardContent>
        </Card>
      </div>

      {/* Actions */}
      <div className="flex items-center justify-between">
        <h2 className="text-xl font-semibold">My Active Listings</h2>
        <div className="flex gap-2">
          <Link href="/creator/upload"><Button><Upload className="h-4 w-4 mr-2" /> Upload New Asset</Button></Link>
          <Link href="/creator/earnings"><Button variant="outline"><DollarSign className="h-4 w-4 mr-2" /> Payouts</Button></Link>
        </div>
      </div>

      {/* Active Assets */}
      <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-6">
        {loadingActive ? (
          [...Array(6)].map((_, i) => (
            <Card key={i}><Skeleton className="aspect-video w-full" /><CardContent className="p-4"><Skeleton className="h-4 w-3/4 mb-2" /><Skeleton className="h-3 w-1/2 mb-4" /></CardContent></Card>
          ))
        ) : (active || []).length === 0 ? (
          <Card><CardContent className="p-6 text-muted-foreground">No active listings yet. Click “Upload New Asset”.</CardContent></Card>
        ) : (
          (active || []).map((a) => (
            <Card key={a.id} className="overflow-hidden">
              <div className="aspect-video bg-gray-100 dark:bg-gray-800">
                {a.previewUrl ? <img src={a.previewUrl} alt={a.title} className="w-full h-full object-cover" /> : <div className="w-full h-full" />}
              </div>
              <CardContent className="p-4 space-y-2">
                <div className="flex items-start justify-between">
                  <div>
                    <div className="font-semibold">{a.title}</div>
                    <div className="text-xs text-muted-foreground">Created {new Date(a.createdAt).toLocaleDateString()}</div>
                  </div>
                  <Badge variant="secondary">{`$${(a.price / 100).toFixed(2)}`}</Badge>
                </div>
                <div className="text-xs text-muted-foreground">Sales: {a.salesCount}</div>
                <div className="flex gap-2">
                  <Link href={`/marketplace/asset/${a.id}`}><Button variant="outline" size="sm"><Eye className="h-4 w-4 mr-2" />View</Button></Link>
                  <Button variant="destructive" size="sm" onClick={() => cancelMutation.mutate(a.id)}><X className="h-4 w-4 mr-2" />Cancel</Button>
                </div>
              </CardContent>
            </Card>
          ))
        )}
      </div>

      {/* Cancelled Assets */}
      <div className="mt-6">
        <div className="flex items-center gap-2 mb-3">
          <Archive className="h-4 w-4" />
          <h3 className="text-lg font-semibold">Cancelled Assets</h3>
        </div>
        <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-6">
          {loadingCancelled ? (
            [...Array(3)].map((_, i) => (<Card key={i}><Skeleton className="aspect-video w-full" /><CardContent className="p-4"><Skeleton className="h-4 w-3/4" /></CardContent></Card>))
          ) : (cancelled || []).length === 0 ? (
            <Card><CardContent className="p-6 text-muted-foreground">No cancelled assets.</CardContent></Card>
          ) : (
            (cancelled || []).map((a) => (
              <Card key={a.id} className="overflow-hidden opacity-80">
                <div className="aspect-video bg-gray-100 dark:bg-gray-800">
                  {a.previewUrl ? <img src={a.previewUrl} alt={a.title} className="w-full h-full object-cover" /> : <div className="w-full h-full" />}
                </div>
                <CardContent className="p-4">
                  <div className="flex items-start justify-between">
                    <div>
                      <div className="font-semibold">{a.title}</div>
                      <div className="text-xs text-muted-foreground">Cancelled</div>
                    </div>
                    <Badge variant="outline">{`$${(a.price / 100).toFixed(2)}`}</Badge>
                  </div>
                </CardContent>
              </Card>
            ))
          )}
        </div>
      </div>
    </div>
  );
}
