{"path":"apps/dashboard/src/pages/dashboard.tsx","size":10792,"locked":false,"binary":false,"truncated":false,"content":"import { Link } from \"wouter\";\nimport { ArrowUpRight, Zap } from \"lucide-react\";\nimport { useGetStats, useListJobs } from \"@workspace/api-client-react\";\nimport { StatusBadge } from \"@/components/status-badge\";\nimport { JobCard } from \"@/components/job-card\";\nimport { formatLamports, truncatePubkey, formatDate } from \"@/lib/format\";\n\nfunction StatCard({\n  label,\n  value,\n  sub,\n  accent,\n  gradient,\n}: {\n  label: string;\n  value: string | number;\n  sub?: string;\n  accent?: boolean;\n  gradient?: \"green\" | \"purple\";\n}) {\n  const glowColor =\n    gradient === \"green\"\n      ? \"hover:shadow-[0_8px_32px_rgba(20,241,149,0.12)] hover:border-emerald-500/25\"\n      : gradient === \"purple\"\n      ? \"hover:shadow-[0_8px_32px_rgba(153,69,255,0.12)] hover:border-purple-500/25\"\n      : \"hover:shadow-[0_4px_24px_rgba(255,255,255,0.04)]\";\n\n  const topBorder =\n    gradient === \"green\"\n      ? \"from-transparent via-emerald-500/40 to-transparent\"\n      : gradient === \"purple\"\n      ? \"from-transparent via-purple-500/40 to-transparent\"\n      : \"from-transparent via-white/10 to-transparent\";\n\n  return (\n    <div className={`group relative glass rounded-2xl p-5 transition-all duration-300 cursor-default hover:-translate-y-0.5 ${glowColor} overflow-hidden`}>\n      {/* Top accent line */}\n      <div className={`absolute inset-x-0 top-0 h-px bg-gradient-to-r ${topBorder}`} />\n\n      <span className=\"text-[9px] font-mono text-white/35 uppercase tracking-[0.15em] leading-none block mb-3\">\n        {label}\n      </span>\n      <span\n        className={`text-2xl md:text-3xl font-bold font-mono leading-none block ${\n          gradient === \"green\"\n            ? \"text-emerald-400\"\n            : gradient === \"purple\"\n            ? \"text-purple-400\"\n            : accent\n            ? \"text-emerald-400\"\n            : \"text-white\"\n        }`}\n      >\n        {value}\n      </span>\n      {sub && (\n        <span className=\"text-[9px] text-white/30 font-mono mt-2 block\">{sub}</span>\n      )}\n    </div>\n  );\n}\n\nfunction SkeletonCard() {\n  return (\n    <div className=\"glass rounded-2xl p-5 shimmer\">\n      <div className=\"h-2 bg-white/[0.05] rounded-full w-14 mb-4\" />\n      <div className=\"h-7 bg-white/[0.08] rounded-full w-10\" />\n    </div>\n  );\n}\n\nfunction PageHeader() {\n  return (\n    <div className=\"relative px-5 md:px-8 py-6 md:py-8 border-b border-white/[0.05] overflow-hidden\">\n      <div className=\"absolute inset-0 bg-gradient-to-r from-emerald-500/[0.03] via-transparent to-purple-500/[0.03]\" />\n      <div className=\"relative flex items-start justify-between gap-4\">\n        <div>\n          <div className=\"flex items-center gap-2 mb-1\">\n            <Zap className=\"w-3.5 h-3.5 text-emerald-400\" />\n            <span className=\"text-[9px] font-mono text-emerald-400/70 uppercase tracking-widest\">Live</span>\n          </div>\n          <h1 className=\"text-2xl md:text-3xl font-bold tracking-tight text-white leading-tight\">\n            Protocol Overview\n          </h1>\n          <p className=\"text-xs text-white/35 font-mono mt-1\">\n            Mappers — Autonomous On-Chain Escrow · Devnet\n          </p>\n        </div>\n        <Link\n          href=\"/jobs/new\"\n          className=\"shrink-0 group relative flex items-center gap-2 px-4 py-2.5 text-xs font-mono font-bold rounded-xl bg-gradient-to-r from-emerald-500 to-emerald-400 text-black hover:from-emerald-400 hover:to-emerald-300 active:scale-[0.97] transition-all duration-200 shadow-[0_0_24px_rgba(20,241,149,0.25)] hover:shadow-[0_0_32px_rgba(20,241,149,0.4)]\"\n        >\n          <span>+ New Job</span>\n        </Link>\n      </div>\n    </div>\n  );\n}\n\nexport default function Dashboard() {\n  const { data: stats, isLoading: statsLoading } = useGetStats();\n  const { data: jobs, isLoading: jobsLoading } = useListJobs();\n\n  const recentJobs = jobs?.slice().reverse().slice(0, 10) ?? [];\n\n  return (\n    <div className=\"flex-1 flex flex-col\">\n      <PageHeader />\n\n      <div className=\"flex-1 p-5 md:p-8 space-y-8\">\n        {/* Stats grid */}\n        <div>\n          <p className=\"text-[9px] font-mono text-white/25 uppercase tracking-widest mb-4\">Protocol Stats</p>\n          <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3\">\n            {statsLoading ? (\n              Array.from({ length: 5 }).map((_, i) => <SkeletonCard key={i} />)\n            ) : (\n              <>\n                <StatCard label=\"Total Jobs\" value={stats?.total ?? 0} />\n                <StatCard label=\"Pending\" value={stats?.pending ?? 0} accent />\n                <StatCard label=\"Completed\" value={stats?.completed ?? 0} gradient=\"green\" />\n                <StatCard label=\"Cancelled\" value={stats?.cancelled ?? 0} />\n                <StatCard\n                  label=\"Escrowed SOL\"\n                  value={`◎ ${formatLamports(stats?.totalEscrowedLamports ?? \"0\")}`}\n                  sub=\"in active vaults\"\n                  gradient=\"purple\"\n                />\n              </>\n            )}\n          </div>\n        </div>\n\n        {/* Program ID pill */}\n        <div className=\"glass rounded-2xl px-5 py-4 flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 overflow-hidden group hover:border-white/[0.12] transition-all duration-300\">\n          <div className=\"flex items-center gap-2 shrink-0\">\n            <div className=\"w-2 h-2 rounded-full bg-emerald-400 animate-pulse shadow-[0_0_6px_rgba(20,241,149,0.8)]\" />\n            <span className=\"text-[9px] font-mono text-white/30 uppercase tracking-widest\">Program</span>\n          </div>\n          <span className=\"text-[11px] font-mono text-emerald-400/80 break-all leading-relaxed flex-1\">\n            52yt1gCbPeiKP4JYjUVKmMJSgBMMcUx8xRGqozMKX2Mu\n          </span>\n          <a\n            href=\"https://explorer.solana.com/address/52yt1gCbPeiKP4JYjUVKmMJSgBMMcUx8xRGqozMKX2Mu?cluster=devnet\"\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            className=\"hidden sm:flex items-center gap-1 text-[9px] font-mono text-white/20 hover:text-emerald-400/60 transition-colors shrink-0\"\n          >\n            DEVNET <ArrowUpRight className=\"w-3 h-3\" />\n          </a>\n        </div>\n\n        {/* Recent jobs */}\n        <div>\n          <div className=\"flex items-center justify-between mb-4\">\n            <p className=\"text-[9px] font-mono text-white/25 uppercase tracking-widest\">Recent Jobs</p>\n            <Link href=\"/jobs\" className=\"flex items-center gap-1 text-[10px] font-mono text-emerald-400/60 hover:text-emerald-400 transition-colors\">\n              View all <ArrowUpRight className=\"w-3 h-3\" />\n            </Link>\n          </div>\n\n          {/* Mobile: card list */}\n          <div className=\"md:hidden space-y-3\">\n            {jobsLoading ? (\n              Array.from({ length: 4 }).map((_, i) => (\n                <div key={i} className=\"glass rounded-2xl p-5 shimmer h-32\" />\n              ))\n            ) : recentJobs.length === 0 ? (\n              <div className=\"glass rounded-2xl px-6 py-12 text-center\">\n                <p className=\"text-sm text-white/30 font-mono mb-3\">No jobs yet.</p>\n                <Link href=\"/jobs/new\" className=\"text-xs text-emerald-400 font-mono hover:text-emerald-300 transition-colors inline-flex items-center gap-1\">\n                  Create your first escrow <ArrowUpRight className=\"w-3 h-3\" />\n                </Link>\n              </div>\n            ) : (\n              recentJobs.map((job) => <JobCard key={job.id} job={job} />)\n            )}\n          </div>\n\n          {/* Desktop: glass table */}\n          <div className=\"hidden md:block glass rounded-2xl overflow-hidden\">\n            <div className=\"overflow-x-auto\">\n              <table className=\"w-full text-xs font-mono\">\n                <thead>\n                  <tr className=\"border-b border-white/[0.05] bg-white/[0.02]\">\n                    {[\"Job ID\", \"Client\", \"Freelancer\", \"Amount\", \"Status\", \"Created\"].map((h) => (\n                      <th\n                        key={h}\n                        className=\"text-left text-[9px] text-white/25 font-normal px-5 py-3 uppercase tracking-[0.12em] whitespace-nowrap\"\n                      >\n                        {h}\n                      </th>\n                    ))}\n                  </tr>\n                </thead>\n                <tbody>\n                  {jobsLoading ? (\n                    Array.from({ length: 5 }).map((_, i) => (\n                      <tr key={i} className=\"border-b border-white/[0.04]\">\n                        {Array.from({ length: 6 }).map((__, j) => (\n                          <td key={j} className=\"px-5 py-4\">\n                            <div className=\"h-2.5 bg-white/[0.05] rounded-full animate-pulse w-20\" />\n                          </td>\n                        ))}\n                      </tr>\n                    ))\n                  ) : recentJobs.length === 0 ? (\n                    <tr>\n                      <td colSpan={6} className=\"px-5 py-14 text-center text-white/25\">\n                        No jobs yet — create your first escrow to get started.\n                      </td>\n                    </tr>\n                  ) : (\n                    recentJobs.map((job) => (\n                      <tr\n                        key={job.id}\n                        className=\"border-b border-white/[0.04] last:border-b-0 hover:bg-white/[0.03] transition-colors group\"\n                      >\n                        <td className=\"px-5 py-4\">\n                          <Link\n                            href={`/jobs/${job.jobId}`}\n                            className=\"text-emerald-400/80 hover:text-emerald-400 transition-colors inline-flex items-center gap-1 group-hover:gap-1.5\"\n                          >\n                            {job.jobId}\n                          </Link>\n                        </td>\n                        <td className=\"px-5 py-4 text-white/35\" title={job.clientPubkey}>\n                          {truncatePubkey(job.clientPubkey)}\n                        </td>\n                        <td className=\"px-5 py-4 text-white/35\" title={job.freelancerPubkey}>\n                          {truncatePubkey(job.freelancerPubkey)}\n                        </td>\n                        <td className=\"px-5 py-4 text-emerald-400/70\">◎ {formatLamports(job.amountLamports)}</td>\n                        <td className=\"px-5 py-4\">\n                          <StatusBadge status={job.status} />\n                        </td>\n                        <td className=\"px-5 py-4 text-white/25 whitespace-nowrap\">\n                          {formatDate(job.createdAt)}\n                        </td>\n                      </tr>\n                    ))\n                  )}\n                </tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"}