Skip to content

Commit

Permalink
Order filtering by the metadata (#5331)
Browse files Browse the repository at this point in the history
* Metadata filter

* Fix styling

* Fix styling

* Fix tuples

* Fix pagination

* Translations

* Extract messages

---------

Co-authored-by: Paweł Chyła <[email protected]>
  • Loading branch information
andrzejewsky and poulch authored Jan 8, 2025
1 parent 513cdf2 commit 5b8c8b0
Show file tree
Hide file tree
Showing 22 changed files with 200 additions and 75 deletions.
5 changes: 5 additions & 0 deletions .changeset/sweet-otters-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

Now it's possible to filter orders by its metadata.
6 changes: 6 additions & 0 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2495,6 +2495,9 @@
"context": "transaction reference",
"string": "Transaction reference"
},
"EcglP9": {
"string": "Key"
},
"Ediw6/": {
"context": "button label",
"string": "Assign references"
Expand Down Expand Up @@ -2928,6 +2931,9 @@
"GsBRWL": {
"string": "Languages"
},
"GufXy5": {
"string": "Value"
},
"GuihaP": {
"context": "order transaction refund summary label",
"string": "Shipping"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ const createAPIHandler = (
return new NoopValuesHandler([]);
}

if (rowType === "metadata") {
return new NoopValuesHandler([]);
}

throw new Error(`Unknown filter element: "${rowType}"`);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ export const createInitialOrderState = (data: InitialOrderAPIResponse[]) =>
isPreorder: createBooleanOptions(),
giftCardBought: createBooleanOptions(),
giftCardUsed: createBooleanOptions(),
customer: [],
ids: [],
created: "",
updatedAt: "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,34 +31,6 @@ describe("ConditionalFilter / API / Orders / InitialOrderState", () => {
expect(result).toEqual(expectedOutput);
});

it("should filter by customer", () => {
// Arrange
const initialOrderState = InitialOrderStateResponse.empty();

initialOrderState.customer = [
{
label: "Customer",
slug: "customer",
value: "test",
},
];

const token = UrlToken.fromUrlEntry(new UrlEntry("s0.customer", "test"));
const expectedOutput = [
{
label: "Customer",
slug: "customer",
value: "test",
},
];

// Act
const result = initialOrderState.filterByUrlToken(token);

// Assert
expect(result).toEqual(expectedOutput);
});

