Skip to content

Commit

Permalink
feat: add duplicate job, closes #426
Browse files Browse the repository at this point in the history
  • Loading branch information
felixmosh committed Sep 19, 2024
1 parent c97a468 commit 6c8f92a
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 28 deletions.
14 changes: 8 additions & 6 deletions packages/ui/src/components/AddJobModal/AddJobModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AppQueue } from '@bull-board/api/typings/app';
import { AppJob, AppQueue } from '@bull-board/api/typings/app';
import React, { FormEvent, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useActiveQueue } from '../../hooks/useActiveQueue';
Expand All @@ -13,7 +13,7 @@ import { Modal } from '../Modal/Modal';

export interface AddJobModalProps {
open: boolean;

job?: AppJob | null;
onClose(): void;
}

Expand All @@ -22,7 +22,7 @@ const jobOptionsSchema = {
bullmq: bullMQJobOptionsSchema,
} as const;

export const AddJobModal = ({ open, onClose }: AddJobModalProps) => {
export const AddJobModal = ({ open, onClose, job }: AddJobModalProps) => {
const { queues, actions } = useQueues();
const activeQueue = useActiveQueue();
const [selectedQueue, setSelectedQueue] = useState<AppQueue | null>(activeQueue);
Expand Down Expand Up @@ -58,10 +58,10 @@ export const AddJobModal = ({ open, onClose }: AddJobModalProps) => {
width="small"
open={open}
onClose={onClose}
title={t('ADD_JOB.TITLE')}
title={t('ADD_JOB.TITLE', { context: job ? 'duplicate' : undefined })}
actionButton={
<Button type="submit" theme="primary" form="add-job-form">
{t('ADD_JOB.ADD')}
{t(`ADD_JOB.${job ? 'DUPLICATE' : 'ADD'}`)}
</Button>
}
>
Expand All @@ -81,14 +81,16 @@ export const AddJobModal = ({ open, onClose }: AddJobModalProps) => {
label={t('ADD_JOB.JOB_NAME')}
id="job-name"
name="jobName"
defaultValue={job?.name}
placeholder="__default__"
/>
<JsonField label={t('ADD_JOB.JOB_DATA')} id="job-data" name="jobData" />
<JsonField label={t('ADD_JOB.JOB_DATA')} id="job-data" name="jobData" value={job?.data} />
<JsonField
label={t('ADD_JOB.JOB_OPTIONS')}
id="job-options"
name="jobOptions"
schema={jobOptionsSchema[selectedQueue.type]}
value={job?.opts}
/>
</form>
</Modal>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/Icons/Copy.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

export const CopyIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512">
<path d="M208 0H332.1c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9V336c0 26.5-21.5 48-48 48H208c-26.5 0-48-21.5-48-48V48c0-26.5 21.5-48 48-48zM48 128h80v64H64V448H256V416h64v48c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V176c0-26.5 21.5-48 48-48z" />
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512">
<path d="M280 64l40 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 128C0 92.7 28.7 64 64 64l40 0 9.6 0C121 27.5 153.3 0 192 0s71 27.5 78.4 64l9.6 0zM64 112c-8.8 0-16 7.2-16 16l0 320c0 8.8 7.2 16 16 16l256 0c8.8 0 16-7.2 16-16l0-320c0-8.8-7.2-16-16-16l-16 0 0 24c0 13.3-10.7 24-24 24l-88 0-88 0c-13.3 0-24-10.7-24-24l0-24-16 0zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z" />
</svg>
);
7 changes: 7 additions & 0 deletions packages/ui/src/components/Icons/Duplicate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';

export const DuplicateIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="M208 0L332.1 0c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9L448 336c0 26.5-21.5 48-48 48l-192 0c-26.5 0-48-21.5-48-48l0-288c0-26.5 21.5-48 48-48zM48 128l80 0 0 64-64 0 0 256 192 0 0-32 64 0 0 48c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 176c0-26.5 21.5-48 48-48z" />
</svg>
);
23 changes: 18 additions & 5 deletions packages/ui/src/components/JobCard/JobActions/JobActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Status } from '@bull-board/api/typings/app';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Button } from '../../Button/Button';
import { DuplicateIcon } from '../../Icons/Duplicate';
import { PromoteIcon } from '../../Icons/Promote';
import { RetryIcon } from '../../Icons/Retry';
import { TrashIcon } from '../../Icons/Trash';
Expand All @@ -18,27 +19,39 @@ interface JobActionsProps {
retryJob: () => Promise<void>;
cleanJob: () => Promise<void>;
updateJobData: () => void;
duplicateJob: () => void;
};
}

interface ButtonType {
titleKey: string;
Icon: React.ElementType;
actionKey: 'promoteJob' | 'cleanJob' | 'retryJob' | 'updateJobData';
actionKey: 'promoteJob' | 'cleanJob' | 'retryJob' | 'updateJobData' | 'duplicateJob';
}

