Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce filters to discount list page #4594

Merged
merged 16 commits into from
Jan 10, 2024
5 changes: 5 additions & 0 deletions .changeset/khaki-cats-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

Introduce filters to discount list page
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { FilterAPIProvider } from "@dashboard/components/ConditionalFilter/API/FilterAPIProvider";

export const useDiscountFilterAPIProvider = (): FilterAPIProvider => {
const fetchRightOptions = async () => {
return [];
};

const fetchLeftOptions = async () => {
return [];
};

return {
fetchRightOptions,
fetchLeftOptions,
};
};
3 changes: 2 additions & 1 deletion src/components/ConditionalFilter/UI/constrains.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Row } from "./types";

export const getItemConstraint = (constraint: Row["constraint"]) => ({
disableRemoveButton: !constraint?.removable,
// eslint-disable-next-line @typescript-eslint/key-spacing, @typescript-eslint/no-unnecessary-boolean-literal-compare
poulch marked this conversation as resolved.
Show resolved Hide resolved
disableRemoveButton: constraint?.removable === false,
disableLeftOperator: constraint?.disabled?.includes("left") ?? false,
disableCondition: constraint?.disabled?.includes("condition") ?? false,
disableRightOperator: constraint?.disabled?.includes("right") ?? false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ const mapToTokens = (urlEntries: Array<ParsedQs | string>): TokenArray =>

const tokenizeUrl = (urlParams: string) => {
const parsedUrl = Object.values(parse(urlParams)) as Array<ParsedQs | string>;

return mapToTokens(parsedUrl);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useEffect, useState } from "react";
import useRouter from "use-react-router";

import { InitialAPIState } from "../API";
import { InitialStateResponse } from "../API/InitialStateResponse";
import { FilterContainer, FilterElement } from "../FilterElement";
import { FilterValueProvider } from "../FilterValueProvider";
import { TokenArray } from "./TokenArray";
Expand All @@ -24,12 +25,12 @@ const prepareStructure = (filterValue: FilterContainer): Structure =>
});

export const useUrlValueProvider = (
initialState: InitialAPIState,
locationSearch: string,
initialState?: InitialAPIState,
): FilterValueProvider => {
const router = useRouter();
const params = new URLSearchParams(locationSearch);
const { data, loading, fetchQueries } = initialState;

const [value, setValue] = useState<FilterContainer>([]);

const activeTab = params.get("activeTab");
Expand All @@ -47,14 +48,26 @@ export const useUrlValueProvider = (
const fetchingParams = tokenizedUrl.getFetchingParams();

useEffect(() => {
fetchQueries(fetchingParams);
initialState?.fetchQueries(fetchingParams);
}, [locationSearch]);

useEffect(() => {
if (!initialState) return;

const { data, loading } = initialState;

if (loading) return;

setValue(tokenizedUrl.asFilterValuesFromResponse(data));
}, [data, loading]);
}, [initialState?.data, initialState?.loading]);

useEffect(() => {
if (initialState) return;

setValue(
tokenizedUrl.asFilterValuesFromResponse(InitialStateResponse.empty()),
);
}, [locationSearch]);

