@10x-media/jobs 0.1.0-beta.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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +351 -0
- package/dist/deriveJobStatus-CM_sCsgm.js +72 -0
- package/dist/deriveJobStatus-CM_sCsgm.js.map +1 -0
- package/dist/exports/client.d.ts +49 -0
- package/dist/exports/client.js +573 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/i18n.d.ts +47 -0
- package/dist/exports/i18n.js +3 -0
- package/dist/exports/rsc.d.ts +19 -0
- package/dist/exports/rsc.js +66 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/exports/types.d.ts +2 -0
- package/dist/exports/types.js +1 -0
- package/dist/index-CYsbNe4q.d.ts +530 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1622 -0
- package/dist/index.js.map +1 -0
- package/dist/jobStatusMeta-BLBSPUcE.js +41 -0
- package/dist/jobStatusMeta-BLBSPUcE.js.map +1 -0
- package/dist/keys-BYs8GTJy.js +41 -0
- package/dist/keys-BYs8GTJy.js.map +1 -0
- package/dist/server-JsvYf5vy.js +13 -0
- package/dist/server-JsvYf5vy.js.map +1 -0
- package/dist/translations-KyQ3962W.js +61 -0
- package/dist/translations-KyQ3962W.js.map +1 -0
- package/package.json +110 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as keys } from "../keys-BYs8GTJy.js";
|
|
3
|
+
import { t as deriveJobStatus } from "../deriveJobStatus-CM_sCsgm.js";
|
|
4
|
+
import { t as jobStatusMeta } from "../jobStatusMeta-BLBSPUcE.js";
|
|
5
|
+
import { Link, Pill, useConfig, useDocumentInfo, useField, useForm, useFormFields, useTranslation } from "@payloadcms/ui";
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { formatDate } from "@payloadcms/ui/shared";
|
|
9
|
+
import { createPortal } from "react-dom";
|
|
10
|
+
//#region src/translations/useTranslation.ts
|
|
11
|
+
/**
|
|
12
|
+
* `useTranslation` bound to this plugin's keys, so `t(keys.X)` typechecks without
|
|
13
|
+
* a per-call `@ts-expect-error`. Returns Payload's `{ t, i18n }` unchanged.
|
|
14
|
+
*/
|
|
15
|
+
const useTranslation$1 = () => useTranslation();
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/jobs/JobDocDescription.tsx
|
|
18
|
+
/** Document-header description for a job: the record ID, kept accessible and copyable. */
|
|
19
|
+
const JobDocDescription = () => {
|
|
20
|
+
const { id } = useDocumentInfo();
|
|
21
|
+
const { t } = useTranslation$1();
|
|
22
|
+
const [copied, setCopied] = useState(false);
|
|
23
|
+
const timer = useRef(void 0);
|
|
24
|
+
useEffect(() => () => clearTimeout(timer.current), []);
|
|
25
|
+
if (id == null) return null;
|
|
26
|
+
const value = String(id);
|
|
27
|
+
const copy = () => {
|
|
28
|
+
navigator.clipboard?.writeText(value);
|
|
29
|
+
setCopied(true);
|
|
30
|
+
clearTimeout(timer.current);
|
|
31
|
+
timer.current = setTimeout(() => setCopied(false), 1200);
|
|
32
|
+
};
|
|
33
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
34
|
+
style: {
|
|
35
|
+
alignItems: "center",
|
|
36
|
+
color: "var(--theme-elevation-500)",
|
|
37
|
+
display: "flex",
|
|
38
|
+
fontSize: "0.8125rem",
|
|
39
|
+
gap: "6px"
|
|
40
|
+
},
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx("span", {
|
|
43
|
+
style: {
|
|
44
|
+
fontSize: "0.6875rem",
|
|
45
|
+
letterSpacing: "0.04em",
|
|
46
|
+
textTransform: "uppercase"
|
|
47
|
+
},
|
|
48
|
+
children: t(keys.fieldId)
|
|
49
|
+
}),
|
|
50
|
+
/* @__PURE__ */ jsx("code", {
|
|
51
|
+
style: { fontFamily: "var(--font-mono, monospace)" },
|
|
52
|
+
children: value
|
|
53
|
+
}),
|
|
54
|
+
/* @__PURE__ */ jsx("button", {
|
|
55
|
+
onClick: copy,
|
|
56
|
+
style: {
|
|
57
|
+
background: "none",
|
|
58
|
+
border: "none",
|
|
59
|
+
color: "var(--theme-elevation-400)",
|
|
60
|
+
cursor: "pointer",
|
|
61
|
+
font: "inherit",
|
|
62
|
+
padding: 0,
|
|
63
|
+
textDecoration: "underline"
|
|
64
|
+
},
|
|
65
|
+
type: "button",
|
|
66
|
+
children: copied ? t(keys.copied) : t(keys.copy)
|
|
67
|
+
})
|
|
68
|
+
]
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/jobs/extractErrorMessage.ts
|
|
73
|
+
/**
|
|
74
|
+
* Pull a short, human-readable reason out of a job's `error` value: a string as
|
|
75
|
+
* is, an object's `message` or `name`, or `"Cancelled"` for a cancellation.
|
|
76
|
+
* Returns `undefined` when there is nothing meaningful to show.
|
|
77
|
+
*/
|
|
78
|
+
const extractErrorMessage = (error) => {
|
|
79
|
+
if (typeof error === "string") return error || void 0;
|
|
80
|
+
if (typeof error === "object" && error !== null) {
|
|
81
|
+
const value = error;
|
|
82
|
+
if (value.cancelled === true) return "Cancelled";
|
|
83
|
+
if (typeof value.message === "string" && value.message) return value.message;
|
|
84
|
+
if (typeof value.name === "string" && value.name) return value.name;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/jobs/JobErrorPanel.tsx
|
|
89
|
+
const isCancelled = (value) => typeof value === "object" && value !== null && value.cancelled === true;
|
|
90
|
+
/**
|
|
91
|
+
* Field component for a job's `error`: renders the reason as a read-only panel
|
|
92
|
+
* instead of raw JSON. Cancellations read neutral; real failures read as errors.
|
|
93
|
+
*/
|
|
94
|
+
const JobErrorPanel = ({ path }) => {
|
|
95
|
+
const { value } = useField({ path });
|
|
96
|
+
const { t } = useTranslation$1();
|
|
97
|
+
const message = extractErrorMessage(value);
|
|
98
|
+
if (!message) return null;
|
|
99
|
+
const cancelled = isCancelled(value);
|
|
100
|
+
const accent = cancelled ? "var(--theme-elevation-400)" : "var(--theme-error-500)";
|
|
101
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
102
|
+
style: { marginBottom: "var(--base)" },
|
|
103
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
104
|
+
style: {
|
|
105
|
+
color: "var(--theme-elevation-500)",
|
|
106
|
+
fontSize: "0.6875rem",
|
|
107
|
+
letterSpacing: "0.04em",
|
|
108
|
+
marginBottom: "4px",
|
|
109
|
+
textTransform: "uppercase"
|
|
110
|
+
},
|
|
111
|
+
children: cancelled ? t(keys.outcome) : t(keys.fieldError)
|
|
112
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
113
|
+
style: {
|
|
114
|
+
backgroundColor: "var(--theme-elevation-50)",
|
|
115
|
+
borderLeft: `3px solid ${accent}`,
|
|
116
|
+
borderRadius: "0 4px 4px 0",
|
|
117
|
+
color: "var(--theme-elevation-800)",
|
|
118
|
+
padding: "calc(var(--base) / 1.5)",
|
|
119
|
+
wordBreak: "break-word"
|
|
120
|
+
},
|
|
121
|
+
children: message
|
|
122
|
+
})]
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/jobs/formatDuration.ts
|
|
127
|
+
/** Human duration between two ISO timestamps; undefined when zero, unknown, or invalid. */
|
|
128
|
+
const formatDuration = (start, end) => {
|
|
129
|
+
if (!start || !end) return;
|
|
130
|
+
const ms = new Date(end).getTime() - new Date(start).getTime();
|
|
131
|
+
if (!Number.isFinite(ms) || ms <= 0) return;
|
|
132
|
+
return ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(ms < 1e4 ? 1 : 0)}s`;
|
|
133
|
+
};
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/jobs/formatRelativeTime.ts
|
|
136
|
+
const DIVISIONS = [
|
|
137
|
+
{
|
|
138
|
+
amount: 60,
|
|
139
|
+
unit: "second"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
amount: 60,
|
|
143
|
+
unit: "minute"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
amount: 24,
|
|
147
|
+
unit: "hour"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
amount: 7,
|
|
151
|
+
unit: "day"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
amount: 4.34524,
|
|
155
|
+
unit: "week"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
amount: 12,
|
|
159
|
+
unit: "month"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
amount: Number.POSITIVE_INFINITY,
|
|
163
|
+
unit: "year"
|
|
164
|
+
}
|
|
165
|
+
];
|
|
166
|
+
const toMillis = (value) => value instanceof Date ? value.getTime() : new Date(value).getTime();
|
|
167
|
+
/**
|
|
168
|
+
* Format a timestamp relative to `now`, for example `2 minutes ago` or
|
|
169
|
+
* `in 1 hour`. Returns an empty string for missing or unparseable input. Pure,
|
|
170
|
+
* so it can be unit-tested and reused outside the cell.
|
|
171
|
+
*/
|
|
172
|
+
const formatRelativeTime = (value, now = Date.now()) => {
|
|
173
|
+
if (value === null || value === void 0) return "";
|
|
174
|
+
const time = toMillis(value);
|
|
175
|
+
if (Number.isNaN(time)) return "";
|
|
176
|
+
const formatter = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
|
177
|
+
let duration = (time - now) / 1e3;
|
|
178
|
+
for (const division of DIVISIONS) {
|
|
179
|
+
if (Math.abs(duration) < division.amount) return formatter.format(Math.round(duration), division.unit);
|
|
180
|
+
duration /= division.amount;
|
|
181
|
+
}
|
|
182
|
+
return formatter.format(Math.round(duration), "year");
|
|
183
|
+
};
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/jobs/JobLogTimeline.tsx
|
|
186
|
+
const DEFAULT_DATE_FORMAT$2 = "MMMM do yyyy, h:mm a";
|
|
187
|
+
const hasKeys = (value) => typeof value === "object" && value !== null && Object.keys(value).length > 0;
|
|
188
|
+
const Chevron = ({ open }) => /* @__PURE__ */ jsx("svg", {
|
|
189
|
+
"aria-hidden": "true",
|
|
190
|
+
height: "10",
|
|
191
|
+
style: {
|
|
192
|
+
flexShrink: 0,
|
|
193
|
+
transform: open ? "rotate(90deg)" : "none",
|
|
194
|
+
transition: "transform 0.15s"
|
|
195
|
+
},
|
|
196
|
+
viewBox: "0 0 10 10",
|
|
197
|
+
width: "10",
|
|
198
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
199
|
+
d: "M3 1 L7 5 L3 9",
|
|
200
|
+
fill: "none",
|
|
201
|
+
stroke: "currentColor",
|
|
202
|
+
strokeWidth: "1.5"
|
|
203
|
+
})
|
|
204
|
+
});
|
|
205
|
+
const Detail = ({ label, value }) => /* @__PURE__ */ jsxs("div", {
|
|
206
|
+
style: {
|
|
207
|
+
display: "flex",
|
|
208
|
+
flexDirection: "column",
|
|
209
|
+
gap: "2px"
|
|
210
|
+
},
|
|
211
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
212
|
+
style: {
|
|
213
|
+
color: "var(--theme-elevation-500)",
|
|
214
|
+
fontSize: "0.6875rem",
|
|
215
|
+
textTransform: "uppercase"
|
|
216
|
+
},
|
|
217
|
+
children: label
|
|
218
|
+
}), /* @__PURE__ */ jsx("span", { children: value })]
|
|
219
|
+
});
|
|
220
|
+
const JsonBlock = ({ label, value }) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
221
|
+
style: {
|
|
222
|
+
color: "var(--theme-elevation-500)",
|
|
223
|
+
fontSize: "0.6875rem",
|
|
224
|
+
marginBottom: "2px",
|
|
225
|
+
textTransform: "uppercase"
|
|
226
|
+
},
|
|
227
|
+
children: label
|
|
228
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
229
|
+
style: {
|
|
230
|
+
backgroundColor: "var(--theme-elevation-100)",
|
|
231
|
+
borderRadius: "4px",
|
|
232
|
+
fontSize: "0.75rem",
|
|
233
|
+
margin: 0,
|
|
234
|
+
maxHeight: "220px",
|
|
235
|
+
overflow: "auto",
|
|
236
|
+
padding: "calc(var(--base) / 2)",
|
|
237
|
+
whiteSpace: "pre-wrap",
|
|
238
|
+
wordBreak: "break-word"
|
|
239
|
+
},
|
|
240
|
+
children: JSON.stringify(value, null, 2)
|
|
241
|
+
})] });
|
|
242
|
+
const LogRow = ({ entry, index, pattern }) => {
|
|
243
|
+
const { i18n, t } = useTranslation$1();
|
|
244
|
+
const [open, setOpen] = useState(false);
|
|
245
|
+
const failed = entry.state === "failed";
|
|
246
|
+
const reason = failed ? extractErrorMessage(entry.error) : void 0;
|
|
247
|
+
const duration = formatDuration(entry.executedAt, entry.completedAt);
|
|
248
|
+
const relative = formatRelativeTime(entry.executedAt ?? null);
|
|
249
|
+
const absolute = (date) => date ? formatDate({
|
|
250
|
+
date,
|
|
251
|
+
i18n,
|
|
252
|
+
pattern
|
|
253
|
+
}) : "—";
|
|
254
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
255
|
+
style: {
|
|
256
|
+
backgroundColor: "var(--theme-elevation-50)",
|
|
257
|
+
borderLeft: `3px solid ${failed ? "var(--theme-error-500)" : "var(--theme-success-500)"}`,
|
|
258
|
+
borderRadius: "0 4px 4px 0"
|
|
259
|
+
},
|
|
260
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
261
|
+
"aria-expanded": open,
|
|
262
|
+
onClick: () => setOpen((prev) => !prev),
|
|
263
|
+
style: {
|
|
264
|
+
alignItems: "center",
|
|
265
|
+
background: "none",
|
|
266
|
+
border: "none",
|
|
267
|
+
color: "inherit",
|
|
268
|
+
cursor: "pointer",
|
|
269
|
+
display: "flex",
|
|
270
|
+
flexWrap: "wrap",
|
|
271
|
+
font: "inherit",
|
|
272
|
+
gap: "calc(var(--base) / 2)",
|
|
273
|
+
padding: "calc(var(--base) / 2)",
|
|
274
|
+
textAlign: "left",
|
|
275
|
+
width: "100%"
|
|
276
|
+
},
|
|
277
|
+
type: "button",
|
|
278
|
+
children: [
|
|
279
|
+
/* @__PURE__ */ jsx(Chevron, { open }),
|
|
280
|
+
/* @__PURE__ */ jsxs("span", {
|
|
281
|
+
style: { color: "var(--theme-elevation-400)" },
|
|
282
|
+
children: ["#", index + 1]
|
|
283
|
+
}),
|
|
284
|
+
/* @__PURE__ */ jsx(Pill, {
|
|
285
|
+
pillStyle: failed ? "error" : "success",
|
|
286
|
+
size: "small",
|
|
287
|
+
children: failed ? t(keys.statusFailed) : t(keys.statusSucceeded)
|
|
288
|
+
}),
|
|
289
|
+
/* @__PURE__ */ jsx("strong", { children: entry.taskSlug || t(keys.fieldTask) }),
|
|
290
|
+
relative ? /* @__PURE__ */ jsx("span", {
|
|
291
|
+
style: { color: "var(--theme-elevation-500)" },
|
|
292
|
+
children: relative
|
|
293
|
+
}) : null,
|
|
294
|
+
duration ? /* @__PURE__ */ jsx("span", {
|
|
295
|
+
style: { color: "var(--theme-elevation-500)" },
|
|
296
|
+
children: duration
|
|
297
|
+
}) : null,
|
|
298
|
+
reason ? /* @__PURE__ */ jsx("span", {
|
|
299
|
+
style: {
|
|
300
|
+
color: "var(--theme-elevation-500)",
|
|
301
|
+
maxWidth: "32ch",
|
|
302
|
+
overflow: "hidden",
|
|
303
|
+
textOverflow: "ellipsis",
|
|
304
|
+
whiteSpace: "nowrap"
|
|
305
|
+
},
|
|
306
|
+
children: reason
|
|
307
|
+
}) : null
|
|
308
|
+
]
|
|
309
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
310
|
+
style: {
|
|
311
|
+
display: "grid",
|
|
312
|
+
gridTemplateRows: open ? "1fr" : "0fr",
|
|
313
|
+
transition: "grid-template-rows 0.2s ease"
|
|
314
|
+
},
|
|
315
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
316
|
+
style: { overflow: "hidden" },
|
|
317
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
318
|
+
style: {
|
|
319
|
+
display: "flex",
|
|
320
|
+
flexDirection: "column",
|
|
321
|
+
gap: "calc(var(--base) / 2)",
|
|
322
|
+
padding: "0 calc(var(--base) / 2) calc(var(--base) / 2)"
|
|
323
|
+
},
|
|
324
|
+
children: [
|
|
325
|
+
/* @__PURE__ */ jsxs("div", {
|
|
326
|
+
style: {
|
|
327
|
+
columnGap: "var(--base)",
|
|
328
|
+
display: "flex",
|
|
329
|
+
flexWrap: "wrap",
|
|
330
|
+
rowGap: "8px"
|
|
331
|
+
},
|
|
332
|
+
children: [
|
|
333
|
+
/* @__PURE__ */ jsx(Detail, {
|
|
334
|
+
label: t(keys.fieldExecuted),
|
|
335
|
+
value: absolute(entry.executedAt)
|
|
336
|
+
}),
|
|
337
|
+
/* @__PURE__ */ jsx(Detail, {
|
|
338
|
+
label: t(keys.fieldCompleted),
|
|
339
|
+
value: absolute(entry.completedAt)
|
|
340
|
+
}),
|
|
341
|
+
entry.taskID ? /* @__PURE__ */ jsx(Detail, {
|
|
342
|
+
label: t(keys.fieldTaskId),
|
|
343
|
+
value: entry.taskID
|
|
344
|
+
}) : null
|
|
345
|
+
]
|
|
346
|
+
}),
|
|
347
|
+
/* @__PURE__ */ jsx(JsonBlock, {
|
|
348
|
+
label: t(keys.fieldInput),
|
|
349
|
+
value: entry.input ?? {}
|
|
350
|
+
}),
|
|
351
|
+
hasKeys(entry.output) ? /* @__PURE__ */ jsx(JsonBlock, {
|
|
352
|
+
label: t(keys.fieldOutput),
|
|
353
|
+
value: entry.output
|
|
354
|
+
}) : null,
|
|
355
|
+
entry.error != null ? /* @__PURE__ */ jsx(JsonBlock, {
|
|
356
|
+
label: t(keys.fieldError),
|
|
357
|
+
value: entry.error
|
|
358
|
+
}) : null
|
|
359
|
+
]
|
|
360
|
+
})
|
|
361
|
+
})
|
|
362
|
+
})]
|
|
363
|
+
});
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* Field component for a job's `log`: a read-only per-attempt timeline. Each
|
|
367
|
+
* attempt collapses to a one-line summary and expands to reveal its full data
|
|
368
|
+
* (timings, task ID, input, output, error) so nothing is hidden.
|
|
369
|
+
*/
|
|
370
|
+
const JobLogTimeline = ({ path }) => {
|
|
371
|
+
const { getDataByPath } = useForm();
|
|
372
|
+
const { config } = useConfig();
|
|
373
|
+
const { t } = useTranslation$1();
|
|
374
|
+
const pattern = config.admin?.dateFormat ?? DEFAULT_DATE_FORMAT$2;
|
|
375
|
+
const raw = getDataByPath(path);
|
|
376
|
+
const entries = Array.isArray(raw) ? raw : [];
|
|
377
|
+
if (entries.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
378
|
+
style: { color: "var(--theme-elevation-450)" },
|
|
379
|
+
children: t(keys.noAttempts)
|
|
380
|
+
});
|
|
381
|
+
return /* @__PURE__ */ jsx("div", {
|
|
382
|
+
style: {
|
|
383
|
+
display: "flex",
|
|
384
|
+
flexDirection: "column",
|
|
385
|
+
gap: "calc(var(--base) / 2)"
|
|
386
|
+
},
|
|
387
|
+
children: entries.map((entry, index) => /* @__PURE__ */ jsx(LogRow, {
|
|
388
|
+
entry,
|
|
389
|
+
index,
|
|
390
|
+
pattern
|
|
391
|
+
}, entry.id ?? index))
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region src/jobs/JobStatusCell.tsx
|
|
396
|
+
/**
|
|
397
|
+
* List cell that renders a job's derived status as a native Payload Pill. On the
|
|
398
|
+
* linked (first) column it wraps the badge in a link to the document, matching
|
|
399
|
+
* Payload's default cell behavior.
|
|
400
|
+
*/
|
|
401
|
+
const JobStatusCell = ({ collectionSlug, link, linkURL, rowData }) => {
|
|
402
|
+
const { config } = useConfig();
|
|
403
|
+
const { t } = useTranslation$1();
|
|
404
|
+
const { labelKey, pillStyle } = jobStatusMeta[deriveJobStatus(rowData ?? {})];
|
|
405
|
+
const pill = /* @__PURE__ */ jsx(Pill, {
|
|
406
|
+
pillStyle,
|
|
407
|
+
size: "small",
|
|
408
|
+
children: t(labelKey)
|
|
409
|
+
});
|
|
410
|
+
if (link && collectionSlug && rowData?.id != null) {
|
|
411
|
+
const adminRoute = config.routes?.admin ?? "/admin";
|
|
412
|
+
return /* @__PURE__ */ jsx(Link, {
|
|
413
|
+
href: linkURL ?? `${adminRoute}/collections/${collectionSlug}/${encodeURIComponent(String(rowData.id))}`,
|
|
414
|
+
prefetch: false,
|
|
415
|
+
children: pill
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
return pill;
|
|
419
|
+
};
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/jobs/JobStatusHeader.tsx
|
|
422
|
+
const DEFAULT_DATE_FORMAT$1 = "MMMM do yyyy, h:mm a";
|
|
423
|
+
const Fact = ({ label, tooltip, value }) => {
|
|
424
|
+
const [coords, setCoords] = useState(null);
|
|
425
|
+
const [mounted, setMounted] = useState(false);
|
|
426
|
+
useEffect(() => setMounted(true), []);
|
|
427
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
428
|
+
style: {
|
|
429
|
+
display: "flex",
|
|
430
|
+
flexDirection: "column",
|
|
431
|
+
gap: "2px"
|
|
432
|
+
},
|
|
433
|
+
children: [
|
|
434
|
+
/* @__PURE__ */ jsx("span", {
|
|
435
|
+
style: {
|
|
436
|
+
color: "var(--theme-elevation-500)",
|
|
437
|
+
fontSize: "0.6875rem",
|
|
438
|
+
letterSpacing: "0.04em",
|
|
439
|
+
textTransform: "uppercase"
|
|
440
|
+
},
|
|
441
|
+
children: label
|
|
442
|
+
}),
|
|
443
|
+
/* @__PURE__ */ jsx("span", {
|
|
444
|
+
onMouseEnter: (event) => {
|
|
445
|
+
if (!tooltip) return;
|
|
446
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
447
|
+
setCoords({
|
|
448
|
+
left: rect.left,
|
|
449
|
+
top: rect.bottom
|
|
450
|
+
});
|
|
451
|
+
},
|
|
452
|
+
onMouseLeave: () => setCoords(null),
|
|
453
|
+
style: {
|
|
454
|
+
cursor: tooltip ? "help" : void 0,
|
|
455
|
+
fontWeight: 600,
|
|
456
|
+
width: "fit-content"
|
|
457
|
+
},
|
|
458
|
+
children: value
|
|
459
|
+
}),
|
|
460
|
+
mounted && tooltip && coords ? createPortal(/* @__PURE__ */ jsx("span", {
|
|
461
|
+
style: {
|
|
462
|
+
backgroundColor: "var(--theme-elevation-900)",
|
|
463
|
+
borderRadius: "4px",
|
|
464
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.25)",
|
|
465
|
+
color: "var(--theme-elevation-0)",
|
|
466
|
+
fontSize: "0.75rem",
|
|
467
|
+
left: `${coords.left}px`,
|
|
468
|
+
padding: "4px 8px",
|
|
469
|
+
pointerEvents: "none",
|
|
470
|
+
position: "fixed",
|
|
471
|
+
top: `${coords.top + 6}px`,
|
|
472
|
+
whiteSpace: "nowrap",
|
|
473
|
+
zIndex: 9999
|
|
474
|
+
},
|
|
475
|
+
children: tooltip
|
|
476
|
+
}), document.body) : null
|
|
477
|
+
]
|
|
478
|
+
});
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* Document header for a job: the derived status as a Pill plus the key facts,
|
|
482
|
+
* read from the form so it stays in sync. Rendered at the top of the edit view.
|
|
483
|
+
*/
|
|
484
|
+
const JobStatusHeader = () => {
|
|
485
|
+
const fields = useFormFields(([formFields]) => formFields);
|
|
486
|
+
const { config } = useConfig();
|
|
487
|
+
const { i18n, t } = useTranslation$1();
|
|
488
|
+
const get = (name) => fields?.[name]?.value;
|
|
489
|
+
const meta = jobStatusMeta[deriveJobStatus({
|
|
490
|
+
completedAt: get("completedAt"),
|
|
491
|
+
error: get("error"),
|
|
492
|
+
hasError: get("hasError"),
|
|
493
|
+
processing: get("processing"),
|
|
494
|
+
totalTried: get("totalTried"),
|
|
495
|
+
waitUntil: get("waitUntil")
|
|
496
|
+
})];
|
|
497
|
+
const workflowSlug = get("workflowSlug");
|
|
498
|
+
const taskSlug = get("taskSlug");
|
|
499
|
+
const runsLabel = workflowSlug ? t(keys.fieldWorkflow) : taskSlug ? t(keys.fieldTask) : t(keys.fieldJob);
|
|
500
|
+
const runsValue = String(workflowSlug || taskSlug || "—");
|
|
501
|
+
const pattern = config.admin?.dateFormat ?? DEFAULT_DATE_FORMAT$1;
|
|
502
|
+
const createdAt = get("createdAt");
|
|
503
|
+
const completedAt = get("completedAt");
|
|
504
|
+
const absolute = (date) => date ? formatDate({
|
|
505
|
+
date,
|
|
506
|
+
i18n,
|
|
507
|
+
pattern
|
|
508
|
+
}) : void 0;
|
|
509
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
510
|
+
style: {
|
|
511
|
+
alignItems: "flex-end",
|
|
512
|
+
borderBottom: "1px solid var(--theme-elevation-100)",
|
|
513
|
+
columnGap: "calc(var(--base) * 1.5)",
|
|
514
|
+
display: "flex",
|
|
515
|
+
flexWrap: "wrap",
|
|
516
|
+
marginBottom: "var(--base)",
|
|
517
|
+
paddingBottom: "var(--base)",
|
|
518
|
+
rowGap: "var(--base)"
|
|
519
|
+
},
|
|
520
|
+
children: [
|
|
521
|
+
/* @__PURE__ */ jsx(Pill, {
|
|
522
|
+
pillStyle: meta.pillStyle,
|
|
523
|
+
size: "medium",
|
|
524
|
+
children: t(meta.labelKey)
|
|
525
|
+
}),
|
|
526
|
+
/* @__PURE__ */ jsx(Fact, {
|
|
527
|
+
label: runsLabel,
|
|
528
|
+
value: runsValue
|
|
529
|
+
}),
|
|
530
|
+
/* @__PURE__ */ jsx(Fact, {
|
|
531
|
+
label: t(keys.fieldQueue),
|
|
532
|
+
value: String(get("queue") || "default")
|
|
533
|
+
}),
|
|
534
|
+
/* @__PURE__ */ jsx(Fact, {
|
|
535
|
+
label: t(keys.fieldAttempts),
|
|
536
|
+
value: String(get("totalTried") ?? 0)
|
|
537
|
+
}),
|
|
538
|
+
/* @__PURE__ */ jsx(Fact, {
|
|
539
|
+
label: t(keys.fieldCreated),
|
|
540
|
+
tooltip: absolute(createdAt),
|
|
541
|
+
value: formatRelativeTime(createdAt) || "—"
|
|
542
|
+
}),
|
|
543
|
+
/* @__PURE__ */ jsx(Fact, {
|
|
544
|
+
label: t(keys.fieldCompleted),
|
|
545
|
+
tooltip: absolute(completedAt),
|
|
546
|
+
value: completedAt ? formatRelativeTime(completedAt) : "—"
|
|
547
|
+
})
|
|
548
|
+
]
|
|
549
|
+
});
|
|
550
|
+
};
|
|
551
|
+
//#endregion
|
|
552
|
+
//#region src/jobs/RelativeTimeCell.tsx
|
|
553
|
+
const DEFAULT_DATE_FORMAT = "MMMM do yyyy, h:mm a";
|
|
554
|
+
/** List cell for date fields: relative time, with the admin-formatted timestamp on hover. */
|
|
555
|
+
const RelativeTimeCell = ({ cellData }) => {
|
|
556
|
+
const { config } = useConfig();
|
|
557
|
+
const { i18n } = useTranslation$1();
|
|
558
|
+
if (cellData === null || cellData === void 0) return null;
|
|
559
|
+
const relative = formatRelativeTime(cellData);
|
|
560
|
+
if (!relative) return null;
|
|
561
|
+
return /* @__PURE__ */ jsx("span", {
|
|
562
|
+
title: formatDate({
|
|
563
|
+
date: cellData,
|
|
564
|
+
i18n,
|
|
565
|
+
pattern: config.admin?.dateFormat ?? DEFAULT_DATE_FORMAT
|
|
566
|
+
}),
|
|
567
|
+
children: relative
|
|
568
|
+
});
|
|
569
|
+
};
|
|
570
|
+
//#endregion
|
|
571
|
+
export { JobDocDescription, JobErrorPanel, JobLogTimeline, JobStatusCell, JobStatusHeader, RelativeTimeCell };
|
|
572
|
+
|
|
573
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["useTranslation","usePayloadTranslation","useTranslation","useTranslation","DEFAULT_DATE_FORMAT","useTranslation","useTranslation","DEFAULT_DATE_FORMAT","useTranslation","useTranslation"],"sources":["../../src/translations/useTranslation.ts","../../src/jobs/JobDocDescription.tsx","../../src/jobs/extractErrorMessage.ts","../../src/jobs/JobErrorPanel.tsx","../../src/jobs/formatDuration.ts","../../src/jobs/formatRelativeTime.ts","../../src/jobs/JobLogTimeline.tsx","../../src/jobs/JobStatusCell.tsx","../../src/jobs/JobStatusHeader.tsx","../../src/jobs/RelativeTimeCell.tsx"],"sourcesContent":["'use client'\n\nimport { useTranslation as usePayloadTranslation } from '@payloadcms/ui'\n\nimport type { TranslationKey } from './keys'\n\n/**\n * `useTranslation` bound to this plugin's keys, so `t(keys.X)` typechecks without\n * a per-call `@ts-expect-error`. Returns Payload's `{ t, i18n }` unchanged.\n */\nexport const useTranslation = () => usePayloadTranslation<Record<string, never>, TranslationKey>()\n","'use client'\n\nimport { useDocumentInfo } from '@payloadcms/ui'\nimport { useEffect, useRef, useState } from 'react'\n\nimport { keys } from '../translations/keys'\nimport { useTranslation } from '../translations/useTranslation'\n\n/** Document-header description for a job: the record ID, kept accessible and copyable. */\nexport const JobDocDescription = () => {\n\tconst { id } = useDocumentInfo()\n\tconst { t } = useTranslation()\n\tconst [copied, setCopied] = useState(false)\n\tconst timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n\tuseEffect(() => () => clearTimeout(timer.current), [])\n\n\tif (id == null) {\n\t\treturn null\n\t}\n\tconst value = String(id)\n\tconst copy = () => {\n\t\tvoid navigator.clipboard?.writeText(value)\n\t\tsetCopied(true)\n\t\tclearTimeout(timer.current)\n\t\ttimer.current = setTimeout(() => setCopied(false), 1200)\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\talignItems: 'center',\n\t\t\t\tcolor: 'var(--theme-elevation-500)',\n\t\t\t\tdisplay: 'flex',\n\t\t\t\tfontSize: '0.8125rem',\n\t\t\t\tgap: '6px',\n\t\t\t}}\n\t\t>\n\t\t\t<span style={{ fontSize: '0.6875rem', letterSpacing: '0.04em', textTransform: 'uppercase' }}>\n\t\t\t\t{t(keys.fieldId)}\n\t\t\t</span>\n\t\t\t<code style={{ fontFamily: 'var(--font-mono, monospace)' }}>{value}</code>\n\t\t\t<button\n\t\t\t\tonClick={copy}\n\t\t\t\tstyle={{\n\t\t\t\t\tbackground: 'none',\n\t\t\t\t\tborder: 'none',\n\t\t\t\t\tcolor: 'var(--theme-elevation-400)',\n\t\t\t\t\tcursor: 'pointer',\n\t\t\t\t\tfont: 'inherit',\n\t\t\t\t\tpadding: 0,\n\t\t\t\t\ttextDecoration: 'underline',\n\t\t\t\t}}\n\t\t\t\ttype=\"button\"\n\t\t\t>\n\t\t\t\t{copied ? t(keys.copied) : t(keys.copy)}\n\t\t\t</button>\n\t\t</div>\n\t)\n}\n","/**\n * Pull a short, human-readable reason out of a job's `error` value: a string as\n * is, an object's `message` or `name`, or `\"Cancelled\"` for a cancellation.\n * Returns `undefined` when there is nothing meaningful to show.\n */\nexport const extractErrorMessage = (error: unknown): string | undefined => {\n\tif (typeof error === 'string') {\n\t\treturn error || undefined\n\t}\n\tif (typeof error === 'object' && error !== null) {\n\t\tconst value = error as { cancelled?: unknown; message?: unknown; name?: unknown }\n\t\tif (value.cancelled === true) {\n\t\t\treturn 'Cancelled'\n\t\t}\n\t\tif (typeof value.message === 'string' && value.message) {\n\t\t\treturn value.message\n\t\t}\n\t\tif (typeof value.name === 'string' && value.name) {\n\t\t\treturn value.name\n\t\t}\n\t}\n\treturn undefined\n}\n","'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport type { JSONFieldClientComponent } from 'payload'\n\nimport { keys } from '../translations/keys'\nimport { useTranslation } from '../translations/useTranslation'\nimport { extractErrorMessage } from './extractErrorMessage'\n\nconst isCancelled = (value: unknown): boolean =>\n\ttypeof value === 'object' &&\n\tvalue !== null &&\n\t(value as { cancelled?: unknown }).cancelled === true\n\n/**\n * Field component for a job's `error`: renders the reason as a read-only panel\n * instead of raw JSON. Cancellations read neutral; real failures read as errors.\n */\nexport const JobErrorPanel: JSONFieldClientComponent = ({ path }) => {\n\tconst { value } = useField<unknown>({ path })\n\tconst { t } = useTranslation()\n\tconst message = extractErrorMessage(value)\n\tif (!message) {\n\t\treturn null\n\t}\n\n\tconst cancelled = isCancelled(value)\n\tconst accent = cancelled ? 'var(--theme-elevation-400)' : 'var(--theme-error-500)'\n\n\treturn (\n\t\t<div style={{ marginBottom: 'var(--base)' }}>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tcolor: 'var(--theme-elevation-500)',\n\t\t\t\t\tfontSize: '0.6875rem',\n\t\t\t\t\tletterSpacing: '0.04em',\n\t\t\t\t\tmarginBottom: '4px',\n\t\t\t\t\ttextTransform: 'uppercase',\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{cancelled ? t(keys.outcome) : t(keys.fieldError)}\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tbackgroundColor: 'var(--theme-elevation-50)',\n\t\t\t\t\tborderLeft: `3px solid ${accent}`,\n\t\t\t\t\tborderRadius: '0 4px 4px 0',\n\t\t\t\t\tcolor: 'var(--theme-elevation-800)',\n\t\t\t\t\tpadding: 'calc(var(--base) / 1.5)',\n\t\t\t\t\twordBreak: 'break-word',\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{message}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n","/** Human duration between two ISO timestamps; undefined when zero, unknown, or invalid. */\nexport const formatDuration = (start?: string, end?: string): string | undefined => {\n\tif (!start || !end) {\n\t\treturn undefined\n\t}\n\tconst ms = new Date(end).getTime() - new Date(start).getTime()\n\tif (!Number.isFinite(ms) || ms <= 0) {\n\t\treturn undefined\n\t}\n\treturn ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(ms < 10_000 ? 1 : 0)}s`\n}\n","const DIVISIONS: { amount: number; unit: Intl.RelativeTimeFormatUnit }[] = [\n\t{ amount: 60, unit: 'second' },\n\t{ amount: 60, unit: 'minute' },\n\t{ amount: 24, unit: 'hour' },\n\t{ amount: 7, unit: 'day' },\n\t{ amount: 4.34524, unit: 'week' },\n\t{ amount: 12, unit: 'month' },\n\t{ amount: Number.POSITIVE_INFINITY, unit: 'year' },\n]\n\nconst toMillis = (value: Date | number | string): number =>\n\tvalue instanceof Date ? value.getTime() : new Date(value).getTime()\n\n/**\n * Format a timestamp relative to `now`, for example `2 minutes ago` or\n * `in 1 hour`. Returns an empty string for missing or unparseable input. Pure,\n * so it can be unit-tested and reused outside the cell.\n */\nexport const formatRelativeTime = (\n\tvalue: Date | null | number | string | undefined,\n\tnow: number = Date.now()\n): string => {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tconst time = toMillis(value)\n\tif (Number.isNaN(time)) {\n\t\treturn ''\n\t}\n\n\tconst formatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })\n\tlet duration = (time - now) / 1000\n\n\tfor (const division of DIVISIONS) {\n\t\tif (Math.abs(duration) < division.amount) {\n\t\t\treturn formatter.format(Math.round(duration), division.unit)\n\t\t}\n\t\tduration /= division.amount\n\t}\n\treturn formatter.format(Math.round(duration), 'year')\n}\n","'use client'\n\nimport { Pill, useConfig, useForm } from '@payloadcms/ui'\nimport { formatDate } from '@payloadcms/ui/shared'\nimport type { ArrayFieldClientComponent } from 'payload'\nimport { useState } from 'react'\n\nimport { keys } from '../translations/keys'\nimport { useTranslation } from '../translations/useTranslation'\nimport { extractErrorMessage } from './extractErrorMessage'\nimport { formatDuration } from './formatDuration'\nimport { formatRelativeTime } from './formatRelativeTime'\n\nconst DEFAULT_DATE_FORMAT = 'MMMM do yyyy, h:mm a'\n\ninterface LogEntry {\n\tcompletedAt?: string\n\terror?: unknown\n\texecutedAt?: string\n\tid?: string\n\tinput?: unknown\n\toutput?: unknown\n\tstate?: 'failed' | 'succeeded'\n\ttaskID?: string\n\ttaskSlug?: string\n}\n\nconst hasKeys = (value: unknown): boolean =>\n\ttypeof value === 'object' && value !== null && Object.keys(value).length > 0\n\nconst Chevron = ({ open }: { open: boolean }) => (\n\t<svg\n\t\taria-hidden=\"true\"\n\t\theight=\"10\"\n\t\tstyle={{\n\t\t\tflexShrink: 0,\n\t\t\ttransform: open ? 'rotate(90deg)' : 'none',\n\t\t\ttransition: 'transform 0.15s',\n\t\t}}\n\t\tviewBox=\"0 0 10 10\"\n\t\twidth=\"10\"\n\t>\n\t\t<path d=\"M3 1 L7 5 L3 9\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n\t</svg>\n)\n\nconst Detail = ({ label, value }: { label: string; value: string }) => (\n\t<div style={{ display: 'flex', flexDirection: 'column', gap: '2px' }}>\n\t\t<span\n\t\t\tstyle={{\n\t\t\t\tcolor: 'var(--theme-elevation-500)',\n\t\t\t\tfontSize: '0.6875rem',\n\t\t\t\ttextTransform: 'uppercase',\n\t\t\t}}\n\t\t>\n\t\t\t{label}\n\t\t</span>\n\t\t<span>{value}</span>\n\t</div>\n)\n\nconst JsonBlock = ({ label, value }: { label: string; value: unknown }) => (\n\t<div>\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tcolor: 'var(--theme-elevation-500)',\n\t\t\t\tfontSize: '0.6875rem',\n\t\t\t\tmarginBottom: '2px',\n\t\t\t\ttextTransform: 'uppercase',\n\t\t\t}}\n\t\t>\n\t\t\t{label}\n\t\t</div>\n\t\t<pre\n\t\t\tstyle={{\n\t\t\t\tbackgroundColor: 'var(--theme-elevation-100)',\n\t\t\t\tborderRadius: '4px',\n\t\t\t\tfontSize: '0.75rem',\n\t\t\t\tmargin: 0,\n\t\t\t\tmaxHeight: '220px',\n\t\t\t\toverflow: 'auto',\n\t\t\t\tpadding: 'calc(var(--base) / 2)',\n\t\t\t\twhiteSpace: 'pre-wrap',\n\t\t\t\twordBreak: 'break-word',\n\t\t\t}}\n\t\t>\n\t\t\t{JSON.stringify(value, null, 2)}\n\t\t</pre>\n\t</div>\n)\n\nconst LogRow = ({ entry, index, pattern }: { entry: LogEntry; index: number; pattern: string }) => {\n\tconst { i18n, t } = useTranslation()\n\tconst [open, setOpen] = useState(false)\n\tconst failed = entry.state === 'failed'\n\tconst reason = failed ? extractErrorMessage(entry.error) : undefined\n\tconst duration = formatDuration(entry.executedAt, entry.completedAt)\n\tconst relative = formatRelativeTime(entry.executedAt ?? null)\n\tconst absolute = (date?: string): string => (date ? formatDate({ date, i18n, pattern }) : '—')\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tbackgroundColor: 'var(--theme-elevation-50)',\n\t\t\t\tborderLeft: `3px solid ${failed ? 'var(--theme-error-500)' : 'var(--theme-success-500)'}`,\n\t\t\t\tborderRadius: '0 4px 4px 0',\n\t\t\t}}\n\t\t>\n\t\t\t<button\n\t\t\t\taria-expanded={open}\n\t\t\t\tonClick={() => setOpen((prev) => !prev)}\n\t\t\t\tstyle={{\n\t\t\t\t\talignItems: 'center',\n\t\t\t\t\tbackground: 'none',\n\t\t\t\t\tborder: 'none',\n\t\t\t\t\tcolor: 'inherit',\n\t\t\t\t\tcursor: 'pointer',\n\t\t\t\t\tdisplay: 'flex',\n\t\t\t\t\tflexWrap: 'wrap',\n\t\t\t\t\tfont: 'inherit',\n\t\t\t\t\tgap: 'calc(var(--base) / 2)',\n\t\t\t\t\tpadding: 'calc(var(--base) / 2)',\n\t\t\t\t\ttextAlign: 'left',\n\t\t\t\t\twidth: '100%',\n\t\t\t\t}}\n\t\t\t\ttype=\"button\"\n\t\t\t>\n\t\t\t\t<Chevron open={open} />\n\t\t\t\t<span style={{ color: 'var(--theme-elevation-400)' }}>#{index + 1}</span>\n\t\t\t\t<Pill pillStyle={failed ? 'error' : 'success'} size=\"small\">\n\t\t\t\t\t{failed ? t(keys.statusFailed) : t(keys.statusSucceeded)}\n\t\t\t\t</Pill>\n\t\t\t\t<strong>{entry.taskSlug || t(keys.fieldTask)}</strong>\n\t\t\t\t{relative ? <span style={{ color: 'var(--theme-elevation-500)' }}>{relative}</span> : null}\n\t\t\t\t{duration ? <span style={{ color: 'var(--theme-elevation-500)' }}>{duration}</span> : null}\n\t\t\t\t{reason ? (\n\t\t\t\t\t<span\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tcolor: 'var(--theme-elevation-500)',\n\t\t\t\t\t\t\tmaxWidth: '32ch',\n\t\t\t\t\t\t\toverflow: 'hidden',\n\t\t\t\t\t\t\ttextOverflow: 'ellipsis',\n\t\t\t\t\t\t\twhiteSpace: 'nowrap',\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{reason}\n\t\t\t\t\t</span>\n\t\t\t\t) : null}\n\t\t\t</button>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tdisplay: 'grid',\n\t\t\t\t\tgridTemplateRows: open ? '1fr' : '0fr',\n\t\t\t\t\ttransition: 'grid-template-rows 0.2s ease',\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div style={{ overflow: 'hidden' }}>\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tdisplay: 'flex',\n\t\t\t\t\t\t\tflexDirection: 'column',\n\t\t\t\t\t\t\tgap: 'calc(var(--base) / 2)',\n\t\t\t\t\t\t\tpadding: '0 calc(var(--base) / 2) calc(var(--base) / 2)',\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tstyle={{ columnGap: 'var(--base)', display: 'flex', flexWrap: 'wrap', rowGap: '8px' }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Detail label={t(keys.fieldExecuted)} value={absolute(entry.executedAt)} />\n\t\t\t\t\t\t\t<Detail label={t(keys.fieldCompleted)} value={absolute(entry.completedAt)} />\n\t\t\t\t\t\t\t{entry.taskID ? <Detail label={t(keys.fieldTaskId)} value={entry.taskID} /> : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<JsonBlock label={t(keys.fieldInput)} value={entry.input ?? {}} />\n\t\t\t\t\t\t{hasKeys(entry.output) ? (\n\t\t\t\t\t\t\t<JsonBlock label={t(keys.fieldOutput)} value={entry.output} />\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{entry.error != null ? (\n\t\t\t\t\t\t\t<JsonBlock label={t(keys.fieldError)} value={entry.error} />\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\n/**\n * Field component for a job's `log`: a read-only per-attempt timeline. Each\n * attempt collapses to a one-line summary and expands to reveal its full data\n * (timings, task ID, input, output, error) so nothing is hidden.\n */\nexport const JobLogTimeline: ArrayFieldClientComponent = ({ path }) => {\n\tconst { getDataByPath } = useForm()\n\tconst { config } = useConfig()\n\tconst { t } = useTranslation()\n\tconst pattern = config.admin?.dateFormat ?? DEFAULT_DATE_FORMAT\n\tconst raw = getDataByPath<LogEntry[]>(path)\n\tconst entries = Array.isArray(raw) ? raw : []\n\n\tif (entries.length === 0) {\n\t\treturn <div style={{ color: 'var(--theme-elevation-450)' }}>{t(keys.noAttempts)}</div>\n\t}\n\n\treturn (\n\t\t<div style={{ display: 'flex', flexDirection: 'column', gap: 'calc(var(--base) / 2)' }}>\n\t\t\t{entries.map((entry, index) => (\n\t\t\t\t<LogRow entry={entry} index={index} key={entry.id ?? index} pattern={pattern} />\n\t\t\t))}\n\t\t</div>\n\t)\n}\n","'use client'\n\nimport { Link, Pill, useConfig } from '@payloadcms/ui'\nimport type { DefaultCellComponentProps } from 'payload'\n\nimport { useTranslation } from '../translations/useTranslation'\nimport { deriveJobStatus } from './deriveJobStatus'\nimport { jobStatusMeta } from './jobStatusMeta'\n\n/**\n * List cell that renders a job's derived status as a native Payload Pill. On the\n * linked (first) column it wraps the badge in a link to the document, matching\n * Payload's default cell behavior.\n */\nexport const JobStatusCell = ({\n\tcollectionSlug,\n\tlink,\n\tlinkURL,\n\trowData,\n}: DefaultCellComponentProps) => {\n\tconst { config } = useConfig()\n\tconst { t } = useTranslation()\n\tconst { labelKey, pillStyle } = jobStatusMeta[deriveJobStatus(rowData ?? {})]\n\tconst pill = (\n\t\t<Pill pillStyle={pillStyle} size=\"small\">\n\t\t\t{t(labelKey)}\n\t\t</Pill>\n\t)\n\n\tif (link && collectionSlug && rowData?.id != null) {\n\t\tconst adminRoute = config.routes?.admin ?? '/admin'\n\t\tconst href =\n\t\t\tlinkURL ??\n\t\t\t`${adminRoute}/collections/${collectionSlug}/${encodeURIComponent(String(rowData.id))}`\n\t\treturn (\n\t\t\t<Link href={href} prefetch={false}>\n\t\t\t\t{pill}\n\t\t\t</Link>\n\t\t)\n\t}\n\n\treturn pill\n}\n","'use client'\n\nimport { Pill, useConfig, useFormFields } from '@payloadcms/ui'\nimport { formatDate } from '@payloadcms/ui/shared'\nimport type { UIFieldClientComponent } from 'payload'\nimport { useEffect, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { keys } from '../translations/keys'\nimport { useTranslation } from '../translations/useTranslation'\nimport { deriveJobStatus } from './deriveJobStatus'\nimport { formatRelativeTime } from './formatRelativeTime'\nimport { jobStatusMeta } from './jobStatusMeta'\n\nconst DEFAULT_DATE_FORMAT = 'MMMM do yyyy, h:mm a'\n\nconst Fact = ({ label, tooltip, value }: { label: string; tooltip?: string; value: string }) => {\n\tconst [coords, setCoords] = useState<{ left: number; top: number } | null>(null)\n\tconst [mounted, setMounted] = useState(false)\n\tuseEffect(() => setMounted(true), [])\n\n\treturn (\n\t\t<div style={{ display: 'flex', flexDirection: 'column', gap: '2px' }}>\n\t\t\t<span\n\t\t\t\tstyle={{\n\t\t\t\t\tcolor: 'var(--theme-elevation-500)',\n\t\t\t\t\tfontSize: '0.6875rem',\n\t\t\t\t\tletterSpacing: '0.04em',\n\t\t\t\t\ttextTransform: 'uppercase',\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{label}\n\t\t\t</span>\n\t\t\t{/* biome-ignore lint/a11y/noStaticElementInteractions: supplementary hover tooltip on a read-only text value */}\n\t\t\t<span\n\t\t\t\tonMouseEnter={(event) => {\n\t\t\t\t\tif (!tooltip) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tconst rect = event.currentTarget.getBoundingClientRect()\n\t\t\t\t\tsetCoords({ left: rect.left, top: rect.bottom })\n\t\t\t\t}}\n\t\t\t\tonMouseLeave={() => setCoords(null)}\n\t\t\t\tstyle={{ cursor: tooltip ? 'help' : undefined, fontWeight: 600, width: 'fit-content' }}\n\t\t\t>\n\t\t\t\t{value}\n\t\t\t</span>\n\t\t\t{mounted && tooltip && coords\n\t\t\t\t? createPortal(\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tbackgroundColor: 'var(--theme-elevation-900)',\n\t\t\t\t\t\t\t\tborderRadius: '4px',\n\t\t\t\t\t\t\t\tboxShadow: '0 2px 8px rgba(0, 0, 0, 0.25)',\n\t\t\t\t\t\t\t\tcolor: 'var(--theme-elevation-0)',\n\t\t\t\t\t\t\t\tfontSize: '0.75rem',\n\t\t\t\t\t\t\t\tleft: `${coords.left}px`,\n\t\t\t\t\t\t\t\tpadding: '4px 8px',\n\t\t\t\t\t\t\t\tpointerEvents: 'none',\n\t\t\t\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\t\t\t\ttop: `${coords.top + 6}px`,\n\t\t\t\t\t\t\t\twhiteSpace: 'nowrap',\n\t\t\t\t\t\t\t\tzIndex: 9999,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{tooltip}\n\t\t\t\t\t\t</span>,\n\t\t\t\t\t\tdocument.body\n\t\t\t\t\t)\n\t\t\t\t: null}\n\t\t</div>\n\t)\n}\n\n/**\n * Document header for a job: the derived status as a Pill plus the key facts,\n * read from the form so it stays in sync. Rendered at the top of the edit view.\n */\nexport const JobStatusHeader: UIFieldClientComponent = () => {\n\tconst fields = useFormFields(([formFields]) => formFields)\n\tconst { config } = useConfig()\n\tconst { i18n, t } = useTranslation()\n\tconst get = (name: string): unknown => fields?.[name]?.value\n\n\tconst status = deriveJobStatus({\n\t\tcompletedAt: get('completedAt') as null | string,\n\t\terror: get('error'),\n\t\thasError: get('hasError') as boolean,\n\t\tprocessing: get('processing') as boolean,\n\t\ttotalTried: get('totalTried') as number,\n\t\twaitUntil: get('waitUntil') as null | string,\n\t})\n\tconst meta = jobStatusMeta[status]\n\n\tconst workflowSlug = get('workflowSlug')\n\tconst taskSlug = get('taskSlug')\n\tconst runsLabel = workflowSlug\n\t\t? t(keys.fieldWorkflow)\n\t\t: taskSlug\n\t\t\t? t(keys.fieldTask)\n\t\t\t: t(keys.fieldJob)\n\tconst runsValue = String(workflowSlug || taskSlug || '—')\n\n\tconst pattern = config.admin?.dateFormat ?? DEFAULT_DATE_FORMAT\n\tconst createdAt = get('createdAt') as null | string\n\tconst completedAt = get('completedAt') as null | string\n\tconst absolute = (date: null | string): string | undefined =>\n\t\tdate ? formatDate({ date, i18n, pattern }) : undefined\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\talignItems: 'flex-end',\n\t\t\t\tborderBottom: '1px solid var(--theme-elevation-100)',\n\t\t\t\tcolumnGap: 'calc(var(--base) * 1.5)',\n\t\t\t\tdisplay: 'flex',\n\t\t\t\tflexWrap: 'wrap',\n\t\t\t\tmarginBottom: 'var(--base)',\n\t\t\t\tpaddingBottom: 'var(--base)',\n\t\t\t\trowGap: 'var(--base)',\n\t\t\t}}\n\t\t>\n\t\t\t<Pill pillStyle={meta.pillStyle} size=\"medium\">\n\t\t\t\t{t(meta.labelKey)}\n\t\t\t</Pill>\n\t\t\t<Fact label={runsLabel} value={runsValue} />\n\t\t\t<Fact label={t(keys.fieldQueue)} value={String(get('queue') || 'default')} />\n\t\t\t<Fact label={t(keys.fieldAttempts)} value={String((get('totalTried') as number) ?? 0)} />\n\t\t\t<Fact\n\t\t\t\tlabel={t(keys.fieldCreated)}\n\t\t\t\ttooltip={absolute(createdAt)}\n\t\t\t\tvalue={formatRelativeTime(createdAt) || '—'}\n\t\t\t/>\n\t\t\t<Fact\n\t\t\t\tlabel={t(keys.fieldCompleted)}\n\t\t\t\ttooltip={absolute(completedAt)}\n\t\t\t\tvalue={completedAt ? formatRelativeTime(completedAt) : '—'}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n","'use client'\n\nimport { useConfig } from '@payloadcms/ui'\nimport { formatDate } from '@payloadcms/ui/shared'\nimport type { DefaultCellComponentProps } from 'payload'\n\nimport { useTranslation } from '../translations/useTranslation'\nimport { formatRelativeTime } from './formatRelativeTime'\n\nconst DEFAULT_DATE_FORMAT = 'MMMM do yyyy, h:mm a'\n\n/** List cell for date fields: relative time, with the admin-formatted timestamp on hover. */\nexport const RelativeTimeCell = ({ cellData }: DefaultCellComponentProps) => {\n\tconst { config } = useConfig()\n\tconst { i18n } = useTranslation()\n\n\tif (cellData === null || cellData === undefined) {\n\t\treturn null\n\t}\n\tconst relative = formatRelativeTime(cellData)\n\tif (!relative) {\n\t\treturn null\n\t}\n\tconst exact = formatDate({\n\t\tdate: cellData,\n\t\ti18n,\n\t\tpattern: config.admin?.dateFormat ?? DEFAULT_DATE_FORMAT,\n\t})\n\treturn <span title={exact}>{relative}</span>\n}\n"],"mappings":"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"}
|