Files
PiCloud/dashboard/src/routes/apps/[slug]/queues/+page.svelte
MechaCat02 529d34af83 feat(v1.1.9): dashboard Queues tab + queue:receive trigger form + 0.15.0
API client (src/lib/api.ts):
- TriggerKind gains 'queue'
- TriggerDetails gains { kind: 'queue', queue_name, visibility_timeout_secs, last_fired_at }
- CreateQueueTriggerInput, QueueSummary, QueueConsumer, QueueDetail
- api.triggers.createQueue(idOrSlug, input) -> POST /admin/.../triggers/queue
- api.queues.list(idOrSlug) -> GET /admin/.../queues
- api.queues.get(idOrSlug, name) -> GET /admin/.../queues/{name}

New routes:
- /apps/[slug]/queues — read-only list view (queue_name, total, pending,
  claimed, drilldown link); empty state explains how queues are created
  (first enqueue) and that consumers register via the Triggers tab
- /apps/[slug]/queues/[name] — drilldown showing depth + registered
  consumer (script name + visibility timeout + last_fired_at + trigger
  id); empty consumer state surfaces clearly

App detail page (src/routes/apps/[slug]/+page.svelte):
- New "Queues" link in the app-level nav between Files and Dead letters
- Triggers tab gains a "Queue:receive trigger (v1.1.9)" form alongside
  cron/pubsub/email — target script select, queue_name, visibility
  timeout (5–3600s, default 30), max_attempts (1–20, default 3)
- Trigger list renders queue triggers with queue_name + visibility
  timeout + last_fired_at

dashboard/package.json: 0.14.0 -> 0.15.0

npm run check — all queue/invoke-related code typechecks clean; the
existing Playwright test errors (149 unrelated) carry forward unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-06 22:41:59 +02:00

129 lines
2.7 KiB
Svelte

<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/state';
import { api, ApiError, type App, type QueueSummary } from '$lib/api';
let slug = $derived(page.params.slug ?? '');
let app = $state<App | null>(null);
let queues = $state<QueueSummary[]>([]);
let loading = $state(false);
let error = $state<string | null>(null);
async function loadApp() {
try {
app = await api.apps.get(slug);
} catch (e) {
error = e instanceof ApiError ? e.message : String(e);
}
}
async function loadQueues() {
loading = true;
error = null;
try {
queues = await api.queues.list(slug);
} catch (e) {
error = e instanceof ApiError ? e.message : String(e);
} finally {
loading = false;
}
}
$effect(() => {
void slug;
void loadApp();
void loadQueues();
});
</script>
<svelte:head>
<title>Queues — {app?.name ?? slug} — PiCloud</title>
</svelte:head>
<header>
<a class="back" href="{base}/apps/{slug}">← Back to {app?.name ?? slug}</a>
<h1>Queues</h1>
<p class="subtle">
Per-app durable queues. Producers call <code>queue::enqueue(name, msg)</code>;
consumers register a <code>queue:receive</code> trigger. v1.1.9.
</p>
</header>
{#if error}
<p class="error">{error}</p>
{/if}
{#if loading && queues.length === 0}
<p class="muted">Loading…</p>
{:else if queues.length === 0}
<p class="muted">
No queues in this app yet. A queue is created implicitly the first time a
message is enqueued. Register a <code>queue:receive</code> trigger from the
Triggers tab to consume messages.
</p>
{:else}
<table class="queues">
<thead>
<tr>
<th>Queue</th>
<th class="num">Total</th>
<th class="num">Pending</th>
<th class="num">Claimed</th>
<th></th>
</tr>
</thead>
<tbody>
{#each queues as q (q.queue_name)}
<tr>
<td><code>{q.queue_name}</code></td>
<td class="num">{q.total}</td>
<td class="num">{q.pending}</td>
<td class="num">{q.claimed}</td>
<td>
<a href="{base}/apps/{slug}/queues/{encodeURIComponent(q.queue_name)}">
drilldown →
</a>
</td>
</tr>
{/each}
</tbody>
</table>
{/if}
<style>
header {
margin-bottom: 1rem;
}
.back {
font-size: 0.875rem;
color: var(--color-link);
}
h1 {
margin: 0.25rem 0;
}
.subtle {
color: var(--color-muted);
font-size: 0.875rem;
}
.error {
color: var(--color-error, #c00);
}
.muted {
color: var(--color-muted);
}
table.queues {
width: 100%;
border-collapse: collapse;
}
table.queues th,
table.queues td {
text-align: left;
padding: 0.5rem;
border-bottom: 1px solid var(--color-border, #eee);
}
.num {
text-align: right;
font-variant-numeric: tabular-nums;
}
</style>