= ({
release={release}
/>
))}
+
+
+
+
diff --git a/apps/webservice/src/app/[workspaceSlug]/(appv2)/_components/job/JobDropdownMenu.tsx b/apps/webservice/src/app/[workspaceSlug]/(appv2)/_components/job/JobDropdownMenu.tsx
index c4f3fa612..b6d821490 100644
--- a/apps/webservice/src/app/[workspaceSlug]/(appv2)/_components/job/JobDropdownMenu.tsx
+++ b/apps/webservice/src/app/[workspaceSlug]/(appv2)/_components/job/JobDropdownMenu.tsx
@@ -69,13 +69,13 @@ const overrideJobStatusFormSchema = z.object({
status: z.nativeEnum(JobStatus),
});
-const OverrideJobStatusDialog: React.FC<{
- job: { id: string; status: JobStatus };
+export const OverrideJobStatusDialog: React.FC<{
+ jobIds: string[];
onClose: () => void;
children: React.ReactNode;
-}> = ({ job, onClose, children }) => {
+}> = ({ jobIds, onClose, children }) => {
const [open, setOpen] = useState(false);
- const updateJob = api.job.update.useMutation();
+ const updateJobs = api.job.updateMany.useMutation();
const utils = api.useUtils();
const form = useForm({
@@ -84,10 +84,10 @@ const OverrideJobStatusDialog: React.FC<{
});
const onSubmit = form.handleSubmit((data) =>
- updateJob
- .mutateAsync({ id: job.id, data })
+ updateJobs
+ .mutateAsync({ ids: jobIds, data })
.then(() => utils.job.config.byReleaseId.invalidate())
- .then(() => utils.job.config.byId.invalidate(job.id))
+ .then(() => jobIds.map((id) => utils.job.config.byId.invalidate(id)))
.then(() => utils.release.list.invalidate())
.then(() => setOpen(false))
.then(() => onClose()),
@@ -139,9 +139,9 @@ const OverrideJobStatusDialog: React.FC<{
)}
/>
- {updateJob.error != null && (
+ {updateJobs.error != null && (
- {updateJob.error.message}
+ {updateJobs.error.message}
)}
@@ -153,7 +153,7 @@ const OverrideJobStatusDialog: React.FC<{
@@ -359,7 +359,10 @@ export const JobDropdownMenu: React.FC<{
)}
- setOpen(false)}>
+ setOpen(false)}
+ >
e.preventDefault()}
className="space-x-2"
diff --git a/packages/api/src/router/job.ts b/packages/api/src/router/job.ts
index 667ac64d5..369413d7d 100644
--- a/packages/api/src/router/job.ts
+++ b/packages/api/src/router/job.ts
@@ -782,6 +782,25 @@ export const jobRouter = createTRPCRouter({
.input(z.object({ id: z.string().uuid(), data: schema.updateJob }))
.mutation(({ ctx, input }) => updateJob(ctx.db, input.id, input.data)),
+ updateMany: protectedProcedure
+ .input(
+ z.object({ ids: z.array(z.string().uuid()), data: schema.updateJob }),
+ )
+ .meta({
+ authorizationCheck: ({ canUser, input }) => {
+ const jobIds: string[] = input.ids;
+ const authzPromises = jobIds.map((id) =>
+ canUser.perform(Permission.JobUpdate).on({ type: "job", id }),
+ );
+ return Promise.all(authzPromises).then((results) =>
+ results.every(Boolean),
+ );
+ },
+ })
+ .mutation(({ ctx, input }) =>
+ Promise.all(input.ids.map((id) => updateJob(ctx.db, id, input.data))),
+ ),
+
config: releaseJobTriggerRouter,
agent: jobAgentRouter,
trigger: jobTriggerRouter,