Mass Editor size and options

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
Signed-off-by: Robin Dadswell <robin@dadswell.email>
pull/770/head
Qstick 4 years ago
parent 5176bdc786
commit cf0439d4c5

@ -6,10 +6,13 @@ import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { align, sortDirections } from 'Helpers/Props';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import { align, icons, sortDirections } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
@ -20,46 +23,6 @@ import AuthorEditorFooter from './AuthorEditorFooter';
import AuthorEditorRowConnector from './AuthorEditorRowConnector';
import OrganizeAuthorModal from './Organize/OrganizeAuthorModal';
function getColumns(showMetadataProfile) {
return [
{
name: 'status',
isSortable: true,
isVisible: true
},
{
name: 'sortName',
label: 'Name',
isSortable: true,
isVisible: true
},
{
name: 'qualityProfileId',
label: 'Quality Profile',
isSortable: true,
isVisible: true
},
{
name: 'metadataProfileId',
label: 'Metadata Profile',
isSortable: true,
isVisible: showMetadataProfile
},
{
name: 'path',
label: 'Path',
isSortable: true,
isVisible: true
},
{
name: 'tags',
label: 'Tags',
isSortable: false,
isVisible: true
}
];
}
class AuthorEditor extends Component {
//
@ -74,8 +37,7 @@ class AuthorEditor extends Component {
lastToggled: null,
selectedState: {},
isOrganizingAuthorModalOpen: false,
isRetaggingAuthorModalOpen: false,
columns: getColumns(props.showMetadataProfile)
isRetaggingAuthorModalOpen: false
};
}
@ -155,6 +117,7 @@ class AuthorEditor extends Component {
error,
totalItems,
items,
columns,
selectedFilterKey,
filters,
customFilters,
@ -166,7 +129,7 @@ class AuthorEditor extends Component {
deleteError,
isOrganizingAuthor,
isRetaggingAuthor,
showMetadataProfile,
onTableOptionChange,
onSortPress,
onFilterSelect
} = this.props;
@ -174,8 +137,7 @@ class AuthorEditor extends Component {
const {
allSelected,
allUnselected,
selectedState,
columns
selectedState
} = this.state;
const selectedAuthorIds = this.getSelectedIds();
@ -185,6 +147,18 @@ class AuthorEditor extends Component {
<PageToolbar>
<PageToolbarSection />
<PageToolbarSection alignContent={align.RIGHT}>
<TableOptionsModalWrapper
columns={columns}
onTableOptionChange={onTableOptionChange}
>
<PageToolbarButton
label="Options"
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>
<PageToolbarSeparator />
<FilterMenu
alignMenu={align.RIGHT}
selectedFilterKey={selectedFilterKey}
@ -254,7 +228,8 @@ class AuthorEditor extends Component {
deleteError={deleteError}
isOrganizingAuthor={isOrganizingAuthor}
isRetaggingAuthor={isRetaggingAuthor}
showMetadataProfile={showMetadataProfile}
columns={columns}
showMetadataProfile={columns.find((column) => column.name === 'metadataProfileId').isVisible}
onSaveSelected={this.onSaveSelected}
onOrganizeAuthorPress={this.onOrganizeAuthorPress}
onRetagAuthorPress={this.onRetagAuthorPress}
@ -283,6 +258,7 @@ AuthorEditor.propTypes = {
error: PropTypes.object,
totalItems: PropTypes.number.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all),
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
@ -294,7 +270,7 @@ AuthorEditor.propTypes = {
deleteError: PropTypes.object,
isOrganizingAuthor: PropTypes.bool.isRequired,
isRetaggingAuthor: PropTypes.bool.isRequired,
showMetadataProfile: PropTypes.bool.isRequired,
onTableOptionChange: PropTypes.func.isRequired,
onSortPress: PropTypes.func.isRequired,
onFilterSelect: PropTypes.func.isRequired,
onSaveSelected: PropTypes.func.isRequired

@ -3,7 +3,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import { saveAuthorEditor, setAuthorEditorFilter, setAuthorEditorSort } from 'Store/Actions/authorEditorActions';
import { saveAuthorEditor, setAuthorEditorFilter, setAuthorEditorSort, setAuthorEditorTableOption } from 'Store/Actions/authorEditorActions';
import { executeCommand } from 'Store/Actions/commandActions';
import { fetchRootFolders } from 'Store/Actions/settingsActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
@ -12,15 +12,13 @@ import AuthorEditor from './AuthorEditor';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.metadataProfiles,
createClientSideCollectionSelector('authors', 'authorEditor'),
createCommandExecutingSelector(commandNames.RENAME_AUTHOR),
createCommandExecutingSelector(commandNames.RETAG_AUTHOR),
(metadataProfiles, author, isOrganizingAuthor, isRetaggingAuthor) => {
(author, isOrganizingAuthor, isRetaggingAuthor) => {
return {
isOrganizingAuthor,
isRetaggingAuthor,
showMetadataProfile: metadataProfiles.items.length > 1,
...author
};
}
@ -30,6 +28,7 @@ function createMapStateToProps() {
const mapDispatchToProps = {
dispatchSetAuthorEditorSort: setAuthorEditorSort,
dispatchSetAuthorEditorFilter: setAuthorEditorFilter,
dispatchSetAuthorEditorTableOption: setAuthorEditorTableOption,
dispatchSaveAuthorEditor: saveAuthorEditor,
dispatchFetchRootFolders: fetchRootFolders,
dispatchExecuteCommand: executeCommand
@ -55,6 +54,10 @@ class AuthorEditorConnector extends Component {
this.props.dispatchSetAuthorEditorFilter({ selectedFilterKey });
}
onTableOptionChange = (payload) => {
this.props.dispatchSetAuthorEditorTableOption(payload);
}
onSaveSelected = (payload) => {
this.props.dispatchSaveAuthorEditor(payload);
}
@ -76,6 +79,7 @@ class AuthorEditorConnector extends Component {
onSortPress={this.onSortPress}
onFilterSelect={this.onFilterSelect}
onSaveSelected={this.onSaveSelected}
onTableOptionChange={this.onTableOptionChange}
/>
);
}
@ -84,6 +88,7 @@ class AuthorEditorConnector extends Component {
AuthorEditorConnector.propTypes = {
dispatchSetAuthorEditorSort: PropTypes.func.isRequired,
dispatchSetAuthorEditorFilter: PropTypes.func.isRequired,
dispatchSetAuthorEditorTableOption: PropTypes.func.isRequired,
dispatchSaveAuthorEditor: PropTypes.func.isRequired,
dispatchFetchRootFolders: PropTypes.func.isRequired,
dispatchExecuteCommand: PropTypes.func.isRequired

@ -138,7 +138,7 @@ class AuthorEditorFooter extends Component {
isDeleting,
isOrganizingAuthor,
isRetaggingAuthor,
showMetadataProfile,
columns,
onOrganizeAuthorPress,
onRetagAuthorPress
} = this.props;
@ -178,56 +178,88 @@ class AuthorEditorFooter extends Component {
/>
</div>
<div className={styles.inputContainer}>
<AuthorEditorFooterLabel
label="Quality Profile"
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
/>
<QualityProfileSelectInputConnector
name="qualityProfileId"
value={qualityProfileId}
includeNoChange={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
{
showMetadataProfile &&
<div className={styles.inputContainer}>
<AuthorEditorFooterLabel
label="Metadata Profile"
isSaving={isSaving && metadataProfileId !== NO_CHANGE}
/>
<MetadataProfileSelectInputConnector
name="metadataProfileId"
value={metadataProfileId}
includeNoChange={true}
includeNone={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
columns.map((column) => {
const {
name,
isVisible
} = column;
if (!isVisible) {
return null;
}
if (name === 'qualityProfileId') {
return (
<div
key={name}
className={styles.inputContainer}
>
<AuthorEditorFooterLabel
label="Quality Profile"
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
/>
<QualityProfileSelectInputConnector
name="qualityProfileId"
value={qualityProfileId}
includeNoChange={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
);
}
if (name === 'metadataProfileId') {
return (
<div
key={name}
className={styles.inputContainer}
>
<AuthorEditorFooterLabel
label="Metadata Profile"
isSaving={isSaving && metadataProfileId !== NO_CHANGE}
/>
<MetadataProfileSelectInputConnector
name="metadataProfileId"
value={metadataProfileId}
includeNoChange={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
);
}
if (name === 'path') {
return (
<div
key={name}
className={styles.inputContainer}
>
<AuthorEditorFooterLabel
label="Root Folder"
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
/>
<RootFolderSelectInputConnector
name="rootFolderPath"
value={rootFolderPath}
includeNoChange={true}
isDisabled={!selectedCount}
selectedValueOptions={{ includeFreeSpace: false }}
onChange={this.onInputChange}
/>
</div>
);
}
return null;
})
}
<div className={styles.inputContainer}>
<AuthorEditorFooterLabel
label="Root Folder"
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
/>
<RootFolderSelectInputConnector
name="rootFolderPath"
value={rootFolderPath}
includeNoChange={true}
isDisabled={!selectedCount}
selectedValueOptions={{ includeFreeSpace: false }}
onChange={this.onInputChange}
/>
</div>
<div className={styles.buttonContainer}>
<div className={styles.buttonContainerContent}>
<AuthorEditorFooterLabel
@ -315,6 +347,7 @@ AuthorEditorFooter.propTypes = {
isOrganizingAuthor: PropTypes.bool.isRequired,
isRetaggingAuthor: PropTypes.bool.isRequired,
showMetadataProfile: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onSaveSelected: PropTypes.func.isRequired,
onOrganizeAuthorPress: PropTypes.func.isRequired,
onRetagAuthorPress: PropTypes.func.isRequired

@ -1,4 +1,3 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import AuthorNameLink from 'Author/AuthorNameLink';
@ -7,6 +6,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import TagListConnector from 'Components/TagListConnector';
import formatBytes from 'Utilities/Number/formatBytes';
class AuthorEditorRow extends Component {
@ -32,6 +32,7 @@ class AuthorEditorRow extends Component {
metadataProfile,
qualityProfile,
path,
statistics,
tags,
columns,
isSelected,
@ -46,39 +47,86 @@ class AuthorEditorRow extends Component {
onSelectedChange={onSelectedChange}
/>
<AuthorStatusCell
authorType={authorType}
monitored={monitored}
status={status}
/>
{
columns.map((column) => {
const {
name,
isVisible
} = column;
<TableRowCell>
<AuthorNameLink
titleSlug={titleSlug}
authorName={authorName}
/>
</TableRowCell>
if (!isVisible) {
return null;
}
<TableRowCell>
{qualityProfile.name}
</TableRowCell>
if (name === 'status') {
return (
<AuthorStatusCell
key={name}
authorType={authorType}
monitored={monitored}
status={status}
/>
);
}
{
_.find(columns, { name: 'metadataProfileId' }).isVisible &&
<TableRowCell>
{metadataProfile.name}
</TableRowCell>
}
if (name === 'sortName') {
return (
<TableRowCell
key={name}
>
<AuthorNameLink
titleSlug={titleSlug}
authorName={authorName}
/>
</TableRowCell>
);
}
if (name === 'qualityProfileId') {
return (
<TableRowCell key={name}>
{qualityProfile.name}
</TableRowCell>
);
}
<TableRowCell>
{path}
</TableRowCell>
if (name === 'metadataProfileId') {
return (
<TableRowCell key={name}>
{metadataProfile.name}
</TableRowCell>
);
}
<TableRowCell>
<TagListConnector
tags={tags}
/>
</TableRowCell>
if (name === 'path') {
return (
<TableRowCell key={name}>
{path}
</TableRowCell>
);
}
if (name === 'sizeOnDisk') {
return (
<TableRowCell key={name}>
{formatBytes(statistics.sizeOnDisk)}
</TableRowCell>
);
}
if (name === 'tags') {
return (
<TableRowCell key={name}>
<TagListConnector
tags={tags}
/>
</TableRowCell>
);
}
return null;
})
}
</TableRow>
);
}
@ -94,6 +142,7 @@ AuthorEditorRow.propTypes = {
metadataProfile: PropTypes.object.isRequired,
qualityProfile: PropTypes.object.isRequired,
path: PropTypes.string.isRequired,
statistics: PropTypes.object.isRequired,
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
isSelected: PropTypes.bool,

@ -8,6 +8,7 @@ import { set, updateItem } from './baseActions';
import createHandleActions from './Creators/createHandleActions';
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
//
// Variables
@ -30,6 +31,52 @@ export const defaultState = {
filters,
filterPredicates,
columns: [
{
name: 'status',
columnLabel: 'Status',
isSortable: true,
isVisible: true,
isModifiable: false
},
{
name: 'sortName',
label: 'Name',
isSortable: true,
isVisible: true
},
{
name: 'qualityProfileId',
label: 'Quality Profile',
isSortable: true,
isVisible: true
},
{
name: 'metadataProfileId',
label: 'Metadata Profile',
isSortable: true,
isVisible: false
},
{
name: 'path',
label: 'Path',
isSortable: true,
isVisible: true
},
{
name: 'sizeOnDisk',
label: 'Size on Disk',
isSortable: true,
isVisible: false
},
{
name: 'tags',
label: 'Tags',
isSortable: false,
isVisible: true
}
],
filterBuilderProps: [
{
name: 'monitored',
@ -65,6 +112,12 @@ export const defaultState = {
label: 'Root Folder Path',
type: filterBuilderTypes.EXACT
},
{
name: 'sizeOnDisk',
label: 'Size on Disk',
type: filterBuilderTypes.NUMBER,
valueType: filterBuilderValueTypes.BYTES
},
{
name: 'tags',
label: 'Tags',
@ -90,6 +143,7 @@ export const SET_AUTHOR_EDITOR_SORT = 'authorEditor/setAuthorEditorSort';
export const SET_AUTHOR_EDITOR_FILTER = 'authorEditor/setAuthorEditorFilter';
export const SAVE_AUTHOR_EDITOR = 'authorEditor/saveAuthorEditor';
export const BULK_DELETE_AUTHOR = 'authorEditor/bulkDeleteAuthor';
export const SET_AUTHOR_EDITOR_TABLE_OPTION = 'authorEditor/setAuthorEditorTableOption';
//
// Action Creators
@ -98,6 +152,7 @@ export const setAuthorEditorSort = createAction(SET_AUTHOR_EDITOR_SORT);
export const setAuthorEditorFilter = createAction(SET_AUTHOR_EDITOR_FILTER);
export const saveAuthorEditor = createThunk(SAVE_AUTHOR_EDITOR);
export const bulkDeleteAuthor = createThunk(BULK_DELETE_AUTHOR);
export const setAuthorEditorTableOption = createAction(SET_AUTHOR_EDITOR_TABLE_OPTION);
//
// Action Handlers
@ -181,6 +236,7 @@ export const actionHandlers = handleThunks({
export const reducers = createHandleActions({
[SET_AUTHOR_EDITOR_TABLE_OPTION]: createSetTableOptionReducer(section),
[SET_AUTHOR_EDITOR_SORT]: createSetClientSideCollectionSortReducer(section),
[SET_AUTHOR_EDITOR_FILTER]: createSetClientSideCollectionFilterReducer(section)

Loading…
Cancel
Save