diff --git a/frontend/src/AlbumStudio/AlbumStudioFooter.js b/frontend/src/AlbumStudio/AlbumStudioFooter.js index 0d649e635..1e56b9cf4 100644 --- a/frontend/src/AlbumStudio/AlbumStudioFooter.js +++ b/frontend/src/AlbumStudio/AlbumStudioFooter.js @@ -6,6 +6,7 @@ import SelectInput from 'Components/Form/SelectInput'; import SpinnerButton from 'Components/Link/SpinnerButton'; import PageContentFooter from 'Components/Page/PageContentFooter'; import { kinds } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; import styles from './AlbumStudioFooter.css'; const NO_CHANGE = 'noChange'; @@ -87,7 +88,7 @@ class AlbumStudioFooter extends Component { } = this.state; const monitoredOptions = [ - { key: NO_CHANGE, value: 'No Change', disabled: true }, + { key: NO_CHANGE, value: translate('NoChange'), disabled: true }, { key: 'monitored', value: 'Monitored' }, { key: 'unmonitored', value: 'Unmonitored' } ]; diff --git a/frontend/src/App/ModelBase.ts b/frontend/src/App/ModelBase.ts new file mode 100644 index 000000000..187b12fb2 --- /dev/null +++ b/frontend/src/App/ModelBase.ts @@ -0,0 +1,5 @@ +interface ModelBase { + id: number; +} + +export default ModelBase; diff --git a/frontend/src/App/State/AppSectionState.ts b/frontend/src/App/State/AppSectionState.ts new file mode 100644 index 000000000..d511963fc --- /dev/null +++ b/frontend/src/App/State/AppSectionState.ts @@ -0,0 +1,48 @@ +import SortDirection from 'Helpers/Props/SortDirection'; + +export interface Error { + responseJSON: { + message: string; + }; +} + +export interface AppSectionDeleteState { + isDeleting: boolean; + deleteError: Error; +} + +export interface AppSectionSaveState { + isSaving: boolean; + saveError: Error; +} + +export interface PagedAppSectionState { + pageSize: number; +} + +export interface AppSectionSchemaState { + isSchemaFetching: boolean; + isSchemaPopulated: boolean; + schemaError: Error; + schema: { + items: T[]; + }; +} + +export interface AppSectionItemState { + isFetching: boolean; + isPopulated: boolean; + error: Error; + item: T; +} + +interface AppSectionState { + isFetching: boolean; + isPopulated: boolean; + error: Error; + items: T[]; + sortKey: string; + sortDirection: SortDirection; +} + +export default AppSectionState; diff --git a/frontend/src/App/State/AppState.ts b/frontend/src/App/State/AppState.ts new file mode 100644 index 000000000..8c8b99fba --- /dev/null +++ b/frontend/src/App/State/AppState.ts @@ -0,0 +1,41 @@ +import SettingsAppState from './SettingsAppState'; +import TagsAppState from './TagsAppState'; + +interface FilterBuilderPropOption { + id: string; + name: string; +} + +export interface FilterBuilderProp { + name: string; + label: string; + type: string; + valueType?: string; + optionsSelector?: (items: T[]) => FilterBuilderPropOption[]; +} + +export interface PropertyFilter { + key: string; + value: boolean | string | number | string[] | number[]; + type: string; +} + +export interface Filter { + key: string; + label: string; + filers: PropertyFilter[]; +} + +export interface CustomFilter { + id: number; + type: string; + label: string; + filers: PropertyFilter[]; +} + +interface AppState { + settings: SettingsAppState; + tags: TagsAppState; +} + +export default AppState; diff --git a/frontend/src/App/State/SettingsAppState.ts b/frontend/src/App/State/SettingsAppState.ts new file mode 100644 index 000000000..4c0680956 --- /dev/null +++ b/frontend/src/App/State/SettingsAppState.ts @@ -0,0 +1,40 @@ +import AppSectionState, { + AppSectionDeleteState, + AppSectionSaveState, +} from 'App/State/AppSectionState'; +import DownloadClient from 'typings/DownloadClient'; +import ImportList from 'typings/ImportList'; +import Indexer from 'typings/Indexer'; +import Notification from 'typings/Notification'; +import { UiSettings } from 'typings/UiSettings'; + +export interface DownloadClientAppState + extends AppSectionState, + AppSectionDeleteState, + AppSectionSaveState {} + +export interface ImportListAppState + extends AppSectionState, + AppSectionDeleteState, + AppSectionSaveState {} + +export interface IndexerAppState + extends AppSectionState, + AppSectionDeleteState, + AppSectionSaveState {} + +export interface NotificationAppState + extends AppSectionState, + AppSectionDeleteState {} + +export type UiSettingsAppState = AppSectionState; + +interface SettingsAppState { + downloadClients: DownloadClientAppState; + importLists: ImportListAppState; + indexers: IndexerAppState; + notifications: NotificationAppState; + uiSettings: UiSettingsAppState; +} + +export default SettingsAppState; diff --git a/frontend/src/App/State/TagsAppState.ts b/frontend/src/App/State/TagsAppState.ts new file mode 100644 index 000000000..d1f1d5a2f --- /dev/null +++ b/frontend/src/App/State/TagsAppState.ts @@ -0,0 +1,12 @@ +import ModelBase from 'App/ModelBase'; +import AppSectionState, { + AppSectionDeleteState, +} from 'App/State/AppSectionState'; + +export interface Tag extends ModelBase { + label: string; +} + +interface TagsAppState extends AppSectionState, AppSectionDeleteState {} + +export default TagsAppState; diff --git a/frontend/src/Artist/Details/ArtistDetails.js b/frontend/src/Artist/Details/ArtistDetails.js index b6f5179ac..7ff777f1e 100644 --- a/frontend/src/Artist/Details/ArtistDetails.js +++ b/frontend/src/Artist/Details/ArtistDetails.js @@ -415,10 +415,7 @@ class ArtistDetails extends Component { name={icons.ARROW_UP} size={30} title={translate('GoToArtistListing')} - to={{ - pathname: '/', - state: { restoreScrollPosition: true } - }} + to={'/'} /> a.value.localeCompare(b.value)); const ValueComponent = getRowValueConnector(selectedFilterBuilderProp); diff --git a/frontend/src/Components/Filter/Builder/FilterBuilderRowValueTag.css b/frontend/src/Components/Filter/Builder/FilterBuilderRowValueTag.css index f77146a06..807f383dd 100644 --- a/frontend/src/Components/Filter/Builder/FilterBuilderRowValueTag.css +++ b/frontend/src/Components/Filter/Builder/FilterBuilderRowValueTag.css @@ -1,4 +1,6 @@ .tag { + display: flex; + &.isLastTag { .or { display: none; diff --git a/frontend/src/Components/Filter/Builder/FilterBuilderRowValueTag.js b/frontend/src/Components/Filter/Builder/FilterBuilderRowValueTag.js index 4408c87b3..6b5846594 100644 --- a/frontend/src/Components/Filter/Builder/FilterBuilderRowValueTag.js +++ b/frontend/src/Components/Filter/Builder/FilterBuilderRowValueTag.js @@ -6,7 +6,7 @@ import styles from './FilterBuilderRowValueTag.css'; function FilterBuilderRowValueTag(props) { return ( - { - !props.isLastTag && - + props.isLastTag ? + null : +
or - +
} -
+ ); } diff --git a/frontend/src/Components/Form/FormInputGroup.js b/frontend/src/Components/Form/FormInputGroup.js index ade8be204..7e6b0c7c9 100644 --- a/frontend/src/Components/Form/FormInputGroup.js +++ b/frontend/src/Components/Form/FormInputGroup.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import Link from 'Components/Link/Link'; -import { inputTypes } from 'Helpers/Props'; +import { inputTypes, kinds } from 'Helpers/Props'; import translate from 'Utilities/String/translate'; import AlbumReleaseSelectInputConnector from './AlbumReleaseSelectInputConnector'; import AutoCompleteInput from './AutoCompleteInput'; @@ -273,16 +273,27 @@ FormInputGroup.propTypes = { className: PropTypes.string.isRequired, containerClassName: PropTypes.string.isRequired, inputClassName: PropTypes.string, + name: PropTypes.string.isRequired, + value: PropTypes.any, + values: PropTypes.arrayOf(PropTypes.any), type: PropTypes.string.isRequired, + kind: PropTypes.oneOf(kinds.all), + min: PropTypes.number, + max: PropTypes.number, unit: PropTypes.string, buttons: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]), helpText: PropTypes.string, helpTexts: PropTypes.arrayOf(PropTypes.string), helpTextWarning: PropTypes.string, helpLink: PropTypes.string, + autoFocus: PropTypes.bool, + includeNoChange: PropTypes.bool, + includeNoChangeDisabled: PropTypes.bool, + selectedValueOptions: PropTypes.object, pending: PropTypes.bool, errors: PropTypes.arrayOf(PropTypes.object), - warnings: PropTypes.arrayOf(PropTypes.object) + warnings: PropTypes.arrayOf(PropTypes.object), + onChange: PropTypes.func.isRequired }; FormInputGroup.defaultProps = { diff --git a/frontend/src/Components/Form/FormLabel.js b/frontend/src/Components/Form/FormLabel.js index d419039b3..d4a4bcffc 100644 --- a/frontend/src/Components/Form/FormLabel.js +++ b/frontend/src/Components/Form/FormLabel.js @@ -4,16 +4,18 @@ import React from 'react'; import { sizes } from 'Helpers/Props'; import styles from './FormLabel.css'; -function FormLabel({ - children, - className, - errorClassName, - size, - name, - hasError, - isAdvanced, - ...otherProps -}) { +function FormLabel(props) { + const { + children, + className, + errorClassName, + size, + name, + hasError, + isAdvanced, + ...otherProps + } = props; + return (