@prenta/admin 2.7.0 → 2.8.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 +34 -0
- package/design-system/tokens.css +17 -0
- package/design-system/tokens.json +35 -1
- package/design-system/tokens.scss +15 -0
- package/design-system/tokens.tailwind.cjs +14 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +2 -2
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/lib/batch-wizard.test.d.ts +2 -0
- package/dist/__tests__/lib/batch-wizard.test.d.ts.map +1 -0
- package/dist/__tests__/lib/batch-wizard.test.js +144 -0
- package/dist/__tests__/lib/batch-wizard.test.js.map +1 -0
- package/dist/__tests__/lib/job-drawer.test.js +2 -0
- package/dist/__tests__/lib/job-drawer.test.js.map +1 -1
- package/dist/__tests__/lib/job-view.test.js +100 -3
- package/dist/__tests__/lib/job-view.test.js.map +1 -1
- package/dist/__tests__/lib/jobs-service.test.js +56 -2
- package/dist/__tests__/lib/jobs-service.test.js.map +1 -1
- package/dist/__tests__/views/appearance-chrome.render.test.d.ts +2 -0
- package/dist/__tests__/views/appearance-chrome.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/appearance-chrome.render.test.js +688 -0
- package/dist/__tests__/views/appearance-chrome.render.test.js.map +1 -0
- package/dist/__tests__/views/header-footer-form.test.d.ts +2 -0
- package/dist/__tests__/views/header-footer-form.test.d.ts.map +1 -0
- package/dist/__tests__/views/header-footer-form.test.js +266 -0
- package/dist/__tests__/views/header-footer-form.test.js.map +1 -0
- package/dist/__tests__/views/jobs-drawer.render.test.js +442 -2
- package/dist/__tests__/views/jobs-drawer.render.test.js.map +1 -1
- package/dist/__tests__/views/jobs-list.render.test.js +7 -5
- package/dist/__tests__/views/jobs-list.render.test.js.map +1 -1
- package/dist/__tests__/views/jobs-wizard.render.test.d.ts +2 -0
- package/dist/__tests__/views/jobs-wizard.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/jobs-wizard.render.test.js +588 -0
- package/dist/__tests__/views/jobs-wizard.render.test.js.map +1 -0
- package/dist/lib/api.d.ts +2 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +8 -1
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/batch-wizard.d.ts +84 -0
- package/dist/lib/batch-wizard.d.ts.map +1 -0
- package/dist/lib/batch-wizard.js +122 -0
- package/dist/lib/batch-wizard.js.map +1 -0
- package/dist/lib/chrome-service.d.ts +69 -0
- package/dist/lib/chrome-service.d.ts.map +1 -0
- package/dist/lib/chrome-service.js +108 -0
- package/dist/lib/chrome-service.js.map +1 -0
- package/dist/lib/job-view.d.ts +43 -0
- package/dist/lib/job-view.d.ts.map +1 -1
- package/dist/lib/job-view.js +57 -1
- package/dist/lib/job-view.js.map +1 -1
- package/dist/lib/jobs-service.d.ts +19 -1
- package/dist/lib/jobs-service.d.ts.map +1 -1
- package/dist/lib/jobs-service.js +41 -0
- package/dist/lib/jobs-service.js.map +1 -1
- package/dist/prenta-admin.css +1 -1
- package/dist/views/AppearanceChrome.d.ts +22 -4
- package/dist/views/AppearanceChrome.d.ts.map +1 -1
- package/dist/views/AppearanceChrome.js +451 -15
- package/dist/views/AppearanceChrome.js.map +1 -1
- package/dist/views/Jobs/CancelJobDialog.d.ts +7 -1
- package/dist/views/Jobs/CancelJobDialog.d.ts.map +1 -1
- package/dist/views/Jobs/CancelJobDialog.js +13 -3
- package/dist/views/Jobs/CancelJobDialog.js.map +1 -1
- package/dist/views/Jobs/DrawerReview.d.ts +7 -0
- package/dist/views/Jobs/DrawerReview.d.ts.map +1 -1
- package/dist/views/Jobs/DrawerReview.js +1 -1
- package/dist/views/Jobs/DrawerReview.js.map +1 -1
- package/dist/views/Jobs/JobDrawer.d.ts +23 -3
- package/dist/views/Jobs/JobDrawer.d.ts.map +1 -1
- package/dist/views/Jobs/JobDrawer.js +30 -5
- package/dist/views/Jobs/JobDrawer.js.map +1 -1
- package/dist/views/Jobs/JobsTable.d.ts +4 -2
- package/dist/views/Jobs/JobsTable.d.ts.map +1 -1
- package/dist/views/Jobs/JobsTable.js +6 -5
- package/dist/views/Jobs/JobsTable.js.map +1 -1
- package/dist/views/Jobs/NewBatchJob.d.ts +34 -0
- package/dist/views/Jobs/NewBatchJob.d.ts.map +1 -0
- package/dist/views/Jobs/NewBatchJob.js +282 -0
- package/dist/views/Jobs/NewBatchJob.js.map +1 -0
- package/dist/views/Jobs/WorkerStrip.d.ts +3 -1
- package/dist/views/Jobs/WorkerStrip.d.ts.map +1 -1
- package/dist/views/Jobs/WorkerStrip.js +2 -2
- package/dist/views/Jobs/WorkerStrip.js.map +1 -1
- package/dist/views/Jobs/focus.d.ts +6 -0
- package/dist/views/Jobs/focus.d.ts.map +1 -1
- package/dist/views/Jobs/focus.js +10 -0
- package/dist/views/Jobs/focus.js.map +1 -1
- package/dist/views/Jobs.d.ts +12 -3
- package/dist/views/Jobs.d.ts.map +1 -1
- package/dist/views/Jobs.js +109 -31
- package/dist/views/Jobs.js.map +1 -1
- package/dist/views/header-footer/AnnouncementTab.d.ts +15 -0
- package/dist/views/header-footer/AnnouncementTab.d.ts.map +1 -0
- package/dist/views/header-footer/AnnouncementTab.js +43 -0
- package/dist/views/header-footer/AnnouncementTab.js.map +1 -0
- package/dist/views/header-footer/ChromeField.d.ts +25 -0
- package/dist/views/header-footer/ChromeField.d.ts.map +1 -0
- package/dist/views/header-footer/ChromeField.js +99 -0
- package/dist/views/header-footer/ChromeField.js.map +1 -0
- package/dist/views/header-footer/ChromePreview.d.ts +31 -0
- package/dist/views/header-footer/ChromePreview.d.ts.map +1 -0
- package/dist/views/header-footer/ChromePreview.js +195 -0
- package/dist/views/header-footer/ChromePreview.js.map +1 -0
- package/dist/views/header-footer/DeveloperTab.d.ts +17 -0
- package/dist/views/header-footer/DeveloperTab.d.ts.map +1 -0
- package/dist/views/header-footer/DeveloperTab.js +98 -0
- package/dist/views/header-footer/DeveloperTab.js.map +1 -0
- package/dist/views/header-footer/RegionTab.d.ts +27 -0
- package/dist/views/header-footer/RegionTab.d.ts.map +1 -0
- package/dist/views/header-footer/RegionTab.js +80 -0
- package/dist/views/header-footer/RegionTab.js.map +1 -0
- package/dist/views/header-footer/chrome-form.d.ts +137 -0
- package/dist/views/header-footer/chrome-form.d.ts.map +1 -0
- package/dist/views/header-footer/chrome-form.js +518 -0
- package/dist/views/header-footer/chrome-form.js.map +1 -0
- package/dist/views/header-footer/code-samples.d.ts +30 -0
- package/dist/views/header-footer/code-samples.d.ts.map +1 -0
- package/dist/views/header-footer/code-samples.js +127 -0
- package/dist/views/header-footer/code-samples.js.map +1 -0
- package/dist/views/header-footer/controls.d.ts +57 -0
- package/dist/views/header-footer/controls.d.ts.map +1 -0
- package/dist/views/header-footer/controls.js +43 -0
- package/dist/views/header-footer/controls.js.map +1 -0
- package/package.json +3 -3
- package/src/AdminRoot.tsx +16 -2
- package/src/__tests__/lib/batch-wizard.test.ts +199 -0
- package/src/__tests__/lib/job-drawer.test.ts +2 -0
- package/src/__tests__/lib/job-view.test.ts +123 -2
- package/src/__tests__/lib/jobs-service.test.ts +72 -3
- package/src/__tests__/views/appearance-chrome.render.test.tsx +818 -0
- package/src/__tests__/views/header-footer-form.test.ts +333 -0
- package/src/__tests__/views/jobs-drawer.render.test.tsx +508 -2
- package/src/__tests__/views/jobs-list.render.test.tsx +8 -5
- package/src/__tests__/views/jobs-wizard.render.test.tsx +707 -0
- package/src/lib/api.ts +12 -1
- package/src/lib/batch-wizard.ts +188 -0
- package/src/lib/chrome-service.ts +200 -0
- package/src/lib/job-view.ts +95 -1
- package/src/lib/jobs-service.ts +56 -1
- package/src/styles/prenta-tokens.css +17 -0
- package/src/styles/theme.css +15 -0
- package/src/views/AppearanceChrome.tsx +704 -46
- package/src/views/Jobs/CancelJobDialog.tsx +20 -2
- package/src/views/Jobs/DrawerReview.tsx +1 -1
- package/src/views/Jobs/JobDrawer.tsx +83 -3
- package/src/views/Jobs/JobsTable.tsx +11 -4
- package/src/views/Jobs/NewBatchJob.tsx +857 -0
- package/src/views/Jobs/WorkerStrip.tsx +16 -11
- package/src/views/Jobs/focus.ts +11 -0
- package/src/views/Jobs.tsx +163 -49
- package/src/views/header-footer/AnnouncementTab.tsx +231 -0
- package/src/views/header-footer/ChromeField.tsx +358 -0
- package/src/views/header-footer/ChromePreview.tsx +497 -0
- package/src/views/header-footer/DeveloperTab.tsx +331 -0
- package/src/views/header-footer/RegionTab.tsx +398 -0
- package/src/views/header-footer/chrome-form.ts +603 -0
- package/src/views/header-footer/code-samples.ts +146 -0
- package/src/views/header-footer/controls.tsx +166 -0
- package/dist/views/Jobs/placeholders.d.ts +0 -6
- package/dist/views/Jobs/placeholders.d.ts.map +0 -1
- package/dist/views/Jobs/placeholders.js +0 -20
- package/dist/views/Jobs/placeholders.js.map +0 -1
- package/src/views/Jobs/placeholders.tsx +0 -66
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState } from 'react'
|
|
3
|
+
import { useRef, useState } from 'react'
|
|
4
4
|
import * as Dialog from '@radix-ui/react-dialog'
|
|
5
5
|
import { Button } from '../../components/ui/Button.js'
|
|
6
6
|
import { jobType, type JobRow } from '../../lib/job-view.js'
|
|
@@ -51,19 +51,28 @@ export interface CancelJobDialogProps {
|
|
|
51
51
|
* as it is once a cancel goes through, or never had focus.
|
|
52
52
|
*/
|
|
53
53
|
onFocusFallback: () => void
|
|
54
|
+
/**
|
|
55
|
+
* The dialog has gone and focus is back. A cancel's outcome is reported
|
|
56
|
+
* from here: while the dialog is open, screen readers are kept inside it.
|
|
57
|
+
*/
|
|
58
|
+
onClosed?: () => void
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
/**
|
|
57
62
|
* The cancel confirmation (jobs-view.jsx `CancelDlg`, JOBS.md "Cancel
|
|
58
63
|
* dialog"): 460px, radius 14, padding 20. Radix traps focus and closes on Esc.
|
|
64
|
+
* It opens from a row or over the drawer; Esc and the backdrop close only it.
|
|
59
65
|
*/
|
|
60
66
|
export function CancelJobDialog({
|
|
61
67
|
job,
|
|
62
68
|
onClose,
|
|
63
69
|
onConfirm,
|
|
64
70
|
onFocusFallback,
|
|
71
|
+
onClosed,
|
|
65
72
|
}: CancelJobDialogProps) {
|
|
66
73
|
const [busy, setBusy] = useState(false)
|
|
74
|
+
/** Set before the first await, so a second click can't post a second cancel. */
|
|
75
|
+
const sending = useRef(false)
|
|
67
76
|
const returnFocus = useReturnFocus(onFocusFallback)
|
|
68
77
|
|
|
69
78
|
return (
|
|
@@ -77,11 +86,17 @@ export function CancelJobDialog({
|
|
|
77
86
|
<Dialog.Overlay className="bg-scrim fixed inset-0 z-[var(--z-modal)]" />
|
|
78
87
|
<Dialog.Content
|
|
79
88
|
{...returnFocus}
|
|
89
|
+
onCloseAutoFocus={(event) => {
|
|
90
|
+
returnFocus.onCloseAutoFocus(event)
|
|
91
|
+
onClosed?.()
|
|
92
|
+
}}
|
|
93
|
+
// Radix makes the rest of the page inert but doesn't say so on the dialog.
|
|
94
|
+
aria-modal="true"
|
|
80
95
|
className="bg-card border-bdr rounded-14 shadow-shd2 fixed top-1/2 left-1/2 z-[var(--z-modal)] flex w-[min(460px,calc(100%-32px))] -translate-x-1/2 -translate-y-1/2 flex-col gap-3 border p-5 leading-[normal]"
|
|
81
96
|
>
|
|
82
97
|
{job ? (
|
|
83
98
|
<>
|
|
84
|
-
<Dialog.Title className="text-txt m-0 text-[length:var(--fs-drawer)] font-bold">
|
|
99
|
+
<Dialog.Title className="text-txt m-0 text-[length:var(--fs-drawer)] leading-[normal] font-bold">
|
|
85
100
|
Cancel “{job.title}”?
|
|
86
101
|
</Dialog.Title>
|
|
87
102
|
<Dialog.Description className="text-body text-sub m-0 leading-[1.55] text-pretty">
|
|
@@ -106,10 +121,13 @@ export function CancelJobDialog({
|
|
|
106
121
|
busy={busy}
|
|
107
122
|
leftIcon={<JobIcons.stop size={12} />}
|
|
108
123
|
onClick={async () => {
|
|
124
|
+
if (sending.current) return
|
|
125
|
+
sending.current = true
|
|
109
126
|
setBusy(true)
|
|
110
127
|
try {
|
|
111
128
|
await onConfirm(job)
|
|
112
129
|
} finally {
|
|
130
|
+
sending.current = false
|
|
113
131
|
setBusy(false)
|
|
114
132
|
}
|
|
115
133
|
onClose()
|
|
@@ -111,7 +111,7 @@ function DiffLabel({ label, children }: { label: string; children?: ReactNode })
|
|
|
111
111
|
* A link suggestion's line, as text: the words around the link, the linked
|
|
112
112
|
* words marked, and where they link. The stored markup is never rendered.
|
|
113
113
|
*/
|
|
114
|
-
function LinkLine({ after }: { after: string }) {
|
|
114
|
+
export function LinkLine({ after }: { after: string }) {
|
|
115
115
|
const parts = linkParts(after)
|
|
116
116
|
if (!parts) return <>{after}</>
|
|
117
117
|
return (
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
drawerTabs,
|
|
14
14
|
type DrawerTab,
|
|
15
15
|
} from '../../lib/job-drawer.js'
|
|
16
|
-
import { active, view, type JobRow } from '../../lib/job-view.js'
|
|
16
|
+
import { active, retryPoint, view, type JobActions, type JobRow } from '../../lib/job-view.js'
|
|
17
17
|
import {
|
|
18
18
|
approveAllReviewItems,
|
|
19
19
|
decideReviewItem,
|
|
@@ -29,7 +29,7 @@ import { DrawerActivity } from './DrawerActivity.js'
|
|
|
29
29
|
import { DrawerOverview } from './DrawerOverview.js'
|
|
30
30
|
import { DrawerResults } from './DrawerResults.js'
|
|
31
31
|
import { DrawerReview, type ReviewItemState } from './DrawerReview.js'
|
|
32
|
-
import { focusJobRow, useReturnFocus } from './focus.js'
|
|
32
|
+
import { focusJobDrawer, focusJobRow, useReturnFocus } from './focus.js'
|
|
33
33
|
import { JobIcons } from './icons.js'
|
|
34
34
|
import type { JobDrawerTab } from './JobsTable.js'
|
|
35
35
|
import { useJobDetail, useJobEvents } from './useJobDetail.js'
|
|
@@ -71,6 +71,12 @@ interface FocusMove {
|
|
|
71
71
|
fallback?: boolean
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/** An outcome the Jobs screen reported, for the drawer's live region; `n` makes a repeat new. */
|
|
75
|
+
export interface DrawerNotice {
|
|
76
|
+
text: string
|
|
77
|
+
n: number
|
|
78
|
+
}
|
|
79
|
+
|
|
74
80
|
export interface JobDrawerProps {
|
|
75
81
|
/** The list's copy of the job, read again every 5 seconds while Live is on. */
|
|
76
82
|
job: Job
|
|
@@ -81,6 +87,20 @@ export interface JobDrawerProps {
|
|
|
81
87
|
/** When the list was read, on the server's clock. Relative times count from it. */
|
|
82
88
|
now: number
|
|
83
89
|
worker: JobWorkerStatus | null
|
|
90
|
+
/** The footer's Cancel job, "Retry from document {n+1}" and "Retry now", for this viewer. */
|
|
91
|
+
actions: JobActions
|
|
92
|
+
/** A retry of this job is out. */
|
|
93
|
+
retrying: boolean
|
|
94
|
+
/**
|
|
95
|
+
* The screen's latest outcome (a cancel, a retry, a reused Run now). Radix
|
|
96
|
+
* hides the toaster from screen readers while the drawer is open, so the
|
|
97
|
+
* drawer announces it too.
|
|
98
|
+
*/
|
|
99
|
+
notice: DrawerNotice | null
|
|
100
|
+
/** Cancel job: opens the cancel dialog over the drawer. */
|
|
101
|
+
onCancel: () => void
|
|
102
|
+
/** Retry from where it stopped, or retry now while it waits out a backoff. Resolves once answered. */
|
|
103
|
+
onRetry: (now: boolean) => Promise<void>
|
|
84
104
|
onClose: () => void
|
|
85
105
|
/** Read the list again: a decision changes the job's review counts. */
|
|
86
106
|
onReload: () => void
|
|
@@ -112,7 +132,8 @@ async function copyText(text: string): Promise<boolean> {
|
|
|
112
132
|
* It reads `GET /jobs/:id` (results, the full error) and, per tab, the
|
|
113
133
|
* events and the review changes, each again only when the list's copy of the
|
|
114
134
|
* job changes. The Review badge and footer use the job's own counts, never
|
|
115
|
-
* the number of changes loaded.
|
|
135
|
+
* the number of changes loaded. The footer's Cancel job and retries (P-19)
|
|
136
|
+
* use the list's handlers, so they act and report the same way from here.
|
|
116
137
|
*/
|
|
117
138
|
export function JobDrawer({
|
|
118
139
|
job,
|
|
@@ -120,6 +141,11 @@ export function JobDrawer({
|
|
|
120
141
|
tab,
|
|
121
142
|
now,
|
|
122
143
|
worker,
|
|
144
|
+
actions,
|
|
145
|
+
retrying,
|
|
146
|
+
notice,
|
|
147
|
+
onCancel,
|
|
148
|
+
onRetry,
|
|
123
149
|
onClose,
|
|
124
150
|
onReload,
|
|
125
151
|
onNavigate,
|
|
@@ -162,6 +188,11 @@ export function JobDrawer({
|
|
|
162
188
|
[],
|
|
163
189
|
)
|
|
164
190
|
|
|
191
|
+
// The screen clears its notice when a drawer opens or closes, so one here is this drawer's.
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (notice) announce(notice.text)
|
|
194
|
+
}, [notice, announce])
|
|
195
|
+
|
|
165
196
|
const [itemState, setItemState] = useState<Record<string, ReviewItemState>>({})
|
|
166
197
|
const [drafts, setDrafts] = useState<Record<string, string>>({})
|
|
167
198
|
/** Documents known to be published or not, from decisions; keyed by document id. */
|
|
@@ -171,6 +202,8 @@ export function JobDrawer({
|
|
|
171
202
|
/** Bumped so the focus effect runs for a move asked for after an await. */
|
|
172
203
|
const [, setFocusTick] = useState(0)
|
|
173
204
|
const afterApproveAll = useRef(false)
|
|
205
|
+
/** A footer retry was answered; its button may have gone with the failure. */
|
|
206
|
+
const afterRetry = useRef(false)
|
|
174
207
|
/** "Approve all" ignores activations before this time (`APPROVE_ALL_ARM_MS`). */
|
|
175
208
|
const approveAllFrom = useRef(0)
|
|
176
209
|
/** A key held on the footer button is repeating, and each repeat clicks it. */
|
|
@@ -248,6 +281,24 @@ export function JobDrawer({
|
|
|
248
281
|
}
|
|
249
282
|
})
|
|
250
283
|
|
|
284
|
+
// A retry's button goes away with the failure it answered. Focus lost with
|
|
285
|
+
// it (Radix puts it on the dialog itself) moves to the panel, which now
|
|
286
|
+
// shows the job queued again.
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
if (!afterRetry.current) return
|
|
289
|
+
afterRetry.current = false
|
|
290
|
+
const focused = document.activeElement
|
|
291
|
+
if (focused && focused !== document.body && focused !== content.current && focused.isConnected)
|
|
292
|
+
return
|
|
293
|
+
focusJobDrawer()
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
async function retryHere(retryNow: boolean) {
|
|
297
|
+
await onRetry(retryNow)
|
|
298
|
+
afterRetry.current = true
|
|
299
|
+
setFocusTick((tick) => tick + 1)
|
|
300
|
+
}
|
|
301
|
+
|
|
251
302
|
// When approve-all leaves nothing to review its button goes away; focus
|
|
252
303
|
// moves to the changes rather than staying on the dialog itself.
|
|
253
304
|
useEffect(() => {
|
|
@@ -514,6 +565,35 @@ export function JobDrawer({
|
|
|
514
565
|
</UnderlineTabPanel>
|
|
515
566
|
|
|
516
567
|
<div className="border-bdr mobile:pb-[calc(12px+env(safe-area-inset-bottom,0px))] flex flex-wrap items-center gap-2 border-t px-[18px] py-3">
|
|
568
|
+
{actions.cancel ? (
|
|
569
|
+
<Button
|
|
570
|
+
variant="danger"
|
|
571
|
+
aria-haspopup="dialog"
|
|
572
|
+
leftIcon={<JobIcons.stop size={12} />}
|
|
573
|
+
onClick={onCancel}
|
|
574
|
+
>
|
|
575
|
+
Cancel job
|
|
576
|
+
</Button>
|
|
577
|
+
) : null}
|
|
578
|
+
{actions.retry ? (
|
|
579
|
+
<BusyButton
|
|
580
|
+
leftIcon={<JobIcons.retry size={13} />}
|
|
581
|
+
busy={retrying}
|
|
582
|
+
onClick={() => void retryHere(false)}
|
|
583
|
+
>
|
|
584
|
+
Retry from {retryPoint(row)}
|
|
585
|
+
</BusyButton>
|
|
586
|
+
) : null}
|
|
587
|
+
{actions.retryNow ? (
|
|
588
|
+
<BusyButton
|
|
589
|
+
variant="secondary"
|
|
590
|
+
leftIcon={<JobIcons.retry size={13} />}
|
|
591
|
+
busy={retrying}
|
|
592
|
+
onClick={() => void retryHere(true)}
|
|
593
|
+
>
|
|
594
|
+
Retry now
|
|
595
|
+
</BusyButton>
|
|
596
|
+
) : null}
|
|
517
597
|
<span className="flex-1" />
|
|
518
598
|
{/* One button for both, so it keeps focus when it turns into Approve all. */}
|
|
519
599
|
{canReview && counts.pending > 0 ? (
|
|
@@ -5,11 +5,12 @@ import { Button } from '../../components/ui/Button.js'
|
|
|
5
5
|
import { Card } from '../../components/ui/Card.js'
|
|
6
6
|
import { Skeleton } from '../../components/ui/Skeleton.js'
|
|
7
7
|
import {
|
|
8
|
-
active,
|
|
9
8
|
hasReviewAction,
|
|
9
|
+
jobActions,
|
|
10
10
|
view,
|
|
11
11
|
type JobFilter,
|
|
12
12
|
type JobRow as JobRowModel,
|
|
13
|
+
type JobViewer,
|
|
13
14
|
} from '../../lib/job-view.js'
|
|
14
15
|
import { Bar, Pill, TypeTile } from './atoms.js'
|
|
15
16
|
import { BusyButton } from './BusyButton.js'
|
|
@@ -53,6 +54,7 @@ interface RowProps {
|
|
|
53
54
|
j: JobRowModel
|
|
54
55
|
selected: boolean
|
|
55
56
|
retrying: boolean
|
|
57
|
+
viewer: JobViewer
|
|
56
58
|
onOpen: (id: string, tab: JobDrawerTab) => void
|
|
57
59
|
onCancel: (row: JobRowModel) => void
|
|
58
60
|
onRetry: (row: JobRowModel) => void
|
|
@@ -63,10 +65,11 @@ interface RowProps {
|
|
|
63
65
|
* the button and its `::after` covers the row, so the Review, Cancel and Retry
|
|
64
66
|
* buttons aren't nested inside another control. They sit above that layer.
|
|
65
67
|
*/
|
|
66
|
-
function JobRowItem({ j, selected, retrying, onOpen, onCancel, onRetry }: RowProps) {
|
|
68
|
+
function JobRowItem({ j, selected, retrying, viewer, onOpen, onCancel, onRetry }: RowProps) {
|
|
67
69
|
const v = view(j)
|
|
68
70
|
const statusId = `job-status-${j.id}`
|
|
69
71
|
const review = hasReviewAction(j)
|
|
72
|
+
const actions = jobActions(j, viewer)
|
|
70
73
|
return (
|
|
71
74
|
<li
|
|
72
75
|
className={`${ROW_GRID} border-bdr relative border-t px-4 py-3 ${
|
|
@@ -114,7 +117,7 @@ function JobRowItem({ j, selected, retrying, onOpen, onCancel, onRetry }: RowPro
|
|
|
114
117
|
Review
|
|
115
118
|
</Button>
|
|
116
119
|
) : null}
|
|
117
|
-
{
|
|
120
|
+
{actions.cancel ? (
|
|
118
121
|
<Button
|
|
119
122
|
variant="icon"
|
|
120
123
|
size="icon"
|
|
@@ -126,7 +129,7 @@ function JobRowItem({ j, selected, retrying, onOpen, onCancel, onRetry }: RowPro
|
|
|
126
129
|
<JobIcons.stop size={13} />
|
|
127
130
|
</Button>
|
|
128
131
|
) : null}
|
|
129
|
-
{
|
|
132
|
+
{actions.retry ? (
|
|
130
133
|
<BusyButton
|
|
131
134
|
variant="icon"
|
|
132
135
|
size="icon"
|
|
@@ -162,6 +165,8 @@ export interface JobsTableProps {
|
|
|
162
165
|
selectedId: string | null
|
|
163
166
|
/** The job whose retry request is out. */
|
|
164
167
|
retryingId: string | null
|
|
168
|
+
/** Who's looking: Cancel and Retry show only to someone who may use them. */
|
|
169
|
+
viewer: JobViewer
|
|
165
170
|
status: 'loading' | 'ready' | 'error'
|
|
166
171
|
error: string | null
|
|
167
172
|
onOpen: (id: string, tab: JobDrawerTab) => void
|
|
@@ -176,6 +181,7 @@ export function JobsTable({
|
|
|
176
181
|
filter,
|
|
177
182
|
selectedId,
|
|
178
183
|
retryingId,
|
|
184
|
+
viewer,
|
|
179
185
|
status,
|
|
180
186
|
error,
|
|
181
187
|
onOpen,
|
|
@@ -238,6 +244,7 @@ export function JobsTable({
|
|
|
238
244
|
j={j}
|
|
239
245
|
selected={selectedId === j.id}
|
|
240
246
|
retrying={retryingId === j.id}
|
|
247
|
+
viewer={viewer}
|
|
241
248
|
onOpen={onOpen}
|
|
242
249
|
onCancel={onCancel}
|
|
243
250
|
onRetry={onRetry}
|