diff --git a/package.json b/package.json
index 05965e3e6..9b74636a2 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,6 @@
"editorjs-undo": "^0.1.4",
"faker": "^5.1.0",
"fast-array-diff": "^0.2.0",
- "fsevents": "^1.2.9",
"fuzzaldrin": "^2.1.0",
"graphql": "^14.4.2",
"graphql-tag": "^2.11.0",
diff --git a/src/collections/components/CollectionAvailabilityStatusLabel/CollectionAvailabilityStatusLabel.tsx b/src/collections/components/CollectionAvailabilityStatusLabel/CollectionAvailabilityStatusLabel.tsx
new file mode 100644
index 000000000..cecf1c017
--- /dev/null
+++ b/src/collections/components/CollectionAvailabilityStatusLabel/CollectionAvailabilityStatusLabel.tsx
@@ -0,0 +1,35 @@
+import StatusLabel from "@saleor/components/StatusLabel";
+import useDateLocalize from "@saleor/hooks/useDateLocalize";
+import React from "react";
+import { useIntl } from "react-intl";
+
+import { messages } from "./messages";
+
+export const CollectionAvailabilityStatusLabel = ({ channel }) => {
+ const intl = useIntl();
+ const localizeDate = useDateLocalize();
+
+ return (
+
+ );
+};
+
+export default CollectionAvailabilityStatusLabel;
diff --git a/src/collections/components/CollectionAvailabilityStatusLabel/index.ts b/src/collections/components/CollectionAvailabilityStatusLabel/index.ts
new file mode 100644
index 000000000..e48f56c4e
--- /dev/null
+++ b/src/collections/components/CollectionAvailabilityStatusLabel/index.ts
@@ -0,0 +1,2 @@
+export { default } from "./CollectionAvailabilityStatusLabel";
+export * from "./CollectionAvailabilityStatusLabel";
diff --git a/src/collections/components/CollectionAvailabilityStatusLabel/messages.ts b/src/collections/components/CollectionAvailabilityStatusLabel/messages.ts
new file mode 100644
index 000000000..00de0eb87
--- /dev/null
+++ b/src/collections/components/CollectionAvailabilityStatusLabel/messages.ts
@@ -0,0 +1,16 @@
+import { defineMessages } from "react-intl";
+
+export const messages = defineMessages({
+ published: {
+ defaultMessage: "Published on {date}",
+ description: "collection publication date"
+ },
+ unpublished: {
+ defaultMessage: "Unpublished",
+ description: "collection publication date"
+ },
+ willBePublished: {
+ defaultMessage: "Becomes published on {date}",
+ description: "collection publication date"
+ }
+});
diff --git a/src/collections/components/CollectionList/CollectionList.tsx b/src/collections/components/CollectionList/CollectionList.tsx
index 062a5946a..1141dd0cc 100644
--- a/src/collections/components/CollectionList/CollectionList.tsx
+++ b/src/collections/components/CollectionList/CollectionList.tsx
@@ -1,5 +1,6 @@
import { TableBody, TableCell, TableFooter, TableRow } from "@material-ui/core";
import { CollectionListUrlSortField } from "@saleor/collections/urls";
+import { canBeSorted } from "@saleor/collections/views/CollectionList/sort";
import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown";
import Checkbox from "@saleor/components/Checkbox";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
@@ -15,6 +16,7 @@ import React from "react";
import { FormattedMessage } from "react-intl";
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
+import CollectionAvailabilityStatusLabel from "../CollectionAvailabilityStatusLabel";
const useStyles = makeStyles(
theme => ({
@@ -116,6 +118,12 @@ const CollectionList: React.FC = props => {
}
onClick={() => onSort(CollectionListUrlSortField.available)}
className={classes.colAvailability}
+ disabled={
+ !canBeSorted(
+ CollectionListUrlSortField.available,
+ !!selectedChannelId
+ )
+ }
>
= props => {
)}
- {collection && !collection?.channelListings?.length ? (
- "-"
- ) : collection?.channelListings !== undefined ? (
- channel ? (
+ {(!collection && ) ||
+ (!collection?.channelListings?.length && "-") ||
+ (collection?.channelListings !== undefined && channel ? (
+
+ ) : (
- ) : null
- ) : (
-
- )}
+ ))}
);
diff --git a/src/collections/components/CollectionListPage/CollectionListPage.tsx b/src/collections/components/CollectionListPage/CollectionListPage.tsx
index 6a8a8d0cc..2e4282d18 100644
--- a/src/collections/components/CollectionListPage/CollectionListPage.tsx
+++ b/src/collections/components/CollectionListPage/CollectionListPage.tsx
@@ -1,11 +1,12 @@
import { Button, Card } from "@material-ui/core";
import { CollectionListUrlSortField } from "@saleor/collections/urls";
import { Container } from "@saleor/components/Container";
+import FilterBar from "@saleor/components/FilterBar";
import PageHeader from "@saleor/components/PageHeader";
-import SearchBar from "@saleor/components/SearchBar";
import { sectionNames } from "@saleor/intl";
import {
ChannelProps,
+ FilterPageProps,
ListActions,
PageListProps,
SearchPageProps,
@@ -17,13 +18,18 @@ import { FormattedMessage, useIntl } from "react-intl";
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
import CollectionList from "../CollectionList/CollectionList";
-
+import {
+ CollectionFilterKeys,
+ CollectionListFilterOpts,
+ createFilterStructure
+} from "./filters";
export interface CollectionListPageProps
extends PageListProps,
ListActions,
SearchPageProps,
SortPage,
TabPageProps,
+ FilterPageProps,
ChannelProps {
collections: CollectionList_collections_edges_node[];
channelsCount: number;
@@ -42,9 +48,13 @@ const CollectionListPage: React.FC = ({
onTabSave,
selectedChannelId,
tabs,
+ filterOpts,
+ onFilterChange,
+ onFilterAttributeFocus,
...listProps
}) => {
const intl = useIntl();
+ const filterStructure = createFilterStructure(intl, filterOpts);
return (
@@ -63,22 +73,25 @@ const CollectionListPage: React.FC = ({
-
;
+ channel: FilterOpts & { choices: MultiAutocompleteChoiceType[] };
}
export enum CollectionFilterKeys {
- status = "status"
+ status = "status",
+ channel = "channel"
}
const messages = defineMessages({
@@ -46,7 +49,18 @@ export function createFilterStructure(
}
]
),
- active: opts.status.active
+ active: opts.status.active,
+ dependencies: [CollectionFilterKeys.channel]
+ },
+ {
+ ...createOptionsField(
+ CollectionFilterKeys.channel,
+ intl.formatMessage(commonMessages.channel),
+ [opts.channel.value],
+ false,
+ opts.channel.choices
+ ),
+ active: opts.channel.active
}
];
}
diff --git a/src/collections/fixtures.ts b/src/collections/fixtures.ts
index ae223bb0d..4f99198d6 100644
--- a/src/collections/fixtures.ts
+++ b/src/collections/fixtures.ts
@@ -1,9 +1,29 @@
+import { CollectionPublished } from "@saleor/types/globalTypes";
+
import * as richTextEditorFixtures from "../components/RichTextEditor/fixtures.json";
+import { CollectionListFilterOpts } from "./components/CollectionListPage";
import { CollectionDetails_collection } from "./types/CollectionDetails";
import { CollectionList_collections_edges_node } from "./types/CollectionList";
const content = richTextEditorFixtures.richTextEditor;
+export const collectionListFilterOpts: CollectionListFilterOpts = {
+ channel: {
+ active: false,
+ value: "default-channel",
+ choices: [
+ {
+ value: "default-channel",
+ label: "Default channel"
+ }
+ ]
+ },
+ status: {
+ active: false,
+ value: CollectionPublished.PUBLISHED
+ }
+};
+
export const collections: CollectionList_collections_edges_node[] = [
{
__typename: "Collection",
diff --git a/src/collections/queries.ts b/src/collections/queries.ts
index ce429bced..231d3ab10 100644
--- a/src/collections/queries.ts
+++ b/src/collections/queries.ts
@@ -25,6 +25,7 @@ export const collectionList = gql`
$before: String
$filter: CollectionFilterInput
$sort: CollectionSortingInput
+ $channel: String
) {
collections(
first: $first
@@ -33,6 +34,7 @@ export const collectionList = gql`
last: $last
filter: $filter
sortBy: $sort
+ channel: $channel
) {
edges {
node {
diff --git a/src/collections/urls.ts b/src/collections/urls.ts
index 22b78f6e2..e5e855772 100644
--- a/src/collections/urls.ts
+++ b/src/collections/urls.ts
@@ -17,7 +17,8 @@ const collectionSectionUrl = "/collections/";
export const collectionListPath = collectionSectionUrl;
export enum CollectionListUrlFiltersEnum {
status = "status",
- query = "query"
+ query = "query",
+ channel = "channel"
}
export type CollectionListUrlFilters = Filters;
export type CollectionListUrlDialog = "remove" | TabActionDialog;
diff --git a/src/collections/views/CollectionList/CollectionList.tsx b/src/collections/views/CollectionList/CollectionList.tsx
index baead7734..dd9c4fcfb 100644
--- a/src/collections/views/CollectionList/CollectionList.tsx
+++ b/src/collections/views/CollectionList/CollectionList.tsx
@@ -17,10 +17,12 @@ import { commonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
+import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
import createSortHandler from "@saleor/utils/handlers/sortHandler";
-import { mapEdgesToItems } from "@saleor/utils/maps";
+import { mapEdgesToItems, mapNodeToChoice } from "@saleor/utils/maps";
import { getSortParams } from "@saleor/utils/sort";
import React from "react";
+import { useEffect } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CollectionListPage from "../../components/CollectionListPage/CollectionListPage";
@@ -37,11 +39,13 @@ import {
areFiltersApplied,
deleteFilterTab,
getActiveFilters,
+ getFilterOpts,
+ getFilterQueryParam,
getFilterTabs,
getFilterVariables,
saveFilterTab
} from "./filters";
-import { getSortQueryVariables } from "./sort";
+import { canBeSorted, DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort";
interface CollectionListProps {
params: CollectionListUrlQueryParams;
@@ -59,12 +63,33 @@ export const CollectionList: React.FC = ({ params }) => {
);
const intl = useIntl();
+ const [
+ changeFilters,
+ resetFilters,
+ handleSearchChange
+ ] = createFilterHandlers({
+ cleanupFn: reset,
+ createUrl: collectionListUrl,
+ getFilterQueryParam,
+ navigate,
+ params
+ });
+
+ const { availableChannels } = useAppChannel(false);
+ const channelOpts = availableChannels
+ ? mapNodeToChoice(availableChannels, channel => channel.slug)
+ : null;
+ const selectedChannel = availableChannels.find(
+ channel => channel.slug === params.channel
+ );
+
const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = React.useMemo(
() => ({
...paginationState,
filter: getFilterVariables(params),
- sort: getSortQueryVariables(params)
+ sort: getSortQueryVariables(params),
+ channel: selectedChannel?.slug
}),
[params]
);
@@ -90,10 +115,21 @@ export const CollectionList: React.FC = ({ params }) => {
}
});
- const { availableChannels, channel } = useAppChannel();
+ const filterOpts = getFilterOpts(params, channelOpts);
const tabs = getFilterTabs();
+ useEffect(() => {
+ if (!canBeSorted(params.sort, !!selectedChannel)) {
+ navigate(
+ collectionListUrl({
+ ...params,
+ sort: DEFAULT_SORT_KEY
+ })
+ );
+ }
+ }, [params]);
+
const currentTab =
params.activeTab === undefined
? areFiltersApplied(params)
@@ -101,16 +137,6 @@ export const CollectionList: React.FC = ({ params }) => {
: 0
: parseInt(params.activeTab, 0);
- const handleSearchChange = (query: string) => {
- navigate(
- collectionListUrl({
- ...getActiveFilters(params),
- activeTab: undefined,
- query
- })
- );
- };
-
const [openModal, closeModal] = createDialogActionHandlers<
CollectionListUrlDialog,
CollectionListUrlQueryParams
@@ -152,7 +178,7 @@ export const CollectionList: React.FC = ({ params }) => {
initialSearch={params.query || ""}
onSearchChange={handleSearchChange}
onAdd={() => navigate(collectionAddUrl())}
- onAll={() => navigate(collectionListUrl())}
+ onAll={resetFilters}
onTabChange={handleTabChange}
onTabDelete={() => openModal("delete-search")}
onTabSave={() => openModal("save-search")}
@@ -184,7 +210,9 @@ export const CollectionList: React.FC = ({ params }) => {
toggle={toggle}
toggleAll={toggleAll}
channelsCount={availableChannels?.length}
- selectedChannelId={channel?.id}
+ selectedChannelId={selectedChannel?.id}
+ filterOpts={filterOpts}
+ onFilterChange={changeFilters}
/>
params.ids.length > 0)}
diff --git a/src/collections/views/CollectionList/filters.test.ts b/src/collections/views/CollectionList/filters.test.ts
index 7e0bd32fb..0fb91f91e 100644
--- a/src/collections/views/CollectionList/filters.test.ts
+++ b/src/collections/views/CollectionList/filters.test.ts
@@ -34,7 +34,8 @@ describe("Filtering URL params", () => {
status: {
active: false,
value: CollectionPublished.PUBLISHED
- }
+ },
+ channel: undefined
});
it("should be empty if no active filters", () => {
@@ -51,7 +52,8 @@ describe("Filtering URL params", () => {
status: {
active: true,
value: CollectionPublished.PUBLISHED
- }
+ },
+ channel: undefined
});
const filterQueryParams = getFilterQueryParams(
diff --git a/src/collections/views/CollectionList/filters.ts b/src/collections/views/CollectionList/filters.ts
index c5d9a4fa1..927b11122 100644
--- a/src/collections/views/CollectionList/filters.ts
+++ b/src/collections/views/CollectionList/filters.ts
@@ -3,6 +3,7 @@ import {
CollectionListFilterOpts
} from "@saleor/collections/components/CollectionListPage";
import { IFilterElement } from "@saleor/components/Filter";
+import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import { findValueInEnum, maybe } from "@saleor/misc";
import {
CollectionFilterInput,
@@ -12,7 +13,8 @@ import {
import {
createFilterTabUtils,
createFilterUtils,
- getSingleEnumValueQueryParam
+ getSingleEnumValueQueryParam,
+ getSingleValueQueryParam
} from "../../../utils/filters";
import {
CollectionListUrlFilters,
@@ -23,9 +25,15 @@ import {
export const COLLECTION_FILTERS_KEY = "collectionFilters";
export function getFilterOpts(
- params: CollectionListUrlFilters
+ params: CollectionListUrlFilters,
+ channels: SingleAutocompleteChoiceType[]
): CollectionListFilterOpts {
return {
+ channel: {
+ active: params?.channel !== undefined,
+ choices: channels,
+ value: params?.channel
+ },
status: {
active: maybe(() => params.status !== undefined, false),
value: maybe(() => findValueInEnum(status, CollectionPublished))
@@ -56,6 +64,11 @@ export function getFilterQueryParam(
CollectionListUrlFiltersEnum.status,
CollectionPublished
);
+ case CollectionFilterKeys.channel:
+ return getSingleValueQueryParam(
+ filter,
+ CollectionListUrlFiltersEnum.channel
+ );
}
}
diff --git a/src/collections/views/CollectionList/sort.ts b/src/collections/views/CollectionList/sort.ts
index 34cf3ed2c..000858f53 100644
--- a/src/collections/views/CollectionList/sort.ts
+++ b/src/collections/views/CollectionList/sort.ts
@@ -2,6 +2,23 @@ import { CollectionListUrlSortField } from "@saleor/collections/urls";
import { CollectionSortField } from "@saleor/types/globalTypes";
import { createGetSortQueryVariables } from "@saleor/utils/sort";
+export const DEFAULT_SORT_KEY = CollectionListUrlSortField.name;
+
+export function canBeSorted(
+ sort: CollectionListUrlSortField,
+ isChannelSelected: boolean
+) {
+ switch (sort) {
+ case CollectionListUrlSortField.name:
+ case CollectionListUrlSortField.productCount:
+ return true;
+ case CollectionListUrlSortField.available:
+ return isChannelSelected;
+ default:
+ return false;
+ }
+}
+
export function getSortQueryField(
sort: CollectionListUrlSortField
): CollectionSortField {
diff --git a/src/fixtures.ts b/src/fixtures.ts
index cba66421c..28c3539e3 100644
--- a/src/fixtures.ts
+++ b/src/fixtures.ts
@@ -307,7 +307,10 @@ export const searchPageProps: SearchPageProps = {
export const filterPageProps: FilterPageProps = {
...searchPageProps,
...tabPageProps,
- filterOpts: {},
+ filterOpts: {
+ status: { value: undefined, active: false },
+ channel: { value: undefined, active: false }
+ },
onFilterChange: () => undefined
};
diff --git a/src/intl.ts b/src/intl.ts
index 03ed4b496..1159ef8c2 100644
--- a/src/intl.ts
+++ b/src/intl.ts
@@ -11,6 +11,9 @@ export const commonMessages = defineMessages({
defaultMessage: "Choose file",
description: "button"
},
+ channel: {
+ defaultMessage: "Channel"
+ },
customApps: {
defaultMessage: "Local Apps"
},
diff --git a/src/storybook/stories/collections/CollectionListPage.tsx b/src/storybook/stories/collections/CollectionListPage.tsx
index 13a798c1c..d43085ee3 100644
--- a/src/storybook/stories/collections/CollectionListPage.tsx
+++ b/src/storybook/stories/collections/CollectionListPage.tsx
@@ -5,11 +5,14 @@ import React from "react";
import CollectionListPage, {
CollectionListPageProps
} from "../../../collections/components/CollectionListPage";
-import { collections } from "../../../collections/fixtures";
import {
+ collectionListFilterOpts,
+ collections
+} from "../../../collections/fixtures";
+import {
+ filterPageProps,
listActionsProps,
pageListProps,
- searchPageProps,
sortPageProps,
tabPageProps
} from "../../../fixtures";
@@ -18,7 +21,7 @@ import Decorator from "../../Decorator";
const props: CollectionListPageProps = {
...listActionsProps,
...pageListProps.default,
- ...searchPageProps,
+ ...filterPageProps,
...sortPageProps,
channelsCount: 2,
sort: {
@@ -27,7 +30,8 @@ const props: CollectionListPageProps = {
},
...tabPageProps,
collections,
- selectedChannelId: "123"
+ selectedChannelId: "123",
+ filterOpts: collectionListFilterOpts
};
storiesOf("Views / Collections / Collection list", module)