@murumets-ee/queue-ui 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -67,12 +67,16 @@ interface QueuePageClientProps {
67
67
  initialData: QueueInitialSnapshot;
68
68
  }
69
69
  /**
70
- * Public component wraps the live page in a RealtimeProvider so this
71
- * surface works whether or not the surrounding admin shell mounts one.
72
- * The provider is cheap (single SSE connection per mount) and the queue
73
- * page only renders once per admin tab.
70
+ * Public component. Subscribes to `queue.**` SSE topics via
71
+ * `useLiveSnapshot` for live revalidation. Must be mounted inside a
72
+ * `<RealtimeProvider>` (the admin shell does this for you); without one
73
+ * the live updates fall back to the no-op subscriber and only the
74
+ * Refresh button revalidates.
74
75
  */
75
- declare function QueuePageClient(props: QueuePageClientProps): React.ReactElement;
76
+ declare function QueuePageClient({
77
+ apiBasePath,
78
+ initialData
79
+ }: QueuePageClientProps): React.ReactElement;
76
80
  //#endregion
77
81
  export { type QueueCatalogEntry, type QueueHeartbeat, type QueueInitialSnapshot, type QueueJobSummary, QueuePageClient, type QueueScheduledSummary };
78
82
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/queue-page-client.tsx"],"mappings":";;;;;;;AAQA;;;;UAAiB,eAAA;EACf,EAAA;EACA,IAAA;EACA,MAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA;EACA,QAAA;EACA,QAAA;EACA,WAAA;EACA,QAAA;EAGA;EADA,SAAA;EACA,QAAA;EACA,SAAA;AAAA;AAAA,UAGe,qBAAA;EACf,EAAA;EACA,IAAA;EACA,IAAA;EACA,QAAA;EACA,OAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,cAAA;EACf,QAAA;EACA,SAAA;EACA,UAAA;EACA,WAAA;EACA,UAAA;EACA,KAAA;AAAA;AAAA,UAce,oBAAA;EAEP;EAAR,MAAA,EAAQ,MAAA;EAIa;EAFrB,IAAA,WAAe,eAAA;EAMK;EAJpB,UAAA,WAAqB,eAAA;EAUH;EARlB,MAAA,WAAiB,eAAA;EAQkB;EANnC,SAAA,WAAoB,qBAAA;EARZ;EAUR,UAAA,WAAqB,cAAA;EARN;EAUf,aAAA;EARqB;EAUrB,OAAA,WAAkB,mBAAA;AAAA;;;UCvBH,oBAAA;EACf,WAAA;EACA,WAAA,EAAa,oBAAA;AAAA;;;ADzBf;;;;iBCkCgB,eAAA,CAAgB,KAAA,EAAO,oBAAA,GAAuB,KAAA,CAAM,YAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/queue-page-client.tsx"],"mappings":";;;;;;;AAQA;;;;UAAiB,eAAA;EACf,EAAA;EACA,IAAA;EACA,MAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA;EACA,QAAA;EACA,QAAA;EACA,WAAA;EACA,QAAA;EAGA;EADA,SAAA;EACA,QAAA;EACA,SAAA;AAAA;AAAA,UAGe,qBAAA;EACf,EAAA;EACA,IAAA;EACA,IAAA;EACA,QAAA;EACA,OAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,cAAA;EACf,QAAA;EACA,SAAA;EACA,UAAA;EACA,WAAA;EACA,UAAA;EACA,KAAA;AAAA;AAAA,UAce,oBAAA;EAEP;EAAR,MAAA,EAAQ,MAAA;EAIa;EAFrB,IAAA,WAAe,eAAA;EAMK;EAJpB,UAAA,WAAqB,eAAA;EAUH;EARlB,MAAA,WAAiB,eAAA;EAQkB;EANnC,SAAA,WAAoB,qBAAA;EARZ;EAUR,UAAA,WAAqB,cAAA;EARN;EAUf,aAAA;EARqB;EAUrB,OAAA,WAAkB,mBAAA;AAAA;;;UC3BH,oBAAA;EACf,WAAA;EACA,WAAA,EAAa,oBAAA;AAAA;;;ADrBf;;;;;iBC+BgB,eAAA,CAAA;EACd,WAAA;EACA;AAAA,GACC,oBAAA,GAAuB,KAAA,CAAM,YAAA"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- import{RealtimeProvider as e,useLiveSnapshot as t,useRealtimeStatus as n}from"@murumets-ee/admin-ui/realtime";import{Badge as r,Button as i,cn as a}from"@murumets-ee/ui";import{useCallback as o,useMemo as s,useState as c}from"react";import{jsx as l,jsxs as u}from"react/jsx-runtime";const d=[`queue.**`],f=[{key:`in-progress`,label:`In progress`},{key:`dead`,label:`Dead letter`},{key:`scheduled`,label:`Scheduled`},{key:`recent`,label:`Recent`},{key:`stats`,label:`Stats`},{key:`catalog`,label:`Catalog`}];function p(t){return l(e,{subscribePatterns:d,children:l(m,{...t})})}function m({apiBasePath:e,initialData:p}){let[m,x]=c(`in-progress`),[S,w]=c(null),[E,D]=c(null),O=n(),{data:k,isLoading:A,error:j,refetch:M}=t({fetcher:o(async t=>{let n={credentials:`same-origin`,signal:t},[r,i,a,o,s,c,l]=await Promise.all([fetch(`${e}/queue/stats`,n),fetch(`${e}/queue/heartbeat`,n),fetch(`${e}/queue/scheduled`,n),fetch(`${e}/queue/jobs?status=dead&limit=50`,n),fetch(`${e}/queue/jobs?status=processing&limit=50`,n),fetch(`${e}/queue/jobs?limit=100`,n),fetch(`${e}/queue/catalog`,n)]);if(!r.ok||!i.ok||!a.ok||!o.ok||!s.ok||!c.ok||!l.ok)throw Error(`Failed to refresh queue data`);let[u,d,f,p,m,h,g]=await Promise.all([r.json(),i.json(),a.json(),o.json(),s.json(),c.json(),l.json()]);return{counts:u.counts,dead:p.items,processing:m.items,recent:h.items,scheduled:f.items,heartbeats:d.workers,healthyWorker:d.healthy,catalog:g.items}},[e]),topics:d,initialData:p}),N=k??p,P=E??(j?j.message:null),F=o(async(t,n,r=`POST`)=>{w(t),D(null);try{let t=await fetch(`${e}/queue${n}`,{method:r,credentials:`same-origin`});if(!t.ok){let e=await t.json().catch(()=>({}));throw Error(e.error??`Request failed (${t.status})`)}M()}catch(e){D(e instanceof Error?e.message:String(e))}finally{w(null)}},[e,M]),I=N.healthyWorker?`Worker healthy`:`Worker stale`,L=N.healthyWorker?`bg-emerald-500/10 text-emerald-600 dark:text-emerald-400`:`bg-rose-500/10 text-rose-600 dark:text-rose-400`,R=s(()=>C(O),[O]);return u(`div`,{className:`p-6 space-y-4`,children:[u(`div`,{className:`flex items-center justify-between flex-wrap gap-3`,children:[u(`div`,{children:[l(`h1`,{className:`text-2xl font-bold`,children:`Queue`}),l(`p`,{className:`text-sm text-muted-foreground`,children:`Background jobs, schedules, and worker health.`})]}),u(`div`,{className:`flex items-center gap-3`,children:[l(`span`,{className:a(`inline-flex items-center rounded-full px-3 py-1 text-xs font-medium`,L),children:I}),l(`span`,{className:a(`inline-flex items-center rounded-full px-3 py-1 text-xs font-medium`,R.color),title:R.title,children:R.label}),l(i,{onClick:M,disabled:A,variant:`outline`,size:`sm`,children:A?`Refreshing…`:`Refresh`})]})]}),P&&l(`div`,{className:`rounded-md border border-rose-300/60 bg-rose-50/80 p-3 text-sm text-rose-700 dark:border-rose-600/40 dark:bg-rose-950/40 dark:text-rose-300`,children:P}),l(h,{counts:N.counts}),l(`nav`,{className:`flex gap-1 border-b`,role:`tablist`,"aria-label":`Queue views`,children:f.map(e=>u(`button`,{role:`tab`,type:`button`,"aria-selected":m===e.key,onClick:()=>x(e.key),className:a(`px-3 py-2 text-sm font-medium border-b-2 -mb-px transition-colors`,m===e.key?`border-foreground text-foreground`:`border-transparent text-muted-foreground hover:text-foreground`),children:[e.label,e.key===`in-progress`&&N.processing.length>0&&l(r,{variant:`secondary`,className:`ml-2`,children:N.processing.length}),e.key===`dead`&&N.dead.length>0&&l(r,{variant:`destructive`,className:`ml-2`,children:N.dead.length})]},e.key))}),m===`in-progress`&&l(g,{jobs:N.processing,action:F,busyId:S}),m===`dead`&&l(_,{jobs:N.dead,action:F,busyId:S}),m===`scheduled`&&l(v,{schedules:N.scheduled,action:F,busyId:S}),m===`recent`&&l(y,{jobs:N.recent}),m===`stats`&&l(b,{counts:N.counts,heartbeats:N.heartbeats}),m===`catalog`&&l(T,{entries:N.catalog})]})}function h({counts:e}){return l(`div`,{className:`flex flex-wrap gap-3`,children:[[`pending`,e.pending??0,`bg-amber-500/10 text-amber-700 dark:text-amber-400`],[`processing`,e.processing??0,`bg-sky-500/10 text-sky-700 dark:text-sky-400`],[`dead`,e.dead??0,`bg-rose-500/10 text-rose-700 dark:text-rose-400`]].map(([e,t,n])=>u(`div`,{className:a(`rounded-md px-3 py-2 text-sm font-medium`,n),children:[l(`span`,{className:`capitalize`,children:e}),l(`span`,{className:`ml-2 font-mono`,children:t})]},e))})}function g({jobs:e,action:t,busyId:n}){return e.length===0?l(S,{text:`Nothing is processing right now.`}):l(`div`,{className:`overflow-x-auto rounded-md border`,children:u(`table`,{className:`w-full text-sm`,children:[l(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:u(`tr`,{children:[l(`th`,{className:`px-3 py-2 text-left`,children:`Type`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Worker`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Locked at`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Runtime`}),l(`th`,{className:`px-3 py-2 text-right`,children:`Actions`})]})}),l(`tbody`,{children:e.map(e=>u(`tr`,{className:`border-t`,children:[l(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.type}),l(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.lockedBy??`—`}),l(`td`,{className:`px-3 py-2`,children:e.lockedAt?new Date(e.lockedAt).toLocaleTimeString():`—`}),l(`td`,{className:`px-3 py-2`,children:e.lockedAt?w(e.lockedAt):`—`}),l(`td`,{className:`px-3 py-2 text-right`,children:l(i,{size:`sm`,variant:`outline`,disabled:n===e.id,onClick:()=>t(e.id,`/jobs/${e.id}/cancel`),children:`Cancel`})})]},e.id))})]})})}function _({jobs:e,action:t,busyId:n}){return e.length===0?l(S,{text:`Dead letter is empty. Nothing to triage.`}):l(`div`,{className:`overflow-x-auto rounded-md border`,children:u(`table`,{className:`w-full text-sm`,children:[l(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:u(`tr`,{children:[l(`th`,{className:`px-3 py-2 text-left`,children:`Type`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Failed at`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Attempts`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Last error`}),l(`th`,{className:`px-3 py-2 text-right`,children:`Actions`})]})}),l(`tbody`,{children:e.map(e=>u(`tr`,{className:`border-t align-top`,children:[l(`td`,{className:`px-3 py-2 font-mono text-xs whitespace-nowrap`,children:e.type}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.failedAt?new Date(e.failedAt).toLocaleString():`—`}),u(`td`,{className:`px-3 py-2`,children:[e.attempts,`/`,e.maxRetries]}),l(`td`,{className:`px-3 py-2 text-xs text-muted-foreground max-w-md break-words`,children:e.lastError??`—`}),u(`td`,{className:`px-3 py-2 text-right whitespace-nowrap`,children:[l(i,{size:`sm`,variant:`outline`,disabled:n===e.id,onClick:()=>t(e.id,`/jobs/${e.id}/requeue`),className:`mr-2`,children:`Requeue`}),l(i,{size:`sm`,variant:`destructive`,disabled:n===e.id,onClick:()=>t(e.id,`/jobs/${e.id}`,`DELETE`),children:`Delete`})]})]},e.id))})]})})}function v({schedules:e,action:t,busyId:n}){return e.length===0?l(S,{text:`No scheduled jobs registered.`}):l(`div`,{className:`overflow-x-auto rounded-md border`,children:u(`table`,{className:`w-full text-sm`,children:[l(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:u(`tr`,{children:[l(`th`,{className:`px-3 py-2 text-left`,children:`Name`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Schedule`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Next run`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Last run`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Status`}),l(`th`,{className:`px-3 py-2 text-right`,children:`Actions`})]})}),l(`tbody`,{children:e.map(e=>u(`tr`,{className:`border-t`,children:[u(`td`,{className:`px-3 py-2`,children:[l(`div`,{className:`font-mono text-xs`,children:e.name}),l(`div`,{className:`text-xs text-muted-foreground`,children:e.type})]}),l(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.schedule}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.nextRunAt?new Date(e.nextRunAt).toLocaleString():`—`}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.lastRunAt?new Date(e.lastRunAt).toLocaleString():`Never`}),l(`td`,{className:`px-3 py-2`,children:e.enabled?l(r,{variant:`secondary`,children:`Enabled`}):l(r,{variant:`outline`,children:`Disabled`})}),u(`td`,{className:`px-3 py-2 text-right whitespace-nowrap`,children:[l(i,{size:`sm`,variant:`outline`,disabled:n===e.id,onClick:()=>t(e.id,`/scheduled/${e.id}/run-now`),className:`mr-1`,children:`Run now`}),l(i,{size:`sm`,variant:`ghost`,disabled:n===e.id,onClick:()=>t(e.id,`/scheduled/${e.id}/${e.enabled?`disable`:`enable`}`),className:`mr-1`,children:e.enabled?`Disable`:`Enable`}),l(i,{size:`sm`,variant:`ghost`,disabled:n===e.id,onClick:()=>t(e.id,`/scheduled/${e.id}/reset-lease`),children:`Reset lease`})]})]},e.id))})]})})}function y({jobs:e}){return e.length===0?l(S,{text:`No recent jobs.`}):l(`div`,{className:`overflow-x-auto rounded-md border`,children:u(`table`,{className:`w-full text-sm`,children:[l(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:u(`tr`,{children:[l(`th`,{className:`px-3 py-2 text-left`,children:`Type`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Status`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Created`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Completed`})]})}),l(`tbody`,{children:e.map(e=>u(`tr`,{className:`border-t`,children:[l(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.type}),l(`td`,{className:`px-3 py-2`,children:l(x,{status:e.status})}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:new Date(e.createdAt).toLocaleString()}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.completedAt?new Date(e.completedAt).toLocaleString():e.failedAt?new Date(e.failedAt).toLocaleString():`—`})]},e.id))})]})})}function b({counts:e,heartbeats:t}){return u(`div`,{className:`space-y-6`,children:[u(`section`,{children:[l(`h2`,{className:`font-semibold mb-2`,children:`Status counts`}),l(`div`,{className:`grid grid-cols-2 md:grid-cols-5 gap-2`,children:Object.entries(e).map(([e,t])=>u(`div`,{className:`rounded-md border p-3`,children:[l(`div`,{className:`text-xs uppercase tracking-wide text-muted-foreground`,children:e}),l(`div`,{className:`text-2xl font-mono`,children:t})]},e))})]}),u(`section`,{children:[l(`h2`,{className:`font-semibold mb-2`,children:`Workers`}),t.length===0?l(`p`,{className:`text-sm text-muted-foreground`,children:`No worker has reported a heartbeat yet.`}):l(`div`,{className:`overflow-x-auto rounded-md border`,children:u(`table`,{className:`w-full text-sm`,children:[l(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:u(`tr`,{children:[l(`th`,{className:`px-3 py-2 text-left`,children:`Worker`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Started`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Last seen`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Active`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Status`})]})}),l(`tbody`,{children:t.map(e=>u(`tr`,{className:`border-t`,children:[l(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.workerId}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:new Date(e.startedAt).toLocaleString()}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:new Date(e.lastSeenAt).toLocaleString()}),u(`td`,{className:`px-3 py-2`,children:[e.activeJobs,`/`,e.concurrency]}),l(`td`,{className:`px-3 py-2`,children:e.stale?l(r,{variant:`destructive`,children:`Stale`}):l(r,{variant:`secondary`,children:`Fresh`})})]},e.workerId))})]})})]})]})}function x({status:e}){return l(r,{variant:e===`completed`?`secondary`:e===`dead`?`destructive`:e===`processing`?`default`:`outline`,children:e})}function S({text:e}){return l(`div`,{className:`rounded-md border border-dashed p-8 text-center text-sm text-muted-foreground`,children:e})}function C(e){return e===`open`?{label:`Live`,color:`bg-emerald-500/10 text-emerald-600 dark:text-emerald-400`,title:`Realtime connection active — table updates as the worker reports them.`}:e===`connecting`?{label:`Connecting…`,color:`bg-amber-500/10 text-amber-700 dark:text-amber-400`,title:`Connecting to realtime stream — the page will revalidate when events arrive.`}:{label:`Offline`,color:`bg-zinc-500/10 text-zinc-600 dark:text-zinc-400`,title:`Realtime stream unavailable — use the Refresh button to update.`}}function w(e){let t=Date.now()-new Date(e).getTime();return t<0?`—`:t<6e4?`${Math.round(t/1e3)}s`:t<36e5?`${Math.round(t/6e4)}m`:`${Math.round(t/36e5)}h`}function T({entries:e}){return e.length===0?l(S,{text:`No jobs registered yet. Plugins declare jobs via defineJob/registerJob.`}):l(`div`,{className:`overflow-x-auto rounded-md border`,children:u(`table`,{className:`w-full text-sm`,children:[l(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:u(`tr`,{children:[l(`th`,{className:`px-3 py-2 text-left`,children:`Job type`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Description`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Schedule`}),l(`th`,{className:`px-3 py-2 text-left`,children:`Last run`}),l(`th`,{className:`px-3 py-2 text-left`,children:`24 h volume`}),l(`th`,{className:`px-3 py-2 text-left`,children:`24 h success`})]})}),l(`tbody`,{children:e.map(e=>u(`tr`,{className:`border-t align-top`,children:[u(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:[l(`div`,{className:`font-mono text-xs`,children:e.name}),!e.registered&&l(r,{variant:`outline`,className:`mt-1`,children:`unregistered`}),e.scheduled&&!e.scheduled.enabled&&l(r,{variant:`outline`,className:`mt-1 ml-1`,children:`schedule disabled`})]}),l(`td`,{className:`px-3 py-2 text-xs text-muted-foreground max-w-md`,children:e.description??l(`span`,{className:`italic`,children:`—`})}),l(`td`,{className:`px-3 py-2 font-mono text-xs whitespace-nowrap`,children:e.schedule??l(`span`,{className:`text-muted-foreground italic`,children:`event-driven`})}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.lastRunAt?new Date(e.lastRunAt).toLocaleString():`—`}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.stats24h.total===0?`—`:u(`span`,{className:`font-mono text-xs`,children:[e.stats24h.total,` total`,e.stats24h.dead>0&&u(`span`,{className:`ml-1 text-rose-600 dark:text-rose-400`,children:[`(`,e.stats24h.dead,` dead)`]})]})}),l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.successRate24h===null?`—`:`${Math.round(e.successRate24h*100)}%`})]},e.name))})]})})}export{p as QueuePageClient};
2
+ import{useLiveSnapshot as e,useRealtimeStatus as t}from"@murumets-ee/admin-ui/realtime";import{Badge as n,Button as r,cn as i}from"@murumets-ee/ui";import{useCallback as a,useMemo as o,useState as s}from"react";import{jsx as c,jsxs as l}from"react/jsx-runtime";const u=[`queue.**`],d=[{key:`in-progress`,label:`In progress`},{key:`dead`,label:`Dead letter`},{key:`scheduled`,label:`Scheduled`},{key:`recent`,label:`Recent`},{key:`stats`,label:`Stats`},{key:`catalog`,label:`Catalog`}];function f({apiBasePath:f,initialData:y}){let[b,S]=s(`in-progress`),[w,T]=s(null),[E,D]=s(null),O=t(),{data:k,isLoading:A,error:j,refetch:M}=e({fetcher:a(async e=>{let t={credentials:`same-origin`,signal:e},[n,r,i,a,o,s,c]=await Promise.all([fetch(`${f}/queue/stats`,t),fetch(`${f}/queue/heartbeat`,t),fetch(`${f}/queue/scheduled`,t),fetch(`${f}/queue/jobs?status=dead&limit=50`,t),fetch(`${f}/queue/jobs?status=processing&limit=50`,t),fetch(`${f}/queue/jobs?limit=100`,t),fetch(`${f}/queue/catalog`,t)]);if(!n.ok||!r.ok||!i.ok||!a.ok||!o.ok||!s.ok||!c.ok)throw Error(`Failed to refresh queue data`);let[l,u,d,p,m,h,g]=await Promise.all([n.json(),r.json(),i.json(),a.json(),o.json(),s.json(),c.json()]);return{counts:l.counts,dead:p.items,processing:m.items,recent:h.items,scheduled:d.items,heartbeats:u.workers,healthyWorker:u.healthy,catalog:g.items}},[f]),topics:u,initialData:y}),N=k??y,P=E??(j?j.message:null),F=a(async(e,t,n=`POST`)=>{T(e),D(null);try{let e=await fetch(`${f}/queue${t}`,{method:n,credentials:`same-origin`});if(!e.ok){let t=await e.json().catch(()=>({}));throw Error(t.error??`Request failed (${e.status})`)}M()}catch(e){D(e instanceof Error?e.message:String(e))}finally{T(null)}},[f,M]),I=N.healthyWorker?`Worker healthy`:`Worker stale`,L=N.healthyWorker?`bg-emerald-500/10 text-emerald-600 dark:text-emerald-400`:`bg-rose-500/10 text-rose-600 dark:text-rose-400`,R=o(()=>x(O),[O]);return l(`div`,{className:`p-6 space-y-4`,children:[l(`div`,{className:`flex items-center justify-between flex-wrap gap-3`,children:[l(`div`,{children:[c(`h1`,{className:`text-2xl font-bold`,children:`Queue`}),c(`p`,{className:`text-sm text-muted-foreground`,children:`Background jobs, schedules, and worker health.`})]}),l(`div`,{className:`flex items-center gap-3`,children:[c(`span`,{className:i(`inline-flex items-center rounded-full px-3 py-1 text-xs font-medium`,L),children:I}),c(`span`,{className:i(`inline-flex items-center rounded-full px-3 py-1 text-xs font-medium`,R.color),title:R.title,children:R.label}),c(r,{onClick:M,disabled:A,variant:`outline`,size:`sm`,children:A?`Refreshing…`:`Refresh`})]})]}),P&&c(`div`,{className:`rounded-md border border-rose-300/60 bg-rose-50/80 p-3 text-sm text-rose-700 dark:border-rose-600/40 dark:bg-rose-950/40 dark:text-rose-300`,children:P}),c(p,{counts:N.counts}),c(`nav`,{className:`flex gap-1 border-b`,role:`tablist`,"aria-label":`Queue views`,children:d.map(e=>l(`button`,{role:`tab`,type:`button`,"aria-selected":b===e.key,onClick:()=>S(e.key),className:i(`px-3 py-2 text-sm font-medium border-b-2 -mb-px transition-colors`,b===e.key?`border-foreground text-foreground`:`border-transparent text-muted-foreground hover:text-foreground`),children:[e.label,e.key===`in-progress`&&N.processing.length>0&&c(n,{variant:`secondary`,className:`ml-2`,children:N.processing.length}),e.key===`dead`&&N.dead.length>0&&c(n,{variant:`destructive`,className:`ml-2`,children:N.dead.length})]},e.key))}),b===`in-progress`&&c(m,{jobs:N.processing,action:F,busyId:w}),b===`dead`&&c(h,{jobs:N.dead,action:F,busyId:w}),b===`scheduled`&&c(g,{schedules:N.scheduled,action:F,busyId:w}),b===`recent`&&c(_,{jobs:N.recent}),b===`stats`&&c(v,{counts:N.counts,heartbeats:N.heartbeats}),b===`catalog`&&c(C,{entries:N.catalog})]})}function p({counts:e}){return c(`div`,{className:`flex flex-wrap gap-3`,children:[[`pending`,e.pending??0,`bg-amber-500/10 text-amber-700 dark:text-amber-400`],[`processing`,e.processing??0,`bg-sky-500/10 text-sky-700 dark:text-sky-400`],[`dead`,e.dead??0,`bg-rose-500/10 text-rose-700 dark:text-rose-400`]].map(([e,t,n])=>l(`div`,{className:i(`rounded-md px-3 py-2 text-sm font-medium`,n),children:[c(`span`,{className:`capitalize`,children:e}),c(`span`,{className:`ml-2 font-mono`,children:t})]},e))})}function m({jobs:e,action:t,busyId:n}){return e.length===0?c(b,{text:`Nothing is processing right now.`}):c(`div`,{className:`overflow-x-auto rounded-md border`,children:l(`table`,{className:`w-full text-sm`,children:[c(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:l(`tr`,{children:[c(`th`,{className:`px-3 py-2 text-left`,children:`Type`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Worker`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Locked at`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Runtime`}),c(`th`,{className:`px-3 py-2 text-right`,children:`Actions`})]})}),c(`tbody`,{children:e.map(e=>l(`tr`,{className:`border-t`,children:[c(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.type}),c(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.lockedBy??`—`}),c(`td`,{className:`px-3 py-2`,children:e.lockedAt?new Date(e.lockedAt).toLocaleTimeString():`—`}),c(`td`,{className:`px-3 py-2`,children:e.lockedAt?S(e.lockedAt):`—`}),c(`td`,{className:`px-3 py-2 text-right`,children:c(r,{size:`sm`,variant:`outline`,disabled:n===e.id,onClick:()=>t(e.id,`/jobs/${e.id}/cancel`),children:`Cancel`})})]},e.id))})]})})}function h({jobs:e,action:t,busyId:n}){return e.length===0?c(b,{text:`Dead letter is empty. Nothing to triage.`}):c(`div`,{className:`overflow-x-auto rounded-md border`,children:l(`table`,{className:`w-full text-sm`,children:[c(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:l(`tr`,{children:[c(`th`,{className:`px-3 py-2 text-left`,children:`Type`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Failed at`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Attempts`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Last error`}),c(`th`,{className:`px-3 py-2 text-right`,children:`Actions`})]})}),c(`tbody`,{children:e.map(e=>l(`tr`,{className:`border-t align-top`,children:[c(`td`,{className:`px-3 py-2 font-mono text-xs whitespace-nowrap`,children:e.type}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.failedAt?new Date(e.failedAt).toLocaleString():`—`}),l(`td`,{className:`px-3 py-2`,children:[e.attempts,`/`,e.maxRetries]}),c(`td`,{className:`px-3 py-2 text-xs text-muted-foreground max-w-md break-words`,children:e.lastError??`—`}),l(`td`,{className:`px-3 py-2 text-right whitespace-nowrap`,children:[c(r,{size:`sm`,variant:`outline`,disabled:n===e.id,onClick:()=>t(e.id,`/jobs/${e.id}/requeue`),className:`mr-2`,children:`Requeue`}),c(r,{size:`sm`,variant:`destructive`,disabled:n===e.id,onClick:()=>t(e.id,`/jobs/${e.id}`,`DELETE`),children:`Delete`})]})]},e.id))})]})})}function g({schedules:e,action:t,busyId:i}){return e.length===0?c(b,{text:`No scheduled jobs registered.`}):c(`div`,{className:`overflow-x-auto rounded-md border`,children:l(`table`,{className:`w-full text-sm`,children:[c(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:l(`tr`,{children:[c(`th`,{className:`px-3 py-2 text-left`,children:`Name`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Schedule`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Next run`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Last run`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Status`}),c(`th`,{className:`px-3 py-2 text-right`,children:`Actions`})]})}),c(`tbody`,{children:e.map(e=>l(`tr`,{className:`border-t`,children:[l(`td`,{className:`px-3 py-2`,children:[c(`div`,{className:`font-mono text-xs`,children:e.name}),c(`div`,{className:`text-xs text-muted-foreground`,children:e.type})]}),c(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.schedule}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.nextRunAt?new Date(e.nextRunAt).toLocaleString():`—`}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.lastRunAt?new Date(e.lastRunAt).toLocaleString():`Never`}),c(`td`,{className:`px-3 py-2`,children:e.enabled?c(n,{variant:`secondary`,children:`Enabled`}):c(n,{variant:`outline`,children:`Disabled`})}),l(`td`,{className:`px-3 py-2 text-right whitespace-nowrap`,children:[c(r,{size:`sm`,variant:`outline`,disabled:i===e.id,onClick:()=>t(e.id,`/scheduled/${e.id}/run-now`),className:`mr-1`,children:`Run now`}),c(r,{size:`sm`,variant:`ghost`,disabled:i===e.id,onClick:()=>t(e.id,`/scheduled/${e.id}/${e.enabled?`disable`:`enable`}`),className:`mr-1`,children:e.enabled?`Disable`:`Enable`}),c(r,{size:`sm`,variant:`ghost`,disabled:i===e.id,onClick:()=>t(e.id,`/scheduled/${e.id}/reset-lease`),children:`Reset lease`})]})]},e.id))})]})})}function _({jobs:e}){return e.length===0?c(b,{text:`No recent jobs.`}):c(`div`,{className:`overflow-x-auto rounded-md border`,children:l(`table`,{className:`w-full text-sm`,children:[c(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:l(`tr`,{children:[c(`th`,{className:`px-3 py-2 text-left`,children:`Type`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Status`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Created`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Completed`})]})}),c(`tbody`,{children:e.map(e=>l(`tr`,{className:`border-t`,children:[c(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.type}),c(`td`,{className:`px-3 py-2`,children:c(y,{status:e.status})}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:new Date(e.createdAt).toLocaleString()}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.completedAt?new Date(e.completedAt).toLocaleString():e.failedAt?new Date(e.failedAt).toLocaleString():`—`})]},e.id))})]})})}function v({counts:e,heartbeats:t}){return l(`div`,{className:`space-y-6`,children:[l(`section`,{children:[c(`h2`,{className:`font-semibold mb-2`,children:`Status counts`}),c(`div`,{className:`grid grid-cols-2 md:grid-cols-5 gap-2`,children:Object.entries(e).map(([e,t])=>l(`div`,{className:`rounded-md border p-3`,children:[c(`div`,{className:`text-xs uppercase tracking-wide text-muted-foreground`,children:e}),c(`div`,{className:`text-2xl font-mono`,children:t})]},e))})]}),l(`section`,{children:[c(`h2`,{className:`font-semibold mb-2`,children:`Workers`}),t.length===0?c(`p`,{className:`text-sm text-muted-foreground`,children:`No worker has reported a heartbeat yet.`}):c(`div`,{className:`overflow-x-auto rounded-md border`,children:l(`table`,{className:`w-full text-sm`,children:[c(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:l(`tr`,{children:[c(`th`,{className:`px-3 py-2 text-left`,children:`Worker`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Started`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Last seen`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Active`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Status`})]})}),c(`tbody`,{children:t.map(e=>l(`tr`,{className:`border-t`,children:[c(`td`,{className:`px-3 py-2 font-mono text-xs`,children:e.workerId}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:new Date(e.startedAt).toLocaleString()}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:new Date(e.lastSeenAt).toLocaleString()}),l(`td`,{className:`px-3 py-2`,children:[e.activeJobs,`/`,e.concurrency]}),c(`td`,{className:`px-3 py-2`,children:e.stale?c(n,{variant:`destructive`,children:`Stale`}):c(n,{variant:`secondary`,children:`Fresh`})})]},e.workerId))})]})})]})]})}function y({status:e}){return c(n,{variant:e===`completed`?`secondary`:e===`dead`?`destructive`:e===`processing`?`default`:`outline`,children:e})}function b({text:e}){return c(`div`,{className:`rounded-md border border-dashed p-8 text-center text-sm text-muted-foreground`,children:e})}function x(e){return e===`open`?{label:`Live`,color:`bg-emerald-500/10 text-emerald-600 dark:text-emerald-400`,title:`Realtime connection active — table updates as the worker reports them.`}:e===`connecting`?{label:`Connecting…`,color:`bg-amber-500/10 text-amber-700 dark:text-amber-400`,title:`Connecting to realtime stream — the page will revalidate when events arrive.`}:{label:`Offline`,color:`bg-zinc-500/10 text-zinc-600 dark:text-zinc-400`,title:`Realtime stream unavailable — use the Refresh button to update.`}}function S(e){let t=Date.now()-new Date(e).getTime();return t<0?`—`:t<6e4?`${Math.round(t/1e3)}s`:t<36e5?`${Math.round(t/6e4)}m`:`${Math.round(t/36e5)}h`}function C({entries:e}){return e.length===0?c(b,{text:`No jobs registered yet. Plugins declare jobs via defineJob/registerJob.`}):c(`div`,{className:`overflow-x-auto rounded-md border`,children:l(`table`,{className:`w-full text-sm`,children:[c(`thead`,{className:`bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground`,children:l(`tr`,{children:[c(`th`,{className:`px-3 py-2 text-left`,children:`Job type`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Description`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Schedule`}),c(`th`,{className:`px-3 py-2 text-left`,children:`Last run`}),c(`th`,{className:`px-3 py-2 text-left`,children:`24 h volume`}),c(`th`,{className:`px-3 py-2 text-left`,children:`24 h success`})]})}),c(`tbody`,{children:e.map(e=>l(`tr`,{className:`border-t align-top`,children:[l(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:[c(`div`,{className:`font-mono text-xs`,children:e.name}),!e.registered&&c(n,{variant:`outline`,className:`mt-1`,children:`unregistered`}),e.scheduled&&!e.scheduled.enabled&&c(n,{variant:`outline`,className:`mt-1 ml-1`,children:`schedule disabled`})]}),c(`td`,{className:`px-3 py-2 text-xs text-muted-foreground max-w-md`,children:e.description??c(`span`,{className:`italic`,children:`—`})}),c(`td`,{className:`px-3 py-2 font-mono text-xs whitespace-nowrap`,children:e.schedule??c(`span`,{className:`text-muted-foreground italic`,children:`event-driven`})}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.lastRunAt?new Date(e.lastRunAt).toLocaleString():`—`}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.stats24h.total===0?`—`:l(`span`,{className:`font-mono text-xs`,children:[e.stats24h.total,` total`,e.stats24h.dead>0&&l(`span`,{className:`ml-1 text-rose-600 dark:text-rose-400`,children:[`(`,e.stats24h.dead,` dead)`]})]})}),c(`td`,{className:`px-3 py-2 whitespace-nowrap`,children:e.successRate24h===null?`—`:`${Math.round(e.successRate24h*100)}%`})]},e.name))})]})})}export{f as QueuePageClient};
3
3
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/queue-page-client.tsx"],"sourcesContent":["/**\n * Interactive client surface for the queue admin page.\n *\n * Renders six tabs: in-progress, dead letter, scheduled, recent activity,\n * per-type stats, catalog. Per-row actions (requeue, delete, cancel,\n * run-now, disable/enable, reset-lease) hit the admin API and refetch\n * on success.\n *\n * The initial snapshot comes from the server page so first paint has data.\n * Subsequent revalidation is event-driven via `useLiveSnapshot` against\n * the `queue.**` topics published by the worker and admin routes\n * (PLAN-REALTIME PR D). The previous manual Refresh button is replaced\n * with a transport-status indicator + an explicit Refresh action that\n * triggers an immediate refetch.\n */\n\nimport {\n RealtimeProvider,\n useLiveSnapshot,\n useRealtimeStatus,\n} from '@murumets-ee/admin-ui/realtime'\nimport { Badge, Button, cn } from '@murumets-ee/ui'\nimport { useCallback, useMemo, useState } from 'react'\nimport type {\n QueueCatalogEntry,\n QueueHeartbeat,\n QueueInitialSnapshot,\n QueueJobSummary,\n QueueScheduledSummary,\n} from './types.js'\n\n/**\n * Topics the queue page revalidates on. Stable reference so\n * `useLiveSnapshot` doesn't re-subscribe each render.\n */\nconst QUEUE_LIVE_TOPICS: readonly string[] = ['queue.**'] as const\n\ntype TabKey = 'in-progress' | 'dead' | 'scheduled' | 'recent' | 'stats' | 'catalog'\n\nconst TABS: ReadonlyArray<{ key: TabKey; label: string }> = [\n { key: 'in-progress', label: 'In progress' },\n { key: 'dead', label: 'Dead letter' },\n { key: 'scheduled', label: 'Scheduled' },\n { key: 'recent', label: 'Recent' },\n { key: 'stats', label: 'Stats' },\n { key: 'catalog', label: 'Catalog' },\n]\n\nexport interface QueuePageClientProps {\n apiBasePath: string\n initialData: QueueInitialSnapshot\n}\n\n/**\n * Public component — wraps the live page in a RealtimeProvider so this\n * surface works whether or not the surrounding admin shell mounts one.\n * The provider is cheap (single SSE connection per mount) and the queue\n * page only renders once per admin tab.\n */\nexport function QueuePageClient(props: QueuePageClientProps): React.ReactElement {\n return (\n <RealtimeProvider subscribePatterns={QUEUE_LIVE_TOPICS}>\n <QueuePageClientInner {...props} />\n </RealtimeProvider>\n )\n}\n\nfunction QueuePageClientInner({\n apiBasePath,\n initialData,\n}: QueuePageClientProps): React.ReactElement {\n const [tab, setTab] = useState<TabKey>('in-progress')\n const [busyId, setBusyId] = useState<string | null>(null)\n const [actionError, setActionError] = useState<string | null>(null)\n const transportStatus = useRealtimeStatus()\n\n // Stable fetcher reference so `useLiveSnapshot` re-uses the same\n // closure across renders. The hook itself holds a ref so a fresh\n // closure on each render wouldn't re-subscribe, but useCallback keeps\n // the function identity matched to its dependencies anyway.\n const fetcher = useCallback(\n async (signal: AbortSignal): Promise<QueueInitialSnapshot> => {\n const opts = { credentials: 'same-origin' as const, signal }\n const [\n statsRes,\n heartbeatRes,\n scheduledRes,\n deadRes,\n processingRes,\n recentRes,\n catalogRes,\n ] = await Promise.all([\n fetch(`${apiBasePath}/queue/stats`, opts),\n fetch(`${apiBasePath}/queue/heartbeat`, opts),\n fetch(`${apiBasePath}/queue/scheduled`, opts),\n fetch(`${apiBasePath}/queue/jobs?status=dead&limit=50`, opts),\n fetch(`${apiBasePath}/queue/jobs?status=processing&limit=50`, opts),\n fetch(`${apiBasePath}/queue/jobs?limit=100`, opts),\n fetch(`${apiBasePath}/queue/catalog`, opts),\n ])\n\n if (\n !statsRes.ok ||\n !heartbeatRes.ok ||\n !scheduledRes.ok ||\n !deadRes.ok ||\n !processingRes.ok ||\n !recentRes.ok ||\n !catalogRes.ok\n ) {\n throw new Error('Failed to refresh queue data')\n }\n\n const [\n statsBody,\n heartbeatBody,\n scheduledBody,\n deadBody,\n processingBody,\n recentBody,\n catalogBody,\n ] = await Promise.all([\n statsRes.json() as Promise<{ counts: Record<string, number> }>,\n heartbeatRes.json() as Promise<{\n workers: QueueHeartbeat[]\n healthy: boolean\n }>,\n scheduledRes.json() as Promise<{ items: QueueScheduledSummary[] }>,\n deadRes.json() as Promise<{ items: QueueJobSummary[] }>,\n processingRes.json() as Promise<{ items: QueueJobSummary[] }>,\n recentRes.json() as Promise<{ items: QueueJobSummary[] }>,\n catalogRes.json() as Promise<{ items: QueueCatalogEntry[] }>,\n ])\n\n return {\n counts: statsBody.counts,\n dead: deadBody.items,\n processing: processingBody.items,\n recent: recentBody.items,\n scheduled: scheduledBody.items,\n heartbeats: heartbeatBody.workers,\n healthyWorker: heartbeatBody.healthy,\n catalog: catalogBody.items,\n }\n },\n [apiBasePath],\n )\n\n const {\n data: live,\n isLoading,\n error: liveError,\n refetch,\n } = useLiveSnapshot<QueueInitialSnapshot>({\n fetcher,\n topics: QUEUE_LIVE_TOPICS,\n initialData,\n })\n\n // `live` cannot be null when `initialData` is supplied; the hook seeds\n // `data` synchronously. Fall back defensively so a misconfigured caller\n // still renders rather than crashing.\n const data = live ?? initialData\n const error = actionError ?? (liveError ? liveError.message : null)\n\n const action = useCallback(\n async (id: string, path: string, method: 'POST' | 'DELETE' = 'POST') => {\n setBusyId(id)\n setActionError(null)\n try {\n const res = await fetch(`${apiBasePath}/queue${path}`, {\n method,\n credentials: 'same-origin',\n })\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: string }\n throw new Error(body.error ?? `Request failed (${res.status})`)\n }\n // The realtime publish from the route handler will trigger\n // `useLiveSnapshot` debounce too, but we refetch immediately so\n // the operator sees their action's effect without waiting for\n // the debounce window.\n refetch()\n } catch (err) {\n setActionError(err instanceof Error ? err.message : String(err))\n } finally {\n setBusyId(null)\n }\n },\n [apiBasePath, refetch],\n )\n\n const healthLabel = data.healthyWorker ? 'Worker healthy' : 'Worker stale'\n const healthColor = data.healthyWorker\n ? 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'\n : 'bg-rose-500/10 text-rose-600 dark:text-rose-400'\n const liveBadge = useMemo(() => liveStatusBadge(transportStatus), [transportStatus])\n\n return (\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-3\">\n <div>\n <h1 className=\"text-2xl font-bold\">Queue</h1>\n <p className=\"text-sm text-muted-foreground\">\n Background jobs, schedules, and worker health.\n </p>\n </div>\n <div className=\"flex items-center gap-3\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium',\n healthColor,\n )}\n >\n {healthLabel}\n </span>\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium',\n liveBadge.color,\n )}\n title={liveBadge.title}\n >\n {liveBadge.label}\n </span>\n <Button onClick={refetch} disabled={isLoading} variant=\"outline\" size=\"sm\">\n {isLoading ? 'Refreshing…' : 'Refresh'}\n </Button>\n </div>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-rose-300/60 bg-rose-50/80 p-3 text-sm text-rose-700 dark:border-rose-600/40 dark:bg-rose-950/40 dark:text-rose-300\">\n {error}\n </div>\n )}\n\n <CountsBar counts={data.counts} />\n\n <nav className=\"flex gap-1 border-b\" role=\"tablist\" aria-label=\"Queue views\">\n {TABS.map((t) => (\n <button\n key={t.key}\n role=\"tab\"\n type=\"button\"\n aria-selected={tab === t.key}\n onClick={() => setTab(t.key)}\n className={cn(\n 'px-3 py-2 text-sm font-medium border-b-2 -mb-px transition-colors',\n tab === t.key\n ? 'border-foreground text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground',\n )}\n >\n {t.label}\n {t.key === 'in-progress' && data.processing.length > 0 && (\n <Badge variant=\"secondary\" className=\"ml-2\">\n {data.processing.length}\n </Badge>\n )}\n {t.key === 'dead' && data.dead.length > 0 && (\n <Badge variant=\"destructive\" className=\"ml-2\">\n {data.dead.length}\n </Badge>\n )}\n </button>\n ))}\n </nav>\n\n {tab === 'in-progress' && (\n <ProcessingTab jobs={data.processing} action={action} busyId={busyId} />\n )}\n {tab === 'dead' && <DeadTab jobs={data.dead} action={action} busyId={busyId} />}\n {tab === 'scheduled' && (\n <ScheduledTab schedules={data.scheduled} action={action} busyId={busyId} />\n )}\n {tab === 'recent' && <RecentTab jobs={data.recent} />}\n {tab === 'stats' && <StatsTab counts={data.counts} heartbeats={data.heartbeats} />}\n {tab === 'catalog' && <CatalogTab entries={data.catalog} />}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction CountsBar({ counts }: { counts: Record<string, number> }): React.ReactElement {\n // Show only the actionable counts: work queued, work in flight, work\n // that died and needs triage. The all-time `completed` count grows\n // forever and tells the operator nothing — it's surfaced on the Stats\n // tab for completeness but doesn't deserve a prime slot here.\n const entries: Array<[string, number, string]> = [\n ['pending', counts.pending ?? 0, 'bg-amber-500/10 text-amber-700 dark:text-amber-400'],\n ['processing', counts.processing ?? 0, 'bg-sky-500/10 text-sky-700 dark:text-sky-400'],\n ['dead', counts.dead ?? 0, 'bg-rose-500/10 text-rose-700 dark:text-rose-400'],\n ]\n return (\n <div className=\"flex flex-wrap gap-3\">\n {entries.map(([label, count, color]) => (\n <div\n key={label}\n className={cn('rounded-md px-3 py-2 text-sm font-medium', color)}\n >\n <span className=\"capitalize\">{label}</span>\n <span className=\"ml-2 font-mono\">{count}</span>\n </div>\n ))}\n </div>\n )\n}\n\nfunction ProcessingTab({\n jobs,\n action,\n busyId,\n}: {\n jobs: readonly QueueJobSummary[]\n action: (id: string, path: string, method?: 'POST' | 'DELETE') => Promise<void>\n busyId: string | null\n}): React.ReactElement {\n if (jobs.length === 0) return <EmptyState text=\"Nothing is processing right now.\" />\n\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Type</th>\n <th className=\"px-3 py-2 text-left\">Worker</th>\n <th className=\"px-3 py-2 text-left\">Locked at</th>\n <th className=\"px-3 py-2 text-left\">Runtime</th>\n <th className=\"px-3 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {jobs.map((j) => (\n <tr key={j.id} className=\"border-t\">\n <td className=\"px-3 py-2 font-mono text-xs\">{j.type}</td>\n <td className=\"px-3 py-2 font-mono text-xs\">{j.lockedBy ?? '—'}</td>\n <td className=\"px-3 py-2\">\n {j.lockedAt ? new Date(j.lockedAt).toLocaleTimeString() : '—'}\n </td>\n <td className=\"px-3 py-2\">{j.lockedAt ? runtimeOf(j.lockedAt) : '—'}</td>\n <td className=\"px-3 py-2 text-right\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={busyId === j.id}\n onClick={() => action(j.id, `/jobs/${j.id}/cancel`)}\n >\n Cancel\n </Button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction DeadTab({\n jobs,\n action,\n busyId,\n}: {\n jobs: readonly QueueJobSummary[]\n action: (id: string, path: string, method?: 'POST' | 'DELETE') => Promise<void>\n busyId: string | null\n}): React.ReactElement {\n if (jobs.length === 0) return <EmptyState text=\"Dead letter is empty. Nothing to triage.\" />\n\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Type</th>\n <th className=\"px-3 py-2 text-left\">Failed at</th>\n <th className=\"px-3 py-2 text-left\">Attempts</th>\n <th className=\"px-3 py-2 text-left\">Last error</th>\n <th className=\"px-3 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {jobs.map((j) => (\n <tr key={j.id} className=\"border-t align-top\">\n <td className=\"px-3 py-2 font-mono text-xs whitespace-nowrap\">{j.type}</td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {j.failedAt ? new Date(j.failedAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2\">\n {j.attempts}/{j.maxRetries}\n </td>\n <td className=\"px-3 py-2 text-xs text-muted-foreground max-w-md break-words\">\n {j.lastError ?? '—'}\n </td>\n <td className=\"px-3 py-2 text-right whitespace-nowrap\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={busyId === j.id}\n onClick={() => action(j.id, `/jobs/${j.id}/requeue`)}\n className=\"mr-2\"\n >\n Requeue\n </Button>\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={busyId === j.id}\n onClick={() => action(j.id, `/jobs/${j.id}`, 'DELETE')}\n >\n Delete\n </Button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction ScheduledTab({\n schedules,\n action,\n busyId,\n}: {\n schedules: readonly QueueScheduledSummary[]\n action: (id: string, path: string, method?: 'POST' | 'DELETE') => Promise<void>\n busyId: string | null\n}): React.ReactElement {\n if (schedules.length === 0) return <EmptyState text=\"No scheduled jobs registered.\" />\n\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Name</th>\n <th className=\"px-3 py-2 text-left\">Schedule</th>\n <th className=\"px-3 py-2 text-left\">Next run</th>\n <th className=\"px-3 py-2 text-left\">Last run</th>\n <th className=\"px-3 py-2 text-left\">Status</th>\n <th className=\"px-3 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {schedules.map((s) => (\n <tr key={s.id} className=\"border-t\">\n <td className=\"px-3 py-2\">\n <div className=\"font-mono text-xs\">{s.name}</div>\n <div className=\"text-xs text-muted-foreground\">{s.type}</div>\n </td>\n <td className=\"px-3 py-2 font-mono text-xs\">{s.schedule}</td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {s.nextRunAt ? new Date(s.nextRunAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {s.lastRunAt ? new Date(s.lastRunAt).toLocaleString() : 'Never'}\n </td>\n <td className=\"px-3 py-2\">\n {s.enabled ? (\n <Badge variant=\"secondary\">Enabled</Badge>\n ) : (\n <Badge variant=\"outline\">Disabled</Badge>\n )}\n </td>\n <td className=\"px-3 py-2 text-right whitespace-nowrap\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={busyId === s.id}\n onClick={() => action(s.id, `/scheduled/${s.id}/run-now`)}\n className=\"mr-1\"\n >\n Run now\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={busyId === s.id}\n onClick={() =>\n action(s.id, `/scheduled/${s.id}/${s.enabled ? 'disable' : 'enable'}`)\n }\n className=\"mr-1\"\n >\n {s.enabled ? 'Disable' : 'Enable'}\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={busyId === s.id}\n onClick={() => action(s.id, `/scheduled/${s.id}/reset-lease`)}\n >\n Reset lease\n </Button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction RecentTab({ jobs }: { jobs: readonly QueueJobSummary[] }): React.ReactElement {\n if (jobs.length === 0) return <EmptyState text=\"No recent jobs.\" />\n\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Type</th>\n <th className=\"px-3 py-2 text-left\">Status</th>\n <th className=\"px-3 py-2 text-left\">Created</th>\n <th className=\"px-3 py-2 text-left\">Completed</th>\n </tr>\n </thead>\n <tbody>\n {jobs.map((j) => (\n <tr key={j.id} className=\"border-t\">\n <td className=\"px-3 py-2 font-mono text-xs\">{j.type}</td>\n <td className=\"px-3 py-2\">\n <StatusBadge status={j.status} />\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {new Date(j.createdAt).toLocaleString()}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {j.completedAt\n ? new Date(j.completedAt).toLocaleString()\n : j.failedAt\n ? new Date(j.failedAt).toLocaleString()\n : '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction StatsTab({\n counts,\n heartbeats,\n}: {\n counts: Record<string, number>\n heartbeats: readonly QueueHeartbeat[]\n}): React.ReactElement {\n return (\n <div className=\"space-y-6\">\n <section>\n <h2 className=\"font-semibold mb-2\">Status counts</h2>\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-2\">\n {Object.entries(counts).map(([status, count]) => (\n <div key={status} className=\"rounded-md border p-3\">\n <div className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n {status}\n </div>\n <div className=\"text-2xl font-mono\">{count}</div>\n </div>\n ))}\n </div>\n </section>\n <section>\n <h2 className=\"font-semibold mb-2\">Workers</h2>\n {heartbeats.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">No worker has reported a heartbeat yet.</p>\n ) : (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Worker</th>\n <th className=\"px-3 py-2 text-left\">Started</th>\n <th className=\"px-3 py-2 text-left\">Last seen</th>\n <th className=\"px-3 py-2 text-left\">Active</th>\n <th className=\"px-3 py-2 text-left\">Status</th>\n </tr>\n </thead>\n <tbody>\n {heartbeats.map((h) => (\n <tr key={h.workerId} className=\"border-t\">\n <td className=\"px-3 py-2 font-mono text-xs\">{h.workerId}</td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {new Date(h.startedAt).toLocaleString()}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {new Date(h.lastSeenAt).toLocaleString()}\n </td>\n <td className=\"px-3 py-2\">\n {h.activeJobs}/{h.concurrency}\n </td>\n <td className=\"px-3 py-2\">\n {h.stale ? (\n <Badge variant=\"destructive\">Stale</Badge>\n ) : (\n <Badge variant=\"secondary\">Fresh</Badge>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </section>\n </div>\n )\n}\n\nfunction StatusBadge({ status }: { status: string }): React.ReactElement {\n const variant: 'default' | 'secondary' | 'outline' | 'destructive' =\n status === 'completed'\n ? 'secondary'\n : status === 'dead'\n ? 'destructive'\n : status === 'processing'\n ? 'default'\n : 'outline'\n return <Badge variant={variant}>{status}</Badge>\n}\n\nfunction EmptyState({ text }: { text: string }): React.ReactElement {\n return (\n <div className=\"rounded-md border border-dashed p-8 text-center text-sm text-muted-foreground\">\n {text}\n </div>\n )\n}\n\nfunction liveStatusBadge(status: 'connecting' | 'open' | 'closed'): {\n label: string\n color: string\n title: string\n} {\n if (status === 'open') {\n return {\n label: 'Live',\n color: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',\n title: 'Realtime connection active — table updates as the worker reports them.',\n }\n }\n if (status === 'connecting') {\n return {\n label: 'Connecting…',\n color: 'bg-amber-500/10 text-amber-700 dark:text-amber-400',\n title: 'Connecting to realtime stream — the page will revalidate when events arrive.',\n }\n }\n return {\n label: 'Offline',\n color: 'bg-zinc-500/10 text-zinc-600 dark:text-zinc-400',\n title: 'Realtime stream unavailable — use the Refresh button to update.',\n }\n}\n\nfunction runtimeOf(iso: string): string {\n const ms = Date.now() - new Date(iso).getTime()\n if (ms < 0) return '—'\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`\n return `${Math.round(ms / 3_600_000)}h`\n}\n\nfunction CatalogTab({\n entries,\n}: {\n entries: readonly QueueCatalogEntry[]\n}): React.ReactElement {\n if (entries.length === 0) {\n return (\n <EmptyState text=\"No jobs registered yet. Plugins declare jobs via defineJob/registerJob.\" />\n )\n }\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Job type</th>\n <th className=\"px-3 py-2 text-left\">Description</th>\n <th className=\"px-3 py-2 text-left\">Schedule</th>\n <th className=\"px-3 py-2 text-left\">Last run</th>\n <th className=\"px-3 py-2 text-left\">24 h volume</th>\n <th className=\"px-3 py-2 text-left\">24 h success</th>\n </tr>\n </thead>\n <tbody>\n {entries.map((entry) => (\n <tr key={entry.name} className=\"border-t align-top\">\n <td className=\"px-3 py-2 whitespace-nowrap\">\n <div className=\"font-mono text-xs\">{entry.name}</div>\n {!entry.registered && (\n <Badge variant=\"outline\" className=\"mt-1\">\n unregistered\n </Badge>\n )}\n {entry.scheduled && !entry.scheduled.enabled && (\n <Badge variant=\"outline\" className=\"mt-1 ml-1\">\n schedule disabled\n </Badge>\n )}\n </td>\n <td className=\"px-3 py-2 text-xs text-muted-foreground max-w-md\">\n {entry.description ?? <span className=\"italic\">—</span>}\n </td>\n <td className=\"px-3 py-2 font-mono text-xs whitespace-nowrap\">\n {entry.schedule ?? (\n <span className=\"text-muted-foreground italic\">event-driven</span>\n )}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {entry.lastRunAt ? new Date(entry.lastRunAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {entry.stats24h.total === 0 ? (\n '—'\n ) : (\n <span className=\"font-mono text-xs\">\n {entry.stats24h.total} total\n {entry.stats24h.dead > 0 && (\n <span className=\"ml-1 text-rose-600 dark:text-rose-400\">\n ({entry.stats24h.dead} dead)\n </span>\n )}\n </span>\n )}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {entry.successRate24h === null\n ? '—'\n : `${Math.round(entry.successRate24h * 100)}%`}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/queue-page-client.tsx"],"sourcesContent":["/**\n * Interactive client surface for the queue admin page.\n *\n * Renders six tabs: in-progress, dead letter, scheduled, recent activity,\n * per-type stats, catalog. Per-row actions (requeue, delete, cancel,\n * run-now, disable/enable, reset-lease) hit the admin API and refetch\n * on success.\n *\n * The initial snapshot comes from the server page so first paint has data.\n * Subsequent revalidation is event-driven via `useLiveSnapshot` against\n * the `queue.**` topics published by the worker and admin routes\n * (PLAN-REALTIME PR D). The previous manual Refresh button is replaced\n * with a transport-status indicator + an explicit Refresh action that\n * triggers an immediate refetch.\n */\n\nimport { useLiveSnapshot, useRealtimeStatus } from '@murumets-ee/admin-ui/realtime'\nimport { Badge, Button, cn } from '@murumets-ee/ui'\nimport { useCallback, useMemo, useState } from 'react'\nimport type {\n QueueCatalogEntry,\n QueueHeartbeat,\n QueueInitialSnapshot,\n QueueJobSummary,\n QueueScheduledSummary,\n} from './types.js'\n\n/**\n * Topics the queue page revalidates on. Stable reference so\n * `useLiveSnapshot` doesn't re-subscribe each render.\n */\nconst QUEUE_LIVE_TOPICS: readonly string[] = ['queue.**'] as const\n\ntype TabKey = 'in-progress' | 'dead' | 'scheduled' | 'recent' | 'stats' | 'catalog'\n\nconst TABS: ReadonlyArray<{ key: TabKey; label: string }> = [\n { key: 'in-progress', label: 'In progress' },\n { key: 'dead', label: 'Dead letter' },\n { key: 'scheduled', label: 'Scheduled' },\n { key: 'recent', label: 'Recent' },\n { key: 'stats', label: 'Stats' },\n { key: 'catalog', label: 'Catalog' },\n]\n\nexport interface QueuePageClientProps {\n apiBasePath: string\n initialData: QueueInitialSnapshot\n}\n\n/**\n * Public component. Subscribes to `queue.**` SSE topics via\n * `useLiveSnapshot` for live revalidation. Must be mounted inside a\n * `<RealtimeProvider>` (the admin shell does this for you); without one\n * the live updates fall back to the no-op subscriber and only the\n * Refresh button revalidates.\n */\nexport function QueuePageClient({\n apiBasePath,\n initialData,\n}: QueuePageClientProps): React.ReactElement {\n const [tab, setTab] = useState<TabKey>('in-progress')\n const [busyId, setBusyId] = useState<string | null>(null)\n const [actionError, setActionError] = useState<string | null>(null)\n const transportStatus = useRealtimeStatus()\n\n // Stable fetcher reference so `useLiveSnapshot` re-uses the same\n // closure across renders. The hook itself holds a ref so a fresh\n // closure on each render wouldn't re-subscribe, but useCallback keeps\n // the function identity matched to its dependencies anyway.\n const fetcher = useCallback(\n async (signal: AbortSignal): Promise<QueueInitialSnapshot> => {\n const opts = { credentials: 'same-origin' as const, signal }\n const [\n statsRes,\n heartbeatRes,\n scheduledRes,\n deadRes,\n processingRes,\n recentRes,\n catalogRes,\n ] = await Promise.all([\n fetch(`${apiBasePath}/queue/stats`, opts),\n fetch(`${apiBasePath}/queue/heartbeat`, opts),\n fetch(`${apiBasePath}/queue/scheduled`, opts),\n fetch(`${apiBasePath}/queue/jobs?status=dead&limit=50`, opts),\n fetch(`${apiBasePath}/queue/jobs?status=processing&limit=50`, opts),\n fetch(`${apiBasePath}/queue/jobs?limit=100`, opts),\n fetch(`${apiBasePath}/queue/catalog`, opts),\n ])\n\n if (\n !statsRes.ok ||\n !heartbeatRes.ok ||\n !scheduledRes.ok ||\n !deadRes.ok ||\n !processingRes.ok ||\n !recentRes.ok ||\n !catalogRes.ok\n ) {\n throw new Error('Failed to refresh queue data')\n }\n\n const [\n statsBody,\n heartbeatBody,\n scheduledBody,\n deadBody,\n processingBody,\n recentBody,\n catalogBody,\n ] = await Promise.all([\n statsRes.json() as Promise<{ counts: Record<string, number> }>,\n heartbeatRes.json() as Promise<{\n workers: QueueHeartbeat[]\n healthy: boolean\n }>,\n scheduledRes.json() as Promise<{ items: QueueScheduledSummary[] }>,\n deadRes.json() as Promise<{ items: QueueJobSummary[] }>,\n processingRes.json() as Promise<{ items: QueueJobSummary[] }>,\n recentRes.json() as Promise<{ items: QueueJobSummary[] }>,\n catalogRes.json() as Promise<{ items: QueueCatalogEntry[] }>,\n ])\n\n return {\n counts: statsBody.counts,\n dead: deadBody.items,\n processing: processingBody.items,\n recent: recentBody.items,\n scheduled: scheduledBody.items,\n heartbeats: heartbeatBody.workers,\n healthyWorker: heartbeatBody.healthy,\n catalog: catalogBody.items,\n }\n },\n [apiBasePath],\n )\n\n const {\n data: live,\n isLoading,\n error: liveError,\n refetch,\n } = useLiveSnapshot<QueueInitialSnapshot>({\n fetcher,\n topics: QUEUE_LIVE_TOPICS,\n initialData,\n })\n\n // `live` cannot be null when `initialData` is supplied; the hook seeds\n // `data` synchronously. Fall back defensively so a misconfigured caller\n // still renders rather than crashing.\n const data = live ?? initialData\n const error = actionError ?? (liveError ? liveError.message : null)\n\n const action = useCallback(\n async (id: string, path: string, method: 'POST' | 'DELETE' = 'POST') => {\n setBusyId(id)\n setActionError(null)\n try {\n const res = await fetch(`${apiBasePath}/queue${path}`, {\n method,\n credentials: 'same-origin',\n })\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: string }\n throw new Error(body.error ?? `Request failed (${res.status})`)\n }\n // The realtime publish from the route handler will trigger\n // `useLiveSnapshot` debounce too, but we refetch immediately so\n // the operator sees their action's effect without waiting for\n // the debounce window.\n refetch()\n } catch (err) {\n setActionError(err instanceof Error ? err.message : String(err))\n } finally {\n setBusyId(null)\n }\n },\n [apiBasePath, refetch],\n )\n\n const healthLabel = data.healthyWorker ? 'Worker healthy' : 'Worker stale'\n const healthColor = data.healthyWorker\n ? 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'\n : 'bg-rose-500/10 text-rose-600 dark:text-rose-400'\n const liveBadge = useMemo(() => liveStatusBadge(transportStatus), [transportStatus])\n\n return (\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-3\">\n <div>\n <h1 className=\"text-2xl font-bold\">Queue</h1>\n <p className=\"text-sm text-muted-foreground\">\n Background jobs, schedules, and worker health.\n </p>\n </div>\n <div className=\"flex items-center gap-3\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium',\n healthColor,\n )}\n >\n {healthLabel}\n </span>\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium',\n liveBadge.color,\n )}\n title={liveBadge.title}\n >\n {liveBadge.label}\n </span>\n <Button onClick={refetch} disabled={isLoading} variant=\"outline\" size=\"sm\">\n {isLoading ? 'Refreshing…' : 'Refresh'}\n </Button>\n </div>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-rose-300/60 bg-rose-50/80 p-3 text-sm text-rose-700 dark:border-rose-600/40 dark:bg-rose-950/40 dark:text-rose-300\">\n {error}\n </div>\n )}\n\n <CountsBar counts={data.counts} />\n\n <nav className=\"flex gap-1 border-b\" role=\"tablist\" aria-label=\"Queue views\">\n {TABS.map((t) => (\n <button\n key={t.key}\n role=\"tab\"\n type=\"button\"\n aria-selected={tab === t.key}\n onClick={() => setTab(t.key)}\n className={cn(\n 'px-3 py-2 text-sm font-medium border-b-2 -mb-px transition-colors',\n tab === t.key\n ? 'border-foreground text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground',\n )}\n >\n {t.label}\n {t.key === 'in-progress' && data.processing.length > 0 && (\n <Badge variant=\"secondary\" className=\"ml-2\">\n {data.processing.length}\n </Badge>\n )}\n {t.key === 'dead' && data.dead.length > 0 && (\n <Badge variant=\"destructive\" className=\"ml-2\">\n {data.dead.length}\n </Badge>\n )}\n </button>\n ))}\n </nav>\n\n {tab === 'in-progress' && (\n <ProcessingTab jobs={data.processing} action={action} busyId={busyId} />\n )}\n {tab === 'dead' && <DeadTab jobs={data.dead} action={action} busyId={busyId} />}\n {tab === 'scheduled' && (\n <ScheduledTab schedules={data.scheduled} action={action} busyId={busyId} />\n )}\n {tab === 'recent' && <RecentTab jobs={data.recent} />}\n {tab === 'stats' && <StatsTab counts={data.counts} heartbeats={data.heartbeats} />}\n {tab === 'catalog' && <CatalogTab entries={data.catalog} />}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction CountsBar({ counts }: { counts: Record<string, number> }): React.ReactElement {\n // Show only the actionable counts: work queued, work in flight, work\n // that died and needs triage. The all-time `completed` count grows\n // forever and tells the operator nothing — it's surfaced on the Stats\n // tab for completeness but doesn't deserve a prime slot here.\n const entries: Array<[string, number, string]> = [\n ['pending', counts.pending ?? 0, 'bg-amber-500/10 text-amber-700 dark:text-amber-400'],\n ['processing', counts.processing ?? 0, 'bg-sky-500/10 text-sky-700 dark:text-sky-400'],\n ['dead', counts.dead ?? 0, 'bg-rose-500/10 text-rose-700 dark:text-rose-400'],\n ]\n return (\n <div className=\"flex flex-wrap gap-3\">\n {entries.map(([label, count, color]) => (\n <div\n key={label}\n className={cn('rounded-md px-3 py-2 text-sm font-medium', color)}\n >\n <span className=\"capitalize\">{label}</span>\n <span className=\"ml-2 font-mono\">{count}</span>\n </div>\n ))}\n </div>\n )\n}\n\nfunction ProcessingTab({\n jobs,\n action,\n busyId,\n}: {\n jobs: readonly QueueJobSummary[]\n action: (id: string, path: string, method?: 'POST' | 'DELETE') => Promise<void>\n busyId: string | null\n}): React.ReactElement {\n if (jobs.length === 0) return <EmptyState text=\"Nothing is processing right now.\" />\n\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Type</th>\n <th className=\"px-3 py-2 text-left\">Worker</th>\n <th className=\"px-3 py-2 text-left\">Locked at</th>\n <th className=\"px-3 py-2 text-left\">Runtime</th>\n <th className=\"px-3 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {jobs.map((j) => (\n <tr key={j.id} className=\"border-t\">\n <td className=\"px-3 py-2 font-mono text-xs\">{j.type}</td>\n <td className=\"px-3 py-2 font-mono text-xs\">{j.lockedBy ?? '—'}</td>\n <td className=\"px-3 py-2\">\n {j.lockedAt ? new Date(j.lockedAt).toLocaleTimeString() : '—'}\n </td>\n <td className=\"px-3 py-2\">{j.lockedAt ? runtimeOf(j.lockedAt) : '—'}</td>\n <td className=\"px-3 py-2 text-right\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={busyId === j.id}\n onClick={() => action(j.id, `/jobs/${j.id}/cancel`)}\n >\n Cancel\n </Button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction DeadTab({\n jobs,\n action,\n busyId,\n}: {\n jobs: readonly QueueJobSummary[]\n action: (id: string, path: string, method?: 'POST' | 'DELETE') => Promise<void>\n busyId: string | null\n}): React.ReactElement {\n if (jobs.length === 0) return <EmptyState text=\"Dead letter is empty. Nothing to triage.\" />\n\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Type</th>\n <th className=\"px-3 py-2 text-left\">Failed at</th>\n <th className=\"px-3 py-2 text-left\">Attempts</th>\n <th className=\"px-3 py-2 text-left\">Last error</th>\n <th className=\"px-3 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {jobs.map((j) => (\n <tr key={j.id} className=\"border-t align-top\">\n <td className=\"px-3 py-2 font-mono text-xs whitespace-nowrap\">{j.type}</td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {j.failedAt ? new Date(j.failedAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2\">\n {j.attempts}/{j.maxRetries}\n </td>\n <td className=\"px-3 py-2 text-xs text-muted-foreground max-w-md break-words\">\n {j.lastError ?? '—'}\n </td>\n <td className=\"px-3 py-2 text-right whitespace-nowrap\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={busyId === j.id}\n onClick={() => action(j.id, `/jobs/${j.id}/requeue`)}\n className=\"mr-2\"\n >\n Requeue\n </Button>\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={busyId === j.id}\n onClick={() => action(j.id, `/jobs/${j.id}`, 'DELETE')}\n >\n Delete\n </Button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction ScheduledTab({\n schedules,\n action,\n busyId,\n}: {\n schedules: readonly QueueScheduledSummary[]\n action: (id: string, path: string, method?: 'POST' | 'DELETE') => Promise<void>\n busyId: string | null\n}): React.ReactElement {\n if (schedules.length === 0) return <EmptyState text=\"No scheduled jobs registered.\" />\n\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Name</th>\n <th className=\"px-3 py-2 text-left\">Schedule</th>\n <th className=\"px-3 py-2 text-left\">Next run</th>\n <th className=\"px-3 py-2 text-left\">Last run</th>\n <th className=\"px-3 py-2 text-left\">Status</th>\n <th className=\"px-3 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {schedules.map((s) => (\n <tr key={s.id} className=\"border-t\">\n <td className=\"px-3 py-2\">\n <div className=\"font-mono text-xs\">{s.name}</div>\n <div className=\"text-xs text-muted-foreground\">{s.type}</div>\n </td>\n <td className=\"px-3 py-2 font-mono text-xs\">{s.schedule}</td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {s.nextRunAt ? new Date(s.nextRunAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {s.lastRunAt ? new Date(s.lastRunAt).toLocaleString() : 'Never'}\n </td>\n <td className=\"px-3 py-2\">\n {s.enabled ? (\n <Badge variant=\"secondary\">Enabled</Badge>\n ) : (\n <Badge variant=\"outline\">Disabled</Badge>\n )}\n </td>\n <td className=\"px-3 py-2 text-right whitespace-nowrap\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={busyId === s.id}\n onClick={() => action(s.id, `/scheduled/${s.id}/run-now`)}\n className=\"mr-1\"\n >\n Run now\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={busyId === s.id}\n onClick={() =>\n action(s.id, `/scheduled/${s.id}/${s.enabled ? 'disable' : 'enable'}`)\n }\n className=\"mr-1\"\n >\n {s.enabled ? 'Disable' : 'Enable'}\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={busyId === s.id}\n onClick={() => action(s.id, `/scheduled/${s.id}/reset-lease`)}\n >\n Reset lease\n </Button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction RecentTab({ jobs }: { jobs: readonly QueueJobSummary[] }): React.ReactElement {\n if (jobs.length === 0) return <EmptyState text=\"No recent jobs.\" />\n\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Type</th>\n <th className=\"px-3 py-2 text-left\">Status</th>\n <th className=\"px-3 py-2 text-left\">Created</th>\n <th className=\"px-3 py-2 text-left\">Completed</th>\n </tr>\n </thead>\n <tbody>\n {jobs.map((j) => (\n <tr key={j.id} className=\"border-t\">\n <td className=\"px-3 py-2 font-mono text-xs\">{j.type}</td>\n <td className=\"px-3 py-2\">\n <StatusBadge status={j.status} />\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {new Date(j.createdAt).toLocaleString()}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {j.completedAt\n ? new Date(j.completedAt).toLocaleString()\n : j.failedAt\n ? new Date(j.failedAt).toLocaleString()\n : '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\nfunction StatsTab({\n counts,\n heartbeats,\n}: {\n counts: Record<string, number>\n heartbeats: readonly QueueHeartbeat[]\n}): React.ReactElement {\n return (\n <div className=\"space-y-6\">\n <section>\n <h2 className=\"font-semibold mb-2\">Status counts</h2>\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-2\">\n {Object.entries(counts).map(([status, count]) => (\n <div key={status} className=\"rounded-md border p-3\">\n <div className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n {status}\n </div>\n <div className=\"text-2xl font-mono\">{count}</div>\n </div>\n ))}\n </div>\n </section>\n <section>\n <h2 className=\"font-semibold mb-2\">Workers</h2>\n {heartbeats.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">No worker has reported a heartbeat yet.</p>\n ) : (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Worker</th>\n <th className=\"px-3 py-2 text-left\">Started</th>\n <th className=\"px-3 py-2 text-left\">Last seen</th>\n <th className=\"px-3 py-2 text-left\">Active</th>\n <th className=\"px-3 py-2 text-left\">Status</th>\n </tr>\n </thead>\n <tbody>\n {heartbeats.map((h) => (\n <tr key={h.workerId} className=\"border-t\">\n <td className=\"px-3 py-2 font-mono text-xs\">{h.workerId}</td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {new Date(h.startedAt).toLocaleString()}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {new Date(h.lastSeenAt).toLocaleString()}\n </td>\n <td className=\"px-3 py-2\">\n {h.activeJobs}/{h.concurrency}\n </td>\n <td className=\"px-3 py-2\">\n {h.stale ? (\n <Badge variant=\"destructive\">Stale</Badge>\n ) : (\n <Badge variant=\"secondary\">Fresh</Badge>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </section>\n </div>\n )\n}\n\nfunction StatusBadge({ status }: { status: string }): React.ReactElement {\n const variant: 'default' | 'secondary' | 'outline' | 'destructive' =\n status === 'completed'\n ? 'secondary'\n : status === 'dead'\n ? 'destructive'\n : status === 'processing'\n ? 'default'\n : 'outline'\n return <Badge variant={variant}>{status}</Badge>\n}\n\nfunction EmptyState({ text }: { text: string }): React.ReactElement {\n return (\n <div className=\"rounded-md border border-dashed p-8 text-center text-sm text-muted-foreground\">\n {text}\n </div>\n )\n}\n\nfunction liveStatusBadge(status: 'connecting' | 'open' | 'closed'): {\n label: string\n color: string\n title: string\n} {\n if (status === 'open') {\n return {\n label: 'Live',\n color: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',\n title: 'Realtime connection active — table updates as the worker reports them.',\n }\n }\n if (status === 'connecting') {\n return {\n label: 'Connecting…',\n color: 'bg-amber-500/10 text-amber-700 dark:text-amber-400',\n title: 'Connecting to realtime stream — the page will revalidate when events arrive.',\n }\n }\n return {\n label: 'Offline',\n color: 'bg-zinc-500/10 text-zinc-600 dark:text-zinc-400',\n title: 'Realtime stream unavailable — use the Refresh button to update.',\n }\n}\n\nfunction runtimeOf(iso: string): string {\n const ms = Date.now() - new Date(iso).getTime()\n if (ms < 0) return '—'\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`\n return `${Math.round(ms / 3_600_000)}h`\n}\n\nfunction CatalogTab({\n entries,\n}: {\n entries: readonly QueueCatalogEntry[]\n}): React.ReactElement {\n if (entries.length === 0) {\n return (\n <EmptyState text=\"No jobs registered yet. Plugins declare jobs via defineJob/registerJob.\" />\n )\n }\n return (\n <div className=\"overflow-x-auto rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-xs uppercase tracking-wide text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left\">Job type</th>\n <th className=\"px-3 py-2 text-left\">Description</th>\n <th className=\"px-3 py-2 text-left\">Schedule</th>\n <th className=\"px-3 py-2 text-left\">Last run</th>\n <th className=\"px-3 py-2 text-left\">24 h volume</th>\n <th className=\"px-3 py-2 text-left\">24 h success</th>\n </tr>\n </thead>\n <tbody>\n {entries.map((entry) => (\n <tr key={entry.name} className=\"border-t align-top\">\n <td className=\"px-3 py-2 whitespace-nowrap\">\n <div className=\"font-mono text-xs\">{entry.name}</div>\n {!entry.registered && (\n <Badge variant=\"outline\" className=\"mt-1\">\n unregistered\n </Badge>\n )}\n {entry.scheduled && !entry.scheduled.enabled && (\n <Badge variant=\"outline\" className=\"mt-1 ml-1\">\n schedule disabled\n </Badge>\n )}\n </td>\n <td className=\"px-3 py-2 text-xs text-muted-foreground max-w-md\">\n {entry.description ?? <span className=\"italic\">—</span>}\n </td>\n <td className=\"px-3 py-2 font-mono text-xs whitespace-nowrap\">\n {entry.schedule ?? (\n <span className=\"text-muted-foreground italic\">event-driven</span>\n )}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {entry.lastRunAt ? new Date(entry.lastRunAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {entry.stats24h.total === 0 ? (\n '—'\n ) : (\n <span className=\"font-mono text-xs\">\n {entry.stats24h.total} total\n {entry.stats24h.dead > 0 && (\n <span className=\"ml-1 text-rose-600 dark:text-rose-400\">\n ({entry.stats24h.dead} dead)\n </span>\n )}\n </span>\n )}\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n {entry.successRate24h === null\n ? '—'\n : `${Math.round(entry.successRate24h * 100)}%`}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@murumets-ee/queue-ui",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "license": "Elastic-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -27,10 +27,10 @@
27
27
  "dependencies": {
28
28
  "clsx": "^2.1.0",
29
29
  "tailwind-merge": "^2.6.0",
30
- "@murumets-ee/admin-ui": "0.12.0",
31
- "@murumets-ee/core": "0.12.0",
32
- "@murumets-ee/queue": "0.12.0",
33
- "@murumets-ee/ui": "0.12.0"
30
+ "@murumets-ee/admin-ui": "0.13.0",
31
+ "@murumets-ee/queue": "0.13.0",
32
+ "@murumets-ee/core": "0.13.0",
33
+ "@murumets-ee/ui": "0.13.0"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "lucide-react": ">=0.400.0",
@@ -58,13 +58,17 @@
58
58
  "tsdown": "^0.21.10",
59
59
  "typescript": "^5.7.3",
60
60
  "vitest": "^2.1.8",
61
- "@murumets-ee/admin-ui": "0.12.0",
62
- "@murumets-ee/queue": "0.12.0",
63
- "@murumets-ee/ui": "0.12.0"
61
+ "@murumets-ee/admin-ui": "0.13.0",
62
+ "@murumets-ee/queue": "0.13.0",
63
+ "@murumets-ee/ui": "0.13.0"
64
64
  },
65
65
  "typeCoverage": {
66
66
  "atLeast": 99
67
67
  },
68
+ "bundleBudget": {
69
+ "dist/pages.mjs": 4096,
70
+ "dist/plugin.mjs": 2048
71
+ },
68
72
  "scripts": {
69
73
  "build": "tsdown",
70
74
  "dev": "tsdown --watch",