it("should filter by click and collect", () => {
// Arrange
const initialOrderState = InitialOrderStateResponse.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ export interface InitialOrderState {
isPreorder: ItemOption[];
giftCardBought: ItemOption[];
giftCardUsed: ItemOption[];
customer: ItemOption[];
created: string | string[];
updatedAt: string | string[];
ids: ItemOption[];
}

const isTextInput = (name: string) => ["customer"].includes(name);
const isDateField = (name: string) => ["created", "updatedAt"].includes(name);

export class InitialOrderStateResponse implements InitialOrderState {
Expand All @@ -31,7 +29,6 @@ export class InitialOrderStateResponse implements InitialOrderState {
public isPreorder: ItemOption[] = [],
public giftCardBought: ItemOption[] = [],
public giftCardUsed: ItemOption[] = [],
public customer: ItemOption[] = [],
public created: string | string[] = [],
public updatedAt: string | string[] = [],
public ids: ItemOption[] = [],
Expand All @@ -48,8 +45,8 @@ export class InitialOrderStateResponse implements InitialOrderState {

const entry = this.getEntryByName(token.name);

if (isTextInput(token.name)) {
return entry;
if (!token.isLoadable()) {
return [token.value] as string[];
}

return (entry as ItemOption[]).filter(({ slug }) => slug && token.value.includes(slug));
Expand All @@ -75,8 +72,6 @@ export class InitialOrderStateResponse implements InitialOrderState {
return this.giftCardBought;
case "giftCardUsed":
return this.giftCardUsed;
case "customer":
return this.customer;
case "ids":
return this.ids;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ import { createInitialOrderState } from "../helpers";
import { InitialOrderAPIResponse } from "../types";
import { InitialOrderStateResponse } from "./InitialOrderState";

const getCustomer = (customer: string[]) => {
if (Array.isArray(customer) && customer.length > 0) {
return customer.at(-1) ?? "";
}

return "";
};

const mapIDsToOptions = (ids: string[]) =>
ids.map(id => ({
type: "ids",
Expand Down Expand Up @@ -53,7 +45,6 @@ export const useInitialOrderState = (): InitialOrderAPIState => {
paymentStatus,
status,
authorizeStatus,
customer,
ids,
}: OrderFetchingParams) => {
if (channels.length > 0) {
Expand Down Expand Up @@ -93,14 +84,6 @@ export const useInitialOrderState = (): InitialOrderAPIState => {
status: await statusInit.fetch(),
authorizeStatus: await authorizeStatusInit.fetch(),
chargeStatus: await chargeStatusInit.fetch(),
customer: [
{
type: "customer",
label: "Customer",
value: getCustomer(customer),
slug: "customer",
},
],
ids: mapIDsToOptions(ids),
};

Expand All @@ -115,7 +98,6 @@ export const useInitialOrderState = (): InitialOrderAPIState => {
initialState.isClickAndCollect,
initialState.giftCardBought,
initialState.giftCardUsed,
initialState.customer,
initialState.created,
initialState.updatedAt,
initialState.ids,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export class Condition {
const isMultiSelect = selectedOption?.type === "multiselect" && valueItems.length > 0;
const isBulkSelect = selectedOption?.type === "bulkselect" && valueItems.length > 0;
const isDate = ["created", "updatedAt", "startDate", "endDate"].includes(token.name);

const value = isMultiSelect || isDate || isBulkSelect ? valueItems : valueItems[0];

if (!selectedOption) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class ConditionSelected {
return (
this.value === "" ||
(isItemOptionArray(this.value) && this.value.length === 0) ||
(isTuple(this.value) && this.value.includes(""))
(isTuple(this.value) && this.value.every(el => el === ""))
);
}

Expand Down
73 changes: 73 additions & 0 deletions src/components/ConditionalFilter/UI/MetadataInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Box, Input } from "@saleor/macaw-ui-next";
import React from "react";
import { useIntl } from "react-intl";

import { metadataInputMessages } from "../intl";
import { FilterEventEmitter } from "./EventEmitter";
import { DoubleTextOperator } from "./types";

interface MetadataInputProps {
index: number;
selected: DoubleTextOperator;
emitter: FilterEventEmitter;
error: boolean;
disabled: boolean;
}

export const MetadataInput = ({
index,
selected,
emitter,
error,
disabled,
}: MetadataInputProps) => {
const intl = useIntl();

return (
<Box
display="flex"
className="conditional-metadata"
borderWidth={1}
borderStyle="solid"
borderColor={{
default: "default1",
focusWithin: "accent1",
}}
borderRadius={3}
>
<Input
data-test-id={`right-${index}-1`}
value={selected.value[0] || ""}
onChange={e => {
emitter.changeRightOperator(index, [e.target.value, selected.value[1]]);
}}
onFocus={() => {
emitter.focusRightOperator(index);
}}
onBlur={() => {
emitter.blurRightOperator(index);
}}
error={error}
placeholder={intl.formatMessage(metadataInputMessages.keyPlaceholder)}
disabled={disabled}
/>
<Box __width="1px" backgroundColor="default1Focused" />
<Input
data-test-id={`right-${index}-2`}
value={selected.value[1] || ""}
onChange={e => {
emitter.changeRightOperator(index, [selected.value[0], e.target.value]);
}}
onFocus={() => {
emitter.focusRightOperator(index);
}}
onBlur={() => {
emitter.blurRightOperator(index);
}}
error={error}
placeholder={intl.formatMessage(metadataInputMessages.valuePlaceholder)}
disabled={disabled}
/>
</Box>
);
};
14 changes: 14 additions & 0 deletions src/components/ConditionalFilter/UI/RightOperator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import React from "react";

import BulkSelect from "./BulkSelect";
import { FilterEventEmitter } from "./EventEmitter";
import { MetadataInput } from "./MetadataInput";
import {
isBulkSelect,
isCombobox,
isDate,
isDateRange,
isDateTime,
isDateTimeRange,
isDoubleText,
isMultiselect,
isNumberInput,
isNumberRange,
Expand Down Expand Up @@ -261,5 +263,17 @@ export const RightOperator = ({
);
}

if (isDoubleText(selected)) {
return (
<MetadataInput
index={index}
selected={selected}
emitter={emitter}
error={error}
disabled={disabled}
/>
);
}

return <Input disabled value={selected.value} data-test-id={`right-${index}`} />;
};
4 changes: 4 additions & 0 deletions src/components/ConditionalFilter/UI/operators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ComboboxOperator,
DateOperator,
DateTimeOperator,
DoubleTextOperator,
InputOperator,
MultiselectOperator,
NumberRangeOperator,
Expand Down Expand Up @@ -42,3 +43,6 @@ export const isDateRange = (value: SelectedOperator): value is DateOperator =>

export const isDateTimeRange = (value: SelectedOperator): value is DateTimeOperator =>
value.conditionValue?.type === "datetime.range";

export const isDoubleText = (value: SelectedOperator): value is DoubleTextOperator =>
value.conditionValue?.type === "text.double";
9 changes: 8 additions & 1 deletion src/components/ConditionalFilter/UI/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type ConditionOptionTypes = ConditionOption<
| "datetime"
| "date.range"
| "datetime.range"
| "text.double"
>;

export interface Row {
Expand Down Expand Up @@ -60,7 +61,8 @@ export type SelectedOperator =
| DateOperator
| DateTimeOperator
| DateRangeOperator
| DateTimeRangeOperator;
| DateTimeRangeOperator
| DoubleTextOperator;

export interface InputOperator {
value: string | RightOperatorOption;
Expand Down Expand Up @@ -119,6 +121,11 @@ export interface DateTimeRangeOperator {
conditionValue: ConditionOption<"datetime.range"> | null;
}

export interface DoubleTextOperator {
value: [string, string];
conditionValue: ConditionOption<"text.double"> | null;
}

export interface FilterEvent extends Event {
detail?:
| RowAddData
Expand Down
1 change: 0 additions & 1 deletion src/components/ConditionalFilter/ValueProvider/UrlToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const ORDER_STATICS = [
"isPreorder",
"isClickAndCollect",
"channels",
"customer",
"ids",
];

Expand Down
13 changes: 13 additions & 0 deletions src/components/ConditionalFilter/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ export const STATIC_CONDITIONS = {
value: "input-1",
},
],
metadata: [
{
type: "text.double",
label: "is",
value: "input-1",
},
],
};

export const CONSTRAINTS = {
Expand Down Expand Up @@ -258,6 +265,12 @@ export const STATIC_ORDER_OPTIONS: LeftOperand[] = [
type: "customer",
slug: "customer",
},
{
value: "metadata",
label: "Metadata",
type: "metadata",
slug: "metadata",
},
];

export const STATIC_OPTIONS = [
Expand Down
1 change: 1 addition & 0 deletions src/components/ConditionalFilter/controlsType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const CONTROL_DEFAULTS = {
datetime: "",
"date.range": ["", ""] as [string, string],
"datetime.range": ["", ""] as [string, string],
"text.double": ["", ""] as [string, string],
};

export const getDefaultByControlName = (name: string): ConditionValue =>
Expand Down
11 changes: 11 additions & 0 deletions src/components/ConditionalFilter/intl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,14 @@ export const leftOperatorsMessages = defineMessages({
defaultMessage: "Is giftcard",
},
});

export const metadataInputMessages = defineMessages({
keyPlaceholder: {
id: "EcglP9",
defaultMessage: "Key",
},
valuePlaceholder: {
id: "GufXy5",
defaultMessage: "Value",
},
});
Loading

0 comments on commit 5b8c8b0

Please sign in to comment.