Skip to content

Commit

Permalink
feat: improve batch operations
Browse files Browse the repository at this point in the history
  • Loading branch information
stepan662 committed Jul 18, 2023
1 parent 25ba436 commit eda493c
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FunctionComponent } from 'react';
import { Select, styled, Typography } from '@mui/material';
import { InputLabel, Select, styled, Typography } from '@mui/material';
import FormControl from '@mui/material/FormControl';

import { components } from 'tg.service/apiSchema.generated';
Expand Down Expand Up @@ -36,6 +36,7 @@ export type Props = {
value: string[];
context: string;
enableEmpty?: boolean;
placeholder?: string;
};

export const LanguagesSelect: FunctionComponent<Props> = (props) => {
Expand All @@ -55,6 +56,11 @@ export const LanguagesSelect: FunctionComponent<Props> = (props) => {
variant="outlined"
size="small"
>
{props.placeholder && props.value.length === 0 && (
<InputLabel focused={false} shrink={false}>
{props.placeholder}
</InputLabel>
)}
<StyledSelect
labelId={`languages-${props.context}`}
id={`languages-select-${props.context}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export function useBatchOperationTypeTranslate() {
return t('batch_operation_type_delete_keys');
case 'AUTO_TRANSLATION':
return t('batch_operation_type_translation');
case 'COPY_TRANSLATIONS':
return t('batch_operation_type_copy_translations');
case 'CLEAR_TRANSLATIONS':
return t('batch_operation_type_clear_translations');
case 'SET_TRANSLATIONS_STATE':
return t('batch_operation_set_translations_state');
default:
return type;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import {
import { BatchSelect } from './BatchSelect';
import { OperationAddTags } from './OperationAddTags';
import { OperationChangeNamespace } from './OperationChangeNamespace';
import { OperationClearTranslations } from './OperationClearTranslations';
import { OperationCopyTranslations } from './OperationCopyTranslations';
import { OperationDelete } from './OperationDelete';
import { OperationMarkAsReviewed } from './OperationMarkAsReviewed';
import { OperationMarkAsTranslated } from './OperationMarkAsTranslated';
import { OperationRemoveTags } from './OperationRemoveTags';
import { BatchOperationDialog } from './OperationsSummary/BatchOperationDialog';
import { OperationTranslate } from './OperationTranslate';
import { OperationAutoTranslate } from './OperationAutoTranslate';
import { BatchActions, BatchJobModel } from './types';

const StyledContainer = styled('div')`
Expand Down Expand Up @@ -62,9 +64,10 @@ const StyledItem = styled(Box)`

type Props = {
open: boolean;
onClose: () => void;
};

export const BatchOperations = ({ open }: Props) => {
export const BatchOperations = ({ open, onClose }: Props) => {
const { t } = useTranslate();
const selection = useTranslationsSelector((c) => c.selection);
const totalCount = useTranslationsSelector((c) => c.translationsTotal || 0);
Expand Down Expand Up @@ -99,8 +102,8 @@ export const BatchOperations = ({ open }: Props) => {
switch (operation) {
case 'delete':
return <OperationDelete {...sharedProps} />;
case 'translate':
return <OperationTranslate {...sharedProps} />;
case 'auto_translate':
return <OperationAutoTranslate {...sharedProps} />;
case 'mark_as_translated':
return <OperationMarkAsTranslated {...sharedProps} />;
case 'mark_as_reviewed':
Expand All @@ -111,13 +114,18 @@ export const BatchOperations = ({ open }: Props) => {
return <OperationRemoveTags {...sharedProps} />;
case 'change_namespace':
return <OperationChangeNamespace {...sharedProps} />;
case 'copy_translations':
return <OperationCopyTranslations {...sharedProps} />;
case 'clear_translations':
return <OperationClearTranslations {...sharedProps} />;
}
}

function onFinished() {
refetchTranslations();
selectionClear();
setOperation(undefined);
onClose();
}

useGlobalLoading(isLoading);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Autocomplete, ListItem, TextField } from '@mui/material';
import { useTranslate } from '@tolgee/react';
import { BatchActions, OperationProps } from './types';
import { BatchActions } from './types';

type Props = OperationProps & {
type Props = {
value: BatchActions | undefined;
onChange: (value: BatchActions | undefined) => void;
};

export const BatchSelect = ({ value, onChange, disabled }: Props) => {
export const BatchSelect = ({ value, onChange }: Props) => {
const { t } = useTranslate();

const options = [
{ id: 'delete', label: t('batch_operations_delete') },
{ id: 'translate', label: t('batch_operations_translate') },
{ id: 'auto_translate', label: t('batch_operations_translate') },
{ id: 'mark_as_reviewed', label: t('batch_operations_mark_as_reviewed') },
{
id: 'mark_as_translated',
Expand All @@ -21,6 +21,11 @@ export const BatchSelect = ({ value, onChange, disabled }: Props) => {
{ id: 'add_tags', label: t('batch_operations_add_tags') },
{ id: 'remove_tags', label: t('batch_operations_remove_tags') },
{ id: 'change_namespace', label: t('batch_operations_change_namespace') },
{ id: 'copy_translations', label: t('batch_operations_copy_translations') },
{
id: 'clear_translations',
label: t('batch_operation_clear_translations'),
},
] as const;

return (
Expand All @@ -40,7 +45,6 @@ export const BatchSelect = ({ value, onChange, disabled }: Props) => {
<TextField {...params} placeholder={t('batch_select_placeholder')} />
)}
size="small"
disabled={disabled}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,19 @@ import LoadingButton from 'tg.component/common/form/LoadingButton';

import { useTranslationsSelector } from '../context/TranslationsContext';
import { OperationProps } from './types';
import { useTranslate } from '@tolgee/react';

type Props = OperationProps;

export const OperationTranslate = ({ disabled, onStart }: Props) => {
export const OperationAutoTranslate = ({ disabled, onStart }: Props) => {
const project = useProject();
const { t } = useTranslate();
const allLanguages = useTranslationsSelector((c) => c.languages) || [];
const selectedGloballyLanguages = useTranslationsSelector(
(c) => c.selectedLanguages
);
const selection = useTranslationsSelector((c) => c.selection);

const languages = allLanguages.filter((l) => !l.base);
const baseLang = allLanguages.find((l) => l.base);

const [selectedLangs, setSelectedLangs] = useState(
selectedGloballyLanguages?.filter((tag) => tag !== baseLang?.tag)
);
const [selectedLangs, setSelectedLangs] = useState<string[]>([]);

const batchLoadable = useApiMutation({
url: '/v2/projects/{projectId}/start-batch-job/translate',
Expand Down Expand Up @@ -64,6 +60,7 @@ export const OperationTranslate = ({ disabled, onStart }: Props) => {
onChange={setSelectedLangs}
enableEmpty
context="batch-operations"
placeholder={t('batch_operations_select_languages_placeholder')}
/>
<LoadingButton
data-cy="batch-operations-submit-button"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useState } from 'react';
import { ChevronRight } from '@mui/icons-material';
import { Box } from '@mui/material';

import { LanguagesSelect } from 'tg.component/common/form/LanguagesSelect/LanguagesSelect';
import { useApiMutation } from 'tg.service/http/useQueryApi';
import { useProject } from 'tg.hooks/useProject';
import LoadingButton from 'tg.component/common/form/LoadingButton';

import { useTranslationsSelector } from '../context/TranslationsContext';
import { OperationProps } from './types';
import { useTranslate } from '@tolgee/react';

type Props = OperationProps;

export const OperationClearTranslations = ({ disabled, onStart }: Props) => {
const project = useProject();
const { t } = useTranslate();
const allLanguages = useTranslationsSelector((c) => c.languages) || [];
const selection = useTranslationsSelector((c) => c.selection);

const [selectedLangs, setSelectedLangs] = useState<string[]>([]);

const batchLoadable = useApiMutation({
url: '/v2/projects/{projectId}/start-batch-job/clear-translations',
method: 'post',
});

function handleSubmit() {
batchLoadable.mutate(
{
path: { projectId: project.id },
content: {
'application/json': {
keyIds: selection,
languageIds: allLanguages
?.filter((l) => selectedLangs?.includes(l.tag))
.map((l) => l.id),
},
},
},
{
onSuccess(data) {
onStart(data);
},
}
);
}

return (
<Box display="flex" gap="10px">
<LanguagesSelect
languages={allLanguages || []}
value={selectedLangs || []}
onChange={setSelectedLangs}
enableEmpty
context="batch-operations"
placeholder={t('batch_operations_select_languages_placeholder')}
/>
<LoadingButton
data-cy="batch-operations-submit-button"
loading={batchLoadable.isLoading}
disabled={disabled}
sx={{ minWidth: 0, minHeight: 0, width: 40, height: 40 }}
onClick={handleSubmit}
variant="contained"
color="primary"
>
<ChevronRight />
</LoadingButton>
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { useState } from 'react';
import { ChevronRight } from '@mui/icons-material';
import { Box, Select, MenuItem, FormControl, InputLabel } from '@mui/material';

import { LanguagesSelect } from 'tg.component/common/form/LanguagesSelect/LanguagesSelect';
import { useApiMutation } from 'tg.service/http/useQueryApi';
import { useProject } from 'tg.hooks/useProject';
import LoadingButton from 'tg.component/common/form/LoadingButton';

import { useTranslationsSelector } from '../context/TranslationsContext';
import { OperationProps } from './types';
import { useTranslate } from '@tolgee/react';

type Props = OperationProps;

export const OperationCopyTranslations = ({ disabled, onStart }: Props) => {
const project = useProject();
const allLanguages = useTranslationsSelector((c) => c.languages) || [];
const selection = useTranslationsSelector((c) => c.selection);
const { t } = useTranslate();

const [sourceLanguage, setSourceLanguage] = useState<string | null>(null);
const [selectedLangs, setSelectedLangs] = useState<string[]>([]);

function handleChangeSource(source: string | null) {
setSourceLanguage(source);
setSelectedLangs((langs) => langs?.filter((l) => l !== source));
}

const batchLoadable = useApiMutation({
url: '/v2/projects/{projectId}/start-batch-job/copy-translations',
method: 'post',
});

function handleSubmit() {
batchLoadable.mutate(
{
path: { projectId: project.id },
content: {
'application/json': {
keyIds: selection,
sourceLanguageId: allLanguages!.find(
(l) => l.tag === sourceLanguage
)!.id,
targetLanguageIds: allLanguages
?.filter((l) => selectedLangs?.includes(l.tag))
.map((l) => l.id),
},
},
},
{
onSuccess(data) {
onStart(data);
},
}
);
}

return (
<Box display="flex" gap="10px" alignItems="center">
<Box>{t('batch_operations_copy_from_label')}</Box>
<FormControl variant="outlined" size="small">
{!sourceLanguage && (
<InputLabel focused={false} shrink={false}>
{t('batch_operations_copy_source_language_placeholder')}
</InputLabel>
)}
<Select
value={sourceLanguage}
onChange={(e) => handleChangeSource(e.target.value)}
size="small"
sx={{ width: 150 }}
>
{allLanguages.map((l) => (
<MenuItem key={l.id} value={l.tag}>
{l.name}
</MenuItem>
))}
</Select>
</FormControl>

<Box>{t('batch_operations_copy_to_label')}</Box>

<LanguagesSelect
languages={allLanguages || []}
value={selectedLangs || []}
onChange={setSelectedLangs}
enableEmpty
disabledLanguages={allLanguages
.filter((l) => l.tag === sourceLanguage)
.map((l) => l.id)}
context="batch-operations"
placeholder={t('batch_operations_select_languages_placeholder')}
/>
<LoadingButton
data-cy="batch-operations-submit-button"
loading={batchLoadable.isLoading}
disabled={disabled || !sourceLanguage || selectedLangs.length === 0}
sx={{ minWidth: 0, minHeight: 0, width: 40, height: 40 }}
onClick={handleSubmit}
variant="contained"
color="primary"
>
<ChevronRight />
</LoadingButton>
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,18 @@ import { useApiMutation } from 'tg.service/http/useQueryApi';

import { useTranslationsSelector } from '../context/TranslationsContext';
import { OperationProps } from './types';
import { useTranslate } from '@tolgee/react';

type Props = OperationProps;

export const OperationMarkAsReviewed = ({ disabled, onStart }: Props) => {
const { t } = useTranslate();
const project = useProject();
const allLanguages = useTranslationsSelector((c) => c.languages) || [];

const selectedGloballyLanguages = useTranslationsSelector(
(c) => c.selectedLanguages
);
const selection = useTranslationsSelector((c) => c.selection);

const baseLang = allLanguages.find((l) => l.base);

const [selectedLangs, setSelectedLangs] = useState(
selectedGloballyLanguages?.filter((tag) => tag !== baseLang?.tag)
);
const [selectedLangs, setSelectedLangs] = useState<string[]>([]);

const batchLoadable = useApiMutation({
url: '/v2/projects/{projectId}/start-batch-job/set-translation-state',
Expand Down Expand Up @@ -62,6 +57,7 @@ export const OperationMarkAsReviewed = ({ disabled, onStart }: Props) => {
onChange={setSelectedLangs}
enableEmpty
context="batch-operations"
placeholder={t('batch_operations_select_languages_placeholder')}
/>
<LoadingButton
data-cy="batch-operations-submit-button"
Expand Down
Loading

0 comments on commit eda493c

Please sign in to comment.