const buttonTypes: Record<string, ButtonType> = {
updateData: { titleKey: 'UPDATE_DATA', Icon: UpdateIcon, actionKey: 'updateJobData' },
promote: { titleKey: 'PROMOTE', Icon: PromoteIcon, actionKey: 'promoteJob' },
clean: { titleKey: 'CLEAN', Icon: TrashIcon, actionKey: 'cleanJob' },
retry: { titleKey: 'RETRY', Icon: RetryIcon, actionKey: 'retryJob' },
duplicate: { titleKey: 'DUPLICATE', Icon: DuplicateIcon, actionKey: 'duplicateJob' },
} as const;

const statusToButtonsMap: Record<string, ButtonType[]> = {
[STATUSES.failed]: [buttonTypes.retry, buttonTypes.updateData, buttonTypes.clean],
[STATUSES.delayed]: [buttonTypes.promote, buttonTypes.updateData, buttonTypes.clean],
[STATUSES.completed]: [buttonTypes.retry, buttonTypes.clean],
[STATUSES.waiting]: [buttonTypes.updateData, buttonTypes.clean],
[STATUSES.failed]: [
buttonTypes.retry,
buttonTypes.duplicate,
buttonTypes.updateData,
buttonTypes.clean,
],
[STATUSES.delayed]: [
buttonTypes.promote,
buttonTypes.duplicate,
buttonTypes.updateData,
buttonTypes.clean,
],
[STATUSES.completed]: [buttonTypes.duplicate, buttonTypes.retry, buttonTypes.clean],
[STATUSES.waiting]: [buttonTypes.duplicate, buttonTypes.updateData, buttonTypes.clean],
} as const;

