From a1c7e670fe27ab0bb31d60e818a10d465c6270f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Thu, 27 Feb 2025 14:37:45 +0100 Subject: [PATCH 01/26] feat: task prefilter sticky --- .../src/ee/task/components/PrefilterTask.tsx | 4 +- .../TranslationHeader/StickyHeader.tsx | 19 +- .../TranslationHeader/TranslationControls.tsx | 127 +++++----- .../TranslationControlsCompact.tsx | 219 +++++++++--------- .../TranslationHeader/TranslationsHeader.tsx | 33 ++- .../projects/translations/Translations.tsx | 4 +- .../prefilters/ContainerPrefilter.tsx | 6 +- .../translations/prefilters/usePrefilter.ts | 27 ++- 8 files changed, 230 insertions(+), 209 deletions(-) diff --git a/webapp/src/ee/task/components/PrefilterTask.tsx b/webapp/src/ee/task/components/PrefilterTask.tsx index 04cc78464e..cab1963cc7 100644 --- a/webapp/src/ee/task/components/PrefilterTask.tsx +++ b/webapp/src/ee/task/components/PrefilterTask.tsx @@ -57,7 +57,7 @@ export const PrefilterTask = ({ taskNumber }: PrefilterTaskProps) => { const [_, setTaskDetail] = useUrlSearchState(QUERY.TRANSLATIONS_TASK_DETAIL); - const { clear } = usePrefilter(); + const prefilter = usePrefilter(); function handleShowDetails() { setTaskDetail(String(taskNumber)); @@ -104,7 +104,7 @@ export const PrefilterTask = ({ taskNumber }: PrefilterTaskProps) => { } closeButton={ - + diff --git a/webapp/src/views/projects/translations/TranslationHeader/StickyHeader.tsx b/webapp/src/views/projects/translations/TranslationHeader/StickyHeader.tsx index 73108bfedf..bc5cfc6288 100644 --- a/webapp/src/views/projects/translations/TranslationHeader/StickyHeader.tsx +++ b/webapp/src/views/projects/translations/TranslationHeader/StickyHeader.tsx @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { styled } from '@mui/material'; +import { styled, useTheme } from '@mui/material'; import { useHeaderNsActions, @@ -9,13 +9,11 @@ import { NamespaceContent } from '../Namespace/NamespaceContent'; import { useGlobalContext } from 'tg.globalContext/GlobalContext'; const StyledContainer = styled('div')` - position: sticky; box-sizing: border-box; - margin: -12px -5px -10px -5px; + margin-bottom: -10px; margin-left: ${({ theme }) => theme.spacing(-2)}; margin-right: ${({ theme }) => theme.spacing(-2)}; background: ${({ theme }) => theme.palette.background.default}; - z-index: ${({ theme }) => theme.zIndex.appBar + 1}; transition: transform 0.2s ease-in-out; overflow: visible; display: grid; @@ -23,6 +21,7 @@ const StyledContainer = styled('div')` const StyledControls = styled('div')` padding: ${({ theme }) => theme.spacing(0, 1.5)}; + padding-top: 5px; background: ${({ theme }) => theme.palette.background.default}; `; @@ -38,6 +37,7 @@ const StyledNs = styled('div')` const StyledShadow = styled('div')` background: ${({ theme }) => theme.palette.divider1}; height: 1px; + margin-bottom: 10px; position: sticky; z-index: ${({ theme }) => theme.zIndex.appBar}; margin-left: ${({ theme }) => theme.spacing(-1)}; @@ -51,13 +51,19 @@ const StyledShadow = styled('div')` type Props = { height: number; + marginBottom?: number; }; -export const StickyHeader: React.FC = ({ height, children }) => { +export const StickyHeader: React.FC = ({ + height, + children, + marginBottom, +}) => { const { setFloatingBannerHeight } = useHeaderNsActions(); const topNamespace = useHeaderNsContext((c) => c.topNamespace); const topBannerHeight = useGlobalContext((c) => c.layout.topBannerHeight); const topBarHidden = useGlobalContext((c) => !c.layout.topBarHeight); + const theme = useTheme(); useEffect(() => { setFloatingBannerHeight(height); @@ -69,6 +75,9 @@ export const StickyHeader: React.FC = ({ height, children }) => { style={{ top: 50 + topBannerHeight, height: height + 5, + position: 'sticky', + zIndex: theme.zIndex.appBar + 1, + marginBottom, transform: topBarHidden ? `translate(0px, -55px)` : `translate(0px, 0px)`, diff --git a/webapp/src/views/projects/translations/TranslationHeader/TranslationControls.tsx b/webapp/src/views/projects/translations/TranslationHeader/TranslationControls.tsx index ff3b446087..4f870a218b 100644 --- a/webapp/src/views/projects/translations/TranslationHeader/TranslationControls.tsx +++ b/webapp/src/views/projects/translations/TranslationHeader/TranslationControls.tsx @@ -13,14 +13,11 @@ import { useTranslationsActions, useTranslationsSelector, } from '../context/TranslationsContext'; -import { StickyHeader } from './StickyHeader'; const StyledContainer = styled('div')` display: grid; grid-template-columns: auto 1fr auto; align-items: start; - padding-bottom: 8px; - padding-top: 13px; `; const StyledSpaced = styled('div')` @@ -65,72 +62,70 @@ export const TranslationControls: React.FC = ({ onDialogOpen }) => { }; return ( - - - - - + + + + + + + {taskPrefilter && ( + - + )} + - - {taskPrefilter && ( - - )} - + + + + changeView('LIST')} + data-cy="translations-view-list-button" + > + + + changeView('TABLE')} + data-cy="translations-view-table-button" + > + + + - - - - changeView('LIST')} - data-cy="translations-view-list-button" + {canCreateKeys && ( + + - - )} - - - + + + + )} + + ); }; diff --git a/webapp/src/views/projects/translations/TranslationHeader/TranslationControlsCompact.tsx b/webapp/src/views/projects/translations/TranslationHeader/TranslationControlsCompact.tsx index fe677a1711..bb90a471e4 100644 --- a/webapp/src/views/projects/translations/TranslationHeader/TranslationControlsCompact.tsx +++ b/webapp/src/views/projects/translations/TranslationHeader/TranslationControlsCompact.tsx @@ -32,7 +32,6 @@ import { useTranslationsSelector, } from '../context/TranslationsContext'; import { ViewMode } from '../context/types'; -import { StickyHeader } from './StickyHeader'; const StyledContainer = styled('div')` display: grid; @@ -43,8 +42,6 @@ const StyledContainer = styled('div')` padding: ${({ theme }) => theme.spacing(0, 1.5)}; z-index: ${({ theme }) => theme.zIndex.appBar + 1}; transition: transform 0.2s ease-in-out; - padding-bottom: 4px; - padding-top: 9px; `; const StyledSpaced = styled('div')` @@ -138,118 +135,116 @@ export const TranslationControlsCompact: React.FC = ({ }; return ( - - - {searchOpen ? ( - - + {searchOpen ? ( + + + setSearchOpen(false)}> + + + + ) : ( + <> + + + + setSearchOpen(true)} + > + + + + + + + + setAnchorFiltersEl(e.currentTarget)} + > + + + + + setAnchorFiltersEl(null)} + filtersContent={filtersContent} + onChange={setFilters} /> - setSearchOpen(false)}> - - - - ) : ( - <> - - - - setSearchOpen(true)} - > - - - - - - - - setAnchorFiltersEl(e.currentTarget)} - > - - - - - setAnchorFiltersEl(null)} - filtersContent={filtersContent} - onChange={setFilters} - /> - - - - {taskPrefilter && ( - - )} - - - - setAnchorLanguagesEl(e.currentTarget)} - > - - - - setAnchorLanguagesEl(null)} - onChange={handleLanguageChange} - value={selectedLanguages} - languages={languages} + + + + {taskPrefilter && ( + + )} + + + + setAnchorLanguagesEl(e.currentTarget)} + > + + - - handleViewChange('LIST')} - data-cy="translations-view-list-button" - > - - - handleViewChange('TABLE')} - data-cy="translations-view-table-button" + setAnchorLanguagesEl(null)} + onChange={handleLanguageChange} + value={selectedLanguages} + languages={languages} + /> + + + handleViewChange('LIST')} + data-cy="translations-view-list-button" + > + + + handleViewChange('TABLE')} + data-cy="translations-view-table-button" + > + + + + + {projectPermissions.satisfiesPermission('keys.edit') && ( + + - - - - - {projectPermissions.satisfiesPermission('keys.edit') && ( - - - - - - )} - - - )} - - + + + + )} + + + )} + ); }; diff --git a/webapp/src/views/projects/translations/TranslationHeader/TranslationsHeader.tsx b/webapp/src/views/projects/translations/TranslationHeader/TranslationsHeader.tsx index 30cb864af2..9d936335a6 100644 --- a/webapp/src/views/projects/translations/TranslationHeader/TranslationsHeader.tsx +++ b/webapp/src/views/projects/translations/TranslationHeader/TranslationsHeader.tsx @@ -1,4 +1,4 @@ -import { Typography, Dialog, useMediaQuery, styled } from '@mui/material'; +import { Typography, Dialog, useMediaQuery, styled, Box } from '@mui/material'; import { T } from '@tolgee/react'; import { useUrlSearchState } from 'tg.hooks/useUrlSearchState'; @@ -12,9 +12,11 @@ import { useState } from 'react'; import { confirmation } from 'tg.hooks/confirmation'; import { useGlobalContext } from 'tg.globalContext/GlobalContext'; import { SelectAllCheckbox } from '../BatchOperations/SelectAllCheckbox'; +import { StickyHeader } from './StickyHeader'; +import { Prefilter } from '../prefilters/Prefilter'; const StyledResultCount = styled('div')` - padding: 9px 0px 4px 0px; + padding: 0px 0px 4px 0px; margin-left: 15px; display: flex; align-items: center; @@ -26,6 +28,7 @@ const StyledDialog = styled(Dialog)` `; export const TranslationsHeader = () => { + const prefilter = useTranslationsSelector((c) => c.prefilter); const [newCreateDialog, setNewCreateDialog] = useUrlSearchState('create', { defaultVal: 'false', }); @@ -61,12 +64,32 @@ export const TranslationsHeader = () => { } } + const controls = isSmall ? ( + + + + ) : ( + + + + ); + return ( <> - {isSmall ? ( - + {prefilter && ( + <> + + + + + + + )} + + {!prefilter ? ( + {controls} ) : ( - + {controls} )} {dataReady && translationsTotal ? ( diff --git a/webapp/src/views/projects/translations/Translations.tsx b/webapp/src/views/projects/translations/Translations.tsx index d476eebb06..e8731f1adb 100644 --- a/webapp/src/views/projects/translations/Translations.tsx +++ b/webapp/src/views/projects/translations/Translations.tsx @@ -25,7 +25,6 @@ import { BaseProjectView } from '../BaseProjectView'; import { TranslationsToolbar } from './TranslationsToolbar'; import { BatchOperationsChangeIndicator } from './BatchOperations/BatchOperationsChangeIndicator'; import { FloatingToolsPanel } from './ToolsPanel/FloatingToolsPanel'; -import { Prefilter } from './prefilters/Prefilter'; import { TranslationsTaskDetail } from 'tg.ee'; import { TaskAllDonePlaceholder } from 'tg.ee'; import { EmptyState } from 'tg.component/common/EmptyState'; @@ -159,10 +158,9 @@ export const Translations = () => { }), ], ]} - wrapperProps={{ pb: 0 }} + wrapperProps={{ style: { paddingBottom: 0, paddingTop: '3px' } }} > - {prefilter && } {translationsEmpty ? ( diff --git a/webapp/src/views/projects/translations/prefilters/ContainerPrefilter.tsx b/webapp/src/views/projects/translations/prefilters/ContainerPrefilter.tsx index 5ec0368c15..f8d26da042 100644 --- a/webapp/src/views/projects/translations/prefilters/ContainerPrefilter.tsx +++ b/webapp/src/views/projects/translations/prefilters/ContainerPrefilter.tsx @@ -7,8 +7,6 @@ import { useGlobalContext } from 'tg.globalContext/GlobalContext'; import React from 'react'; const StyledContainer = styled('div')` - margin-top: -4px; - margin-bottom: 12px; background: ${({ theme }) => theme.palette.revisionFilterBanner.background}; padding: 0px 4px 0px 14px; border-radius: 4px; @@ -56,7 +54,7 @@ export const PrefilterContainer = ({ icon, closeButton, }: Props) => { - const { clear } = usePrefilter(); + const prefilter = usePrefilter(); const rightPanelWidth = useGlobalContext((c) => c.layout.rightPanelWidth); const isSmall = useMediaQuery( @@ -72,7 +70,7 @@ export const PrefilterContainer = ({ {!isSmall && content} {closeButton ?? ( - )} diff --git a/webapp/src/views/projects/translations/prefilters/usePrefilter.ts b/webapp/src/views/projects/translations/prefilters/usePrefilter.ts index d98ef2d4fa..2aa5fe32e1 100644 --- a/webapp/src/views/projects/translations/prefilters/usePrefilter.ts +++ b/webapp/src/views/projects/translations/prefilters/usePrefilter.ts @@ -17,7 +17,7 @@ const stringToNumber = (input: string | undefined) => { return undefined; }; -export const usePrefilter = (): PrefilterType => { +export const usePrefilter = (): PrefilterType | undefined => { const [activity, setActivity] = useUrlSearchState( QUERY.TRANSLATIONS_PREFILTERS_ACTIVITY, { @@ -59,20 +59,23 @@ export const usePrefilter = (): PrefilterType => { setTaskHideDone(undefined); } - const result: PrefilterType = { - clear, - }; - if (activityId !== undefined) { - result.activity = activityId; + return { + activity: activityId, + clear, + }; } else if (failedJobId !== undefined) { - result.failedJob = failedJobId; + return { + failedJob: failedJobId, + clear, + }; } else if (taskNumber !== undefined) { - result.task = taskNumber; - if (taskHideDone !== undefined) { - result.taskFilterNotDone = taskHideDone === 'true'; - } + return { + task: taskNumber, + taskFilterNotDone: taskHideDone === 'true', + clear, + }; } - return result; + return undefined; }; From 03ca3224e4d06f42545c3538ccecd8c0c2aed85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Thu, 27 Feb 2025 15:02:49 +0100 Subject: [PATCH 02/26] fix: taskDone filter not in history --- .../src/views/projects/translations/prefilters/usePrefilter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/webapp/src/views/projects/translations/prefilters/usePrefilter.ts b/webapp/src/views/projects/translations/prefilters/usePrefilter.ts index 2aa5fe32e1..f8273f2329 100644 --- a/webapp/src/views/projects/translations/prefilters/usePrefilter.ts +++ b/webapp/src/views/projects/translations/prefilters/usePrefilter.ts @@ -44,7 +44,6 @@ export const usePrefilter = (): PrefilterType | undefined => { QUERY.TRANSLATIONS_PREFILTERS_TASK_HIDE_DONE, { defaultVal: undefined, - history: true, } ); From e3e633dfbfd100804ba7da84336205988bf1c26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Thu, 27 Feb 2025 15:06:51 +0100 Subject: [PATCH 03/26] feat: task prefilter sticky --- .../projects/translations/BatchOperations/SelectAllCheckbox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/views/projects/translations/BatchOperations/SelectAllCheckbox.tsx b/webapp/src/views/projects/translations/BatchOperations/SelectAllCheckbox.tsx index 85c41c4d7f..dcbebfaacd 100644 --- a/webapp/src/views/projects/translations/BatchOperations/SelectAllCheckbox.tsx +++ b/webapp/src/views/projects/translations/BatchOperations/SelectAllCheckbox.tsx @@ -8,7 +8,7 @@ import { const StyledToggleAllButton = styled(Box)` width: 38px; height: 38px; - margin: 0px -12px 0px -12.5px; + margin: -1px -12px 0px -12.5px; `; export const SelectAllCheckbox = () => { From 26d03b9377ccfc75f333cb024882225ff6c59cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Thu, 27 Feb 2025 15:30:11 +0100 Subject: [PATCH 04/26] fix: move hide done filter to the prefilter --- .../src/ee/task/components/PrefilterTask.tsx | 16 +++++++----- .../TranslationHeader/TranslationControls.tsx | 18 ++----------- .../TranslationControlsCompact.tsx | 25 ++----------------- .../prefilters/ContainerPrefilter.tsx | 12 +++++++-- 4 files changed, 24 insertions(+), 47 deletions(-) diff --git a/webapp/src/ee/task/components/PrefilterTask.tsx b/webapp/src/ee/task/components/PrefilterTask.tsx index cab1963cc7..fb66a4b02a 100644 --- a/webapp/src/ee/task/components/PrefilterTask.tsx +++ b/webapp/src/ee/task/components/PrefilterTask.tsx @@ -21,6 +21,7 @@ import { TaskLabel } from './TaskLabel'; import { PrefilterTaskProps } from '../../../eeSetup/EeModuleType'; import { TASK_ACTIVE_STATES } from 'tg.component/task/taskActiveStates'; import { QUERY } from 'tg.constants/links'; +import { PrefilterTaskHideDoneSwitch } from './PrefilterTaskHideDoneSwitch'; const StyledWarning = styled('div')` display: flex; @@ -124,14 +125,17 @@ export const PrefilterTask = ({ taskNumber }: PrefilterTaskProps) => { {!isActive && } - {alert ? ( - - - {alert} - - ) : null} } + controls={} + alert={ + Boolean(alert) && ( + + + {alert} + + ) + } /> ); diff --git a/webapp/src/views/projects/translations/TranslationHeader/TranslationControls.tsx b/webapp/src/views/projects/translations/TranslationHeader/TranslationControls.tsx index 4f870a218b..0f911dd8df 100644 --- a/webapp/src/views/projects/translations/TranslationHeader/TranslationControls.tsx +++ b/webapp/src/views/projects/translations/TranslationHeader/TranslationControls.tsx @@ -1,5 +1,5 @@ import { LayoutGrid02, LayoutLeft, Plus } from '@untitled-ui/icons-react'; -import { Box, Button, ButtonGroup, styled } from '@mui/material'; +import { Button, ButtonGroup, styled } from '@mui/material'; import { T, useTranslate } from '@tolgee/react'; import { LanguagesSelect } from 'tg.component/common/form/LanguagesSelect/LanguagesSelect'; @@ -7,7 +7,6 @@ import { useProjectPermissions } from 'tg.hooks/useProjectPermissions'; import { TranslationFilters } from 'tg.component/translation/translationFilters/TranslationFilters'; import { QuickStartHighlight } from 'tg.component/layout/QuickStartGuide/QuickStartHighlight'; import { HeaderSearchField } from 'tg.component/layout/HeaderSearchField'; -import { PrefilterTaskShowDoneSwitch } from 'tg.ee'; import { useTranslationsActions, @@ -16,7 +15,7 @@ import { const StyledContainer = styled('div')` display: grid; - grid-template-columns: auto 1fr auto; + grid-template-columns: 1fr auto; align-items: start; `; @@ -53,9 +52,6 @@ export const TranslationControls: React.FC = ({ onDialogOpen }) => { const { setFilters } = useTranslationsActions(); const selectedLanguagesMapped = allLanguages?.filter((l) => selectedLanguages?.includes(l.tag)) ?? []; - const taskPrefilter = useTranslationsSelector( - (c) => c.prefilter?.task !== undefined - ); const handleAddTranslation = () => { onDialogOpen(); @@ -78,16 +74,6 @@ export const TranslationControls: React.FC = ({ onDialogOpen }) => { /> - - {taskPrefilter && ( - - )} - - theme.spacing(-1)}; margin-right: ${({ theme }) => theme.spacing(-2)}; @@ -109,9 +101,6 @@ export const TranslationControlsCompact: React.FC = ({ setSearch(value); }; const filters = useTranslationsSelector((c) => c.filters); - const taskPrefilter = useTranslationsSelector( - (c) => c.prefilter?.task !== undefined - ); const activeFilters = getActiveFilters(filters); const { setFilters } = useTranslationsActions(); const selectedLanguagesMapped = @@ -187,16 +176,6 @@ export const TranslationControlsCompact: React.FC = ({ /> - - {taskPrefilter && ( - - )} - - { const prefilter = usePrefilter(); @@ -67,7 +71,11 @@ export const PrefilterContainer = ({ {icon ?? } {title} - {!isSmall && content} + + {!isSmall && content} + {controls} + {!isSmall && alert} + {closeButton ?? ( - {canEditTask && ( - submitForm()} - > - {t('task_detail_submit_button')} - - )} + + + + + {canEditTask && ( + submitForm()} + > + {t('task_detail_submit_button')} + + )} + )} diff --git a/webapp/src/ee/task/components/TaskDetailActions.tsx b/webapp/src/ee/task/components/TaskDetailActions.tsx new file mode 100644 index 0000000000..4e95d04e9a --- /dev/null +++ b/webapp/src/ee/task/components/TaskDetailActions.tsx @@ -0,0 +1,140 @@ +import { Button } from '@mui/material'; +import { T, useTranslate } from '@tolgee/react'; + +import { confirmation } from 'tg.hooks/confirmation'; +import { components } from 'tg.service/apiSchema.generated'; +import { Scope } from 'tg.fixtures/permissions'; +import { messageService } from 'tg.service/MessageService'; +import { useApiMutation } from 'tg.service/http/useQueryApi'; + +import { useUser } from 'tg.globalContext/helpers'; +import { TASK_ACTIVE_STATES } from 'tg.component/task/taskActiveStates'; + +type TaskModel = components['schemas']['TaskModel']; +type SimpleProjectModel = components['schemas']['SimpleProjectModel']; + +type Props = { + task: TaskModel; + project: SimpleProjectModel; + projectScopes?: Scope[]; +}; + +export const TaskDetailActions = ({ task, project, projectScopes }: Props) => { + const user = useUser(); + const cancelMutation = useApiMutation({ + url: '/v2/projects/{projectId}/tasks/{taskNumber}/close', + method: 'put', + invalidatePrefix: [ + '/v2/projects/{projectId}/translations', + '/v2/projects/{projectId}/tasks', + '/v2/user-tasks', + ], + }); + + const reopenMutation = useApiMutation({ + url: '/v2/projects/{projectId}/tasks/{taskNumber}/reopen', + method: 'put', + invalidatePrefix: [ + '/v2/projects/{projectId}/translations', + '/v2/projects/{projectId}/tasks', + '/v2/user-tasks', + ], + }); + + const finishMutation = useApiMutation({ + url: '/v2/projects/{projectId}/tasks/{taskNumber}/finish', + method: 'put', + invalidatePrefix: [ + '/v2/projects/{projectId}/translations', + '/v2/projects/{projectId}/tasks', + '/v2/user-tasks', + ], + }); + + const canEditTask = projectScopes?.includes('tasks.edit'); + const canMarkAsDone = + projectScopes?.includes('tasks.edit') || + Boolean(task.assignees.find((u) => u.id === user?.id)); + + function handleClose() { + confirmation({ + title: , + onConfirm() { + cancelMutation.mutate( + { + path: { projectId: project.id, taskNumber: task.number }, + }, + { + onSuccess() { + messageService.success(); + }, + } + ); + }, + }); + } + + function handleReopen() { + reopenMutation.mutate( + { + path: { projectId: project.id, taskNumber: task.number }, + }, + { + onSuccess() { + messageService.success(); + }, + } + ); + } + + function handleMarkAsDone() { + finishMutation.mutate( + { + path: { projectId: project.id, taskNumber: task.number }, + }, + { + onSuccess() { + messageService.success(); + }, + } + ); + } + + const { t } = useTranslate(); + return ( + <> + {TASK_ACTIVE_STATES.includes(task.state) && ( + + )} + + {TASK_ACTIVE_STATES.includes(task.state) ? ( + + ) : ( + + )} + + ); +}; diff --git a/webapp/src/ee/task/components/TaskInfoMessage.tsx b/webapp/src/ee/task/components/TaskInfoMessage.tsx new file mode 100644 index 0000000000..f89e8ff092 --- /dev/null +++ b/webapp/src/ee/task/components/TaskInfoMessage.tsx @@ -0,0 +1,116 @@ +import { Alert, AlertTitle, styled } from '@mui/material'; +import { T } from '@tolgee/react'; +import { components } from 'tg.service/apiSchema.generated'; +import { useProject } from 'tg.hooks/useProject'; +import { TaskTooltip } from './TaskTooltip'; + +type TaskModel = components['schemas']['KeyTaskViewModel']; + +const StyledTaskId = styled('span')` + color: ${({ theme }) => theme.palette.primary.main}; + cursor: default; + text-decoration-line: underline; + text-decoration-style: solid; + text-decoration-skip-ink: none; + text-decoration-thickness: auto; + text-underline-offset: auto; + text-underline-position: from-font; +`; + +const TaskLink = (props: { task: number }) => { + const project = useProject(); + return ( + + #{props.task} + + ); +}; + +type Props = { + tasks: TaskModel[] | undefined; + currentTask: number | undefined; +}; + +export const TaskInfoMessage = ({ tasks, currentTask }: Props) => { + const firstTask = tasks?.[0]; + const userAssignedTask = tasks?.find((t) => t.userAssigned); + + if (firstTask && currentTask && firstTask.number !== currentTask) { + return ( + + , + blockingTask: , + }} + /> + + ); + } + + if ( + firstTask && + !firstTask?.userAssigned && + userAssignedTask && + currentTask !== firstTask.number + ) { + return ( + + , + blockingTask: , + }} + /> + + ); + } + + if (firstTask && firstTask.userAssigned && !currentTask) { + if (firstTask.type === 'TRANSLATE') { + return ( + + }} + /> + + ); + } else { + return ( + + }} + /> + + ); + } + } + + if (firstTask && !firstTask?.userAssigned) { + if (firstTask.type === 'TRANSLATE') { + return ( + + }} + /> + + ); + } else { + return ( + + }} + /> + + ); + } + } + + return null; +}; diff --git a/webapp/src/ee/task/components/TaskMenu.tsx b/webapp/src/ee/task/components/TaskMenu.tsx index 38f82c2004..b7ea60bbcd 100644 --- a/webapp/src/ee/task/components/TaskMenu.tsx +++ b/webapp/src/ee/task/components/TaskMenu.tsx @@ -226,7 +226,7 @@ export const TaskMenu = ({ )} {TASK_ACTIVE_STATES.includes(task.state) && ( - {t('task_menu_close_task')} + {t('task_menu_cancel_task')} )} {!hideTaskDetail && ( diff --git a/webapp/src/ee/task/components/TaskReference.tsx b/webapp/src/ee/task/components/TaskReference.tsx index 2a6bd46d52..4a74a5a462 100644 --- a/webapp/src/ee/task/components/TaskReference.tsx +++ b/webapp/src/ee/task/components/TaskReference.tsx @@ -15,11 +15,7 @@ export const TaskReference: React.FC = ({ data }) => { const project = useProject(); return ( - + ; actions?: Action[] | React.ReactNode | ((task: TaskModel) => React.ReactNode); - newTaskActions: boolean; } & Omit, 'title'>; export const TaskTooltip = ({ @@ -29,7 +28,6 @@ export const TaskTooltip = ({ project, children, actions = ['open', 'detail'], - newTaskActions, ...tooltipProps }: Props) => { const [taskDetailData, setTaskDetailData] = useState(); diff --git a/webapp/src/ee/task/components/TasksPanel.tsx b/webapp/src/ee/task/components/TasksPanel.tsx index dbf5822357..7a2f577178 100644 --- a/webapp/src/ee/task/components/TasksPanel.tsx +++ b/webapp/src/ee/task/components/TasksPanel.tsx @@ -51,7 +51,6 @@ export const TasksPanel: React.FC = ({ taskNumber={task.number} project={project} enterDelay={1000} - newTaskActions={false} > {task && ( - + (existingItems) => existingItems; export const TrialAnnouncement = Empty; export const TrialChip = Empty; +export const TaskInfoMessage = Empty; diff --git a/webapp/src/views/projects/translations/TranslationsList/TranslationWrite.tsx b/webapp/src/views/projects/translations/TranslationsList/TranslationWrite.tsx index 820d2c9a2d..892b6532d3 100644 --- a/webapp/src/views/projects/translations/TranslationsList/TranslationWrite.tsx +++ b/webapp/src/views/projects/translations/TranslationsList/TranslationWrite.tsx @@ -1,7 +1,10 @@ import { useRef, useState } from 'react'; import { Box, IconButton, Tooltip, styled } from '@mui/material'; import { Placeholder } from '@tginternal/editor'; +import { useTranslate } from '@tolgee/react'; +import { HelpCircle } from '@untitled-ui/icons-react'; +import { TaskInfoMessage } from 'tg.ee'; import { ControlsEditorMain } from '../cell/ControlsEditorMain'; import { ControlsEditorSmall } from '../cell/ControlsEditorSmall'; import { useTranslationsSelector } from '../context/TranslationsContext'; @@ -14,8 +17,6 @@ import { useMissingPlaceholders } from '../cell/useMissingPlaceholders'; import { TranslationVisual } from '../translationVisual/TranslationVisual'; import { ControlsEditorReadOnly } from '../cell/ControlsEditorReadOnly'; import { useBaseTranslation } from '../useBaseTranslation'; -import { HelpCircle } from '@untitled-ui/icons-react'; -import { useTranslate } from '@tolgee/react'; const StyledContainer = styled('div')` display: grid; @@ -48,9 +49,14 @@ const StyledContainer = styled('div')` const StyledBottom = styled(Box)` grid-area: controls-b; padding: 0px 12px 4px 16px; + display: grid; + gap: 8px; + margin-bottom: 8px; +`; + +const StyledControls = styled(Box)` display: flex; justify-content: space-between; - margin-bottom: 8px; flex-wrap: wrap; gap: 8px; align-items: center; @@ -83,7 +89,11 @@ export const TranslationWrite: React.FC = ({ tools }) => { const editVal = tools.editVal!; const state = translation?.state || 'UNTRANSLATED'; const activeVariant = editVal.activeVariant; - + const prefilteredTask = useTranslationsSelector((c) => + c.prefilteredTask?.language.id === language.id + ? c.prefilteredTask + : undefined + ); const [mode, setMode] = useState<'placeholders' | 'syntax'>('placeholders'); const editorRef = useRef(null); const baseLanguage = useTranslationsSelector((c) => c.baseLanguage); @@ -127,6 +137,10 @@ export const TranslationWrite: React.FC = ({ tools }) => { } }; + const translationTasks = keyData.tasks?.filter( + (t) => t.languageTag === language.tag + ); + return ( = ({ tools }) => { e.preventDefault()}> {editEnabled ? ( <> - - - - - - - - - handleClose(true)} - tasks={keyData.tasks?.filter( - (t) => t.languageTag === language.tag - )} + + + + + + + + + + + handleClose(true)} + tasks={translationTasks} + /> + ) : ( = ({ tools }) => { const editorRef = useRef(null); const baseLanguage = useTranslationsSelector((c) => c.baseLanguage); const nested = Boolean(editVal.value.parameter); + const prefilteredTask = useTranslationsSelector((c) => + c.prefilteredTask?.language.id === language.id + ? c.prefilteredTask + : undefined + ); const baseTranslation = useBaseTranslation( activeVariant, @@ -81,6 +92,10 @@ export const TranslationWrite: React.FC = ({ tools }) => { enabled: baseLanguage !== language.tag, }); + const translationTasks = keyData.tasks?.filter( + (t) => t.languageTag === language.tag + ); + const handleModeToggle = () => { setMode((mode) => (mode === 'syntax' ? 'placeholders' : 'syntax')); }; @@ -122,43 +137,51 @@ export const TranslationWrite: React.FC = ({ tools }) => { - {Boolean(missingPlaceholders.length) && ( - )} + + {Boolean(missingPlaceholders.length) && ( + + )} - - e.preventDefault(), - }} - state={state} - mode={mode} - isBaseLanguage={language.base} - stateChangeEnabled={canChangeState} - onInsertBase={editEnabled ? handleInsertBase : undefined} - onStateChange={setState} - onModeToggle={editEnabled ? handleModeToggle : undefined} - tasks={keyData.tasks?.filter((t) => t.languageTag === language.tag)} - onTaskStateChange={setAssignedTaskState} - /> - {editEnabled ? ( - handleClose(true)} + + e.preventDefault(), + }} + state={state} + mode={mode} + isBaseLanguage={language.base} + stateChangeEnabled={canChangeState} + onInsertBase={editEnabled ? handleInsertBase : undefined} + onStateChange={setState} + onModeToggle={editEnabled ? handleModeToggle : undefined} tasks={keyData.tasks?.filter( (t) => t.languageTag === language.tag )} + onTaskStateChange={setAssignedTaskState} /> - ) : ( - handleClose(true)} /> - )} - + {editEnabled ? ( + handleClose(true)} + tasks={translationTasks} + /> + ) : ( + handleClose(true)} /> + )} + + ); diff --git a/webapp/src/views/projects/translations/cell/ControlsEditorMain.tsx b/webapp/src/views/projects/translations/cell/ControlsEditorMain.tsx index 7ad4b1c1c7..757f5bd4c1 100644 --- a/webapp/src/views/projects/translations/cell/ControlsEditorMain.tsx +++ b/webapp/src/views/projects/translations/cell/ControlsEditorMain.tsx @@ -39,13 +39,8 @@ export const ControlsEditorMain: React.FC = ({ const anchorEl = useRef(null); const [open, setOpen] = useState(false); const task = tasks?.[0]; - const prefilteredTask = useTranslationsSelector((c) => c.prefilter?.task); const displayTaskControls = - task && - task.number === prefilteredTask && - task.userAssigned && - !task.done && - task.type === 'TRANSLATE'; + task && task.userAssigned && !task.done && task.type === 'TRANSLATE'; const withClose = (callback?: () => void) => () => { setOpen(false); diff --git a/webapp/src/views/projects/translations/context/TranslationsContext.ts b/webapp/src/views/projects/translations/context/TranslationsContext.ts index e384a27a1f..3d8a3ed1d3 100644 --- a/webapp/src/views/projects/translations/context/TranslationsContext.ts +++ b/webapp/src/views/projects/translations/context/TranslationsContext.ts @@ -68,6 +68,18 @@ export const [ const { satisfiesLanguageAccess } = useProjectPermissions(); + const prefilteredTaskLoadable = useApiQuery({ + url: '/v2/projects/{projectId}/tasks/{taskNumber}', + method: 'get', + path: { + projectId: props.projectId, + taskNumber: props.prefilter?.task as number, + }, + options: { + enabled: props.prefilter?.task !== undefined, + }, + }); + const languagesLoadable = useApiQuery({ url: '/v2/projects/{projectId}/languages', method: 'get', @@ -303,6 +315,7 @@ export const [ elementsRef: viewRefs.elementsRef, reactList: viewRefs.reactList, prefilter: props.prefilter, + prefilteredTask: prefilteredTaskLoadable.data, layout, }; diff --git a/webapp/src/views/projects/translations/context/services/useTranslationsService.tsx b/webapp/src/views/projects/translations/context/services/useTranslationsService.tsx index 061b3a1853..1c3471e760 100644 --- a/webapp/src/views/projects/translations/context/services/useTranslationsService.tsx +++ b/webapp/src/views/projects/translations/context/services/useTranslationsService.tsx @@ -6,6 +6,7 @@ import { T } from '@tolgee/react'; import { useApiInfiniteQuery, useApiMutation, + useApiQuery, } from 'tg.service/http/useQueryApi'; import { components, operations } from 'tg.service/apiSchema.generated'; import { useUrlSearchState } from 'tg.hooks/useUrlSearchState'; From 2272eb91fb6fd35cb39448af159156c7d8021126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Wed, 5 Mar 2025 16:05:04 +0100 Subject: [PATCH 08/26] feat: task info messages --- webapp/src/ee/task/components/TaskInfoMessage.tsx | 8 ++++---- .../context/services/useTranslationsService.tsx | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/webapp/src/ee/task/components/TaskInfoMessage.tsx b/webapp/src/ee/task/components/TaskInfoMessage.tsx index f89e8ff092..e33fe6fe5d 100644 --- a/webapp/src/ee/task/components/TaskInfoMessage.tsx +++ b/webapp/src/ee/task/components/TaskInfoMessage.tsx @@ -1,10 +1,10 @@ -import { Alert, AlertTitle, styled } from '@mui/material'; +import { Alert, styled } from '@mui/material'; import { T } from '@tolgee/react'; import { components } from 'tg.service/apiSchema.generated'; import { useProject } from 'tg.hooks/useProject'; import { TaskTooltip } from './TaskTooltip'; -type TaskModel = components['schemas']['KeyTaskViewModel']; +type KeyTaskViewModel = components['schemas']['KeyTaskViewModel']; const StyledTaskId = styled('span')` color: ${({ theme }) => theme.palette.primary.main}; @@ -27,7 +27,7 @@ const TaskLink = (props: { task: number }) => { }; type Props = { - tasks: TaskModel[] | undefined; + tasks: KeyTaskViewModel[] | undefined; currentTask: number | undefined; }; @@ -35,7 +35,7 @@ export const TaskInfoMessage = ({ tasks, currentTask }: Props) => { const firstTask = tasks?.[0]; const userAssignedTask = tasks?.find((t) => t.userAssigned); - if (firstTask && currentTask && firstTask.number !== currentTask) { + if (currentTask && firstTask && firstTask.number !== currentTask) { return ( Date: Wed, 5 Mar 2025 16:15:14 +0100 Subject: [PATCH 09/26] feat: task info messages --- .../ee/task/components/TaskInfoMessage.tsx | 39 +++++++++++++++---- .../TranslationsList/TranslationWrite.tsx | 2 +- .../TranslationsTable/TranslationWrite.tsx | 2 +- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/webapp/src/ee/task/components/TaskInfoMessage.tsx b/webapp/src/ee/task/components/TaskInfoMessage.tsx index e33fe6fe5d..92db1ea208 100644 --- a/webapp/src/ee/task/components/TaskInfoMessage.tsx +++ b/webapp/src/ee/task/components/TaskInfoMessage.tsx @@ -5,15 +5,13 @@ import { useProject } from 'tg.hooks/useProject'; import { TaskTooltip } from './TaskTooltip'; type KeyTaskViewModel = components['schemas']['KeyTaskViewModel']; +type TaskModel = components['schemas']['TaskModel']; const StyledTaskId = styled('span')` color: ${({ theme }) => theme.palette.primary.main}; cursor: default; text-decoration-line: underline; text-decoration-style: solid; - text-decoration-skip-ink: none; - text-decoration-thickness: auto; - text-underline-offset: auto; text-underline-position: from-font; `; @@ -28,20 +26,47 @@ const TaskLink = (props: { task: number }) => { type Props = { tasks: KeyTaskViewModel[] | undefined; - currentTask: number | undefined; + currentTask: TaskModel | undefined; }; export const TaskInfoMessage = ({ tasks, currentTask }: Props) => { const firstTask = tasks?.[0]; const userAssignedTask = tasks?.find((t) => t.userAssigned); - if (currentTask && firstTask && firstTask.number !== currentTask) { + if (currentTask?.closedAt) { + return ( + + , + }} + /> + + ); + } + + if (currentTask && firstTask && firstTask.number !== currentTask.number) { + return ( + + , + blockingTask: , + }} + /> + + ); + } + + if (currentTask && firstTask && firstTask.number !== currentTask.number) { return ( , + currentTask: , blockingTask: , }} /> @@ -53,7 +78,7 @@ export const TaskInfoMessage = ({ tasks, currentTask }: Props) => { firstTask && !firstTask?.userAssigned && userAssignedTask && - currentTask !== firstTask.number + currentTask?.number !== firstTask.number ) { return ( diff --git a/webapp/src/views/projects/translations/TranslationsList/TranslationWrite.tsx b/webapp/src/views/projects/translations/TranslationsList/TranslationWrite.tsx index 892b6532d3..ed38336072 100644 --- a/webapp/src/views/projects/translations/TranslationsList/TranslationWrite.tsx +++ b/webapp/src/views/projects/translations/TranslationsList/TranslationWrite.tsx @@ -181,7 +181,7 @@ export const TranslationWrite: React.FC = ({ tools }) => { <> diff --git a/webapp/src/views/projects/translations/TranslationsTable/TranslationWrite.tsx b/webapp/src/views/projects/translations/TranslationsTable/TranslationWrite.tsx index 71f7561107..b28a2e1680 100644 --- a/webapp/src/views/projects/translations/TranslationsTable/TranslationWrite.tsx +++ b/webapp/src/views/projects/translations/TranslationsTable/TranslationWrite.tsx @@ -140,7 +140,7 @@ export const TranslationWrite: React.FC = ({ tools }) => { {editEnabled && ( )} From 754b4fd2cb77621d1856f64b85f2ac21a0438b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Gran=C3=A1t?= Date: Wed, 5 Mar 2025 16:58:41 +0100 Subject: [PATCH 10/26] feat: rename task states --- .../kotlin/io/tolgee/model/enums/TaskState.kt | 4 +- .../main/resources/db/changelog/schema.xml | 6 ++ .../ee/api/v2/controllers/TaskController.kt | 20 +++++- .../io/tolgee/ee/data/task/TaskFilters.kt | 6 -- .../io/tolgee/ee/repository/TaskRepository.kt | 5 -- .../io/tolgee/ee/service/TaskService.kt | 8 +-- webapp/src/component/task/TaskState.tsx | 2 +- webapp/src/constants/links.tsx | 2 +- .../PrefilterTaskHideDoneSwitch.tsx | 2 +- .../components/TaskAllDonePlaceholder.tsx | 6 +- .../ee/task/components/TaskDetailActions.tsx | 4 +- webapp/src/ee/task/components/TaskMenu.tsx | 8 +-- webapp/src/ee/task/components/TasksBoard.tsx | 36 +++------- .../ee/task/views/myTasks/MyTasksBoard.tsx | 2 +- .../views/projectTasks/ProjectTasksBoard.tsx | 2 +- webapp/src/service/apiSchema.generated.ts | 67 ++++++++++++++++--- .../useTaskStateTranslation.ts | 8 +-- webapp/src/views/projects/TaskRedirect.tsx | 2 +- .../translations/prefilters/usePrefilter.ts | 2 +- 19 files changed, 117 insertions(+), 75 deletions(-) diff --git a/backend/data/src/main/kotlin/io/tolgee/model/enums/TaskState.kt b/backend/data/src/main/kotlin/io/tolgee/model/enums/TaskState.kt index 2f4a18c98f..2d363eca2a 100644 --- a/backend/data/src/main/kotlin/io/tolgee/model/enums/TaskState.kt +++ b/backend/data/src/main/kotlin/io/tolgee/model/enums/TaskState.kt @@ -3,6 +3,6 @@ package io.tolgee.model.enums enum class TaskState { NEW, IN_PROGRESS, - DONE, - CLOSED, + FINISHED, + CANCELED, } diff --git a/backend/data/src/main/resources/db/changelog/schema.xml b/backend/data/src/main/resources/db/changelog/schema.xml index 5ab36ee4cb..1d4f335800 100644 --- a/backend/data/src/main/resources/db/changelog/schema.xml +++ b/backend/data/src/main/resources/db/changelog/schema.xml @@ -4167,4 +4167,10 @@ + + + UPDATE task SET state = 'CANCELED' WHERE state = 'CLOSED'; + UPDATE task SET state = 'FINISHED' WHERE state = 'DONE'; + + diff --git a/ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/controllers/TaskController.kt b/ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/controllers/TaskController.kt index 88176e08e1..9e94c7c5be 100644 --- a/ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/controllers/TaskController.kt +++ b/ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/controllers/TaskController.kt @@ -176,12 +176,12 @@ class TaskController( ): TaskModel { // users can only finish tasks assigned to them securityService.hasTaskEditScopeOrIsAssigned(projectHolder.project.id, taskNumber) - val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.DONE) + val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.FINISHED) return taskModelAssembler.toModel(task) } @PutMapping("/{taskNumber}/close") - @Operation(summary = "Close task") + @Operation(summary = "Close task", deprecated = true) @RequiresProjectPermissions([Scope.TASKS_EDIT]) @AllowApiAccess @RequestActivity(ActivityType.TASK_CLOSE) @@ -190,7 +190,21 @@ class TaskController( @PathVariable taskNumber: Long, ): TaskModel { - val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.CLOSED) + val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.CANCELED) + return taskModelAssembler.toModel(task) + } + + @PutMapping("/{taskNumber}/cancel") + @Operation(summary = "Close task") + @RequiresProjectPermissions([Scope.TASKS_EDIT]) + @AllowApiAccess + @RequestActivity(ActivityType.TASK_CLOSE) + @OpenApiOrderExtension(7) + fun cancelTask( + @PathVariable + taskNumber: Long, + ): TaskModel { + val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.CANCELED) return taskModelAssembler.toModel(task) } diff --git a/ee/backend/app/src/main/kotlin/io/tolgee/ee/data/task/TaskFilters.kt b/ee/backend/app/src/main/kotlin/io/tolgee/ee/data/task/TaskFilters.kt index e1ee4dd057..35a3ce12a9 100644 --- a/ee/backend/app/src/main/kotlin/io/tolgee/ee/data/task/TaskFilters.kt +++ b/ee/backend/app/src/main/kotlin/io/tolgee/ee/data/task/TaskFilters.kt @@ -61,12 +61,6 @@ open class TaskFilters { ) var filterAgency: List? = null - @Deprecated("Confusing logic and naming", ReplaceWith("filterNotClosedBefore")) - @field:Parameter( - description = """Exclude "done" tasks which are older than specified timestamp""", - ) - var filterDoneMinClosedAt: Long? = null - @field:Parameter( description = """Exclude tasks which were closed before specified timestamp""", ) diff --git a/ee/backend/app/src/main/kotlin/io/tolgee/ee/repository/TaskRepository.kt b/ee/backend/app/src/main/kotlin/io/tolgee/ee/repository/TaskRepository.kt index 5a6f395e64..e5dba342d2 100644 --- a/ee/backend/app/src/main/kotlin/io/tolgee/ee/repository/TaskRepository.kt +++ b/ee/backend/app/src/main/kotlin/io/tolgee/ee/repository/TaskRepository.kt @@ -75,11 +75,6 @@ private const val TASK_FILTERS = """ where element(tt).key.id in :#{#filters.filterKey} ) ) - and ( - tk.state != 'DONE' - or :#{#filters.filterDoneMinClosedAt} is null - or tk.closedAt > :#{#filters.filterDoneMinClosedAt} - ) and ( :#{#filters.filterNotClosedBefore} is null or tk.closedAt is null diff --git a/ee/backend/app/src/main/kotlin/io/tolgee/ee/service/TaskService.kt b/ee/backend/app/src/main/kotlin/io/tolgee/ee/service/TaskService.kt index db4e6bd144..273a9ade69 100644 --- a/ee/backend/app/src/main/kotlin/io/tolgee/ee/service/TaskService.kt +++ b/ee/backend/app/src/main/kotlin/io/tolgee/ee/service/TaskService.kt @@ -232,7 +232,7 @@ class TaskService( ): TaskWithScopeView { val task = findByNumber(projectId, taskNumber) val taskWithScope = getTaskWithScope(task) - if (state == TaskState.DONE && taskWithScope.doneItems != taskWithScope.totalItems) { + if (state == TaskState.FINISHED && taskWithScope.doneItems != taskWithScope.totalItems) { throw BadRequestException(Message.TASK_NOT_FINISHED) } if (state == TaskState.NEW || state == TaskState.IN_PROGRESS) { @@ -297,7 +297,7 @@ class TaskService( ): UpdateTaskKeyResponse { val task = findByNumber(projectId, taskNumber) - if (task.state == TaskState.CLOSED || task.state == TaskState.DONE) { + if (task.state == TaskState.CANCELED || task.state == TaskState.FINISHED) { throw BadRequestException(Message.TASK_NOT_OPEN) } @@ -559,8 +559,8 @@ class TaskService( private fun createNotificationIfApplicable(task: Task) { val notificationType = when (task.state) { - TaskState.DONE -> NotificationType.TASK_COMPLETED - TaskState.CLOSED -> NotificationType.TASK_CLOSED + TaskState.FINISHED -> NotificationType.TASK_COMPLETED + TaskState.CANCELED -> NotificationType.TASK_CLOSED else -> return } diff --git a/webapp/src/component/task/TaskState.tsx b/webapp/src/component/task/TaskState.tsx index 7e6545dae7..89c30ea869 100644 --- a/webapp/src/component/task/TaskState.tsx +++ b/webapp/src/component/task/TaskState.tsx @@ -16,7 +16,7 @@ export const useStateColor = () => { const theme = useTheme(); return (state: TaskState) => - state === 'DONE' + state === 'FINISHED' ? theme.palette.tokens._components.progressbar.task.done : state === 'IN_PROGRESS' ? theme.palette.tokens._components.progressbar.task.inProgress diff --git a/webapp/src/constants/links.tsx b/webapp/src/constants/links.tsx index e1c91b2dde..033147f7f7 100644 --- a/webapp/src/constants/links.tsx +++ b/webapp/src/constants/links.tsx @@ -409,7 +409,7 @@ export enum QUERY { TRANSLATIONS_PREFILTERS_ACTIVITY = 'activity', TRANSLATIONS_PREFILTERS_FAILED_JOB = 'failedJob', TRANSLATIONS_PREFILTERS_TASK = 'task', - TRANSLATIONS_PREFILTERS_TASK_HIDE_DONE = 'taskHideDone', + TRANSLATIONS_PREFILTERS_TASK_HIDE_CLOSED = 'taskHideClosed', TRANSLATIONS_TASK_DETAIL = 'taskDetail', TASKS_FILTERS_SHOW_ALL = 'showAll', } diff --git a/webapp/src/ee/task/components/PrefilterTaskHideDoneSwitch.tsx b/webapp/src/ee/task/components/PrefilterTaskHideDoneSwitch.tsx index 827f1279da..06c59f0e31 100644 --- a/webapp/src/ee/task/components/PrefilterTaskHideDoneSwitch.tsx +++ b/webapp/src/ee/task/components/PrefilterTaskHideDoneSwitch.tsx @@ -17,7 +17,7 @@ type Props = { export const PrefilterTaskHideDoneSwitch = ({ sx }: Props) => { const [taskHideDone, setTaskHideDone] = useUrlSearchState( - QUERY.TRANSLATIONS_PREFILTERS_TASK_HIDE_DONE + QUERY.TRANSLATIONS_PREFILTERS_TASK_HIDE_CLOSED ); const { t } = useTranslate(); diff --git a/webapp/src/ee/task/components/TaskAllDonePlaceholder.tsx b/webapp/src/ee/task/components/TaskAllDonePlaceholder.tsx index 631223f420..51fd16e72c 100644 --- a/webapp/src/ee/task/components/TaskAllDonePlaceholder.tsx +++ b/webapp/src/ee/task/components/TaskAllDonePlaceholder.tsx @@ -15,7 +15,7 @@ type Props = { export const TaskAllDonePlaceholder = ({ taskNumber, projectId }: Props) => { const [_, setTaskHideDone] = useUrlSearchState( - QUERY.TRANSLATIONS_PREFILTERS_TASK_HIDE_DONE + QUERY.TRANSLATIONS_PREFILTERS_TASK_HIDE_CLOSED ); const { finishTask } = useTranslationsActions(); const user = useUser(); @@ -40,7 +40,9 @@ export const TaskAllDonePlaceholder = ({ taskNumber, projectId }: Props) => { height="0px" hint={ isAssigned && - !['DONE', 'CLOSED'].includes(taskLoadable.data?.state as TaskState) && ( + !['FINISHED', 'CANCELED'].includes( + taskLoadable.data?.state as TaskState + ) && ( diff --git a/webapp/src/ee/task/components/TaskDetailActions.tsx b/webapp/src/ee/task/components/TaskDetailActions.tsx index 4e95d04e9a..e15a473e3a 100644 --- a/webapp/src/ee/task/components/TaskDetailActions.tsx +++ b/webapp/src/ee/task/components/TaskDetailActions.tsx @@ -22,7 +22,7 @@ type Props = { export const TaskDetailActions = ({ task, project, projectScopes }: Props) => { const user = useUser(); const cancelMutation = useApiMutation({ - url: '/v2/projects/{projectId}/tasks/{taskNumber}/close', + url: '/v2/projects/{projectId}/tasks/{taskNumber}/cancel', method: 'put', invalidatePrefix: [ '/v2/projects/{projectId}/translations', @@ -123,7 +123,7 @@ export const TaskDetailActions = ({ task, project, projectScopes }: Props) => { onClick={handleMarkAsDone} disabled={task.doneItems !== task.totalItems || !canMarkAsDone} > - {t('task_menu_mark_as_done')} + {t('task_menu_mark_as_finished')} ) : (