@happyvertical/smrt-jobs 0.30.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/AGENTS.md +71 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +151 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/background-policy.d.ts +121 -0
- package/dist/background-policy.d.ts.map +1 -0
- package/dist/chunks/runner-DV8FBO0y.js +1642 -0
- package/dist/chunks/runner-DV8FBO0y.js.map +1 -0
- package/dist/chunks/worker-liveness-DOTjoIjr.js +65 -0
- package/dist/chunks/worker-liveness-DOTjoIjr.js.map +1 -0
- package/dist/error-redaction.d.ts +48 -0
- package/dist/error-redaction.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +926 -0
- package/dist/index.js.map +1 -0
- package/dist/job-builder.d.ts +94 -0
- package/dist/job-builder.d.ts.map +1 -0
- package/dist/job-handle.d.ts +71 -0
- package/dist/job-handle.d.ts.map +1 -0
- package/dist/logger-extension.d.ts +58 -0
- package/dist/logger-extension.d.ts.map +1 -0
- package/dist/manifest.json +1327 -0
- package/dist/object-extension.d.ts +68 -0
- package/dist/object-extension.d.ts.map +1 -0
- package/dist/playground.d.ts +2 -0
- package/dist/playground.d.ts.map +1 -0
- package/dist/playground.js +179 -0
- package/dist/playground.js.map +1 -0
- package/dist/runner.d.ts +189 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +15 -0
- package/dist/runner.js.map +1 -0
- package/dist/schedule-runner.d.ts +151 -0
- package/dist/schedule-runner.d.ts.map +1 -0
- package/dist/smrt-job-event.d.ts +54 -0
- package/dist/smrt-job-event.d.ts.map +1 -0
- package/dist/smrt-job.d.ts +215 -0
- package/dist/smrt-job.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +508 -0
- package/dist/smrt-worker.d.ts +72 -0
- package/dist/smrt-worker.d.ts.map +1 -0
- package/dist/stale-recovery.d.ts +34 -0
- package/dist/stale-recovery.d.ts.map +1 -0
- package/dist/svelte/components/JobActions.svelte +103 -0
- package/dist/svelte/components/JobActions.svelte.d.ts +23 -0
- package/dist/svelte/components/JobActions.svelte.d.ts.map +1 -0
- package/dist/svelte/components/JobDashboard.svelte +199 -0
- package/dist/svelte/components/JobDashboard.svelte.d.ts +27 -0
- package/dist/svelte/components/JobDashboard.svelte.d.ts.map +1 -0
- package/dist/svelte/components/JobDetail.svelte +256 -0
- package/dist/svelte/components/JobDetail.svelte.d.ts +17 -0
- package/dist/svelte/components/JobDetail.svelte.d.ts.map +1 -0
- package/dist/svelte/components/JobList.svelte +360 -0
- package/dist/svelte/components/JobList.svelte.d.ts +28 -0
- package/dist/svelte/components/JobList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/JobStats.svelte +242 -0
- package/dist/svelte/components/JobStats.svelte.d.ts +15 -0
- package/dist/svelte/components/JobStats.svelte.d.ts.map +1 -0
- package/dist/svelte/components/JobStatusBadge.svelte +23 -0
- package/dist/svelte/components/JobStatusBadge.svelte.d.ts +9 -0
- package/dist/svelte/components/JobStatusBadge.svelte.d.ts.map +1 -0
- package/dist/svelte/components/types.d.ts +9 -0
- package/dist/svelte/components/types.d.ts.map +1 -0
- package/dist/svelte/components/types.js +8 -0
- package/dist/svelte/i18n.d.ts +22 -0
- package/dist/svelte/i18n.d.ts.map +1 -0
- package/dist/svelte/i18n.js +22 -0
- package/dist/svelte/index.d.ts +25 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +28 -0
- package/dist/svelte/playground.d.ts +329 -0
- package/dist/svelte/playground.d.ts.map +1 -0
- package/dist/svelte/playground.js +174 -0
- package/dist/svelte/types.d.ts +191 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +87 -0
- package/dist/ui.d.ts +10 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +69 -0
- package/dist/ui.js.map +1 -0
- package/dist/worker-liveness-thread.d.ts +2 -0
- package/dist/worker-liveness-thread.d.ts.map +1 -0
- package/dist/worker-liveness-thread.js +66 -0
- package/dist/worker-liveness-thread.js.map +1 -0
- package/dist/worker-liveness-ticker.d.ts +30 -0
- package/dist/worker-liveness-ticker.d.ts.map +1 -0
- package/dist/worker-liveness.d.ts +71 -0
- package/dist/worker-liveness.d.ts.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* JobActions - Action buttons for job management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
7
|
+
import type { JobData } from './types.js';
|
|
8
|
+
|
|
9
|
+
export interface Props {
|
|
10
|
+
/** Job to perform actions on */
|
|
11
|
+
job: JobData;
|
|
12
|
+
/** Show retry button */
|
|
13
|
+
showRetry?: boolean;
|
|
14
|
+
/** Show cancel button */
|
|
15
|
+
showCancel?: boolean;
|
|
16
|
+
/** Show delete button */
|
|
17
|
+
showDelete?: boolean;
|
|
18
|
+
/** Callback when retry is clicked */
|
|
19
|
+
onRetry?: (job: JobData) => void;
|
|
20
|
+
/** Callback when cancel is clicked */
|
|
21
|
+
onCancel?: (job: JobData) => void;
|
|
22
|
+
/** Callback when delete is clicked */
|
|
23
|
+
onDelete?: (job: JobData) => void;
|
|
24
|
+
/** Compact mode */
|
|
25
|
+
compact?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
job,
|
|
30
|
+
showRetry = true,
|
|
31
|
+
showCancel = true,
|
|
32
|
+
showDelete = false,
|
|
33
|
+
onRetry,
|
|
34
|
+
onCancel,
|
|
35
|
+
onDelete,
|
|
36
|
+
compact = false,
|
|
37
|
+
}: Props = $props();
|
|
38
|
+
|
|
39
|
+
const canRetry = $derived(
|
|
40
|
+
job.status === 'failed' || job.status === 'cancelled',
|
|
41
|
+
);
|
|
42
|
+
const canCancel = $derived(
|
|
43
|
+
job.status === 'pending' ||
|
|
44
|
+
job.status === 'ready' ||
|
|
45
|
+
job.status === 'running',
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
function handleRetry() {
|
|
49
|
+
onRetry?.(job);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function handleCancel() {
|
|
53
|
+
onCancel?.(job);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function handleDelete() {
|
|
57
|
+
onDelete?.(job);
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<div class="job-actions" class:compact>
|
|
62
|
+
{#if showRetry && canRetry}
|
|
63
|
+
<Button
|
|
64
|
+
variant="secondary"
|
|
65
|
+
size={compact ? 'sm' : 'md'}
|
|
66
|
+
onclick={handleRetry}
|
|
67
|
+
>
|
|
68
|
+
Retry
|
|
69
|
+
</Button>
|
|
70
|
+
{/if}
|
|
71
|
+
|
|
72
|
+
{#if showCancel && canCancel}
|
|
73
|
+
<Button
|
|
74
|
+
variant="secondary"
|
|
75
|
+
size={compact ? 'sm' : 'md'}
|
|
76
|
+
onclick={handleCancel}
|
|
77
|
+
>
|
|
78
|
+
Cancel
|
|
79
|
+
</Button>
|
|
80
|
+
{/if}
|
|
81
|
+
|
|
82
|
+
{#if showDelete}
|
|
83
|
+
<Button
|
|
84
|
+
variant="danger"
|
|
85
|
+
size={compact ? 'sm' : 'md'}
|
|
86
|
+
onclick={handleDelete}
|
|
87
|
+
>
|
|
88
|
+
Delete
|
|
89
|
+
</Button>
|
|
90
|
+
{/if}
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<style>
|
|
94
|
+
.job-actions {
|
|
95
|
+
display: flex;
|
|
96
|
+
gap: var(--smrt-spacing-sm, 0.5rem);
|
|
97
|
+
align-items: center;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.compact {
|
|
101
|
+
gap: var(--smrt-spacing-xs, 0.25rem);
|
|
102
|
+
}
|
|
103
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JobData } from './types.js';
|
|
2
|
+
export interface Props {
|
|
3
|
+
/** Job to perform actions on */
|
|
4
|
+
job: JobData;
|
|
5
|
+
/** Show retry button */
|
|
6
|
+
showRetry?: boolean;
|
|
7
|
+
/** Show cancel button */
|
|
8
|
+
showCancel?: boolean;
|
|
9
|
+
/** Show delete button */
|
|
10
|
+
showDelete?: boolean;
|
|
11
|
+
/** Callback when retry is clicked */
|
|
12
|
+
onRetry?: (job: JobData) => void;
|
|
13
|
+
/** Callback when cancel is clicked */
|
|
14
|
+
onCancel?: (job: JobData) => void;
|
|
15
|
+
/** Callback when delete is clicked */
|
|
16
|
+
onDelete?: (job: JobData) => void;
|
|
17
|
+
/** Compact mode */
|
|
18
|
+
compact?: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare const JobActions: import("svelte").Component<Props, {}, "">;
|
|
21
|
+
type JobActions = ReturnType<typeof JobActions>;
|
|
22
|
+
export default JobActions;
|
|
23
|
+
//# sourceMappingURL=JobActions.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JobActions.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/JobActions.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,KAAK;IACpB,gCAAgC;IAChC,GAAG,EAAE,OAAO,CAAC;IACb,wBAAwB;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yBAAyB;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yBAAyB;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,sCAAsC;IACtC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAiED,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* JobDashboard - Combined overview panel for background jobs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
7
|
+
import { Card } from '@happyvertical/smrt-ui/ui';
|
|
8
|
+
import { M } from '../i18n.js';
|
|
9
|
+
import JobList from './JobList.svelte';
|
|
10
|
+
import JobStatsSummary from './JobStats.svelte';
|
|
11
|
+
import type { JobData, JobStats, QueueStats } from './types.js';
|
|
12
|
+
|
|
13
|
+
const { t } = useI18n();
|
|
14
|
+
|
|
15
|
+
export interface Props {
|
|
16
|
+
/** Statistics data */
|
|
17
|
+
stats: JobStats;
|
|
18
|
+
/** Queue breakdown */
|
|
19
|
+
queues?: QueueStats[];
|
|
20
|
+
/** Recent jobs */
|
|
21
|
+
recentJobs?: JobData[];
|
|
22
|
+
/** Failed jobs */
|
|
23
|
+
failedJobs?: JobData[];
|
|
24
|
+
/** Loading state */
|
|
25
|
+
loading?: boolean;
|
|
26
|
+
/** Callback when job is clicked */
|
|
27
|
+
onJobClick?: (job: JobData) => void;
|
|
28
|
+
/** Callback when retry is clicked */
|
|
29
|
+
onRetry?: (job: JobData) => void;
|
|
30
|
+
/** Callback when cancel is clicked */
|
|
31
|
+
onCancel?: (job: JobData) => void;
|
|
32
|
+
/** Callback when view all is clicked */
|
|
33
|
+
onViewAll?: () => void;
|
|
34
|
+
/** Callback when view failed is clicked */
|
|
35
|
+
onViewFailed?: () => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let {
|
|
39
|
+
stats,
|
|
40
|
+
queues = [],
|
|
41
|
+
recentJobs = [],
|
|
42
|
+
failedJobs = [],
|
|
43
|
+
loading = false,
|
|
44
|
+
onJobClick,
|
|
45
|
+
onRetry,
|
|
46
|
+
onCancel,
|
|
47
|
+
onViewAll,
|
|
48
|
+
onViewFailed,
|
|
49
|
+
}: Props = $props();
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<div class="job-dashboard" class:loading>
|
|
53
|
+
<!-- Stats Overview -->
|
|
54
|
+
<section class="job-dashboard__section">
|
|
55
|
+
<JobStatsSummary {stats} {queues} {loading} />
|
|
56
|
+
</section>
|
|
57
|
+
|
|
58
|
+
<div class="job-dashboard__panels">
|
|
59
|
+
<!-- Recent Jobs -->
|
|
60
|
+
<section class="job-dashboard__section">
|
|
61
|
+
<Card>
|
|
62
|
+
{#snippet header()}
|
|
63
|
+
<div class="panel-header">
|
|
64
|
+
<h2>{t(M['jobs.job_dashboard.recent_jobs'])}</h2>
|
|
65
|
+
{#if onViewAll}
|
|
66
|
+
<button class="view-all-btn" onclick={onViewAll}>{t(M['jobs.job_dashboard.view_all'])}</button>
|
|
67
|
+
{/if}
|
|
68
|
+
</div>
|
|
69
|
+
{/snippet}
|
|
70
|
+
|
|
71
|
+
<JobList
|
|
72
|
+
jobs={recentJobs}
|
|
73
|
+
{loading}
|
|
74
|
+
showActions={false}
|
|
75
|
+
{onJobClick}
|
|
76
|
+
>
|
|
77
|
+
{#snippet empty()}
|
|
78
|
+
<p class="empty-message">{t(M['jobs.job_dashboard.no_recent_jobs'])}</p>
|
|
79
|
+
{/snippet}
|
|
80
|
+
</JobList>
|
|
81
|
+
</Card>
|
|
82
|
+
</section>
|
|
83
|
+
|
|
84
|
+
<!-- Failed Jobs -->
|
|
85
|
+
{#if failedJobs.length > 0 || stats.failed > 0}
|
|
86
|
+
<section class="job-dashboard__section">
|
|
87
|
+
<Card>
|
|
88
|
+
{#snippet header()}
|
|
89
|
+
<div class="panel-header panel-header--error">
|
|
90
|
+
<h2>{t(M['jobs.job_dashboard.failed_jobs'])}</h2>
|
|
91
|
+
{#if onViewFailed}
|
|
92
|
+
<button class="view-all-btn" onclick={onViewFailed}
|
|
93
|
+
>{t(M['jobs.job_dashboard.view_all_count'], { count: stats.failed })}</button
|
|
94
|
+
>
|
|
95
|
+
{/if}
|
|
96
|
+
</div>
|
|
97
|
+
{/snippet}
|
|
98
|
+
|
|
99
|
+
<JobList
|
|
100
|
+
jobs={failedJobs}
|
|
101
|
+
{loading}
|
|
102
|
+
showActions={true}
|
|
103
|
+
{onJobClick}
|
|
104
|
+
{onRetry}
|
|
105
|
+
{onCancel}
|
|
106
|
+
>
|
|
107
|
+
{#snippet empty()}
|
|
108
|
+
<p class="empty-message empty-message--success">
|
|
109
|
+
{t(M['jobs.job_dashboard.no_failed_jobs'])}
|
|
110
|
+
</p>
|
|
111
|
+
{/snippet}
|
|
112
|
+
</JobList>
|
|
113
|
+
</Card>
|
|
114
|
+
</section>
|
|
115
|
+
{/if}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<style>
|
|
120
|
+
.job-dashboard {
|
|
121
|
+
width: 100%;
|
|
122
|
+
display: flex;
|
|
123
|
+
flex-direction: column;
|
|
124
|
+
gap: var(--smrt-spacing-lg, 1.5rem);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.job-dashboard.loading {
|
|
128
|
+
opacity: 0.7;
|
|
129
|
+
pointer-events: none;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@media (prefers-reduced-motion: reduce) {
|
|
133
|
+
.job-dashboard.loading {
|
|
134
|
+
transition: none;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.job-dashboard__section {
|
|
139
|
+
width: 100%;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.job-dashboard__panels {
|
|
143
|
+
display: grid;
|
|
144
|
+
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
|
145
|
+
gap: var(--smrt-spacing-lg, 1.5rem);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@media (max-width: 900px) {
|
|
149
|
+
.job-dashboard__panels {
|
|
150
|
+
grid-template-columns: 1fr;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.panel-header {
|
|
155
|
+
display: flex;
|
|
156
|
+
justify-content: space-between;
|
|
157
|
+
align-items: center;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.panel-header h2 {
|
|
161
|
+
margin: 0;
|
|
162
|
+
font: var(--smrt-typography-title-medium-font, 600 1rem / 1.5 sans-serif);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.panel-header--error h2 {
|
|
166
|
+
color: var(--smrt-color-error, #ba1a1a);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.view-all-btn {
|
|
170
|
+
padding: var(--smrt-spacing-xs, 0.25rem) var(--smrt-spacing-sm, 0.5rem);
|
|
171
|
+
border: none;
|
|
172
|
+
background: none;
|
|
173
|
+
color: var(--smrt-color-primary, #005ac1);
|
|
174
|
+
font: var(--smrt-typography-label-large-font, 500 0.875rem / 1.25 sans-serif);
|
|
175
|
+
cursor: pointer;
|
|
176
|
+
border-radius: var(--smrt-radius-small, 0.25rem);
|
|
177
|
+
transition: background-color var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.view-all-btn:hover {
|
|
181
|
+
background: var(--smrt-color-primary-container, #d6e3ff);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.empty-message {
|
|
185
|
+
padding: var(--smrt-spacing-md, 1rem);
|
|
186
|
+
text-align: center;
|
|
187
|
+
color: var(--smrt-color-on-surface-variant, #43474e);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.empty-message--success {
|
|
191
|
+
color: var(--smrt-color-tertiary, #006c4f);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@media (prefers-reduced-motion: reduce) {
|
|
195
|
+
.view-all-btn {
|
|
196
|
+
transition: none;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { JobData, JobStats, QueueStats } from './types.js';
|
|
2
|
+
export interface Props {
|
|
3
|
+
/** Statistics data */
|
|
4
|
+
stats: JobStats;
|
|
5
|
+
/** Queue breakdown */
|
|
6
|
+
queues?: QueueStats[];
|
|
7
|
+
/** Recent jobs */
|
|
8
|
+
recentJobs?: JobData[];
|
|
9
|
+
/** Failed jobs */
|
|
10
|
+
failedJobs?: JobData[];
|
|
11
|
+
/** Loading state */
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
/** Callback when job is clicked */
|
|
14
|
+
onJobClick?: (job: JobData) => void;
|
|
15
|
+
/** Callback when retry is clicked */
|
|
16
|
+
onRetry?: (job: JobData) => void;
|
|
17
|
+
/** Callback when cancel is clicked */
|
|
18
|
+
onCancel?: (job: JobData) => void;
|
|
19
|
+
/** Callback when view all is clicked */
|
|
20
|
+
onViewAll?: () => void;
|
|
21
|
+
/** Callback when view failed is clicked */
|
|
22
|
+
onViewFailed?: () => void;
|
|
23
|
+
}
|
|
24
|
+
declare const JobDashboard: import("svelte").Component<Props, {}, "">;
|
|
25
|
+
type JobDashboard = ReturnType<typeof JobDashboard>;
|
|
26
|
+
export default JobDashboard;
|
|
27
|
+
//# sourceMappingURL=JobDashboard.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JobDashboard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/JobDashboard.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGhE,MAAM,WAAW,KAAK;IACpB,sBAAsB;IACtB,KAAK,EAAE,QAAQ,CAAC;IAChB,sBAAsB;IACtB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB,kBAAkB;IAClB,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;IACvB,kBAAkB;IAClB,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;IACvB,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mCAAmC;IACnC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAqFD,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* JobDetail - Detailed view of a single job
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
7
|
+
import { Card } from '@happyvertical/smrt-ui/ui';
|
|
8
|
+
import { M } from '../i18n.js';
|
|
9
|
+
import JobActions from './JobActions.svelte';
|
|
10
|
+
import JobStatusBadge from './JobStatusBadge.svelte';
|
|
11
|
+
import type { JobData } from './types.js';
|
|
12
|
+
import {
|
|
13
|
+
formatDuration,
|
|
14
|
+
formatRelativeTime,
|
|
15
|
+
getPriorityLabel,
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
const { t } = useI18n();
|
|
19
|
+
|
|
20
|
+
export interface Props {
|
|
21
|
+
/** Job to display */
|
|
22
|
+
job: JobData;
|
|
23
|
+
/** Show result data */
|
|
24
|
+
showResult?: boolean;
|
|
25
|
+
/** Callback when retry is clicked */
|
|
26
|
+
onRetry?: (job: JobData) => void;
|
|
27
|
+
/** Callback when cancel is clicked */
|
|
28
|
+
onCancel?: (job: JobData) => void;
|
|
29
|
+
/** Callback when delete is clicked */
|
|
30
|
+
onDelete?: (job: JobData) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let { job, showResult = true, onRetry, onCancel, onDelete }: Props = $props();
|
|
34
|
+
|
|
35
|
+
// Calculate duration if job has started
|
|
36
|
+
const duration = $derived.by(() => {
|
|
37
|
+
if (!job.startedAt) return null;
|
|
38
|
+
const start = new Date(job.startedAt).getTime();
|
|
39
|
+
const end = job.completedAt
|
|
40
|
+
? new Date(job.completedAt).getTime()
|
|
41
|
+
: Date.now();
|
|
42
|
+
return end - start;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Format args for display
|
|
46
|
+
const formattedArgs = $derived(JSON.stringify(job.args, null, 2));
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<div class="job-detail">
|
|
50
|
+
<Card>
|
|
51
|
+
{#snippet header()}
|
|
52
|
+
<div class="job-detail__header">
|
|
53
|
+
<div class="job-detail__title">
|
|
54
|
+
<h2>{job.objectType}.{job.method}()</h2>
|
|
55
|
+
<JobStatusBadge status={job.status} size="md" />
|
|
56
|
+
</div>
|
|
57
|
+
<div class="job-detail__actions">
|
|
58
|
+
<JobActions
|
|
59
|
+
{job}
|
|
60
|
+
showDelete={!!onDelete}
|
|
61
|
+
{onRetry}
|
|
62
|
+
{onCancel}
|
|
63
|
+
{onDelete}
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
{/snippet}
|
|
68
|
+
|
|
69
|
+
<div class="job-detail__content">
|
|
70
|
+
<div class="job-detail__grid">
|
|
71
|
+
<!-- Basic Info -->
|
|
72
|
+
<div class="job-detail__section">
|
|
73
|
+
<h3>{t(M['jobs.job_detail.job_info'])}</h3>
|
|
74
|
+
<dl class="job-detail__list">
|
|
75
|
+
<dt>ID</dt>
|
|
76
|
+
<dd><code>{job.id}</code></dd>
|
|
77
|
+
|
|
78
|
+
<dt>Queue</dt>
|
|
79
|
+
<dd>{job.queue}</dd>
|
|
80
|
+
|
|
81
|
+
<dt>Priority</dt>
|
|
82
|
+
<dd>{getPriorityLabel(job.priority)} ({job.priority})</dd>
|
|
83
|
+
|
|
84
|
+
<dt>{t(M['jobs.job_detail.object_type'])}</dt>
|
|
85
|
+
<dd>{job.objectType}</dd>
|
|
86
|
+
|
|
87
|
+
{#if job.objectId}
|
|
88
|
+
<dt>{t(M['jobs.job_detail.object_id'])}</dt>
|
|
89
|
+
<dd><code>{job.objectId}</code></dd>
|
|
90
|
+
{/if}
|
|
91
|
+
|
|
92
|
+
<dt>Method</dt>
|
|
93
|
+
<dd><code>{job.method}</code></dd>
|
|
94
|
+
</dl>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<!-- Timing -->
|
|
98
|
+
<div class="job-detail__section">
|
|
99
|
+
<h3>Timing</h3>
|
|
100
|
+
<dl class="job-detail__list">
|
|
101
|
+
<dt>Created</dt>
|
|
102
|
+
<dd>{formatRelativeTime(job.createdAt)}</dd>
|
|
103
|
+
|
|
104
|
+
<dt>{t(M['jobs.job_detail.scheduled_for'])}</dt>
|
|
105
|
+
<dd>{formatRelativeTime(job.runAt)}</dd>
|
|
106
|
+
|
|
107
|
+
{#if job.startedAt}
|
|
108
|
+
<dt>Started</dt>
|
|
109
|
+
<dd>{formatRelativeTime(job.startedAt)}</dd>
|
|
110
|
+
{/if}
|
|
111
|
+
|
|
112
|
+
{#if job.completedAt}
|
|
113
|
+
<dt>Completed</dt>
|
|
114
|
+
<dd>{formatRelativeTime(job.completedAt)}</dd>
|
|
115
|
+
{/if}
|
|
116
|
+
|
|
117
|
+
{#if duration !== null}
|
|
118
|
+
<dt>Duration</dt>
|
|
119
|
+
<dd>{formatDuration(duration)}</dd>
|
|
120
|
+
{/if}
|
|
121
|
+
|
|
122
|
+
<dt>Timeout</dt>
|
|
123
|
+
<dd>{formatDuration(job.timeout)} ({job.timeoutBehavior})</dd>
|
|
124
|
+
</dl>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<!-- Execution -->
|
|
128
|
+
<div class="job-detail__section">
|
|
129
|
+
<h3>Execution</h3>
|
|
130
|
+
<dl class="job-detail__list">
|
|
131
|
+
<dt>Attempts</dt>
|
|
132
|
+
<dd>{job.attempts} / {job.maxAttempts}</dd>
|
|
133
|
+
|
|
134
|
+
{#if job.workerId}
|
|
135
|
+
<dt>Worker</dt>
|
|
136
|
+
<dd><code>{job.workerId}</code></dd>
|
|
137
|
+
{/if}
|
|
138
|
+
|
|
139
|
+
{#if job.resultPointer}
|
|
140
|
+
<dt>Result</dt>
|
|
141
|
+
<dd><code>{job.resultPointer}</code></dd>
|
|
142
|
+
{/if}
|
|
143
|
+
</dl>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<!-- Arguments -->
|
|
148
|
+
<div class="job-detail__section job-detail__section--full">
|
|
149
|
+
<h3>Arguments</h3>
|
|
150
|
+
<pre class="job-detail__code">{formattedArgs}</pre>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<!-- Error -->
|
|
154
|
+
{#if job.lastError}
|
|
155
|
+
<div class="job-detail__section job-detail__section--full job-detail__section--error">
|
|
156
|
+
<h3>{t(M['jobs.job_detail.last_error'])}</h3>
|
|
157
|
+
<pre class="job-detail__code job-detail__code--error">{job.lastError}</pre>
|
|
158
|
+
</div>
|
|
159
|
+
{/if}
|
|
160
|
+
</div>
|
|
161
|
+
</Card>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<style>
|
|
165
|
+
.job-detail {
|
|
166
|
+
width: 100%;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.job-detail__header {
|
|
170
|
+
display: flex;
|
|
171
|
+
justify-content: space-between;
|
|
172
|
+
align-items: center;
|
|
173
|
+
gap: var(--smrt-spacing-md, 1rem);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.job-detail__title {
|
|
177
|
+
display: flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: var(--smrt-spacing-md, 1rem);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.job-detail__title h2 {
|
|
183
|
+
margin: 0;
|
|
184
|
+
font: var(--smrt-typography-title-large-font, 1.125rem / 1.25 sans-serif);
|
|
185
|
+
font-family: var(--smrt-font-family-mono, ui-monospace, monospace);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.job-detail__content {
|
|
189
|
+
padding: var(--smrt-spacing-md, 1rem) 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.job-detail__grid {
|
|
193
|
+
display: grid;
|
|
194
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
195
|
+
gap: var(--smrt-spacing-lg, 1.5rem);
|
|
196
|
+
margin-bottom: var(--smrt-spacing-lg, 1.5rem);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.job-detail__section h3 {
|
|
200
|
+
margin: 0 0 var(--spacing-sm, 0.5rem) 0;
|
|
201
|
+
font-size: var(--smrt-typography-title-small-size, 0.875rem);
|
|
202
|
+
font-weight: var(--smrt-typography-weight-semibold, 600);
|
|
203
|
+
color: var(--color-text-secondary, #6b7280);
|
|
204
|
+
text-transform: uppercase;
|
|
205
|
+
letter-spacing: var(--smrt-typography-title-small-tracking, 0.05em);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.job-detail__section--full {
|
|
209
|
+
grid-column: 1 / -1;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.job-detail__section--error h3 {
|
|
213
|
+
color: var(--color-error, #dc2626);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.job-detail__list {
|
|
217
|
+
display: grid;
|
|
218
|
+
grid-template-columns: auto 1fr;
|
|
219
|
+
gap: var(--smrt-spacing-xs, 0.25rem) var(--smrt-spacing-md, 1rem);
|
|
220
|
+
margin: 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.job-detail__list dt {
|
|
224
|
+
color: var(--smrt-color-on-surface-variant, #43474e);
|
|
225
|
+
font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.job-detail__list dd {
|
|
229
|
+
margin: 0;
|
|
230
|
+
font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.job-detail__list code {
|
|
234
|
+
padding: 0.125rem 0.375rem;
|
|
235
|
+
background: var(--smrt-color-surface-container, #f3f4f6);
|
|
236
|
+
border-radius: var(--smrt-radius-small, 0.25rem);
|
|
237
|
+
font: var(--smrt-typography-body-small-font, 0.75rem / 1.25 sans-serif);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.job-detail__code {
|
|
241
|
+
margin: 0;
|
|
242
|
+
padding: var(--smrt-spacing-md, 1rem);
|
|
243
|
+
background: var(--smrt-color-surface-container, #f3f4f6);
|
|
244
|
+
border-radius: var(--smrt-radius-medium, 0.5rem);
|
|
245
|
+
font-family: var(--smrt-font-family-mono, ui-monospace, monospace);
|
|
246
|
+
font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
|
|
247
|
+
overflow-x: auto;
|
|
248
|
+
white-space: pre-wrap;
|
|
249
|
+
word-break: break-word;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.job-detail__code--error {
|
|
253
|
+
background: var(--smrt-color-error-container, #ffdad6);
|
|
254
|
+
color: var(--smrt-color-on-error-container, #410002);
|
|
255
|
+
}
|
|
256
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { JobData } from './types.js';
|
|
2
|
+
export interface Props {
|
|
3
|
+
/** Job to display */
|
|
4
|
+
job: JobData;
|
|
5
|
+
/** Show result data */
|
|
6
|
+
showResult?: boolean;
|
|
7
|
+
/** Callback when retry is clicked */
|
|
8
|
+
onRetry?: (job: JobData) => void;
|
|
9
|
+
/** Callback when cancel is clicked */
|
|
10
|
+
onCancel?: (job: JobData) => void;
|
|
11
|
+
/** Callback when delete is clicked */
|
|
12
|
+
onDelete?: (job: JobData) => void;
|
|
13
|
+
}
|
|
14
|
+
declare const JobDetail: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type JobDetail = ReturnType<typeof JobDetail>;
|
|
16
|
+
export default JobDetail;
|
|
17
|
+
//# sourceMappingURL=JobDetail.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JobDetail.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/JobDetail.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAQ1C,MAAM,WAAW,KAAK;IACpB,qBAAqB;IACrB,GAAG,EAAE,OAAO,CAAC;IACb,uBAAuB;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,sCAAsC;IACtC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC;AAgJD,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|