export const JobActions = ({ actions, status, allowRetries }: JobActionsProps) => {
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/JobCard/JobCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface JobCardProps {
allowRetries: boolean;
actions: {
updateJobData: () => void;
duplicateJob: () => void;
promoteJob: () => Promise<void>;
retryJob: () => Promise<void>;
cleanJob: () => Promise<void>;
Expand Down
6 changes: 1 addition & 5 deletions packages/ui/src/hooks/useJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,7 @@ export function useJob(): Omit<JobState, 'updateJob'> & { actions: JobActions }
);

const updateJobData = (queueName: string, job: AppJob, newData: Record<string, any>) =>
withConfirmAndUpdate(
() => api.updateJobData(queueName, job.id, newData),
t('JOB.ACTIONS.CONFIRM.UPDATE_JOB_DATA'),
false
);
withConfirmAndUpdate(() => api.updateJobData(queueName, job.id, newData), '', false);

const getJobLogs = (queueName: string) => (job: AppJob) => () =>
api.getJobLogs(queueName, job.id);
Expand Down
16 changes: 15 additions & 1 deletion packages/ui/src/pages/JobPage/JobPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import { useSelectedStatuses } from '../../hooks/useSelectedStatuses';
import { links } from '../../utils/links';
import buttonS from '../../components/Button/Button.module.css';

const AddJobModalLazy = React.lazy(() =>
import('../../components/AddJobModal/AddJobModal').then(({ AddJobModal }) => ({
default: AddJobModal,
}))
);

const UpdateJobDataModalLazy = React.lazy(() =>
import('../../components/UpdateJobDataModal/UpdateJobDataModal').then(
({ UpdateJobDataModal }) => ({
Expand All @@ -28,7 +34,7 @@ export const JobPage = () => {
const queue = useActiveQueue();
const { job, status, actions } = useJob();
const selectedStatuses = useSelectedStatuses();
const modal = useModal<'updateJobData'>();
const modal = useModal<'updateJobData' | 'addJob'>();

actions.pollJob();

Expand Down Expand Up @@ -70,11 +76,19 @@ export const JobPage = () => {
retryJob: actions.retryJob(queue.name, status as JobRetryStatus)(job),
getJobLogs: actions.getJobLogs(queue.name)(job),
updateJobData: () => modal.open('updateJobData'),
duplicateJob: () => modal.open('addJob'),
}}
readOnlyMode={queue.readOnlyMode}
allowRetries={(job.isFailed || queue.allowCompletedRetries) && queue.allowRetries}
/>
<Suspense fallback={null}>
{modal.isMounted('addJob') && (
<AddJobModalLazy
open={modal.isOpen('addJob')}
onClose={modal.close('addJob')}
job={job}
/>
)}
{modal.isMounted('updateJobData') && (
<UpdateJobDataModalLazy
open={modal.isOpen('updateJobData')}
Expand Down
10 changes: 9 additions & 1 deletion packages/ui/src/pages/QueuePage/QueuePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,22 @@ export const QueuePage = () => {
setEditJob(job);
modal.open('updateJobData');
},
duplicateJob: () => {
setEditJob(job);
modal.open('addJob');
},
}}
readOnlyMode={queue?.readOnlyMode}
allowRetries={(job.isFailed || queue.allowCompletedRetries) && queue.allowRetries}
/>
))}
<Suspense fallback={null}>
{modal.isMounted('addJob') && (
<AddJobModalLazy open={modal.isOpen('addJob')} onClose={modal.close('addJob')} />
<AddJobModalLazy
open={modal.isOpen('addJob')}
onClose={modal.close('addJob')}
job={editJob}
/>
)}
{modal.isMounted('updateJobData') && !!editJob && (
<UpdateJobDataModalLazy
Expand Down
8 changes: 5 additions & 3 deletions packages/ui/src/static/locales/en-US/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@
"CLEAN": "Clean",
"RETRY": "Retry",
"UPDATE_DATA": "Update Data",
"DUPLICATE": "Duplicate",
"CONFIRM": {
"PROMOTE": "Are you sure that you want to promote this job?",
"RETRY": "Are you sure that you want to retry this job?",
"CLEAN": "Are you sure that you want to clean this job?",
"UPDATE_JOB_DATA": "Are you sure that you want to update the data of this job?"
"CLEAN": "Are you sure that you want to clean this job?"
}
},
"TABS": {
Expand Down Expand Up @@ -129,11 +129,13 @@
},
"ADD_JOB": {
"TITLE": "Add job",
"TITLE_duplicate": "Duplicate job",
"QUEUE_NAME": "Queue name",
"JOB_NAME": "Job name",
"JOB_DATA": "Job data",
"JOB_OPTIONS": "Job options",
"ADD": "Add"
"ADD": "Add",
"DUPLICATE": "Duplicate"
},
"UPDATE_JOB_DATA": {
"TITLE": "Update job data",
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/src/static/locales/fr-FR/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
"PROMOTE": "Promouvoir",
"CLEAN": "Supprimer",
"RETRY": "Réessayer",
"UPDATE_DATA": "Mettre à jour les données",
"DUPLICATE": "Dupliquer",
"CONFIRM": {
"PROMOTE": "Êtes-vous sûr de vouloir promouvoir ce poste ?",
"RETRY": "Êtes-vous sûr de vouloir réessayer ce poste ?",
Expand Down Expand Up @@ -131,11 +133,13 @@
},
"ADD_JOB": {
"TITLE": "Ajouter une tâche",
"TITLE_duplicate": "Dupliquer une tâche",
"QUEUE_NAME": "Nom de la file",
"JOB_NAME": "Nom de la tâche",
"JOB_DATA": "Données de la tâche",
"JOB_OPTIONS": "Options de la tâche",
"ADD": "Ajouter"
"ADD": "Ajouter",
"DUPLICATE": "Dupliquer"
},
"UPDATE_JOB_DATA": {
"TITLE": "Mettre à jour les données de tâche",
Expand Down
10 changes: 7 additions & 3 deletions packages/ui/src/static/locales/pt-BR/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
"PROMOTE": "Promover",
"CLEAN": "Limpar",
"RETRY": "Tente Novamente",
"UPDATE_DATA": "Atualizar dados",
"DUPLICATE": "Duplicar",
"CONFIRM": {
"PROMOTE": "Você tem certeza de que deseja promover este trabalho?",
"RETRY": "Você tem certeza de que deseja tentar novamente este trabalho?",
Expand Down Expand Up @@ -131,15 +133,17 @@
},
"ADD_JOB": {
"TITLE": "Adicionar tarefa",
"TITLE_duplicate": "Duplicar tarefa",
"QUEUE_NAME": "Nome da fila",
"JOB_NAME": "Nome da tarefa",
"JOB_DATA": "Dados da tarefa",
"JOB_OPTIONS": "Opções da tarefa",
"ADD": "Adicionar"
"ADD": "Adicionar",
"DUPLICATE": "Duplicar"
},
"UPDATE_JOB_DATA": {
"TITLE": "Atualizar dados do trabalho",
"TITLE": "Atualizar dados do tarefa",
"UPDATE": "Atualizar",
"JOB_DATA": "Dados do trabalho"
"JOB_DATA": "Dados do tarefa"
}
}
6 changes: 5 additions & 1 deletion packages/ui/src/static/locales/zh-CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
"PROMOTE": "提升",
"CLEAN": "清理",
"RETRY": "重试",
"UPDATE_DATA": "更新数据",
"DUPLICATE": "复制",
"CONFIRM": {
"PROMOTE": "您确定要推广这项工作吗?",
"RETRY": "您确定要重试这项工作吗?",
Expand Down Expand Up @@ -125,11 +127,13 @@
},
"ADD_JOB": {
"TITLE": "添加作业",
"TITLE_duplicate": "复制工作",
"QUEUE_NAME": "队列名称",
"JOB_NAME": "作业名称",
"JOB_DATA": "作业数据",
"JOB_OPTIONS": "作业选项",
"ADD": "添加"
"ADD": "添加",
"DUPLICATE": "复制"
},
"UPDATE_JOB_DATA": {
"TITLE": "更新工作数据",
Expand Down

0 comments on commit 6c8f92a

Please sign in to comment.