{"path":"apps/dashboard/src/pages/jobs.tsx","size":9086,"locked":false,"binary":false,"truncated":false,"content":"import { useState } from \"react\";\nimport { Link } from \"wouter\";\nimport { Search, ArrowUpRight, SlidersHorizontal } from \"lucide-react\";\nimport { 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\ntype StatusFilter = \"all\" | \"pending\" | \"completed\" | \"cancelled\";\n\nconst STATUS_TABS: { value: StatusFilter; label: string }[] = [\n  { value: \"all\",       label: \"All\" },\n  { value: \"pending\",   label: \"Pending\" },\n  { value: \"completed\", label: \"Completed\" },\n  { value: \"cancelled\", label: \"Cancelled\" },\n];\n\nconst TAB_COLORS: Record<StatusFilter, string> = {\n  all:       \"bg-white/10 text-white border-white/20\",\n  pending:   \"bg-amber-400/15 text-amber-300 border-amber-400/30 shadow-[0_0_12px_rgba(251,191,36,0.1)]\",\n  completed: \"bg-emerald-400/15 text-emerald-300 border-emerald-400/30 shadow-[0_0_12px_rgba(20,241,149,0.1)]\",\n  cancelled: \"bg-rose-400/15 text-rose-300 border-rose-400/30 shadow-[0_0_12px_rgba(244,63,94,0.1)]\",\n};\n\nexport default function Jobs() {\n  const [statusFilter, setStatusFilter] = useState<StatusFilter>(\"all\");\n  const [search, setSearch] = useState(\"\");\n\n  const { data: jobs, isLoading } = useListJobs(\n    statusFilter !== \"all\" ? { status: statusFilter } : undefined\n  );\n\n  const filtered = (jobs ?? []).filter((job) => {\n    if (!search) return true;\n    const q = search.toLowerCase();\n    return (\n      job.jobId.toLowerCase().includes(q) ||\n      job.clientPubkey.toLowerCase().includes(q) ||\n      job.freelancerPubkey.toLowerCase().includes(q)\n    );\n  });\n\n  return (\n    <div className=\"flex-1 flex flex-col\">\n      {/* Header */}\n      <div className=\"relative px-5 md:px-8 py-6 md:py-7 border-b border-white/[0.05] overflow-hidden\">\n        <div className=\"absolute inset-0 bg-gradient-to-r from-purple-500/[0.02] via-transparent to-emerald-500/[0.02]\" />\n        <div className=\"relative flex items-center justify-between gap-4\">\n          <div>\n            <h1 className=\"text-2xl md:text-3xl font-bold tracking-tight text-white\">All Jobs</h1>\n            <p className=\"text-[10px] text-white/30 font-mono mt-1\">\n              {isLoading ? \"Loading…\" : `${filtered.length} escrow${filtered.length !== 1 ? \"s\" : \"\"}`}\n            </p>\n          </div>\n          <Link\n            href=\"/jobs/new\"\n            className=\"shrink-0 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.2)]\"\n          >\n            + New Job\n          </Link>\n        </div>\n      </div>\n\n      {/* Filters */}\n      <div className=\"px-5 md:px-8 py-4 border-b border-white/[0.05] space-y-3\">\n        {/* Status filter pills */}\n        <div className=\"flex items-center gap-2 flex-wrap\">\n          <SlidersHorizontal className=\"w-3.5 h-3.5 text-white/20 shrink-0\" />\n          {STATUS_TABS.map(({ value, label }) => (\n            <button\n              key={value}\n              onClick={() => setStatusFilter(value)}\n              className={`px-3.5 py-1.5 text-[10px] font-mono font-medium tracking-wide rounded-full border transition-all duration-200 ${\n                statusFilter === value\n                  ? TAB_COLORS[value]\n                  : \"bg-white/[0.03] text-white/30 border-white/[0.07] hover:bg-white/[0.06] hover:text-white/60\"\n              }`}\n            >\n              {label}\n            </button>\n          ))}\n        </div>\n\n        {/* Search */}\n        <div className=\"relative\">\n          <Search className=\"absolute left-3.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-white/25 pointer-events-none\" />\n          <input\n            type=\"search\"\n            placeholder=\"Search by job ID, client, or freelancer…\"\n            value={search}\n            onChange={(e) => setSearch(e.target.value)}\n            className=\"w-full pl-9 pr-4 py-2.5 text-xs font-mono glass rounded-xl placeholder:text-white/20 text-white/80 outline-none focus:border-emerald-500/30 focus:shadow-[0_0_16px_rgba(20,241,149,0.06)] transition-all duration-200 min-h-[40px]\"\n          />\n        </div>\n      </div>\n\n      {/* Content */}\n      <div className=\"flex-1 overflow-y-auto p-5 md:p-8\">\n        {/* Mobile cards */}\n        <div className=\"md:hidden space-y-3\">\n          {isLoading ? (\n            Array.from({ length: 6 }).map((_, i) => (\n              <div key={i} className=\"glass rounded-2xl p-5 shimmer h-36\" />\n            ))\n          ) : filtered.length === 0 ? (\n            <div className=\"glass rounded-2xl px-6 py-14 text-center\">\n              <p className=\"text-sm text-white/30 font-mono mb-2\">\n                {search ? \"No jobs match your search.\" : \"No jobs yet.\"}\n              </p>\n              {!search && (\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              )}\n            </div>\n          ) : (\n            filtered.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.5 uppercase tracking-[0.12em] whitespace-nowrap\"\n                    >\n                      {h}\n                    </th>\n                  ))}\n                </tr>\n              </thead>\n              <tbody>\n                {isLoading ? (\n                  Array.from({ length: 8 }).map((_, i) => (\n                    <tr key={i} className=\"border-b border-white/[0.04]\">\n                      {Array.from({ length: 7 }).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\" style={{ width: `${40 + (j * 17) % 40}px` }} />\n                        </td>\n                      ))}\n                    </tr>\n                  ))\n                ) : filtered.length === 0 ? (\n                  <tr>\n                    <td colSpan={7} className=\"px-5 py-16 text-center text-white/25\">\n                      {search ? \"No jobs match your search.\" : \"No jobs yet — create your first escrow.\"}\n                    </td>\n                  </tr>\n                ) : (\n                  filtered.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 font-semibold\"\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 font-semibold\">\n                        ◎ {formatLamports(job.amountLamports)}\n                      </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                      <td className=\"px-5 py-4 text-right\">\n                        <Link\n                          href={`/jobs/${job.jobId}`}\n                          className=\"inline-flex items-center gap-1 text-[10px] text-white/20 group-hover:text-emerald-400/60 transition-colors\"\n                        >\n                          View <ArrowUpRight className=\"w-3 h-3\" />\n                        </Link>\n                      </td>\n                    </tr>\n                  ))\n                )}\n              </tbody>\n            </table>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"}