From 1a1ffd06d29a9bff31ed3279a5a78668edc9474a Mon Sep 17 00:00:00 2001 From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com> Date: Sun, 19 May 2024 14:32:07 +0300 Subject: [PATCH] refactor: move job modal to queue dropdown actions --- .../HeaderActions/HeaderActions.tsx | 56 ++++--------------- packages/ui/src/components/Icons/Add.tsx | 25 +-------- .../QueueDropdownActions.tsx | 8 ++- .../src/components/StatusMenu/StatusMenu.tsx | 11 +--- packages/ui/src/hooks/useModal.ts | 24 ++++++++ packages/ui/src/pages/QueuePage/QueuePage.tsx | 21 ++++++- .../ui/src/static/locales/en-US/messages.json | 1 + 7 files changed, 68 insertions(+), 78 deletions(-) create mode 100644 packages/ui/src/hooks/useModal.ts diff --git a/packages/ui/src/components/HeaderActions/HeaderActions.tsx b/packages/ui/src/components/HeaderActions/HeaderActions.tsx index e7da0f3f..26c418ba 100644 --- a/packages/ui/src/components/HeaderActions/HeaderActions.tsx +++ b/packages/ui/src/components/HeaderActions/HeaderActions.tsx @@ -1,25 +1,14 @@ -import React, { useState, Suspense } from 'react'; +import React, { Suspense } from 'react'; +import { useModal } from '../../hooks/useModal'; import { useUIConfig } from '../../hooks/useUIConfig'; +import { Button } from '../Button/Button'; import { CustomLinksDropdown } from '../CustomLinksDropdown/CustomLinksDropdown'; -import { AddIcon } from '../Icons/Add'; import { FullscreenIcon } from '../Icons/Fullscreen'; import { RedisIcon } from '../Icons/Redis'; import { Settings } from '../Icons/Settings'; -import { Button } from '../Button/Button'; import s from './HeaderActions.module.css'; -type ModalTypes = 'redis' | 'settings' | 'addJobs'; -type AllModalTypes = ModalTypes | `${ModalTypes}Closing` | null; - -function waitForClosingAnimation( - state: ModalTypes, - setModalOpen: (newState: AllModalTypes) => void -) { - return () => { - setModalOpen(`${state}Closing`); - setTimeout(() => setModalOpen(null), 300); // fadeout animation duration - }; -} +type ModalTypes = 'redis' | 'settings'; const RedisStatsModalLazy = React.lazy(() => import('../RedisStatsModal/RedisStatsModal').then(({ RedisStatsModal }) => ({ @@ -39,21 +28,15 @@ const onClickFullScreen = async () => { return document.exitFullscreen(); }; -const AddJobModalLazy = React.lazy(() => - import('../AddJobModal/AddJobModal').then(({ AddJobModal }) => ({ - default: AddJobModal, - })) -); - export const HeaderActions = () => { - const [openedModal, setModalOpen] = useState(null); const { miscLinks = [] } = useUIConfig(); + const modal = useModal(); return ( <> - {(openedModal === 'redis' || openedModal === 'redisClosing') && ( - - )} - {(openedModal === 'settings' || openedModal === 'settingsClosing') && ( - + {modal.isMounted('redis') && ( + )} - {(openedModal === 'addJobs' || openedModal === 'addJobsClosing') && ( - + {modal.isMounted('settings') && ( + )} diff --git a/packages/ui/src/components/Icons/Add.tsx b/packages/ui/src/components/Icons/Add.tsx index 3be7f05f..e7946b4d 100644 --- a/packages/ui/src/components/Icons/Add.tsx +++ b/packages/ui/src/components/Icons/Add.tsx @@ -1,28 +1,7 @@ import React from 'react'; export const AddIcon = () => ( - - - - - - - + + ); diff --git a/packages/ui/src/components/QueueDropdownActions/QueueDropdownActions.tsx b/packages/ui/src/components/QueueDropdownActions/QueueDropdownActions.tsx index e3597f20..5bcc3477 100644 --- a/packages/ui/src/components/QueueDropdownActions/QueueDropdownActions.tsx +++ b/packages/ui/src/components/QueueDropdownActions/QueueDropdownActions.tsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'; import { QueueActions } from '../../../typings/app'; import { Button } from '../Button/Button'; import { DropdownContent } from '../DropdownContent/DropdownContent'; +import { AddIcon } from '../Icons/Add'; import { EllipsisVerticalIcon } from '../Icons/EllipsisVertical'; import { PauseIcon } from '../Icons/Pause'; import { PlayIcon } from '../Icons/Play'; @@ -16,9 +17,10 @@ export const QueueDropdownActions = ({ actions, }: { queue: AppQueue; - actions: QueueActions; + actions: Omit & { addJob: () => void }; }) => { const { t } = useTranslation(); + return ( @@ -29,6 +31,10 @@ export const QueueDropdownActions = ({ + + + {t('QUEUE.ACTIONS.ADD_JOB')} + { +export const StatusMenu = ({ queue, children }: PropsWithChildren<{ queue: AppQueue }>) => { const { t } = useTranslation(); return ( @@ -29,11 +28,7 @@ export const StatusMenu = ({ queue, actions }: { queue: AppQueue; actions: any } ); })} - {!queue.readOnlyMode && ( -
- -
- )} + {!!children &&
{children}
} ); }; diff --git a/packages/ui/src/hooks/useModal.ts b/packages/ui/src/hooks/useModal.ts new file mode 100644 index 00000000..97653e54 --- /dev/null +++ b/packages/ui/src/hooks/useModal.ts @@ -0,0 +1,24 @@ +import { useState } from 'react'; + +export function useModal() { + type AllModalTypes = ModalTypes | `${ModalTypes}Closing` | null; + const [openedModal, setModalOpen] = useState(null); + + return { + isOpen(modal: ModalTypes): boolean { + return openedModal === modal; + }, + isMounted(modal: ModalTypes): boolean { + return [modal, `${modal}Closing`].includes(openedModal as any); + }, + open(modal: ModalTypes): void { + setModalOpen(modal); + }, + close(modal: ModalTypes): () => void { + return () => { + setModalOpen(`${modal}Closing`); + setTimeout(() => setModalOpen(null), 300); // fadeout animation duration + }; + }, + }; +} diff --git a/packages/ui/src/pages/QueuePage/QueuePage.tsx b/packages/ui/src/pages/QueuePage/QueuePage.tsx index 521b4880..028ba8b1 100644 --- a/packages/ui/src/pages/QueuePage/QueuePage.tsx +++ b/packages/ui/src/pages/QueuePage/QueuePage.tsx @@ -5,20 +5,29 @@ import { useTranslation } from 'react-i18next'; import { JobCard } from '../../components/JobCard/JobCard'; import { Pagination } from '../../components/Pagination/Pagination'; import { QueueActions } from '../../components/QueueActions/QueueActions'; +import { QueueDropdownActions } from '../../components/QueueDropdownActions/QueueDropdownActions'; import { StatusMenu } from '../../components/StatusMenu/StatusMenu'; import { StickyHeader } from '../../components/StickyHeader/StickyHeader'; import { useActiveQueue } from '../../hooks/useActiveQueue'; import { useJob } from '../../hooks/useJob'; +import { useModal } from '../../hooks/useModal'; import { useQueues } from '../../hooks/useQueues'; import { useSelectedStatuses } from '../../hooks/useSelectedStatuses'; import { links } from '../../utils/links'; +const AddJobModalLazy = React.lazy(() => + import('../../components/AddJobModal/AddJobModal').then(({ AddJobModal }) => ({ + default: AddJobModal, + })) +); + export const QueuePage = () => { const { t } = useTranslation(); const selectedStatus = useSelectedStatuses(); const { actions } = useQueues(); const { actions: jobActions } = useJob(); const queue = useActiveQueue(); + const modal = useModal<'addJob'>(); actions.pollQueues(); if (!queue) { @@ -50,7 +59,14 @@ export const QueuePage = () => { } > - + + {!queue.readOnlyMode && ( + modal.open('addJob') }} + /> + )} + {queue.jobs.map((job) => ( { allowRetries={(job.isFailed || queue.allowCompletedRetries) && queue.allowRetries} /> ))} + {modal.isMounted('addJob') && ( + + )} ); }; diff --git a/packages/ui/src/static/locales/en-US/messages.json b/packages/ui/src/static/locales/en-US/messages.json index 3e5f9dab..27643279 100644 --- a/packages/ui/src/static/locales/en-US/messages.json +++ b/packages/ui/src/static/locales/en-US/messages.json @@ -56,6 +56,7 @@ "RESUME": "Resume", "PAUSE": "Pause", "EMPTY": "Empty", + "ADD_JOB": "Add job", "RETRY_ALL_CONFIRM_MSG": "Are you sure that you want to retry all {{status}} jobs?", "CLEAN_ALL_CONFIRM_MSG": "Are you sure that you want to clean all {{status}} jobs?", "PROMOTE_ALL_CONFIRM_MSG": "Are you sure that you want to promote all delayed jobs?",