const persist = (filterValue: FilterContainer) => {
router.history.replace({
Expand Down Expand Up @@ -89,7 +102,7 @@ export const useUrlValueProvider = (

return {
value,
loading,
loading: initialState?.loading || false,
persist,
clear,
isPersisted,
Expand Down
32 changes: 31 additions & 1 deletion src/components/ConditionalFilter/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ export const STATIC_CONDITIONS = {
isVisibleInListing: [{ type: "select", label: "is", value: "input-1" }],
hasCategory: [{ type: "select", label: "is", value: "input-1" }],
giftCard: [{ type: "select", label: "is", value: "input-1" }],
startDate: [
{ type: "datetime", label: "lower", value: "input-1" },
{ type: "datetime", label: "greater", value: "input-2" },
{ type: "datetime.range", label: "between", value: "input-3" },
],
endDate: [
{ type: "datetime", label: "lower", value: "input-1" },
{ type: "datetime", label: "greater", value: "input-2" },
{ type: "datetime.range", label: "between", value: "input-3" },
],
};

export const CONSTRAINTS = {
Expand All @@ -34,7 +44,7 @@ export const CONSTRAINTS = {
},
};

export const STATIC_OPTIONS: LeftOperand[] = [
export const STATIC_PRODUCT_OPTIONS: LeftOperand[] = [
{ value: "price", label: "Price", type: "price", slug: "price" },
{ value: "category", label: "Category", type: "category", slug: "category" },
{
Expand Down Expand Up @@ -82,6 +92,26 @@ export const STATIC_OPTIONS: LeftOperand[] = [
},
];

export const STATIC_DISCOUNT_OPTIONS: LeftOperand[] = [
{
value: "startDate",
label: "Start date",
type: "startDate",
slug: "startDate",
},
{
value: "endDate",
label: "End date",
type: "endDate",
slug: "endDate",
},
];

export const STATIC_OPTIONS = [
...STATIC_PRODUCT_OPTIONS,
...STATIC_DISCOUNT_OPTIONS,
];

export const ATTRIBUTE_INPUT_TYPE_CONDITIONS = {
DROPDOWN: [{ type: "multiselect", label: "in", value: "input-2" }],
MULTISELECT: [{ type: "multiselect", label: "in", value: "input-2" }],
Expand Down
33 changes: 31 additions & 2 deletions src/components/ConditionalFilter/context/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { FC } from "react";

import { useDiscountFilterAPIProvider } from "../API/DiscountFiltersAPIProvider";
import { useProductInitialAPIState } from "../API/initialState/useInitialAPIState";
import { useProductFilterAPIProvider } from "../API/ProductFilterAPIProvider";
import { STATIC_DISCOUNT_OPTIONS, STATIC_PRODUCT_OPTIONS } from "../constants";
import { useContainerState } from "../useContainerState";
import { useFilterLeftOperandsProvider } from "../useFilterLeftOperands";
import { useUrlValueProvider } from "../ValueProvider/useUrlValueProvider";
Expand All @@ -12,8 +14,35 @@ export const ConditionalProductFilterProvider: FC<{
}> = ({ children, locationSearch }) => {
const apiProvider = useProductFilterAPIProvider();
const initialState = useProductInitialAPIState();
const valueProvider = useUrlValueProvider(initialState, locationSearch);
const leftOperandsProvider = useFilterLeftOperandsProvider();
const valueProvider = useUrlValueProvider(locationSearch, initialState);
const leftOperandsProvider = useFilterLeftOperandsProvider(
STATIC_PRODUCT_OPTIONS,
);
const containerState = useContainerState(valueProvider);

return (
<ConditionalFilterContext.Provider
value={{
apiProvider,
valueProvider,
leftOperandsProvider,
containerState,
}}
>
{children}
</ConditionalFilterContext.Provider>
);
};

export const ConditionalDiscountFilterProvider: FC<{
locationSearch: string;
}> = ({ children, locationSearch }) => {
const apiProvider = useDiscountFilterAPIProvider();

const valueProvider = useUrlValueProvider(locationSearch);
const leftOperandsProvider = useFilterLeftOperandsProvider(
STATIC_DISCOUNT_OPTIONS,
);
const containerState = useContainerState(valueProvider);

return (
Expand Down
16 changes: 16 additions & 0 deletions src/components/ConditionalFilter/queryVariables.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
AttributeInput,
DateTimeFilterInput,
DecimalFilterInput,
GlobalIdFilterInput,
ProductWhereInput,
PromotionWhereInput,
} from "@dashboard/graphql";

import { FilterContainer } from "./FilterElement";
Expand Down Expand Up @@ -165,3 +167,17 @@ export const createProductQueryVariables = (
return p;
}, {} as ProductWhereInput);
};

export const creatDiscountsQueryVariables = (
value: FilterContainer,
): PromotionWhereInput => {
return value.reduce((p, c) => {
if (typeof c === "string" || Array.isArray(c)) return p;

p[c.value.value as "endDate" | "startDate"] = createStaticQueryPart(
c.condition.selected,
) as DateTimeFilterInput;

return p;
}, {} as PromotionWhereInput);
};
10 changes: 6 additions & 4 deletions src/components/ConditionalFilter/useFilterLeftOperands.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { AttributeInputTypeEnum } from "@dashboard/graphql";
import { act, renderHook } from "@testing-library/react-hooks";

import { STATIC_OPTIONS } from "./constants";
import { STATIC_PRODUCT_OPTIONS } from "./constants";
import { useFilterLeftOperandsProvider } from "./useFilterLeftOperands";

describe("ConditionalFilter / useFilterLeftOperandsProvider", () => {
it("should set unique operands", () => {
// Arrange
const { result } = renderHook(() => useFilterLeftOperandsProvider());
const { result } = renderHook(() =>
useFilterLeftOperandsProvider(STATIC_PRODUCT_OPTIONS),
);
// Act
act(() => {
result.current.setOperands([
Expand All @@ -21,7 +23,7 @@ describe("ConditionalFilter / useFilterLeftOperandsProvider", () => {
});
// Assert
expect(result.current.operands).toEqual([
...STATIC_OPTIONS,
...STATIC_PRODUCT_OPTIONS,
{ label: "SKU", value: "sku", type: "DROPDOWN", slug: "sku" },
]);
// Act
Expand All @@ -37,7 +39,7 @@ describe("ConditionalFilter / useFilterLeftOperandsProvider", () => {
});
// Assert
expect(result.current.operands).toEqual([
...STATIC_OPTIONS,
...STATIC_PRODUCT_OPTIONS,
{ label: "SKU", value: "sku", type: "DROPDOWN", slug: "sku" },
]);
});
Expand Down
7 changes: 4 additions & 3 deletions src/components/ConditionalFilter/useFilterLeftOperands.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import unionBy from "lodash/unionBy";
import { useState } from "react";

import { STATIC_OPTIONS } from "./constants";
import { LeftOperand, LeftOperandsProvider } from "./LeftOperandsProvider";

export const useFilterLeftOperandsProvider = (): LeftOperandsProvider => {
const [operands, setOperands] = useState<LeftOperand[]>(STATIC_OPTIONS);
export const useFilterLeftOperandsProvider = (
options: LeftOperand[],
): LeftOperandsProvider => {
const [operands, setOperands] = useState<LeftOperand[]>(options);

return {
operands,
Expand Down
34 changes: 24 additions & 10 deletions src/discounts/components/DiscountListPage/DiscountListPage.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ExpressionFilters } from "@dashboard/components/AppLayout/ListFilters/components/ExpressionFilters";
import { LegacyFiltersPresetsAlert } from "@dashboard/components/AppLayout/ListFilters/components/LegacyFiltersPresetsAlert";
import SearchInput from "@dashboard/components/AppLayout/ListFilters/components/SearchInput";
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import { DashboardCard } from "@dashboard/components/Card";
Expand Down Expand Up @@ -102,17 +104,29 @@ const DiscountListPage: React.FC<DiscountListPageProps> = ({
</TopNav>

<DashboardCard>
<Box __width="320px" marginLeft={4} marginBottom={2}>
{/* TODO: remove when new fileters will be implemented */}
<SearchInput
initialSearch={initialSearch}
placeholder={intl.formatMessage({
id: "+bhokL",
defaultMessage: "Search discounts...",
})}
onSearchChange={onSearchChange}
/>
<LegacyFiltersPresetsAlert />
<Box
display="grid"
__gridTemplateColumns="auto 1fr"
gap={4}
paddingBottom={2}
paddingX={6}
>
<Box display="flex" alignItems="center" gap={4}>
<ExpressionFilters data-test-id="filters-button" />
<Box __width="320px">
<SearchInput
initialSearch={initialSearch}
placeholder={intl.formatMessage({
id: "+bhokL",
defaultMessage: "Search discounts...",
})}
onSearchChange={onSearchChange}
/>
</Box>
</Box>
</Box>

<DiscountListDatagrid {...listProps} onRowClick={handleRowClick} />
</DashboardCard>
</ListPageLayout>
Expand Down
7 changes: 6 additions & 1 deletion src/discounts/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConditionalDiscountFilterProvider } from "@dashboard/components/ConditionalFilter";
import { useFlag } from "@dashboard/featureFlags";
import { sectionNames } from "@dashboard/intl";
import { asSortParams } from "@dashboard/utils/sort";
Expand Down Expand Up @@ -45,7 +46,11 @@ const SaleListView: React.FC<RouteComponentProps<{}>> = ({ location }) => {
qs,
DiscountListUrlSortField,
);
return <DiscountList params={params} />;
return (
<ConditionalDiscountFilterProvider locationSearch={location.search}>
<DiscountList params={params} />;
</ConditionalDiscountFilterProvider>
);
}

return <SaleListViewComponent params={params} />;
Expand Down
7 changes: 6 additions & 1 deletion src/discounts/views/DiscountList/DiscountList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useConditionalFilterContext } from "@dashboard/components/ConditionalFilter";
import { creatDiscountsQueryVariables } from "@dashboard/components/ConditionalFilter/queryVariables";
import DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog";
import SaveFilterTabDialog from "@dashboard/components/SaveFilterTabDialog";
import { WindowTitle } from "@dashboard/components/WindowTitle";
Expand Down Expand Up @@ -44,11 +46,15 @@ export const DiscountList: React.FC<DiscountListProps> = ({ params }) => {
usePaginationReset(discountListUrl, params, settings.rowNumber);
const paginationState = createPaginationState(settings.rowNumber, params);

const { valueProvider } = useConditionalFilterContext();
const where = creatDiscountsQueryVariables(valueProvider.value);

const queryVariables = React.useMemo(
() => ({
...paginationState,
sort: getSortQueryVariables(params),
where: {
...where,
...(params?.query && {
name: { eq: params.query },
}),
Expand All @@ -68,7 +74,6 @@ export const DiscountList: React.FC<DiscountListProps> = ({ params }) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, resetFilters, handleSearchChange] = createFilterHandlers({
createUrl: discountListUrl,
// TODO: implement getFilterQueryParam when new filter will be implemented
getFilterQueryParam: () => 0,
navigate,
params,
Expand Down
Loading