New: Bulk Manage Applications, Download Clients

Co-authored-by: Qstick <qstick@gmail.com>
This commit is contained in:
Bogdan
2023-07-16 19:47:59 +03:00
parent f7c135faaf
commit f436d730fe
123 changed files with 4076 additions and 223 deletions
@@ -8,6 +8,7 @@ import { icons } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
import IndexersConnector from './Indexers/IndexersConnector';
import ManageIndexersModal from './Indexers/Manage/ManageIndexersModal';
import IndexerOptionsConnector from './Options/IndexerOptionsConnector';
class IndexerSettings extends Component {
@@ -22,7 +23,8 @@ class IndexerSettings extends Component {
this.state = {
isSaving: false,
hasPendingChanges: false
hasPendingChanges: false,
isManageIndexersOpen: false
};
}
@@ -37,6 +39,14 @@ class IndexerSettings extends Component {
this.setState(payload);
};
onManageIndexersPress = () => {
this.setState({ isManageIndexersOpen: true });
};
onManageIndexersModalClose = () => {
this.setState({ isManageIndexersOpen: false });
};
onSavePress = () => {
if (this._saveCallback) {
this._saveCallback();
@@ -54,7 +64,8 @@ class IndexerSettings extends Component {
const {
isSaving,
hasPendingChanges
hasPendingChanges,
isManageIndexersOpen
} = this.state;
return (
@@ -72,6 +83,12 @@ class IndexerSettings extends Component {
isSpinning={isTestingAll}
onPress={dispatchTestAllIndexers}
/>
<PageToolbarButton
label={translate('ManageIndexers')}
iconName={icons.MANAGE}
onPress={this.onManageIndexersPress}
/>
</Fragment>
}
onSavePress={this.onSavePress}
@@ -84,6 +101,11 @@ class IndexerSettings extends Component {
onChildMounted={this.onChildMounted}
onChildStateChange={this.onChildStateChange}
/>
<ManageIndexersModal
isOpen={isManageIndexersOpen}
onModalClose={this.onManageIndexersModalClose}
/>
</PageContentBody>
</PageContent>
);
@@ -0,0 +1,26 @@
import React from 'react';
import Modal from 'Components/Modal/Modal';
import ManageIndexersEditModalContent from './ManageIndexersEditModalContent';
interface ManageIndexersEditModalProps {
isOpen: boolean;
indexerIds: number[];
onSavePress(payload: object): void;
onModalClose(): void;
}
function ManageIndexersEditModal(props: ManageIndexersEditModalProps) {
const { isOpen, indexerIds, onSavePress, onModalClose } = props;
return (
<Modal isOpen={isOpen} onModalClose={onModalClose}>
<ManageIndexersEditModalContent
indexerIds={indexerIds}
onSavePress={onSavePress}
onModalClose={onModalClose}
/>
</Modal>
);
}
export default ManageIndexersEditModal;
@@ -0,0 +1,16 @@
.modalFooter {
composes: modalFooter from '~Components/Modal/ModalFooter.css';
justify-content: space-between;
}
.selected {
font-weight: bold;
}
@media only screen and (max-width: $breakpointExtraSmall) {
.modalFooter {
flex-direction: column;
gap: 10px;
}
}
@@ -0,0 +1,8 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'modalFooter': string;
'selected': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -0,0 +1,178 @@
import React, { useCallback, useState } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './ManageIndexersEditModalContent.css';
interface SavePayload {
enableRss?: boolean;
enableAutomaticSearch?: boolean;
enableInteractiveSearch?: boolean;
priority?: number;
}
interface ManageIndexersEditModalContentProps {
indexerIds: number[];
onSavePress(payload: object): void;
onModalClose(): void;
}
const NO_CHANGE = 'noChange';
const enableOptions = [
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
{ key: 'enabled', value: translate('Enabled') },
{ key: 'disabled', value: translate('Disabled') },
];
function ManageIndexersEditModalContent(
props: ManageIndexersEditModalContentProps
) {
const { indexerIds, onSavePress, onModalClose } = props;
const [enableRss, setEnableRss] = useState(NO_CHANGE);
const [enableAutomaticSearch, setEnableAutomaticSearch] = useState(NO_CHANGE);
const [enableInteractiveSearch, setEnableInteractiveSearch] =
useState(NO_CHANGE);
const [priority, setPriority] = useState<null | string | number>(null);
const save = useCallback(() => {
let hasChanges = false;
const payload: SavePayload = {};
if (enableRss !== NO_CHANGE) {
hasChanges = true;
payload.enableRss = enableRss === 'enabled';
}
if (enableAutomaticSearch !== NO_CHANGE) {
hasChanges = true;
payload.enableAutomaticSearch = enableAutomaticSearch === 'enabled';
}
if (enableInteractiveSearch !== NO_CHANGE) {
hasChanges = true;
payload.enableInteractiveSearch = enableInteractiveSearch === 'enabled';
}
if (priority !== null) {
hasChanges = true;
payload.priority = priority as number;
}
if (hasChanges) {
onSavePress(payload);
}
onModalClose();
}, [
enableRss,
enableAutomaticSearch,
enableInteractiveSearch,
priority,
onSavePress,
onModalClose,
]);
const onInputChange = useCallback(
({ name, value }: { name: string; value: string }) => {
switch (name) {
case 'enableRss':
setEnableRss(value);
break;
case 'enableAutomaticSearch':
setEnableAutomaticSearch(value);
break;
case 'enableInteractiveSearch':
setEnableInteractiveSearch(value);
break;
case 'priority':
setPriority(value);
break;
default:
console.warn(`EditIndexersModalContent Unknown Input: '${name}'`);
}
},
[]
);
const selectedCount = indexerIds.length;
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('EditSelectedIndexers')}</ModalHeader>
<ModalBody>
<FormGroup>
<FormLabel>{translate('EnableRSS')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="enableRss"
value={enableRss}
values={enableOptions}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('EnableAutomaticSearch')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="enableAutomaticSearch"
value={enableAutomaticSearch}
values={enableOptions}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('EnableInteractiveSearch')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="enableInteractiveSearch"
value={enableInteractiveSearch}
values={enableOptions}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('Priority')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="priority"
value={priority}
min={1}
max={50}
onChange={onInputChange}
/>
</FormGroup>
</ModalBody>
<ModalFooter className={styles.modalFooter}>
<div className={styles.selected}>
{translate('CountIndexersSelected', [selectedCount])}
</div>
<div>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button onPress={save}>{translate('ApplyChanges')}</Button>
</div>
</ModalFooter>
</ModalContent>
);
}
export default ManageIndexersEditModalContent;
@@ -0,0 +1,20 @@
import React from 'react';
import Modal from 'Components/Modal/Modal';
import ManageIndexersModalContent from './ManageIndexersModalContent';
interface ManageIndexersModalProps {
isOpen: boolean;
onModalClose(): void;
}
function ManageIndexersModal(props: ManageIndexersModalProps) {
const { isOpen, onModalClose } = props;
return (
<Modal isOpen={isOpen} onModalClose={onModalClose}>
<ManageIndexersModalContent onModalClose={onModalClose} />
</Modal>
);
}
export default ManageIndexersModal;
@@ -0,0 +1,16 @@
.leftButtons,
.rightButtons {
display: flex;
flex: 1 0 50%;
flex-wrap: wrap;
}
.rightButtons {
justify-content: flex-end;
}
.deleteButton {
composes: button from '~Components/Link/Button.css';
margin-right: 10px;
}
@@ -0,0 +1,9 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'deleteButton': string;
'leftButtons': string;
'rightButtons': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -0,0 +1,295 @@
import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { IndexerAppState } from 'App/State/SettingsAppState';
import Alert from 'Components/Alert';
import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { kinds } from 'Helpers/Props';
import {
bulkDeleteIndexers,
bulkEditIndexers,
} from 'Store/Actions/settingsActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import { SelectStateInputProps } from 'typings/props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import ManageIndexersEditModal from './Edit/ManageIndexersEditModal';
import ManageIndexersModalRow from './ManageIndexersModalRow';
import TagsModal from './Tags/TagsModal';
import styles from './ManageIndexersModalContent.css';
// TODO: This feels janky to do, but not sure of a better way currently
type OnSelectedChangeCallback = React.ComponentProps<
typeof ManageIndexersModalRow
>['onSelectedChange'];
const COLUMNS = [
{
name: 'name',
label: translate('Name'),
isSortable: true,
isVisible: true,
},
{
name: 'implementation',
label: translate('Implementation'),
isSortable: true,
isVisible: true,
},
{
name: 'enableRss',
label: translate('EnableRSS'),
isSortable: true,
isVisible: true,
},
{
name: 'enableAutomaticSearch',
label: translate('EnableAutomaticSearch'),
isSortable: true,
isVisible: true,
},
{
name: 'enableInteractiveSearch',
label: translate('EnableInteractiveSearch'),
isSortable: true,
isVisible: true,
},
{
name: 'priority',
label: translate('Priority'),
isSortable: true,
isVisible: true,
},
{
name: 'tags',
label: translate('Tags'),
isSortable: true,
isVisible: true,
},
];
interface ManageIndexersModalContentProps {
onModalClose(): void;
}
function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
const { onModalClose } = props;
const {
isFetching,
isPopulated,
isDeleting,
isSaving,
error,
items,
}: IndexerAppState = useSelector(
createClientSideCollectionSelector('settings.indexers')
);
const dispatch = useDispatch();
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [isTagsModalOpen, setIsTagsModalOpen] = useState(false);
const [isSavingTags, setIsSavingTags] = useState(false);
const [selectState, setSelectState] = useSelectState();
const { allSelected, allUnselected, selectedState } = selectState;
const selectedIds: number[] = useMemo(() => {
return getSelectedIds(selectedState);
}, [selectedState]);
const selectedCount = selectedIds.length;
const onDeletePress = useCallback(() => {
setIsDeleteModalOpen(true);
}, [setIsDeleteModalOpen]);
const onDeleteModalClose = useCallback(() => {
setIsDeleteModalOpen(false);
}, [setIsDeleteModalOpen]);
const onEditPress = useCallback(() => {
setIsEditModalOpen(true);
}, [setIsEditModalOpen]);
const onEditModalClose = useCallback(() => {
setIsEditModalOpen(false);
}, [setIsEditModalOpen]);
const onConfirmDelete = useCallback(() => {
dispatch(bulkDeleteIndexers({ ids: selectedIds }));
setIsDeleteModalOpen(false);
}, [selectedIds, dispatch]);
const onSavePress = useCallback(
(payload: object) => {
setIsEditModalOpen(false);
dispatch(
bulkEditIndexers({
ids: selectedIds,
...payload,
})
);
},
[selectedIds, dispatch]
);
const onTagsPress = useCallback(() => {
setIsTagsModalOpen(true);
}, [setIsTagsModalOpen]);
const onTagsModalClose = useCallback(() => {
setIsTagsModalOpen(false);
}, [setIsTagsModalOpen]);
const onApplyTagsPress = useCallback(
(tags: number[], applyTags: string) => {
setIsSavingTags(true);
setIsTagsModalOpen(false);
dispatch(
bulkEditIndexers({
ids: selectedIds,
tags,
applyTags,
})
);
},
[selectedIds, dispatch]
);
const onSelectAllChange = useCallback(
({ value }: SelectStateInputProps) => {
setSelectState({ type: value ? 'selectAll' : 'unselectAll', items });
},
[items, setSelectState]
);
const onSelectedChange = useCallback<OnSelectedChangeCallback>(
({ id, value, shiftKey = false }) => {
setSelectState({
type: 'toggleSelected',
items,
id,
isSelected: value,
shiftKey,
});
},
[items, setSelectState]
);
const errorMessage = getErrorMessage(error, 'Unable to load indexers.');
const anySelected = selectedCount > 0;
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('ManageIndexers')}</ModalHeader>
<ModalBody>
{isFetching ? <LoadingIndicator /> : null}
{error ? <div>{errorMessage}</div> : null}
{isPopulated && !error && !items.length && (
<Alert kind={kinds.INFO}>{translate('NoIndexersFound')}</Alert>
)}
{isPopulated && !!items.length && !isFetching && !isFetching ? (
<Table
columns={COLUMNS}
horizontalScroll={true}
selectAll={true}
allSelected={allSelected}
allUnselected={allUnselected}
onSelectAllChange={onSelectAllChange}
>
<TableBody>
{items.map((item) => {
return (
<ManageIndexersModalRow
key={item.id}
isSelected={selectedState[item.id]}
{...item}
columns={COLUMNS}
onSelectedChange={onSelectedChange}
/>
);
})}
</TableBody>
</Table>
) : null}
</ModalBody>
<ModalFooter>
<div className={styles.leftButtons}>
<SpinnerButton
kind={kinds.DANGER}
isSpinning={isDeleting}
isDisabled={!anySelected}
onPress={onDeletePress}
>
{translate('Delete')}
</SpinnerButton>
<SpinnerButton
isSpinning={isSaving}
isDisabled={!anySelected}
onPress={onEditPress}
>
{translate('Edit')}
</SpinnerButton>
<SpinnerButton
isSpinning={isSaving && isSavingTags}
isDisabled={!anySelected}
onPress={onTagsPress}
>
{translate('SetTags')}
</SpinnerButton>
</div>
<Button onPress={onModalClose}>{translate('Close')}</Button>
</ModalFooter>
<ManageIndexersEditModal
isOpen={isEditModalOpen}
onModalClose={onEditModalClose}
onSavePress={onSavePress}
indexerIds={selectedIds}
/>
<TagsModal
isOpen={isTagsModalOpen}
ids={selectedIds}
onApplyTagsPress={onApplyTagsPress}
onModalClose={onTagsModalClose}
/>
<ConfirmModal
isOpen={isDeleteModalOpen}
kind={kinds.DANGER}
title={translate('DeleteSelectedIndexers')}
message={translate('DeleteSelectedIndexersMessageText', [
selectedIds.length,
])}
confirmLabel={translate('Delete')}
onConfirm={onConfirmDelete}
onCancel={onDeleteModalClose}
/>
</ModalContent>
);
}
export default ManageIndexersModalContent;
@@ -0,0 +1,11 @@
.name,
.tags,
.enableRss,
.enableAutomaticSearch,
.enableInteractiveSearch,
.priority,
.implementation {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
word-break: break-all;
}
@@ -0,0 +1,13 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'enableAutomaticSearch': string;
'enableInteractiveSearch': string;
'enableRss': string;
'implementation': string;
'name': string;
'priority': string;
'tags': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -0,0 +1,100 @@
import React, { useCallback } from 'react';
import Label from 'Components/Label';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import Column from 'Components/Table/Column';
import TableRow from 'Components/Table/TableRow';
import TagListConnector from 'Components/TagListConnector';
import { kinds } from 'Helpers/Props';
import { SelectStateInputProps } from 'typings/props';
import translate from 'Utilities/String/translate';
import styles from './ManageIndexersModalRow.css';
interface ManageIndexersModalRowProps {
id: number;
name: string;
enableRss: boolean;
enableAutomaticSearch: boolean;
enableInteractiveSearch: boolean;
priority: number;
implementation: string;
tags: number[];
columns: Column[];
isSelected?: boolean;
onSelectedChange(result: SelectStateInputProps): void;
}
function ManageIndexersModalRow(props: ManageIndexersModalRowProps) {
const {
id,
isSelected,
name,
enableRss,
enableAutomaticSearch,
enableInteractiveSearch,
priority,
implementation,
tags,
onSelectedChange,
} = props;
const onSelectedChangeWrapper = useCallback(
(result: SelectStateInputProps) => {
onSelectedChange({
...result,
});
},
[onSelectedChange]
);
return (
<TableRow>
<TableSelectCell
id={id}
isSelected={isSelected}
onSelectedChange={onSelectedChangeWrapper}
/>
<TableRowCell className={styles.name}>{name}</TableRowCell>
<TableRowCell className={styles.implementation}>
{implementation}
</TableRowCell>
<TableRowCell className={styles.enableRss}>
<Label
kind={enableRss ? kinds.SUCCESS : kinds.DISABLED}
outline={!enableRss}
>
{enableRss ? translate('Yes') : translate('No')}
</Label>
</TableRowCell>
<TableRowCell className={styles.enableAutomaticSearch}>
<Label
kind={enableAutomaticSearch ? kinds.SUCCESS : kinds.DISABLED}
outline={!enableAutomaticSearch}
>
{enableAutomaticSearch ? translate('Yes') : translate('No')}
</Label>
</TableRowCell>
<TableRowCell className={styles.enableInteractiveSearch}>
<Label
kind={enableInteractiveSearch ? kinds.SUCCESS : kinds.DISABLED}
outline={!enableInteractiveSearch}
>
{enableInteractiveSearch ? translate('Yes') : translate('No')}
</Label>
</TableRowCell>
<TableRowCell className={styles.priority}>{priority}</TableRowCell>
<TableRowCell className={styles.tags}>
<TagListConnector tags={tags} />
</TableRowCell>
</TableRow>
);
}
export default ManageIndexersModalRow;
@@ -0,0 +1,22 @@
import React from 'react';
import Modal from 'Components/Modal/Modal';
import TagsModalContent from './TagsModalContent';
interface TagsModalProps {
isOpen: boolean;
ids: number[];
onApplyTagsPress: (tags: number[], applyTags: string) => void;
onModalClose: () => void;
}
function TagsModal(props: TagsModalProps) {
const { isOpen, onModalClose, ...otherProps } = props;
return (
<Modal isOpen={isOpen} onModalClose={onModalClose}>
<TagsModalContent {...otherProps} onModalClose={onModalClose} />
</Modal>
);
}
export default TagsModal;
@@ -0,0 +1,12 @@
.renameIcon {
margin-left: 5px;
}
.message {
margin-top: 20px;
margin-bottom: 10px;
}
.result {
padding-top: 4px;
}
@@ -0,0 +1,9 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'message': string;
'renameIcon': string;
'result': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -0,0 +1,183 @@
import { uniq } from 'lodash';
import React, { useCallback, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
import { IndexerAppState } from 'App/State/SettingsAppState';
import { Tag } from 'App/State/TagsAppState';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import Label from 'Components/Label';
import Button from 'Components/Link/Button';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props';
import createTagsSelector from 'Store/Selectors/createTagsSelector';
import Indexer from 'typings/Indexer';
import translate from 'Utilities/String/translate';
import styles from './TagsModalContent.css';
interface TagsModalContentProps {
ids: number[];
onApplyTagsPress: (tags: number[], applyTags: string) => void;
onModalClose: () => void;
}
function TagsModalContent(props: TagsModalContentProps) {
const { ids, onModalClose, onApplyTagsPress } = props;
const allIndexers: IndexerAppState = useSelector(
(state: AppState) => state.settings.indexers
);
const tagList: Tag[] = useSelector(createTagsSelector());
const [tags, setTags] = useState<number[]>([]);
const [applyTags, setApplyTags] = useState('add');
const indexersTags = useMemo(() => {
const tags = ids.reduce((acc: number[], id) => {
const s = allIndexers.items.find((s: Indexer) => s.id === id);
if (s) {
acc.push(...s.tags);
}
return acc;
}, []);
return uniq(tags);
}, [ids, allIndexers]);
const onTagsChange = useCallback(
({ value }: { value: number[] }) => {
setTags(value);
},
[setTags]
);
const onApplyTagsChange = useCallback(
({ value }: { value: string }) => {
setApplyTags(value);
},
[setApplyTags]
);
const onApplyPress = useCallback(() => {
onApplyTagsPress(tags, applyTags);
}, [tags, applyTags, onApplyTagsPress]);
const applyTagsOptions = [
{ key: 'add', value: translate('Add') },
{ key: 'remove', value: translate('Remove') },
{ key: 'replace', value: translate('Replace') },
];
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('Tags')}</ModalHeader>
<ModalBody>
<Form>
<FormGroup>
<FormLabel>{translate('Tags')}</FormLabel>
<FormInputGroup
type={inputTypes.TAG}
name="tags"
value={tags}
onChange={onTagsChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('ApplyTags')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="applyTags"
value={applyTags}
values={applyTagsOptions}
helpTexts={[
translate('ApplyTagsHelpTextHowToApplyIndexers'),
translate('ApplyTagsHelpTextAdd'),
translate('ApplyTagsHelpTextRemove'),
translate('ApplyTagsHelpTextReplace'),
]}
onChange={onApplyTagsChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('Result')}</FormLabel>
<div className={styles.result}>
{indexersTags.map((id) => {
const tag = tagList.find((t) => t.id === id);
if (!tag) {
return null;
}
const removeTag =
(applyTags === 'remove' && tags.indexOf(id) > -1) ||
(applyTags === 'replace' && tags.indexOf(id) === -1);
return (
<Label
key={tag.id}
title={
removeTag
? translate('RemovingTag')
: translate('ExistingTag')
}
kind={removeTag ? kinds.INVERSE : kinds.INFO}
size={sizes.LARGE}
>
{tag.label}
</Label>
);
})}
{(applyTags === 'add' || applyTags === 'replace') &&
tags.map((id) => {
const tag = tagList.find((t) => t.id === id);
if (!tag) {
return null;
}
if (indexersTags.indexOf(id) > -1) {
return null;
}
return (
<Label
key={tag.id}
title={translate('AddingTag')}
kind={kinds.SUCCESS}
size={sizes.LARGE}
>
{tag.label}
</Label>
);
})}
</div>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button kind={kinds.PRIMARY} onPress={onApplyPress}>
{translate('Apply')}
</Button>
</ModalFooter>
</ModalContent>
);
}
export default TagsModalContent;