Static datagrid in order view (#3276)
* Init datagrid * Product type and channels on datagrid * Move get cell content to utils * Add channels to storybook * Add product description to datagrid * Thubnail cell with avatar * Translations * Allow add new empty product in datagrid * Search product types * useProductForm wth save bar and exit modal * Add description to product fixture * Add loading placeholder * Remove old column picker * Sorting * Remove old ProductList * Update filter props * Add header icons * Add sort icons to datagrid column * Show available channels * Add price and updatedAt columns * Fix sorting, only sort on selected columns * Sort by channel * Allow delete name and product type * Fix show not product found * Extract mssages * Product datagrid custom column picker * Column picker in data grid in dirty hack for bug * fix storybook props * Restore Datagrid defalt column picker with custom render * Add sort by attributes * Use datagrid loading cells * Fix product searching * Show attributes before last updated * Readonly all fields in datagrid * Fix creating new datagrid row * Remove add new procut button from datagrid * Show only active sorted column * Temp fix for column filter * Fix column mismatch * Add comments and spred props to ColumnPicker * Cleanup * Update avatar size and styles * On row click with hover on row styles * Use new theme * Change placeholder image * Draw rounded image with border * Readonly product datagrid * Use new theme colors in datagrid * Add vertical borders control to datagrid * Add empty column to add padding * Add coursor to datagrid * Restore vertical borders, fix cursor pointer * Add custom freezed column * Initial tooltip for column * Move tooltip to datagrid * Adjust datagrid colors style, add possibility to select column * Change datagrid selected cells colors * Fix typo and extract messages * Base order datagrid * Cleanup Datagrid component * Cleanup and code refactor * Remove cursor pointer props from readonlyCell * Use money cell for total column * Add custom cell renderers and fix types * Simple tags implemenrtion for status and payment col * Add colors from theme * Make tagCell more dynamic * Refactor Datagrid file structure * Add loading indicators * Selecting column without cells in readonly * Add sort icons to orders list * Refactor after CR. fix typos * Change color of selected colum cell on hover * Improve selected header text contrast * Move useColumnPickerColumns to hooks dir with tests * Add less padding to column picker button * Remove double border top * Fix cursor pointer for tagCell and moneyCell * useGetCellContent hook * On loading show only one row * Add missing darkmod color for warning tag * Refactor columns in datagrid * Add new macaw theme provider to storybook * Fix passing props in datagrid * Trigger deployment * Fix column picker in products * useDatagridColumns * Fix one more time * Add column picker with default columns * Change color for selected header change to textBrandDefault * Remove unused code, move attributes colums as last * Cleanup useDatagridColumns * Improve DatagridProps * Static datagrid for products (#3144) * Migrate top nav of product list page to new MacawUI (#3290) * feat: migrate top nav of product page * feat: add proper deprecation links --------- Co-authored-by: Michał Droń <dron.official@yahoo.com> * Remove datagrid card paddding (#3310) * Implement card view for product list (#3292) * Add temporary view switcher * Add basic product tile view * Bump macaw-ui * Add ellipsis * Bump macaw-ui * Add status dot & fix non-rectangular thumbnails * Bump macaw-ui * Add variable size placeholder icon * Improve loader * Fix top nav menu key error * Add pagination * Add unit tests * Extract messages * Extract status color to function * CR Refactor * Hold product view state in local storage (#3315) * Remember view state for product list * Use util status function for status dots * Fix for empty column and hover in datagrid for product (#3324) * Remove datagrid card paddding (#3310) * Fix for empty column and hover in datagrid for product (#3324) * Use themeValues from macaw (#3326) * Upgrade macaw * Use themeValues * Use themeValues from macaw (#3326) * Upgrade macaw * Use themeValues * Add empty column from datagrid, improve theme types * Use theme type from typeof * Filter empty column from default * New product header (#3346) * Extraxt messages * Remve title left padding * Fix switching view * Add margin right to nav button * Improve view switch * Update switch view icons * Add spacing to switch * Add more space * Add new filterbar to order list * Code refactor and tests * Extract messages * Write unit tests * Improve switch component * Overwrite Pill styles * Common method to get status color for pills * Local Pill component POC * Add ThemeProvider to test wrapper * Extract messages * Refactor Pill * Fix Pill path * Fix tests mocks * Remove scrollbar and border bottom * Add custom border to to datagrid * Fix borders * Fix border bottom * Refactor and cleanup * Remove not needed selectionActions code * Move logic code t misc * Fix scrollbar and zindex datagrid borders * Fix product tiles condition * Fix empty column when save column change * Fix bottom line in layout overlap * Keep first column in datagrid not removable * Fix for not existing column * Add loader over datagrid, fix problem with border top when empty text in variants * Fix error color and change color in datagrid * Filter presets select * Fix delete tab name in modal, change order preset key * Extract messages and apply cr fixes * Keep active tab when filter and search change * Apply filter in useColumnDefault --------- Co-authored-by: Michał Droń <dron.official@yahoo.com> Co-authored-by: Krzysztof Żuraw <9116238+krzysztofzuraw@users.noreply.github.com> Co-authored-by: Michał Droń <droniu@droniu.dev>
This commit is contained in:
parent
c5f476152d
commit
8adadfb2ab
51 changed files with 1948 additions and 903 deletions
|
@ -3314,6 +3314,9 @@
|
|||
"context": "WarehouseSettings pickup title",
|
||||
"string": "Pickup"
|
||||
},
|
||||
"MJ2jZQ": {
|
||||
"string": "Total"
|
||||
},
|
||||
"MJBAqv": {
|
||||
"context": "column title used by/customer",
|
||||
"string": "Used by"
|
||||
|
@ -3522,6 +3525,9 @@
|
|||
"context": "channels section name",
|
||||
"string": "Channels"
|
||||
},
|
||||
"NmK6zy": {
|
||||
"string": "Payment"
|
||||
},
|
||||
"NnhrxZ": {
|
||||
"context": "amount of sent refund for transaction",
|
||||
"string": "{transactionType} refund"
|
||||
|
@ -4748,10 +4754,6 @@
|
|||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to publish this page?} other{Are you sure you want to publish {displayQuantity} pages?}}"
|
||||
},
|
||||
"WRkCFt": {
|
||||
"context": "tab name",
|
||||
"string": "All Orders"
|
||||
},
|
||||
"WS4ov0": {
|
||||
"context": "notifier message",
|
||||
"string": "Note was added sucessfully"
|
||||
|
@ -4922,6 +4924,9 @@
|
|||
"context": "Transaction void button - return preauthorized amount to client",
|
||||
"string": "Void"
|
||||
},
|
||||
"XPruqs": {
|
||||
"string": "Order"
|
||||
},
|
||||
"XQBVEJ": {
|
||||
"context": "order return error description when cannot refund",
|
||||
"string": "We’ve encountered a problem while refunding the products. Product’s were not refunded. Please try again."
|
||||
|
@ -6723,6 +6728,9 @@
|
|||
"lLwtgs": {
|
||||
"string": "Variants are disabled in this product type"
|
||||
},
|
||||
"lNZuWl": {
|
||||
"string": "All orders"
|
||||
},
|
||||
"lOMgms": {
|
||||
"context": "fulfillment group",
|
||||
"string": "Fulfilled from:"
|
||||
|
@ -6795,6 +6803,9 @@
|
|||
"context": "deleted multiple attributes",
|
||||
"string": "Attributes successfully delete"
|
||||
},
|
||||
"lwjzVj": {
|
||||
"string": "Edit order"
|
||||
},
|
||||
"lzdvwp": {
|
||||
"context": "field is optional",
|
||||
"string": "Optional"
|
||||
|
@ -7959,6 +7970,9 @@
|
|||
"un+VWt": {
|
||||
"string": "Search products"
|
||||
},
|
||||
"uoKAmI": {
|
||||
"string": "Add new order"
|
||||
},
|
||||
"usSkzP": {
|
||||
"context": "navigator order mode description",
|
||||
"string": "Search Orders"
|
||||
|
|
1610
package-lock.json
generated
1610
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -25,6 +25,7 @@
|
|||
"@editorjs/paragraph": "^2.8.0",
|
||||
"@editorjs/quote": "^2.4.0",
|
||||
"@glideapps/glide-data-grid": "^5.0.0",
|
||||
"@glideapps/glide-data-grid-cells": "^5.2.1",
|
||||
"@graphiql/plugin-explorer": "^0.1.12",
|
||||
"@graphiql/react": "^0.15.0",
|
||||
"@graphiql/toolkit": "^0.8.0",
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
getChannelAvailabilityLabel,
|
||||
} from "@dashboard/components/ChannelsAvailabilityDropdown/utils";
|
||||
import Checkbox from "@dashboard/components/Checkbox";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
||||
|
@ -28,7 +29,7 @@ import {
|
|||
} from "@dashboard/types";
|
||||
import { getArrowDirection } from "@dashboard/utils/sort";
|
||||
import { TableBody, TableCell, TableFooter } from "@material-ui/core";
|
||||
import { makeStyles, Pill } from "@saleor/macaw-ui";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Card, Popper } from "@material-ui/core";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import ChannelsAvailabilityMenuContent from "../ChannelsAvailabilityMenuContent";
|
||||
import { Pill } from "../Pill";
|
||||
import { messages } from "./messages";
|
||||
import {
|
||||
CollectionChannels,
|
||||
|
@ -15,16 +15,17 @@ export interface ChannelsAvailabilityDropdownProps {
|
|||
channels: CollectionChannels[] | null;
|
||||
}
|
||||
|
||||
export const ChannelsAvailabilityDropdown: React.FC<ChannelsAvailabilityDropdownProps> = ({
|
||||
channels,
|
||||
}) => {
|
||||
export const ChannelsAvailabilityDropdown: React.FC<
|
||||
ChannelsAvailabilityDropdownProps
|
||||
> = ({ channels }) => {
|
||||
const intl = useIntl();
|
||||
const [isPopupOpen, setPopupOpen] = React.useState(false);
|
||||
const anchor = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const dropdownColor = React.useMemo(() => getDropdownColor(channels), [
|
||||
channels,
|
||||
]);
|
||||
const dropdownColor = React.useMemo(
|
||||
() => getDropdownColor(channels),
|
||||
[channels],
|
||||
);
|
||||
|
||||
if (!channels?.length) {
|
||||
return (
|
||||
|
|
|
@ -2,11 +2,12 @@ import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
|||
import { CollectionFragment } from "@dashboard/graphql";
|
||||
import ScrollableContent from "@dashboard/plugins/components/PluginsList/PluginAvailabilityStatusPopup/ScrollableContent";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { Pill, PillColor } from "@saleor/macaw-ui";
|
||||
import { PillColor } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { MessageDescriptor, useIntl } from "react-intl";
|
||||
|
||||
import { messages } from "../ChannelsAvailabilityDropdown/messages";
|
||||
import { Pill } from "../Pill";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
export interface ChannelsAvailabilityMenuContentProps {
|
||||
|
|
|
@ -13,8 +13,8 @@ import DataEditor, {
|
|||
Item,
|
||||
} from "@glideapps/glide-data-grid";
|
||||
import { GetRowThemeCallback } from "@glideapps/glide-data-grid/dist/ts/data-grid/data-grid-render";
|
||||
import { Card, CardContent, Typography } from "@material-ui/core";
|
||||
import { Box, useTheme } from "@saleor/macaw-ui/next";
|
||||
import { Card, CardContent, CircularProgress } from "@material-ui/core";
|
||||
import { Box, Text, useTheme } from "@saleor/macaw-ui/next";
|
||||
import clsx from "clsx";
|
||||
import range from "lodash/range";
|
||||
import React, {
|
||||
|
@ -35,7 +35,7 @@ import { FullScreenContainer } from "./components/FullScreenContainer";
|
|||
import { Header } from "./components/Header";
|
||||
import { RowActions } from "./components/RowActions";
|
||||
import { TooltipContainer } from "./components/TooltipContainer";
|
||||
import useCells from "./customCells/useCells";
|
||||
import { useCustomCellRenderers } from "./customCells/useCustomCellRenderers";
|
||||
import { headerIcons } from "./headerIcons";
|
||||
import useDatagridChange, {
|
||||
DatagridChange,
|
||||
|
@ -75,6 +75,7 @@ export interface DatagridProps {
|
|||
rows: number;
|
||||
title?: string;
|
||||
fullScreenTitle?: string;
|
||||
loading?: boolean;
|
||||
selectionActions: (
|
||||
selection: number[],
|
||||
actions: MenuItemsActions,
|
||||
|
@ -118,6 +119,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
|||
columnSelect = "none",
|
||||
onColumnMoved,
|
||||
onColumnResize,
|
||||
loading,
|
||||
hasRowHover = false,
|
||||
...datagridProps
|
||||
}): ReactElement => {
|
||||
|
@ -125,7 +127,8 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
|||
const { themeValues } = useTheme();
|
||||
const datagridTheme = useDatagridTheme(readonly);
|
||||
const editor = useRef<DataEditorRef>();
|
||||
const cellProps = useCells();
|
||||
const customRenderers = useCustomCellRenderers();
|
||||
|
||||
const { scrolledToRight, scroller } = useScrollRight();
|
||||
|
||||
const defualtColumnPickerProps = getDefultColumnPickerProps(
|
||||
|
@ -318,6 +321,14 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
|||
[selection, selectionActions, handleRemoveRows],
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Box display="flex" justifyContent="center" marginY={12}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FullScreenContainer
|
||||
open={isOpen}
|
||||
|
@ -366,8 +377,8 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
|||
margin="auto"
|
||||
/>
|
||||
<DataEditor
|
||||
{...cellProps}
|
||||
{...datagridProps}
|
||||
customRenderers={customRenderers}
|
||||
verticalBorder={verticalBorder}
|
||||
headerIcons={headerIcons}
|
||||
theme={datagridTheme}
|
||||
|
@ -463,13 +474,8 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
|||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Box
|
||||
borderTopStyle="solid"
|
||||
borderTopWidth={1}
|
||||
borderColor="neutralHighlight"
|
||||
paddingY={9}
|
||||
>
|
||||
<Typography align="center">{emptyText}</Typography>
|
||||
<Box padding={9} textAlign="center">
|
||||
<Text size="small">{emptyText}</Text>
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import {
|
||||
CustomCell,
|
||||
CustomCellRenderer,
|
||||
CustomRenderer,
|
||||
getMiddleCenterBias,
|
||||
GridCellKind,
|
||||
ProvideEditorCallback,
|
||||
} from "@glideapps/glide-data-grid";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
@ -93,7 +94,8 @@ const DropdownCellEdit: ReturnType<ProvideEditorCallback<DropdownCell>> = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const dropdownCellRenderer: CustomCellRenderer<DropdownCell> = {
|
||||
export const dropdownCellRenderer: CustomRenderer<DropdownCell> = {
|
||||
kind: GridCellKind.Custom,
|
||||
isMatch: (c): c is DropdownCell => (c.data as any).kind === "dropdown-cell",
|
||||
draw: (args, cell) => {
|
||||
const { ctx, theme, rect } = args;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import {
|
||||
CustomCell,
|
||||
CustomCellRenderer,
|
||||
CustomRenderer,
|
||||
getMiddleCenterBias,
|
||||
GridCellKind,
|
||||
ProvideEditorCallback,
|
||||
} from "@glideapps/glide-data-grid";
|
||||
import React from "react";
|
||||
|
@ -11,7 +12,7 @@ import { usePriceField } from "../../PriceField/usePriceField";
|
|||
interface MoneyCellProps {
|
||||
readonly kind: "money-cell";
|
||||
readonly currency: string;
|
||||
readonly value: number | null;
|
||||
readonly value: number | string | null;
|
||||
}
|
||||
|
||||
export type MoneyCell = CustomCell<MoneyCellProps>;
|
||||
|
@ -45,7 +46,8 @@ const MoneyCellEdit: ReturnType<ProvideEditorCallback<MoneyCell>> = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const moneyCellRenderer = (): CustomCellRenderer<MoneyCell> => ({
|
||||
export const moneyCellRenderer = (): CustomRenderer<MoneyCell> => ({
|
||||
kind: GridCellKind.Custom,
|
||||
isMatch: (c): c is MoneyCell => (c.data as any).kind === "money-cell",
|
||||
draw: (args, cell) => {
|
||||
const { ctx, theme, rect } = args;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import {
|
||||
CustomCell,
|
||||
CustomCellRenderer,
|
||||
CustomRenderer,
|
||||
getMiddleCenterBias,
|
||||
GridCellKind,
|
||||
ProvideEditorCallback,
|
||||
} from "@glideapps/glide-data-grid";
|
||||
import React from "react";
|
||||
|
@ -38,7 +39,8 @@ const NumberCellEdit: ReturnType<ProvideEditorCallback<NumberCell>> = ({
|
|||
|
||||
export const numberCellRenderer = (
|
||||
locale: Locale,
|
||||
): CustomCellRenderer<NumberCell> => ({
|
||||
): CustomRenderer<NumberCell> => ({
|
||||
kind: GridCellKind.Custom,
|
||||
isMatch: (c): c is NumberCell => (c.data as any).kind === "number-cell",
|
||||
draw: (args, cell) => {
|
||||
const { ctx, theme, rect } = args;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import {
|
||||
CustomCell,
|
||||
CustomCellRenderer,
|
||||
CustomRenderer,
|
||||
getMiddleCenterBias,
|
||||
GridCellKind,
|
||||
TextCellEntry,
|
||||
} from "@glideapps/glide-data-grid";
|
||||
import React from "react";
|
||||
|
@ -14,7 +15,8 @@ export interface ThumbnailCellProps {
|
|||
|
||||
export type ThumbnailCell = CustomCell<ThumbnailCellProps>;
|
||||
|
||||
export const thumbnailCellRenderer: CustomCellRenderer<ThumbnailCell> = {
|
||||
export const thumbnailCellRenderer: CustomRenderer<ThumbnailCell> = {
|
||||
kind: GridCellKind.Custom,
|
||||
isMatch: (cell: CustomCell): cell is ThumbnailCell =>
|
||||
(cell.data as any).kind === "thumbnail-cell",
|
||||
draw: (args, cell) => {
|
||||
|
|
|
@ -40,6 +40,24 @@ export function readonlyTextCell(
|
|||
};
|
||||
}
|
||||
|
||||
export function tagsCell(
|
||||
tags: Array<{ tag: string; color: string }>,
|
||||
selectedTags: string[],
|
||||
opts?: Partial<GridCell>,
|
||||
): GridCell {
|
||||
return {
|
||||
...opts,
|
||||
kind: GridCellKind.Custom,
|
||||
allowOverlay: true,
|
||||
copyData: selectedTags.join(", "),
|
||||
data: {
|
||||
kind: "tags-cell",
|
||||
possibleTags: tags,
|
||||
tags: selectedTags,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function booleanCell(value: boolean): GridCell {
|
||||
return {
|
||||
...common,
|
||||
|
@ -51,8 +69,12 @@ export function booleanCell(value: boolean): GridCell {
|
|||
|
||||
export function loadingCell(): GridCell {
|
||||
return {
|
||||
kind: GridCellKind.Loading,
|
||||
kind: GridCellKind.Custom,
|
||||
allowOverlay: true,
|
||||
copyData: "",
|
||||
data: {
|
||||
kind: "spinner-cell",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -70,9 +92,14 @@ export function numberCell(
|
|||
};
|
||||
}
|
||||
|
||||
export function moneyCell(value: number | null, currency: string): MoneyCell {
|
||||
export function moneyCell(
|
||||
value: number | string | null,
|
||||
currency: string,
|
||||
opts?: Partial<GridCell>,
|
||||
): MoneyCell {
|
||||
return {
|
||||
...common,
|
||||
...opts,
|
||||
kind: GridCellKind.Custom,
|
||||
data: {
|
||||
kind: "money-cell",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import useLocale from "@dashboard/hooks/useLocale";
|
||||
import { useCustomCells } from "@glideapps/glide-data-grid";
|
||||
import { useExtraCells } from "@glideapps/glide-data-grid-cells";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { dropdownCellRenderer } from "./DropdownCell";
|
||||
|
@ -7,19 +7,20 @@ import { moneyCellRenderer } from "./MoneyCell";
|
|||
import { numberCellRenderer } from "./NumberCell";
|
||||
import { thumbnailCellRenderer } from "./ThumbnailCell";
|
||||
|
||||
function useCells() {
|
||||
export function useCustomCellRenderers() {
|
||||
const { locale } = useLocale();
|
||||
const value = useMemo(
|
||||
const { customRenderers } = useExtraCells();
|
||||
|
||||
const renderers = useMemo(
|
||||
() => [
|
||||
moneyCellRenderer(),
|
||||
numberCellRenderer(locale),
|
||||
dropdownCellRenderer,
|
||||
thumbnailCellRenderer,
|
||||
...customRenderers,
|
||||
],
|
||||
[locale],
|
||||
[customRenderers, locale],
|
||||
);
|
||||
|
||||
return useCustomCells(value);
|
||||
return renderers;
|
||||
}
|
||||
|
||||
export default useCells;
|
|
@ -16,11 +16,29 @@ export function useColumnsDefault(
|
|||
|
||||
const onColumnMoved = useCallback(
|
||||
(startIndex: number, endIndex: number): void => {
|
||||
// When empty column prevent to rearrange it order
|
||||
if (availableColumns[0]?.id === "empty") {
|
||||
if (startIndex === 0) {
|
||||
return setDisplayedColumns(prevColumns => [...prevColumns]);
|
||||
}
|
||||
|
||||
// Keep empty column always at beginning
|
||||
if (endIndex === 0) {
|
||||
return setDisplayedColumns(old =>
|
||||
addAtIndex(
|
||||
old[startIndex],
|
||||
removeAtIndex(old, startIndex),
|
||||
endIndex + 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setDisplayedColumns(old =>
|
||||
addAtIndex(old[startIndex], removeAtIndex(old, startIndex), endIndex),
|
||||
);
|
||||
},
|
||||
[setDisplayedColumns],
|
||||
[availableColumns, setDisplayedColumns],
|
||||
);
|
||||
const onColumnResize = useCallback(
|
||||
(column: GridColumn, newSize: number) =>
|
||||
|
@ -34,13 +52,22 @@ export function useColumnsDefault(
|
|||
[setColumnState],
|
||||
);
|
||||
const onColumnsChange = useCallback(
|
||||
(picked: string[]) =>
|
||||
(picked: string[]) => {
|
||||
// Keep empty column at first place
|
||||
const isEmptyColumn = availableColumns[0]?.id === "empty";
|
||||
const emptyColumn = isEmptyColumn ? [availableColumns[0].id] : [];
|
||||
|
||||
setDisplayedColumns(prevColumns => [
|
||||
...emptyColumn,
|
||||
...(isEmptyColumn
|
||||
? [availableColumns[1].id]
|
||||
: [availableColumns[0].id]),
|
||||
...prevColumns.filter(column => picked.includes(column)),
|
||||
...picked
|
||||
.filter(column => !prevColumns.find(c => c === column))
|
||||
.map(column => availableColumns.find(ac => ac.id === column).id),
|
||||
]),
|
||||
]);
|
||||
},
|
||||
[availableColumns, setDisplayedColumns],
|
||||
);
|
||||
|
||||
|
@ -48,9 +75,10 @@ export function useColumnsDefault(
|
|||
() => displayedColumns.map(id => columnState.find(ac => ac.id === id)),
|
||||
[displayedColumns, columnState],
|
||||
);
|
||||
|
||||
const columnChoices = useMemo(
|
||||
() =>
|
||||
columns.map(({ id, title }) => ({
|
||||
applyFilters(columns).map(({ id, title }) => ({
|
||||
label: title,
|
||||
value: id,
|
||||
})),
|
||||
|
@ -58,14 +86,14 @@ export function useColumnsDefault(
|
|||
);
|
||||
const availableColumnsChoices = useMemo(
|
||||
() =>
|
||||
availableColumns.map(({ id, title }) => ({
|
||||
applyFilters(availableColumns).map(({ id, title }) => ({
|
||||
label: title,
|
||||
value: id,
|
||||
})),
|
||||
[availableColumns],
|
||||
);
|
||||
const defaultColumns = useMemo(
|
||||
() => availableColumns.map(({ id }) => id),
|
||||
() => applyFilters(availableColumns).map(({ id }) => id),
|
||||
[availableColumns],
|
||||
);
|
||||
|
||||
|
@ -84,3 +112,23 @@ export function useColumnsDefault(
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
function applyFilters(columns: readonly AvailableColumn[]) {
|
||||
return columns.filter(byNoEmptyColumn).filter(byNotFirstColumn);
|
||||
}
|
||||
|
||||
function byNoEmptyColumn(column: AvailableColumn) {
|
||||
return column.id !== "empty";
|
||||
}
|
||||
|
||||
function byNotFirstColumn(
|
||||
_: AvailableColumn,
|
||||
index: number,
|
||||
array: AvailableColumn[],
|
||||
) {
|
||||
if (array.some(col => col.id === "empty")) {
|
||||
return index > 1;
|
||||
}
|
||||
|
||||
return index > 0;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import { themes, useTheme } from "@saleor/macaw-ui/next";
|
||||
import { useTheme } from "@saleor/macaw-ui/next";
|
||||
|
||||
export const useEmptyColumn = () => {
|
||||
const { theme: currentTheme } = useTheme();
|
||||
const theme = themes[currentTheme];
|
||||
const { themeValues } = useTheme();
|
||||
|
||||
return {
|
||||
id: "empty",
|
||||
title: "",
|
||||
width: 20,
|
||||
themeOverride: {
|
||||
accentColor: theme.colors.background.plain,
|
||||
accentLight: theme.colors.background.plain,
|
||||
bgHeaderHovered: theme.colors.background.plain,
|
||||
accentColor: themeValues.colors.background.plain,
|
||||
accentLight: themeValues.colors.background.plain,
|
||||
bgHeaderHovered: themeValues.colors.background.plain,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -31,12 +31,10 @@ export const Money: React.FC<MoneyProps> = props => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const amount = formatMoneyAmount(money, locale);
|
||||
|
||||
return (
|
||||
<span className={classes.root} {...rest}>
|
||||
<span className={classes.currency}>{money.currency}</span>
|
||||
{amount}
|
||||
{formatMoneyAmount(money, locale)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
|
35
src/components/Pill/Pill.tsx
Normal file
35
src/components/Pill/Pill.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { getStatusColor } from "@dashboard/misc";
|
||||
import { makeStyles, Pill as MacawuiPill, PillProps } from "@saleor/macaw-ui";
|
||||
import { useTheme, vars } from "@saleor/macaw-ui/next";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
|
||||
const useStyles = makeStyles<{
|
||||
color: PillProps["color"];
|
||||
}>(
|
||||
{
|
||||
pill: {
|
||||
borderRadius: "32px",
|
||||
border: "none",
|
||||
backgroundColor: ({ color }) => `${color} !important`,
|
||||
},
|
||||
},
|
||||
{ name: "Pill" },
|
||||
);
|
||||
|
||||
// Main purpose of this component is to override default Pill component
|
||||
// from macaw-ui to add custom styles
|
||||
// TODO: migrate to Pill component from new macaw-ui when it will be ready
|
||||
export const Pill = ({ color, ...props }: PillProps) => {
|
||||
const { theme: currentTheme } = useTheme();
|
||||
const backgroundColor = getStatusColor(color, currentTheme);
|
||||
const classes = useStyles({
|
||||
color: backgroundColor.startsWith("#")
|
||||
? backgroundColor
|
||||
: vars.colors.background[backgroundColor],
|
||||
});
|
||||
|
||||
return (
|
||||
<MacawuiPill {...props} className={clsx(classes.pill, props.className)} />
|
||||
);
|
||||
};
|
1
src/components/Pill/index.ts
Normal file
1
src/components/Pill/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./Pill";
|
|
@ -1,8 +1,8 @@
|
|||
import { Grow, Paper, Popper, Typography } from "@material-ui/core";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { Pill } from "../Pill";
|
||||
import messages from "./messages";
|
||||
import useStyles from "./styles";
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
import Hr from "@dashboard/components/Hr";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import {
|
||||
WebhookEventTypeAsyncEnum,
|
||||
WebhookEventTypeSyncEnum,
|
||||
|
@ -17,7 +18,6 @@ import {
|
|||
PageTab,
|
||||
PageTabPanel,
|
||||
PageTabs,
|
||||
Pill,
|
||||
useListWidths,
|
||||
} from "@saleor/macaw-ui";
|
||||
import React, { Dispatch, SetStateAction, useState } from "react";
|
||||
|
|
|
@ -2,6 +2,7 @@ import CardTitle from "@dashboard/components/CardTitle";
|
|||
import FormSpacer from "@dashboard/components/FormSpacer";
|
||||
import Hr from "@dashboard/components/Hr";
|
||||
import Link from "@dashboard/components/Link";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import { WebhookErrorFragment } from "@dashboard/graphql";
|
||||
import { commonMessages } from "@dashboard/intl";
|
||||
import { getFormErrors } from "@dashboard/utils/errors";
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
TextField,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Button } from "@dashboard/components/Button";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper";
|
||||
|
@ -21,7 +22,7 @@ import {
|
|||
TableCell,
|
||||
TableHead,
|
||||
} from "@material-ui/core";
|
||||
import { DeleteIcon, IconButton, Pill } from "@saleor/macaw-ui";
|
||||
import { DeleteIcon, IconButton } from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Button } from "@dashboard/components/Button";
|
|||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import { DateTime } from "@dashboard/components/Date";
|
||||
import Money from "@dashboard/components/Money";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import TableRowLink from "@dashboard/components/TableRowLink";
|
||||
|
@ -9,7 +10,7 @@ import { CustomerDetailsQuery } from "@dashboard/graphql";
|
|||
import { orderUrl } from "@dashboard/orders/urls";
|
||||
import { RelayToFlat } from "@dashboard/types";
|
||||
import { Card, TableBody, TableCell, TableHead } from "@material-ui/core";
|
||||
import { makeStyles, Pill } from "@saleor/macaw-ui";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import { Pill } from "@dashboard/components/Pill";
|
||||
import {
|
||||
ExtendedGiftCard,
|
||||
GiftCardBase,
|
||||
} from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages";
|
||||
|
||||
interface GiftCardStatusChipProps<
|
||||
T extends ExtendedGiftCard<GiftCardBase & { isActive: boolean }>
|
||||
T extends ExtendedGiftCard<GiftCardBase & { isActive: boolean }>,
|
||||
> {
|
||||
giftCard: T;
|
||||
}
|
||||
|
||||
function GiftCardStatusChip<
|
||||
T extends ExtendedGiftCard<GiftCardBase & { isActive: boolean }>
|
||||
T extends ExtendedGiftCard<GiftCardBase & { isActive: boolean }>,
|
||||
>({ giftCard }: GiftCardStatusChipProps<T>) {
|
||||
const { isExpired, isActive } = giftCard;
|
||||
const intl = useIntl();
|
||||
|
|
42
src/misc.ts
42
src/misc.ts
|
@ -8,6 +8,7 @@ import {
|
|||
} from "@dashboard/graphql";
|
||||
import { Node, SlugNode } from "@dashboard/types";
|
||||
import { ConfirmButtonTransitionState, ThemeType } from "@saleor/macaw-ui";
|
||||
import { DefaultTheme, ThemeTokensValues } from "@saleor/macaw-ui/next";
|
||||
import uniqBy from "lodash/uniqBy";
|
||||
import moment from "moment-timezone";
|
||||
import { IntlShape } from "react-intl";
|
||||
|
@ -559,3 +560,44 @@ export const getByUnmatchingId =
|
|||
|
||||
export const findById = <T extends Node>(id: string, list?: T[]) =>
|
||||
list?.find(getById(id));
|
||||
|
||||
const COLOR_WARNING = "#FBE5AC";
|
||||
const COLOR_WARNING_DARK = "#3E2F0A";
|
||||
type CustomWarningColor = typeof COLOR_WARNING | typeof COLOR_WARNING_DARK;
|
||||
|
||||
export const getStatusColor = (
|
||||
status: "error" | "warning" | "info" | "success" | "generic",
|
||||
currentTheme?: DefaultTheme,
|
||||
): keyof ThemeTokensValues["colors"]["background"] | CustomWarningColor => {
|
||||
switch (status) {
|
||||
case "error":
|
||||
return "surfaceCriticalDepressed";
|
||||
case "info":
|
||||
return "surfaceBrandDepressed";
|
||||
case "success":
|
||||
return "decorativeSurfaceSubdued2";
|
||||
case "warning":
|
||||
// TODO: use color from new macaw theme when will be ready
|
||||
return currentTheme === "defaultDark"
|
||||
? COLOR_WARNING_DARK
|
||||
: COLOR_WARNING;
|
||||
case "generic":
|
||||
return "surfaceBrandSubdued";
|
||||
default:
|
||||
return "surfaceBrandSubdued";
|
||||
}
|
||||
};
|
||||
|
||||
export const isFirstColumn = (column: number) => [-1, 0].includes(column);
|
||||
|
||||
const getAllRemovedRowsBeforeRowIndex = (
|
||||
rowIndex: number,
|
||||
removedRowsIndexs: number[],
|
||||
) => removedRowsIndexs.filter(r => r <= rowIndex);
|
||||
|
||||
export const getDatagridRowDataIndex = (
|
||||
rowIndex: number,
|
||||
removedRowsIndexs: number[],
|
||||
) =>
|
||||
rowIndex +
|
||||
getAllRemovedRowsBeforeRowIndex(rowIndex, removedRowsIndexs).length;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { DateTime } from "@dashboard/components/Date";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import { transformOrderStatus } from "@dashboard/misc";
|
||||
import { OrderSharedType } from "@dashboard/orders/types";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { Skeleton } from "@material-ui/lab";
|
||||
import { makeStyles, Pill } from "@saleor/macaw-ui";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
|
|
@ -3,6 +3,7 @@ import CardTitle from "@dashboard/components/CardTitle";
|
|||
import EventTime from "@dashboard/components/EventTime";
|
||||
import Money, { formatMoney } from "@dashboard/components/Money";
|
||||
import OverflowTooltip from "@dashboard/components/OverflowTooltip";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import { OrderDetailsFragment } from "@dashboard/graphql/transactions";
|
||||
import useLocale from "@dashboard/hooks/useLocale";
|
||||
|
@ -10,7 +11,7 @@ import { buttonMessages } from "@dashboard/intl";
|
|||
import { getUserInitials, renderCollection } from "@dashboard/misc";
|
||||
import { orderGrantRefundEditUrl } from "@dashboard/orders/urls";
|
||||
import { Card, TableCell, TableRow } from "@material-ui/core";
|
||||
import { Avatar, Pill } from "@saleor/macaw-ui";
|
||||
import { Avatar } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { DateTime } from "@dashboard/components/Date";
|
||||
import Money from "@dashboard/components/Money";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
||||
|
@ -22,7 +23,7 @@ import {
|
|||
TableHead,
|
||||
} from "@material-ui/core";
|
||||
import { CSSProperties } from "@material-ui/styles";
|
||||
import { makeStyles, Pill } from "@saleor/macaw-ui";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
|
129
src/orders/components/OrderListDatagrid/OrderListDatagrid.tsx
Normal file
129
src/orders/components/OrderListDatagrid/OrderListDatagrid.tsx
Normal file
|
@ -0,0 +1,129 @@
|
|||
import ColumnPicker from "@dashboard/components/ColumnPicker";
|
||||
import Datagrid from "@dashboard/components/Datagrid/Datagrid";
|
||||
import { useColumnsDefault } from "@dashboard/components/Datagrid/hooks/useColumnsDefault";
|
||||
import {
|
||||
DatagridChangeStateContext,
|
||||
useDatagridChangeState,
|
||||
} from "@dashboard/components/Datagrid/hooks/useDatagridChange";
|
||||
import { TablePaginationWithContext } from "@dashboard/components/TablePagination";
|
||||
import { OrderListQuery } from "@dashboard/graphql";
|
||||
import { OrderListUrlSortField } from "@dashboard/orders/urls";
|
||||
import { ListProps, RelayToFlat, SortPage } from "@dashboard/types";
|
||||
import { Item } from "@glideapps/glide-data-grid";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React, { useCallback } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { useColumns, useGetCellContent } from "./datagrid";
|
||||
import { messages } from "./messages";
|
||||
import { canBeSorted, getColumnNameAndId, getOrdersRowsLength } from "./utils";
|
||||
|
||||
interface OrderListDatagridProps
|
||||
extends ListProps,
|
||||
SortPage<OrderListUrlSortField> {
|
||||
orders: RelayToFlat<OrderListQuery["orders"]>;
|
||||
onRowClick: (id: string) => void;
|
||||
hasRowHover?: boolean;
|
||||
}
|
||||
|
||||
export const OrderListDatagrid: React.FC<OrderListDatagridProps> = ({
|
||||
orders,
|
||||
disabled,
|
||||
settings,
|
||||
onUpdateListSettings,
|
||||
onSort,
|
||||
sort,
|
||||
onRowClick,
|
||||
hasRowHover,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const datagrid = useDatagridChangeState();
|
||||
const availableColumns = useColumns(sort);
|
||||
const ordersLength = getOrdersRowsLength(orders, disabled);
|
||||
|
||||
const {
|
||||
availableColumnsChoices,
|
||||
columnChoices,
|
||||
columns,
|
||||
defaultColumns,
|
||||
onColumnMoved,
|
||||
onColumnResize,
|
||||
onColumnsChange,
|
||||
picker,
|
||||
} = useColumnsDefault(availableColumns);
|
||||
|
||||
const handleHeaderClick = useCallback(
|
||||
(col: number) => {
|
||||
const { columnName, columnId } = getColumnNameAndId(columns[col].id);
|
||||
|
||||
if (canBeSorted(columnName)) {
|
||||
onSort(columnName, columnId);
|
||||
}
|
||||
},
|
||||
[columns, onSort],
|
||||
);
|
||||
|
||||
const handleRowClick = useCallback(
|
||||
([_, row]: Item) => {
|
||||
const rowData = orders[row];
|
||||
onRowClick(rowData.id);
|
||||
},
|
||||
[onRowClick, orders],
|
||||
);
|
||||
|
||||
const getCellContent = useGetCellContent({
|
||||
columns,
|
||||
orders,
|
||||
});
|
||||
|
||||
return (
|
||||
<Box __marginTop={ordersLength > 0 ? -1 : 0}>
|
||||
<DatagridChangeStateContext.Provider value={datagrid}>
|
||||
<Datagrid
|
||||
readonly
|
||||
rowMarkers="none"
|
||||
loading={disabled}
|
||||
columnSelect="single"
|
||||
hasRowHover={hasRowHover}
|
||||
freezeColumns={2}
|
||||
verticalBorder={col => (col > 1 ? true : false)}
|
||||
availableColumns={columns}
|
||||
onHeaderClicked={handleHeaderClick}
|
||||
emptyText={intl.formatMessage(messages.emptyText)}
|
||||
getCellContent={getCellContent}
|
||||
getCellError={() => false}
|
||||
menuItems={() => []}
|
||||
rows={getOrdersRowsLength(orders, disabled)}
|
||||
selectionActions={() => null}
|
||||
onColumnResize={onColumnResize}
|
||||
onColumnMoved={onColumnMoved}
|
||||
renderColumnPicker={defaultProps => (
|
||||
<ColumnPicker
|
||||
{...defaultProps}
|
||||
availableColumns={availableColumnsChoices}
|
||||
initialColumns={columnChoices}
|
||||
defaultColumns={defaultColumns}
|
||||
onSave={onColumnsChange}
|
||||
hasMore={false}
|
||||
loading={false}
|
||||
onFetchMore={() => undefined}
|
||||
onQueryChange={picker.setQuery}
|
||||
query={picker.query}
|
||||
/>
|
||||
)}
|
||||
fullScreenTitle={intl.formatMessage(messages.orders)}
|
||||
onRowClick={handleRowClick}
|
||||
/>
|
||||
|
||||
<Box paddingX={9}>
|
||||
<TablePaginationWithContext
|
||||
component="div"
|
||||
settings={settings}
|
||||
disabled={disabled}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
/>
|
||||
</Box>
|
||||
</DatagridChangeStateContext.Provider>
|
||||
</Box>
|
||||
);
|
||||
};
|
228
src/orders/components/OrderListDatagrid/datagrid.ts
Normal file
228
src/orders/components/OrderListDatagrid/datagrid.ts
Normal file
|
@ -0,0 +1,228 @@
|
|||
import {
|
||||
moneyCell,
|
||||
readonlyTextCell,
|
||||
tagsCell,
|
||||
textCell,
|
||||
} from "@dashboard/components/Datagrid/customCells/cells";
|
||||
import { GetCellContentOpts } from "@dashboard/components/Datagrid/Datagrid";
|
||||
import { useEmptyColumn } from "@dashboard/components/Datagrid/hooks/useEmptyColumn";
|
||||
import { AvailableColumn } from "@dashboard/components/Datagrid/types";
|
||||
import { Locale } from "@dashboard/components/Locale";
|
||||
import { formatMoneyAmount } from "@dashboard/components/Money";
|
||||
import { OrderListQuery } from "@dashboard/graphql";
|
||||
import useLocale from "@dashboard/hooks/useLocale";
|
||||
import {
|
||||
getStatusColor,
|
||||
isFirstColumn,
|
||||
transformOrderStatus,
|
||||
transformPaymentStatus,
|
||||
} from "@dashboard/misc";
|
||||
import { OrderListUrlSortField } from "@dashboard/orders/urls";
|
||||
import { RelayToFlat, Sort } from "@dashboard/types";
|
||||
import { getColumnSortDirectionIcon } from "@dashboard/utils/columns/getColumnSortDirectionIcon";
|
||||
import { GridCell, Item } from "@glideapps/glide-data-grid";
|
||||
import {
|
||||
DefaultTheme,
|
||||
ThemeTokensValues,
|
||||
useTheme,
|
||||
} from "@saleor/macaw-ui/next";
|
||||
import moment from "moment-timezone";
|
||||
import { useMemo } from "react";
|
||||
import { IntlShape, useIntl } from "react-intl";
|
||||
|
||||
import { columnsMessages } from "./messages";
|
||||
|
||||
export const useColumns = (sort: Sort<OrderListUrlSortField>) => {
|
||||
const intl = useIntl();
|
||||
const emptyColumn = useEmptyColumn();
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
emptyColumn,
|
||||
{
|
||||
id: "number",
|
||||
title: intl.formatMessage(columnsMessages.number),
|
||||
width: 100,
|
||||
icon: getColumnSortDirectionIcon(sort, OrderListUrlSortField.number),
|
||||
},
|
||||
{
|
||||
id: "date",
|
||||
title: intl.formatMessage(columnsMessages.date),
|
||||
width: 200,
|
||||
icon: getColumnSortDirectionIcon(sort, OrderListUrlSortField.date),
|
||||
},
|
||||
{
|
||||
id: "customer",
|
||||
title: intl.formatMessage(columnsMessages.customer),
|
||||
width: 200,
|
||||
icon: getColumnSortDirectionIcon(sort, OrderListUrlSortField.customer),
|
||||
},
|
||||
{
|
||||
id: "payment",
|
||||
title: intl.formatMessage(columnsMessages.payment),
|
||||
width: 200,
|
||||
icon: getColumnSortDirectionIcon(sort, OrderListUrlSortField.payment),
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
title: intl.formatMessage(columnsMessages.status),
|
||||
width: 200,
|
||||
icon: getColumnSortDirectionIcon(
|
||||
sort,
|
||||
OrderListUrlSortField.fulfillment,
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "total",
|
||||
title: intl.formatMessage(columnsMessages.total),
|
||||
width: 150,
|
||||
},
|
||||
],
|
||||
[emptyColumn, intl, sort],
|
||||
);
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
interface GetCellContentProps {
|
||||
columns: AvailableColumn[];
|
||||
orders: RelayToFlat<OrderListQuery["orders"]>;
|
||||
}
|
||||
|
||||
function getDatagridRowDataIndex(row, removeArray) {
|
||||
return row + removeArray.filter(r => r <= row).length;
|
||||
}
|
||||
|
||||
export const useGetCellContent = ({ columns, orders }: GetCellContentProps) => {
|
||||
const intl = useIntl();
|
||||
const { locale } = useLocale();
|
||||
const { theme: currentTheme, themeValues } = useTheme();
|
||||
|
||||
return (
|
||||
[column, row]: Item,
|
||||
{ added, removed }: GetCellContentOpts,
|
||||
): GridCell => {
|
||||
if (isFirstColumn(column)) {
|
||||
return readonlyTextCell("");
|
||||
}
|
||||
|
||||
const columnId = columns[column]?.id;
|
||||
|
||||
if (!columnId) {
|
||||
return readonlyTextCell("");
|
||||
}
|
||||
|
||||
const rowData = added.includes(row)
|
||||
? undefined
|
||||
: orders[getDatagridRowDataIndex(row, removed)];
|
||||
|
||||
switch (columnId) {
|
||||
case "number":
|
||||
return readonlyTextCell(rowData.number);
|
||||
case "date":
|
||||
return getDateCellContent(locale, rowData);
|
||||
case "customer":
|
||||
return getCustomerCellContent(rowData);
|
||||
case "payment":
|
||||
return getPaymentCellContent(intl, themeValues, currentTheme, rowData);
|
||||
case "status":
|
||||
return getStatusCellContent(intl, themeValues, currentTheme, rowData);
|
||||
case "total":
|
||||
return getTotalCellContent(locale, rowData);
|
||||
default:
|
||||
return textCell("");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export function getDateCellContent(
|
||||
locale: Locale,
|
||||
rowData: RelayToFlat<OrderListQuery["orders"]>[number],
|
||||
) {
|
||||
return readonlyTextCell(moment(rowData.created).locale(locale).format("lll"));
|
||||
}
|
||||
|
||||
export function getCustomerCellContent(
|
||||
rowData: RelayToFlat<OrderListQuery["orders"]>[number],
|
||||
) {
|
||||
if (rowData.billingAddress) {
|
||||
return readonlyTextCell(
|
||||
`${rowData.billingAddress.firstName} ${rowData.billingAddress.lastName}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (rowData.userEmail) {
|
||||
return readonlyTextCell(rowData.userEmail);
|
||||
}
|
||||
|
||||
return readonlyTextCell("-");
|
||||
}
|
||||
|
||||
export function getPaymentCellContent(
|
||||
intl: IntlShape,
|
||||
theme: ThemeTokensValues,
|
||||
currentTheme: DefaultTheme,
|
||||
rowData: RelayToFlat<OrderListQuery["orders"]>[number],
|
||||
) {
|
||||
const paymentStatus = transformPaymentStatus(rowData.paymentStatus, intl);
|
||||
if (paymentStatus?.status) {
|
||||
const statusColor = getStatusColor(paymentStatus.status, currentTheme);
|
||||
|
||||
return tagsCell(
|
||||
[
|
||||
{
|
||||
tag: paymentStatus.localized,
|
||||
color: statusColor.startsWith("#")
|
||||
? statusColor
|
||||
: theme.colors.background[statusColor],
|
||||
},
|
||||
],
|
||||
[paymentStatus.localized],
|
||||
{ cursor: "pointer" },
|
||||
);
|
||||
}
|
||||
|
||||
return readonlyTextCell("-");
|
||||
}
|
||||
|
||||
export function getStatusCellContent(
|
||||
intl: IntlShape,
|
||||
theme: ThemeTokensValues,
|
||||
currentTheme: DefaultTheme,
|
||||
rowData: RelayToFlat<OrderListQuery["orders"]>[number],
|
||||
) {
|
||||
const status = transformOrderStatus(rowData.status, intl);
|
||||
const statusColor = getStatusColor(status.status, currentTheme);
|
||||
|
||||
if (status) {
|
||||
return tagsCell(
|
||||
[
|
||||
{
|
||||
tag: status.localized,
|
||||
color: statusColor.startsWith("#")
|
||||
? statusColor
|
||||
: theme.colors.background[statusColor],
|
||||
},
|
||||
],
|
||||
[status.localized],
|
||||
{ cursor: "pointer" },
|
||||
);
|
||||
}
|
||||
|
||||
return readonlyTextCell("-");
|
||||
}
|
||||
|
||||
export function getTotalCellContent(
|
||||
locale: Locale,
|
||||
rowData: RelayToFlat<OrderListQuery["orders"]>[number],
|
||||
) {
|
||||
if (rowData?.total?.gross) {
|
||||
return moneyCell(
|
||||
formatMoneyAmount(rowData.total.gross, locale),
|
||||
rowData.total.gross.currency,
|
||||
{ cursor: "pointer" },
|
||||
);
|
||||
}
|
||||
|
||||
return readonlyTextCell("-");
|
||||
}
|
1
src/orders/components/OrderListDatagrid/index.ts
Normal file
1
src/orders/components/OrderListDatagrid/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./OrderListDatagrid";
|
47
src/orders/components/OrderListDatagrid/messages.ts
Normal file
47
src/orders/components/OrderListDatagrid/messages.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
emptyText: {
|
||||
id: "RlfqSV",
|
||||
defaultMessage: "No orders found",
|
||||
},
|
||||
addOrder: {
|
||||
id: "uoKAmI",
|
||||
defaultMessage: "Add new order",
|
||||
},
|
||||
editOrder: {
|
||||
defaultMessage: "Edit order",
|
||||
id: "lwjzVj",
|
||||
},
|
||||
orders: {
|
||||
defaultMessage: "Order",
|
||||
id: "XPruqs",
|
||||
},
|
||||
});
|
||||
|
||||
export const columnsMessages = defineMessages({
|
||||
number: {
|
||||
id: "kFkPWB",
|
||||
defaultMessage: "Number",
|
||||
},
|
||||
date: {
|
||||
id: "P7PLVj",
|
||||
defaultMessage: "Date",
|
||||
},
|
||||
customer: {
|
||||
id: "hkENym",
|
||||
defaultMessage: "Customer",
|
||||
},
|
||||
payment: {
|
||||
id: "NmK6zy",
|
||||
defaultMessage: "Payment",
|
||||
},
|
||||
status: {
|
||||
id: "NWxomz",
|
||||
defaultMessage: "Fulfillment status",
|
||||
},
|
||||
total: {
|
||||
id: "MJ2jZQ",
|
||||
defaultMessage: "Total",
|
||||
},
|
||||
});
|
79
src/orders/components/OrderListDatagrid/uitls.test.ts
Normal file
79
src/orders/components/OrderListDatagrid/uitls.test.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { OrderListQuery } from "@dashboard/graphql";
|
||||
import { OrderListUrlSortField } from "@dashboard/orders/urls";
|
||||
import { RelayToFlat } from "@dashboard/types";
|
||||
|
||||
import { canBeSorted, getColumnNameAndId, getOrdersRowsLength } from "./utils";
|
||||
|
||||
describe("OrderListDatagrid utils", () => {
|
||||
describe("getOrdersRowsLength", () => {
|
||||
it("should return 1 when loading", () => {
|
||||
// Arrange & Act
|
||||
const rowLength = getOrdersRowsLength([], true);
|
||||
|
||||
// Asset
|
||||
expect(rowLength).toBe(1);
|
||||
});
|
||||
|
||||
it("should return orders length", () => {
|
||||
// Arrange & Act
|
||||
const rowLength = getOrdersRowsLength(
|
||||
[
|
||||
{} as RelayToFlat<OrderListQuery["orders"]>[number],
|
||||
{} as RelayToFlat<OrderListQuery["orders"]>[number],
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
// Asset
|
||||
expect(rowLength).toBe(2);
|
||||
});
|
||||
|
||||
it("should return 0 when no orders and no loading", () => {
|
||||
// Arrange & Act
|
||||
const rowLength = getOrdersRowsLength([], false);
|
||||
|
||||
// Asset
|
||||
expect(rowLength).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getColumnNameAndId", () => {
|
||||
it("should return column name with id when column name included colon", () => {
|
||||
// Arrange & Act
|
||||
const rowLength = getColumnNameAndId("attributes:123");
|
||||
|
||||
// Asset
|
||||
expect(rowLength).toEqual({
|
||||
columnName: "attributes",
|
||||
columnId: "123",
|
||||
});
|
||||
});
|
||||
|
||||
it("should return column name whem column name without colon", () => {
|
||||
// Arrange & Act
|
||||
const rowLength = getColumnNameAndId("test123");
|
||||
|
||||
// Asset
|
||||
expect(rowLength).toEqual({
|
||||
columnName: "test123",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("canBeSorted", () => {
|
||||
it.each([
|
||||
OrderListUrlSortField.number,
|
||||
OrderListUrlSortField.date,
|
||||
OrderListUrlSortField.customer,
|
||||
OrderListUrlSortField.payment,
|
||||
OrderListUrlSortField.fulfillment,
|
||||
])(`should return true when sortable field %s`, sortField => {
|
||||
expect(canBeSorted(sortField)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when not sortable field", () => {
|
||||
expect(canBeSorted(OrderListUrlSortField.total)).toBe(false);
|
||||
expect(canBeSorted(OrderListUrlSortField.rank)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
49
src/orders/components/OrderListDatagrid/utils.ts
Normal file
49
src/orders/components/OrderListDatagrid/utils.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { OrderListQuery } from "@dashboard/graphql";
|
||||
import { OrderListUrlSortField } from "@dashboard/orders/urls";
|
||||
import { RelayToFlat } from "@dashboard/types";
|
||||
|
||||
export function getOrdersRowsLength(
|
||||
orders?: RelayToFlat<OrderListQuery["orders"]>,
|
||||
loading?: boolean,
|
||||
) {
|
||||
if (loading) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (orders?.length) {
|
||||
return orders.length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getColumnNameAndId(column: string): {
|
||||
columnName: OrderListUrlSortField;
|
||||
columnId?: string;
|
||||
} {
|
||||
if (column.includes(":")) {
|
||||
const [columnName, columnId] = column.split(":");
|
||||
|
||||
return {
|
||||
columnName: columnName as OrderListUrlSortField,
|
||||
columnId,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
columnName: column as OrderListUrlSortField,
|
||||
};
|
||||
}
|
||||
|
||||
export function canBeSorted(sort: OrderListUrlSortField) {
|
||||
switch (sort) {
|
||||
case OrderListUrlSortField.number:
|
||||
case OrderListUrlSortField.date:
|
||||
case OrderListUrlSortField.customer:
|
||||
case OrderListUrlSortField.payment:
|
||||
case OrderListUrlSortField.fulfillment:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -80,6 +80,10 @@ const props: OrderListPageProps = {
|
|||
sort: OrderListUrlSortField.number,
|
||||
},
|
||||
params: {},
|
||||
currentTab: 0,
|
||||
hasPresetsChanged: false,
|
||||
onTabSave: () => undefined,
|
||||
onTabUpdate: () => undefined,
|
||||
};
|
||||
|
||||
storiesOf("Orders / Order list", module)
|
||||
|
|
|
@ -4,18 +4,22 @@ import {
|
|||
useExtensions,
|
||||
} from "@dashboard/apps/hooks/useExtensions";
|
||||
import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
|
||||
import { ListFilters } from "@dashboard/components/AppLayout/ListFilters";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect";
|
||||
import CardMenu from "@dashboard/components/CardMenu";
|
||||
import { useDevModeContext } from "@dashboard/components/DevModePanel/hooks";
|
||||
import FilterBar from "@dashboard/components/FilterBar";
|
||||
import { FilterPresetsSelect } from "@dashboard/components/FilterPresetsSelect";
|
||||
import { ListPageLayout } from "@dashboard/components/Layouts";
|
||||
import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { orderMessages } from "@dashboard/orders/messages";
|
||||
import { DevModeQuery } from "@dashboard/orders/queries";
|
||||
import {
|
||||
OrderListUrlQueryParams,
|
||||
OrderListUrlSortField,
|
||||
orderUrl,
|
||||
} from "@dashboard/orders/urls";
|
||||
import { getFilterVariables } from "@dashboard/orders/views/OrderList/filters";
|
||||
import {
|
||||
|
@ -27,11 +31,12 @@ import {
|
|||
import { hasLimits, isLimitReached } from "@dashboard/utils/limits";
|
||||
import { Card } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { Box, ChevronRightIcon } from "@saleor/macaw-ui/next";
|
||||
import React, { useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import OrderLimitReached from "../OrderLimitReached";
|
||||
import OrderList from "../OrderList";
|
||||
import { OrderListDatagrid } from "../OrderListDatagrid";
|
||||
import {
|
||||
createFilterStructure,
|
||||
OrderFilterKeys,
|
||||
|
@ -40,13 +45,16 @@ import {
|
|||
|
||||
export interface OrderListPageProps
|
||||
extends PageListProps,
|
||||
FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
|
||||
Omit<FilterPageProps<OrderFilterKeys, OrderListFilterOpts>, "onTabDelete">,
|
||||
SortPage<OrderListUrlSortField> {
|
||||
limits: RefreshLimitsQuery["shop"]["limits"];
|
||||
orders: RelayToFlat<OrderListQuery["orders"]>;
|
||||
hasPresetsChanged: boolean;
|
||||
onSettingsOpen: () => void;
|
||||
onAdd: () => void;
|
||||
params: OrderListUrlQueryParams;
|
||||
onTabUpdate: (tabName: string) => void;
|
||||
onTabDelete: (tabIndex: number) => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
|
@ -59,26 +67,30 @@ const useStyles = makeStyles(
|
|||
);
|
||||
|
||||
const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||
currentTab,
|
||||
initialSearch,
|
||||
filterOpts,
|
||||
limits,
|
||||
tabs,
|
||||
onAdd,
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onSettingsOpen,
|
||||
onFilterChange,
|
||||
params,
|
||||
onTabChange,
|
||||
onTabDelete,
|
||||
onTabSave,
|
||||
params,
|
||||
onTabUpdate,
|
||||
tabs,
|
||||
onAll,
|
||||
currentTab,
|
||||
hasPresetsChanged,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles({});
|
||||
const navigate = useNavigator();
|
||||
const filterStructure = createFilterStructure(intl, filterOpts);
|
||||
const limitsReached = isLimitReached(limits, "orders");
|
||||
const [isFilterPresetOpen, setFilterPresetOpen] = useState(false);
|
||||
|
||||
const { ORDER_OVERVIEW_CREATE, ORDER_OVERVIEW_MORE_ACTIONS } = useExtensions(
|
||||
extensionMountPoints.ORDER_LIST,
|
||||
|
@ -106,83 +118,113 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
|||
|
||||
return (
|
||||
<ListPageLayout>
|
||||
<TopNav title={intl.formatMessage(sectionNames.orders)}>
|
||||
{!!onSettingsOpen && (
|
||||
<CardMenu
|
||||
className={classes.settings}
|
||||
menuItems={[
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: "vEwjub",
|
||||
defaultMessage: "Open in GraphiQL",
|
||||
description: "button",
|
||||
}),
|
||||
onSelect: openPlaygroundURL,
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: "WbV1Xm",
|
||||
defaultMessage: "Order Settings",
|
||||
description: "button",
|
||||
}),
|
||||
onSelect: onSettingsOpen,
|
||||
},
|
||||
...extensionMenuItems,
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<ButtonWithSelect
|
||||
disabled={limitsReached}
|
||||
options={extensionCreateButtonItems}
|
||||
data-test-id="create-order-button"
|
||||
onClick={onAdd}
|
||||
<TopNav
|
||||
title={intl.formatMessage(sectionNames.orders)}
|
||||
withoutBorder
|
||||
isAlignToRight={false}
|
||||
>
|
||||
<Box
|
||||
__flex={1}
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="LshEVn"
|
||||
defaultMessage="Create order"
|
||||
description="button"
|
||||
/>
|
||||
</ButtonWithSelect>
|
||||
{hasLimits(limits, "orders") && (
|
||||
<LimitsInfo
|
||||
text={intl.formatMessage(
|
||||
{
|
||||
id: "zyceue",
|
||||
defaultMessage: "{count}/{max} orders",
|
||||
description: "placed order counter",
|
||||
},
|
||||
{
|
||||
count: limits.currentUsage.orders,
|
||||
max: limits.allowedUsage.orders,
|
||||
},
|
||||
<Box display="flex">
|
||||
<Box marginX={6} display="flex" alignItems="center">
|
||||
<ChevronRightIcon />
|
||||
</Box>
|
||||
|
||||
<FilterPresetsSelect
|
||||
presetsChanged={hasPresetsChanged}
|
||||
onSelect={onTabChange}
|
||||
onRemove={onTabDelete}
|
||||
onUpdate={onTabUpdate}
|
||||
savedPresets={tabs}
|
||||
activePreset={currentTab}
|
||||
onSelectAll={onAll}
|
||||
onSave={onTabSave}
|
||||
isOpen={isFilterPresetOpen}
|
||||
onOpenChange={setFilterPresetOpen}
|
||||
selectAllLabel={intl.formatMessage(
|
||||
orderMessages.filterPresetsAll,
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" alignItems="center" gap={5}>
|
||||
{!!onSettingsOpen && (
|
||||
<CardMenu
|
||||
className={classes.settings}
|
||||
menuItems={[
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: "vEwjub",
|
||||
defaultMessage: "Open in GraphiQL",
|
||||
description: "button",
|
||||
}),
|
||||
onSelect: openPlaygroundURL,
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: "WbV1Xm",
|
||||
defaultMessage: "Order Settings",
|
||||
description: "button",
|
||||
}),
|
||||
onSelect: onSettingsOpen,
|
||||
},
|
||||
...extensionMenuItems,
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<ButtonWithSelect
|
||||
disabled={limitsReached}
|
||||
options={extensionCreateButtonItems}
|
||||
data-test-id="create-order-button"
|
||||
onClick={onAdd}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="LshEVn"
|
||||
defaultMessage="Create order"
|
||||
description="button"
|
||||
/>
|
||||
</ButtonWithSelect>
|
||||
{hasLimits(limits, "orders") && (
|
||||
<LimitsInfo
|
||||
text={intl.formatMessage(
|
||||
{
|
||||
id: "zyceue",
|
||||
defaultMessage: "{count}/{max} orders",
|
||||
description: "placed order counter",
|
||||
},
|
||||
{
|
||||
count: limits.currentUsage.orders,
|
||||
max: limits.allowedUsage.orders,
|
||||
},
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</TopNav>
|
||||
{limitsReached && <OrderLimitReached />}
|
||||
<Card>
|
||||
<FilterBar
|
||||
currentTab={currentTab}
|
||||
<ListFilters
|
||||
initialSearch={initialSearch}
|
||||
onAll={onAll}
|
||||
onFilterChange={onFilterChange}
|
||||
onSearchChange={onSearchChange}
|
||||
onTabChange={onTabChange}
|
||||
onTabDelete={onTabDelete}
|
||||
onTabSave={onTabSave}
|
||||
tabs={tabs}
|
||||
allTabLabel={intl.formatMessage({
|
||||
id: "WRkCFt",
|
||||
defaultMessage: "All Orders",
|
||||
description: "tab name",
|
||||
})}
|
||||
filterStructure={filterStructure}
|
||||
searchPlaceholder={intl.formatMessage({
|
||||
id: "wTHjt3",
|
||||
defaultMessage: "Search Orders...",
|
||||
})}
|
||||
/>
|
||||
<OrderList {...listProps} />
|
||||
<OrderListDatagrid
|
||||
{...listProps}
|
||||
hasRowHover={!isFilterPresetOpen}
|
||||
onRowClick={id => {
|
||||
navigate(orderUrl(id));
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</ListPageLayout>
|
||||
);
|
||||
|
|
|
@ -3,6 +3,7 @@ import CardTitle from "@dashboard/components/CardTitle";
|
|||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { Hr } from "@dashboard/components/Hr";
|
||||
import Money from "@dashboard/components/Money";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import {
|
||||
OrderAction,
|
||||
|
@ -11,7 +12,6 @@ import {
|
|||
OrderStatus,
|
||||
} from "@dashboard/graphql";
|
||||
import { Card, CardContent } from "@material-ui/core";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
|
|
@ -27,6 +27,23 @@ jest.mock("@saleor/macaw-ui", () => ({
|
|||
ResponsiveTable: jest.fn(() => <></>),
|
||||
}));
|
||||
|
||||
jest.mock("@saleor/macaw-ui/next", () => ({
|
||||
useTheme: jest.fn(() => () => ({})),
|
||||
vars: {
|
||||
colors: {
|
||||
border: {
|
||||
naturalPlain: "",
|
||||
},
|
||||
background: {
|
||||
surfaceCriticalDepressed: "",
|
||||
surfaceBrandDepressed: "",
|
||||
decorativeSurfaceSubdued2: "",
|
||||
surfaceBrandSubdued: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock("@dashboard/hooks/useFlags", () => ({
|
||||
useFlags: jest.fn(() => ({ orderTransactions: { enabled: false } })),
|
||||
}));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Button } from "@dashboard/components/Button";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import { Hr } from "@dashboard/components/Hr";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import {
|
||||
OrderAction,
|
||||
|
@ -12,7 +13,6 @@ import {
|
|||
orderSendRefundUrl,
|
||||
} from "@dashboard/orders/urls";
|
||||
import { Card, CardContent, Typography } from "@material-ui/core";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Pill } from "@dashboard/components/Pill";
|
||||
import { TransactionEventStatus } from "@dashboard/orders/types";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ export const orderMessages = defineMessages({
|
|||
defaultMessage: "Order #{orderNumber}",
|
||||
description: "page header",
|
||||
},
|
||||
filterPresetsAll: {
|
||||
defaultMessage: "All orders",
|
||||
id: "lNZuWl",
|
||||
},
|
||||
});
|
||||
|
||||
export const transactionEventTypeMap = defineMessages<TransactionEventType>({
|
||||
|
|
|
@ -18,14 +18,19 @@ import usePaginator, {
|
|||
PaginatorContext,
|
||||
} from "@dashboard/hooks/usePaginator";
|
||||
import { useSortRedirects } from "@dashboard/hooks/useSortRedirects";
|
||||
import { getStringOrPlaceholder } from "@dashboard/misc";
|
||||
import {
|
||||
getActiveTabIndexAfterTabDelete,
|
||||
getNextUniqueTabName,
|
||||
} from "@dashboard/products/views/ProductList/utils";
|
||||
import { ListViews } from "@dashboard/types";
|
||||
import { prepareQs } from "@dashboard/utils/filters/qs";
|
||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
|
||||
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
||||
import { mapEdgesToItems, mapNodeToChoice } from "@dashboard/utils/maps";
|
||||
import { getSortParams } from "@dashboard/utils/sort";
|
||||
import React from "react";
|
||||
import { stringify } from "qs";
|
||||
import React, { useState } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import OrderListPage from "../../components/OrderListPage/OrderListPage";
|
||||
|
@ -39,13 +44,12 @@ import {
|
|||
} from "../../urls";
|
||||
import {
|
||||
deleteFilterTab,
|
||||
getActiveFilters,
|
||||
getFilterOpts,
|
||||
getFilterQueryParam,
|
||||
getFiltersCurrentTab,
|
||||
getFilterTabs,
|
||||
getFilterVariables,
|
||||
saveFilterTab,
|
||||
updateFilterTab,
|
||||
} from "./filters";
|
||||
import { DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort";
|
||||
|
||||
|
@ -60,6 +64,8 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
|||
ListViews.ORDER_LIST,
|
||||
);
|
||||
|
||||
const [tabIndexToDelete, setTabIndexToDelete] = useState<number | null>(null);
|
||||
|
||||
usePaginationReset(orderListUrl, params, settings.rowNumber);
|
||||
|
||||
const intl = useIntl();
|
||||
|
@ -91,7 +97,8 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
|||
|
||||
const tabs = getFilterTabs();
|
||||
|
||||
const currentTab = getFiltersCurrentTab(params, tabs);
|
||||
const currentTab =
|
||||
params.activeTab !== undefined ? parseInt(params.activeTab, 10) : undefined;
|
||||
|
||||
const [changeFilters, resetFilters, handleSearchChange] =
|
||||
createFilterHandlers({
|
||||
|
@ -99,6 +106,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
|||
getFilterQueryParam,
|
||||
navigate,
|
||||
params,
|
||||
keepActiveTab: true,
|
||||
});
|
||||
|
||||
const [openModal, closeModal] = createDialogActionHandlers<
|
||||
|
@ -106,21 +114,62 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
|||
OrderListUrlQueryParams
|
||||
>(navigate, orderListUrl, params);
|
||||
|
||||
const handleTabChange = (tab: number) =>
|
||||
navigate(
|
||||
orderListUrl({
|
||||
activeTab: tab.toString(),
|
||||
...getFilterTabs()[tab - 1].data,
|
||||
}),
|
||||
const hasPresetsChanged = () => {
|
||||
const activeTab = tabs[currentTab - 1];
|
||||
const { paresedQs } = prepareQs(location.search);
|
||||
|
||||
return (
|
||||
activeTab?.data !== stringify(paresedQs) &&
|
||||
location.search !== "" &&
|
||||
stringify(paresedQs) !== ""
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
const qs = new URLSearchParams(getFilterTabs()[tab - 1]?.data ?? "");
|
||||
qs.append("activeTab", tab.toString());
|
||||
|
||||
navigate(orderListUrl() + "?" + qs.toString());
|
||||
};
|
||||
|
||||
const handleFilterTabDelete = () => {
|
||||
deleteFilterTab(currentTab);
|
||||
navigate(orderListUrl());
|
||||
deleteFilterTab(tabIndexToDelete);
|
||||
|
||||
// When deleting the current tab, navigate to the All orders tab
|
||||
if (tabIndexToDelete === currentTab) {
|
||||
navigate(orderListUrl());
|
||||
} else {
|
||||
const currentParams = { ...params };
|
||||
// When deleting a tab that is not the current one, only remove the action param from the query
|
||||
delete currentParams.action;
|
||||
// When deleting a tab that is before the current one, decrease the activeTab param by 1
|
||||
currentParams.activeTab = getActiveTabIndexAfterTabDelete(
|
||||
currentTab,
|
||||
tabIndexToDelete,
|
||||
);
|
||||
navigate(orderListUrl() + "?" + stringify(currentParams));
|
||||
}
|
||||
};
|
||||
|
||||
const hanleFilterTabUpdate = (tabName: string) => {
|
||||
const { paresedQs, activeTab } = prepareQs(location.search);
|
||||
|
||||
updateFilterTab(tabName, stringify(paresedQs));
|
||||
paresedQs.activeTab = activeTab;
|
||||
|
||||
navigate(orderListUrl() + "?" + stringify(paresedQs));
|
||||
};
|
||||
|
||||
const handleFilterTabSave = (data: SaveFilterTabDialogFormData) => {
|
||||
saveFilterTab(data.name, getActiveFilters(params));
|
||||
const { paresedQs } = prepareQs(location.search);
|
||||
|
||||
saveFilterTab(
|
||||
getNextUniqueTabName(
|
||||
data.name,
|
||||
tabs.map(tab => tab.name),
|
||||
),
|
||||
stringify(paresedQs),
|
||||
);
|
||||
handleTabChange(tabs.length + 1);
|
||||
};
|
||||
|
||||
|
@ -169,13 +218,18 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
|||
onSearchChange={handleSearchChange}
|
||||
onFilterChange={changeFilters}
|
||||
onTabSave={() => openModal("save-search")}
|
||||
onTabDelete={() => openModal("delete-search")}
|
||||
onTabDelete={(tabIndex: number) => {
|
||||
setTabIndexToDelete(tabIndex);
|
||||
openModal("delete-search");
|
||||
}}
|
||||
onTabChange={handleTabChange}
|
||||
onTabUpdate={hanleFilterTabUpdate}
|
||||
initialSearch={params.query || ""}
|
||||
tabs={getFilterTabs().map(tab => tab.name)}
|
||||
onAll={resetFilters}
|
||||
onSettingsOpen={() => navigate(orderSettingsPath)}
|
||||
params={params}
|
||||
hasPresetsChanged={hasPresetsChanged()}
|
||||
/>
|
||||
<SaveFilterTabDialog
|
||||
open={params.action === "save-search"}
|
||||
|
@ -188,7 +242,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
|||
confirmButtonState="default"
|
||||
onClose={closeModal}
|
||||
onSubmit={handleFilterTabDelete}
|
||||
tabName={getStringOrPlaceholder(tabs[currentTab - 1]?.name)}
|
||||
tabName={tabs[tabIndexToDelete - 1]?.name ?? "..."}
|
||||
/>
|
||||
{!noChannel && (
|
||||
<ChannelPickerDialog
|
||||
|
|
|
@ -35,7 +35,7 @@ import {
|
|||
OrderListUrlQueryParams,
|
||||
} from "../../urls";
|
||||
|
||||
export const ORDER_FILTERS_KEY = "orderFilters";
|
||||
export const ORDER_FILTERS_KEY = "orderFiltersPresets";
|
||||
|
||||
export function getFilterOpts(
|
||||
params: OrderListUrlFilters,
|
||||
|
@ -199,8 +199,12 @@ export function getFilterQueryParam(
|
|||
}
|
||||
}
|
||||
|
||||
export const { deleteFilterTab, getFilterTabs, saveFilterTab } =
|
||||
createFilterTabUtils<OrderListUrlFilters>(ORDER_FILTERS_KEY);
|
||||
export const {
|
||||
deleteFilterTab,
|
||||
getFilterTabs,
|
||||
saveFilterTab,
|
||||
updateFilterTab,
|
||||
} = createFilterTabUtils<string>(ORDER_FILTERS_KEY);
|
||||
|
||||
export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } =
|
||||
createFilterUtils<OrderListUrlQueryParams, OrderListUrlFilters>({
|
||||
|
|
|
@ -21,6 +21,5 @@ export function getSortQueryField(sort: OrderListUrlSortField): OrderSortField {
|
|||
}
|
||||
}
|
||||
|
||||
export const getSortQueryVariables = createGetSortQueryVariables(
|
||||
getSortQueryField,
|
||||
);
|
||||
export const getSortQueryVariables =
|
||||
createGetSortQueryVariables(getSortQueryField);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Checkbox from "@dashboard/components/Checkbox";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
||||
|
@ -11,7 +12,7 @@ import { PageListUrlSortField, pageUrl } from "@dashboard/pages/urls";
|
|||
import { ListActions, ListProps, SortPage } from "@dashboard/types";
|
||||
import { getArrowDirection } from "@dashboard/utils/sort";
|
||||
import { Card, TableBody, TableCell, TableFooter } from "@material-ui/core";
|
||||
import { makeStyles, Pill } from "@saleor/macaw-ui";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Pill } from "@dashboard/components/Pill";
|
||||
import { PluginBaseFragment } from "@dashboard/graphql";
|
||||
import { isPluginGlobal } from "@dashboard/plugins/views/utils";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import { Pill } from "@dashboard/components/Pill";
|
||||
import { PluginBaseFragment } from "@dashboard/graphql";
|
||||
import { CardContent, Typography } from "@material-ui/core";
|
||||
import { Pill } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
|
@ -14,9 +14,9 @@ interface GlobalConfigPluginPopupBodyProps {
|
|||
plugin: PluginBaseFragment;
|
||||
}
|
||||
|
||||
const GlobalConfigPluginPopupBody: React.FC<GlobalConfigPluginPopupBodyProps> = ({
|
||||
plugin,
|
||||
}) => {
|
||||
const GlobalConfigPluginPopupBody: React.FC<
|
||||
GlobalConfigPluginPopupBodyProps
|
||||
> = ({ plugin }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { active } = plugin.globalConfiguration;
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
SearchAvailableInGridAttributesQuery,
|
||||
} from "@dashboard/graphql";
|
||||
import useLocale from "@dashboard/hooks/useLocale";
|
||||
import { buttonMessages } from "@dashboard/intl";
|
||||
import { ProductListUrlSortField } from "@dashboard/products/urls";
|
||||
import { canBeSorted } from "@dashboard/products/views/ProductList/sort";
|
||||
import { useSearchProductTypes } from "@dashboard/searches/useProductTypeSearch";
|
||||
|
@ -27,10 +26,9 @@ import {
|
|||
} from "@dashboard/types";
|
||||
import { addAtIndex, removeAtIndex } from "@dashboard/utils/lists";
|
||||
import { GridColumn, Item } from "@glideapps/glide-data-grid";
|
||||
import { Button } from "@saleor/macaw-ui";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { isAttributeColumnValue } from "../ProductListPage/utils";
|
||||
import { useColumnPickerColumns } from "./hooks/useColumnPickerColumns";
|
||||
|
@ -87,6 +85,7 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
|||
const searchProductType = useSearchProductTypes();
|
||||
const datagrid = useDatagridChangeState();
|
||||
const { locale } = useLocale();
|
||||
const productsLength = getProductRowsLength(disabled, products, disabled);
|
||||
const gridAttributesFromSettings = useMemo(
|
||||
() => settings.columns.filter(isAttributeColumnValue),
|
||||
[settings.columns],
|
||||
|
@ -160,14 +159,12 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
|||
gridAttributes,
|
||||
gridAttributesFromSettings,
|
||||
selectedChannelId,
|
||||
loading,
|
||||
}),
|
||||
[
|
||||
columns,
|
||||
gridAttributes,
|
||||
gridAttributesFromSettings,
|
||||
intl,
|
||||
loading,
|
||||
locale,
|
||||
products,
|
||||
searchProductType,
|
||||
|
@ -225,10 +222,11 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<Box __marginTop={-1}>
|
||||
<Box __marginTop={productsLength > 0 ? -1 : 0}>
|
||||
<DatagridChangeStateContext.Provider value={datagrid}>
|
||||
<Datagrid
|
||||
readonly
|
||||
loading={loading}
|
||||
rowMarkers="none"
|
||||
columnSelect="single"
|
||||
freezeColumns={2}
|
||||
|
@ -243,12 +241,8 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
|||
getCellContent={getCellContent}
|
||||
getCellError={() => false}
|
||||
menuItems={() => []}
|
||||
rows={getProductRowsLength(disabled, products)}
|
||||
selectionActions={(indexes, { removeRows }) => (
|
||||
<Button variant="tertiary" onClick={() => removeRows(indexes)}>
|
||||
<FormattedMessage {...buttonMessages.delete} />
|
||||
</Button>
|
||||
)}
|
||||
rows={productsLength}
|
||||
selectionActions={() => null}
|
||||
fullScreenTitle={intl.formatMessage(messages.products)}
|
||||
onRowClick={handleRowClick}
|
||||
renderColumnPicker={defaultProps => (
|
||||
|
|
|
@ -2,7 +2,6 @@ import { messages } from "@dashboard/components/ChannelsAvailabilityDropdown/mes
|
|||
import { getChannelAvailabilityLabel } from "@dashboard/components/ChannelsAvailabilityDropdown/utils";
|
||||
import {
|
||||
dropdownCell,
|
||||
loadingCell,
|
||||
readonlyTextCell,
|
||||
thumbnailCell,
|
||||
} from "@dashboard/components/Datagrid/customCells/cells";
|
||||
|
@ -18,8 +17,10 @@ import { getMoneyRange } from "@dashboard/components/MoneyRange";
|
|||
import { ProductListColumns } from "@dashboard/config";
|
||||
import { GridAttributesQuery, ProductListQuery } from "@dashboard/graphql";
|
||||
import { commonMessages } from "@dashboard/intl";
|
||||
import { getDatagridRowDataIndex, isFirstColumn } from "@dashboard/misc";
|
||||
import { ProductListUrlSortField } from "@dashboard/products/urls";
|
||||
import { RelayToFlat, Sort } from "@dashboard/types";
|
||||
import { getColumnSortDirectionIcon } from "@dashboard/utils/columns/getColumnSortDirectionIcon";
|
||||
import { Item } from "@glideapps/glide-data-grid";
|
||||
import moment from "moment-timezone";
|
||||
import { IntlShape } from "react-intl";
|
||||
|
@ -50,7 +51,7 @@ export function getColumns({
|
|||
id: "name",
|
||||
title: intl.formatMessage(commonMessages.product),
|
||||
width: 300,
|
||||
icon: getColumnSortIconName(sort, ProductListUrlSortField.name),
|
||||
icon: getColumnSortDirectionIcon(sort, ProductListUrlSortField.name),
|
||||
},
|
||||
{
|
||||
id: "productType",
|
||||
|
@ -105,7 +106,7 @@ export function toAttributeColumnData(
|
|||
width: 200,
|
||||
icon:
|
||||
attributeId === activeAttributeSortId &&
|
||||
getColumnSortIconName(sort, ProductListUrlSortField.attribute),
|
||||
getColumnSortDirectionIcon(sort, ProductListUrlSortField.attribute),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -134,7 +135,6 @@ interface GetCellContentProps {
|
|||
gridAttributes: RelayToFlat<GridAttributesQuery["grid"]>;
|
||||
gridAttributesFromSettings: ProductListColumns[];
|
||||
selectedChannelId?: string;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export function createGetCellContent({
|
||||
|
@ -144,20 +144,15 @@ export function createGetCellContent({
|
|||
locale,
|
||||
products,
|
||||
selectedChannelId,
|
||||
loading,
|
||||
}: GetCellContentProps) {
|
||||
return (
|
||||
[column, row]: Item,
|
||||
{ changes, getChangeIndex, added, removed }: GetCellContentOpts,
|
||||
) => {
|
||||
if (column === -1) {
|
||||
if (isFirstColumn(column)) {
|
||||
return readonlyTextCell("");
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return loadingCell();
|
||||
}
|
||||
|
||||
const columnId = columns[column]?.id;
|
||||
|
||||
if (!columnId) {
|
||||
|
@ -167,7 +162,7 @@ export function createGetCellContent({
|
|||
const change = changes.current[getChangeIndex(columnId, row)]?.data;
|
||||
const rowData = added.includes(row)
|
||||
? undefined
|
||||
: products[row + removed.filter(r => r <= row).length];
|
||||
: products[getDatagridRowDataIndex(row, removed)];
|
||||
|
||||
const channel = rowData?.channelListings?.find(
|
||||
listing => listing.channel.id === selectedChannelId,
|
||||
|
@ -364,7 +359,12 @@ export function getColumnMetadata(column: string) {
|
|||
export function getProductRowsLength(
|
||||
disabled: boolean,
|
||||
product?: RelayToFlat<ProductListQuery["products"]>,
|
||||
loading?: boolean,
|
||||
) {
|
||||
if (loading) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (product?.length) {
|
||||
return product.length;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ export const ProductListTiles: React.FC<ProductListTilesProps> = ({
|
|||
const renderContent = useCallback(() => {
|
||||
if (loading) {
|
||||
return (
|
||||
<Box display="flex" justifyContent="center" height="100%" marginY={12}>
|
||||
<Box display="flex" justifyContent="center" marginY={12}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,8 @@ import { ExternalAppProvider } from "@dashboard/apps/components/ExternalAppConte
|
|||
import { Provider as DateProvider } from "@dashboard/components/Date/DateContext";
|
||||
import { Locale, RawLocaleProvider } from "@dashboard/components/Locale";
|
||||
import { TimezoneProvider } from "@dashboard/components/Timezone";
|
||||
import { ThemeProvider } from "@saleor/macaw-ui";
|
||||
import { ThemeProvider as LegacyThemeProvider } from "@saleor/macaw-ui";
|
||||
import { ThemeProvider } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { IntlProvider } from "react-intl";
|
||||
|
||||
|
@ -19,9 +20,11 @@ const Wrapper: React.FC = ({ children }) => (
|
|||
>
|
||||
<DateProvider value={+new Date("2018-08-07T14:30:44+00:00")}>
|
||||
<TimezoneProvider value="America/New_York">
|
||||
<ThemeProvider>
|
||||
<ExternalAppProvider>{children}</ExternalAppProvider>
|
||||
</ThemeProvider>
|
||||
<LegacyThemeProvider>
|
||||
<ThemeProvider>
|
||||
<ExternalAppProvider>{children}</ExternalAppProvider>
|
||||
</ThemeProvider>
|
||||
</LegacyThemeProvider>
|
||||
</TimezoneProvider>
|
||||
</DateProvider>
|
||||
</RawLocaleProvider>
|
||||
|
|
Loading…
Reference in a new issue