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",
|
"context": "WarehouseSettings pickup title",
|
||||||
"string": "Pickup"
|
"string": "Pickup"
|
||||||
},
|
},
|
||||||
|
"MJ2jZQ": {
|
||||||
|
"string": "Total"
|
||||||
|
},
|
||||||
"MJBAqv": {
|
"MJBAqv": {
|
||||||
"context": "column title used by/customer",
|
"context": "column title used by/customer",
|
||||||
"string": "Used by"
|
"string": "Used by"
|
||||||
|
@ -3522,6 +3525,9 @@
|
||||||
"context": "channels section name",
|
"context": "channels section name",
|
||||||
"string": "Channels"
|
"string": "Channels"
|
||||||
},
|
},
|
||||||
|
"NmK6zy": {
|
||||||
|
"string": "Payment"
|
||||||
|
},
|
||||||
"NnhrxZ": {
|
"NnhrxZ": {
|
||||||
"context": "amount of sent refund for transaction",
|
"context": "amount of sent refund for transaction",
|
||||||
"string": "{transactionType} refund"
|
"string": "{transactionType} refund"
|
||||||
|
@ -4748,10 +4754,6 @@
|
||||||
"context": "dialog content",
|
"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?}}"
|
"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": {
|
"WS4ov0": {
|
||||||
"context": "notifier message",
|
"context": "notifier message",
|
||||||
"string": "Note was added sucessfully"
|
"string": "Note was added sucessfully"
|
||||||
|
@ -4922,6 +4924,9 @@
|
||||||
"context": "Transaction void button - return preauthorized amount to client",
|
"context": "Transaction void button - return preauthorized amount to client",
|
||||||
"string": "Void"
|
"string": "Void"
|
||||||
},
|
},
|
||||||
|
"XPruqs": {
|
||||||
|
"string": "Order"
|
||||||
|
},
|
||||||
"XQBVEJ": {
|
"XQBVEJ": {
|
||||||
"context": "order return error description when cannot refund",
|
"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."
|
"string": "We’ve encountered a problem while refunding the products. Product’s were not refunded. Please try again."
|
||||||
|
@ -6723,6 +6728,9 @@
|
||||||
"lLwtgs": {
|
"lLwtgs": {
|
||||||
"string": "Variants are disabled in this product type"
|
"string": "Variants are disabled in this product type"
|
||||||
},
|
},
|
||||||
|
"lNZuWl": {
|
||||||
|
"string": "All orders"
|
||||||
|
},
|
||||||
"lOMgms": {
|
"lOMgms": {
|
||||||
"context": "fulfillment group",
|
"context": "fulfillment group",
|
||||||
"string": "Fulfilled from:"
|
"string": "Fulfilled from:"
|
||||||
|
@ -6795,6 +6803,9 @@
|
||||||
"context": "deleted multiple attributes",
|
"context": "deleted multiple attributes",
|
||||||
"string": "Attributes successfully delete"
|
"string": "Attributes successfully delete"
|
||||||
},
|
},
|
||||||
|
"lwjzVj": {
|
||||||
|
"string": "Edit order"
|
||||||
|
},
|
||||||
"lzdvwp": {
|
"lzdvwp": {
|
||||||
"context": "field is optional",
|
"context": "field is optional",
|
||||||
"string": "Optional"
|
"string": "Optional"
|
||||||
|
@ -7959,6 +7970,9 @@
|
||||||
"un+VWt": {
|
"un+VWt": {
|
||||||
"string": "Search products"
|
"string": "Search products"
|
||||||
},
|
},
|
||||||
|
"uoKAmI": {
|
||||||
|
"string": "Add new order"
|
||||||
|
},
|
||||||
"usSkzP": {
|
"usSkzP": {
|
||||||
"context": "navigator order mode description",
|
"context": "navigator order mode description",
|
||||||
"string": "Search Orders"
|
"string": "Search Orders"
|
||||||
|
|
1608
package-lock.json
generated
1608
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -25,6 +25,7 @@
|
||||||
"@editorjs/paragraph": "^2.8.0",
|
"@editorjs/paragraph": "^2.8.0",
|
||||||
"@editorjs/quote": "^2.4.0",
|
"@editorjs/quote": "^2.4.0",
|
||||||
"@glideapps/glide-data-grid": "^5.0.0",
|
"@glideapps/glide-data-grid": "^5.0.0",
|
||||||
|
"@glideapps/glide-data-grid-cells": "^5.2.1",
|
||||||
"@graphiql/plugin-explorer": "^0.1.12",
|
"@graphiql/plugin-explorer": "^0.1.12",
|
||||||
"@graphiql/react": "^0.15.0",
|
"@graphiql/react": "^0.15.0",
|
||||||
"@graphiql/toolkit": "^0.8.0",
|
"@graphiql/toolkit": "^0.8.0",
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
getChannelAvailabilityLabel,
|
getChannelAvailabilityLabel,
|
||||||
} from "@dashboard/components/ChannelsAvailabilityDropdown/utils";
|
} from "@dashboard/components/ChannelsAvailabilityDropdown/utils";
|
||||||
import Checkbox from "@dashboard/components/Checkbox";
|
import Checkbox from "@dashboard/components/Checkbox";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||||
import Skeleton from "@dashboard/components/Skeleton";
|
import Skeleton from "@dashboard/components/Skeleton";
|
||||||
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
||||||
|
@ -28,7 +29,7 @@ import {
|
||||||
} from "@dashboard/types";
|
} from "@dashboard/types";
|
||||||
import { getArrowDirection } from "@dashboard/utils/sort";
|
import { getArrowDirection } from "@dashboard/utils/sort";
|
||||||
import { TableBody, TableCell, TableFooter } from "@material-ui/core";
|
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 React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Card, Popper } from "@material-ui/core";
|
import { Card, Popper } from "@material-ui/core";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import ChannelsAvailabilityMenuContent from "../ChannelsAvailabilityMenuContent";
|
import ChannelsAvailabilityMenuContent from "../ChannelsAvailabilityMenuContent";
|
||||||
|
import { Pill } from "../Pill";
|
||||||
import { messages } from "./messages";
|
import { messages } from "./messages";
|
||||||
import {
|
import {
|
||||||
CollectionChannels,
|
CollectionChannels,
|
||||||
|
@ -15,16 +15,17 @@ export interface ChannelsAvailabilityDropdownProps {
|
||||||
channels: CollectionChannels[] | null;
|
channels: CollectionChannels[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChannelsAvailabilityDropdown: React.FC<ChannelsAvailabilityDropdownProps> = ({
|
export const ChannelsAvailabilityDropdown: React.FC<
|
||||||
channels,
|
ChannelsAvailabilityDropdownProps
|
||||||
}) => {
|
> = ({ channels }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [isPopupOpen, setPopupOpen] = React.useState(false);
|
const [isPopupOpen, setPopupOpen] = React.useState(false);
|
||||||
const anchor = React.useRef<HTMLDivElement>(null);
|
const anchor = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const dropdownColor = React.useMemo(() => getDropdownColor(channels), [
|
const dropdownColor = React.useMemo(
|
||||||
channels,
|
() => getDropdownColor(channels),
|
||||||
]);
|
[channels],
|
||||||
|
);
|
||||||
|
|
||||||
if (!channels?.length) {
|
if (!channels?.length) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,11 +2,12 @@ import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||||
import { CollectionFragment } from "@dashboard/graphql";
|
import { CollectionFragment } from "@dashboard/graphql";
|
||||||
import ScrollableContent from "@dashboard/plugins/components/PluginsList/PluginAvailabilityStatusPopup/ScrollableContent";
|
import ScrollableContent from "@dashboard/plugins/components/PluginsList/PluginAvailabilityStatusPopup/ScrollableContent";
|
||||||
import { Typography } from "@material-ui/core";
|
import { Typography } from "@material-ui/core";
|
||||||
import { Pill, PillColor } from "@saleor/macaw-ui";
|
import { PillColor } from "@saleor/macaw-ui";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { MessageDescriptor, useIntl } from "react-intl";
|
import { MessageDescriptor, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { messages } from "../ChannelsAvailabilityDropdown/messages";
|
import { messages } from "../ChannelsAvailabilityDropdown/messages";
|
||||||
|
import { Pill } from "../Pill";
|
||||||
import { useStyles } from "./styles";
|
import { useStyles } from "./styles";
|
||||||
|
|
||||||
export interface ChannelsAvailabilityMenuContentProps {
|
export interface ChannelsAvailabilityMenuContentProps {
|
||||||
|
|
|
@ -13,8 +13,8 @@ import DataEditor, {
|
||||||
Item,
|
Item,
|
||||||
} from "@glideapps/glide-data-grid";
|
} from "@glideapps/glide-data-grid";
|
||||||
import { GetRowThemeCallback } from "@glideapps/glide-data-grid/dist/ts/data-grid/data-grid-render";
|
import { GetRowThemeCallback } from "@glideapps/glide-data-grid/dist/ts/data-grid/data-grid-render";
|
||||||
import { Card, CardContent, Typography } from "@material-ui/core";
|
import { Card, CardContent, CircularProgress } from "@material-ui/core";
|
||||||
import { Box, useTheme } from "@saleor/macaw-ui/next";
|
import { Box, Text, useTheme } from "@saleor/macaw-ui/next";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import range from "lodash/range";
|
import range from "lodash/range";
|
||||||
import React, {
|
import React, {
|
||||||
|
@ -35,7 +35,7 @@ import { FullScreenContainer } from "./components/FullScreenContainer";
|
||||||
import { Header } from "./components/Header";
|
import { Header } from "./components/Header";
|
||||||
import { RowActions } from "./components/RowActions";
|
import { RowActions } from "./components/RowActions";
|
||||||
import { TooltipContainer } from "./components/TooltipContainer";
|
import { TooltipContainer } from "./components/TooltipContainer";
|
||||||
import useCells from "./customCells/useCells";
|
import { useCustomCellRenderers } from "./customCells/useCustomCellRenderers";
|
||||||
import { headerIcons } from "./headerIcons";
|
import { headerIcons } from "./headerIcons";
|
||||||
import useDatagridChange, {
|
import useDatagridChange, {
|
||||||
DatagridChange,
|
DatagridChange,
|
||||||
|
@ -75,6 +75,7 @@ export interface DatagridProps {
|
||||||
rows: number;
|
rows: number;
|
||||||
title?: string;
|
title?: string;
|
||||||
fullScreenTitle?: string;
|
fullScreenTitle?: string;
|
||||||
|
loading?: boolean;
|
||||||
selectionActions: (
|
selectionActions: (
|
||||||
selection: number[],
|
selection: number[],
|
||||||
actions: MenuItemsActions,
|
actions: MenuItemsActions,
|
||||||
|
@ -118,6 +119,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
||||||
columnSelect = "none",
|
columnSelect = "none",
|
||||||
onColumnMoved,
|
onColumnMoved,
|
||||||
onColumnResize,
|
onColumnResize,
|
||||||
|
loading,
|
||||||
hasRowHover = false,
|
hasRowHover = false,
|
||||||
...datagridProps
|
...datagridProps
|
||||||
}): ReactElement => {
|
}): ReactElement => {
|
||||||
|
@ -125,7 +127,8 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
||||||
const { themeValues } = useTheme();
|
const { themeValues } = useTheme();
|
||||||
const datagridTheme = useDatagridTheme(readonly);
|
const datagridTheme = useDatagridTheme(readonly);
|
||||||
const editor = useRef<DataEditorRef>();
|
const editor = useRef<DataEditorRef>();
|
||||||
const cellProps = useCells();
|
const customRenderers = useCustomCellRenderers();
|
||||||
|
|
||||||
const { scrolledToRight, scroller } = useScrollRight();
|
const { scrolledToRight, scroller } = useScrollRight();
|
||||||
|
|
||||||
const defualtColumnPickerProps = getDefultColumnPickerProps(
|
const defualtColumnPickerProps = getDefultColumnPickerProps(
|
||||||
|
@ -318,6 +321,14 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
||||||
[selection, selectionActions, handleRemoveRows],
|
[selection, selectionActions, handleRemoveRows],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Box display="flex" justifyContent="center" marginY={12}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FullScreenContainer
|
<FullScreenContainer
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
|
@ -366,8 +377,8 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
||||||
margin="auto"
|
margin="auto"
|
||||||
/>
|
/>
|
||||||
<DataEditor
|
<DataEditor
|
||||||
{...cellProps}
|
|
||||||
{...datagridProps}
|
{...datagridProps}
|
||||||
|
customRenderers={customRenderers}
|
||||||
verticalBorder={verticalBorder}
|
verticalBorder={verticalBorder}
|
||||||
headerIcons={headerIcons}
|
headerIcons={headerIcons}
|
||||||
theme={datagridTheme}
|
theme={datagridTheme}
|
||||||
|
@ -463,13 +474,8 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Box padding={9} textAlign="center">
|
||||||
borderTopStyle="solid"
|
<Text size="small">{emptyText}</Text>
|
||||||
borderTopWidth={1}
|
|
||||||
borderColor="neutralHighlight"
|
|
||||||
paddingY={9}
|
|
||||||
>
|
|
||||||
<Typography align="center">{emptyText}</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import {
|
import {
|
||||||
CustomCell,
|
CustomCell,
|
||||||
CustomCellRenderer,
|
CustomRenderer,
|
||||||
getMiddleCenterBias,
|
getMiddleCenterBias,
|
||||||
|
GridCellKind,
|
||||||
ProvideEditorCallback,
|
ProvideEditorCallback,
|
||||||
} from "@glideapps/glide-data-grid";
|
} from "@glideapps/glide-data-grid";
|
||||||
import { makeStyles } from "@saleor/macaw-ui";
|
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",
|
isMatch: (c): c is DropdownCell => (c.data as any).kind === "dropdown-cell",
|
||||||
draw: (args, cell) => {
|
draw: (args, cell) => {
|
||||||
const { ctx, theme, rect } = args;
|
const { ctx, theme, rect } = args;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import {
|
import {
|
||||||
CustomCell,
|
CustomCell,
|
||||||
CustomCellRenderer,
|
CustomRenderer,
|
||||||
getMiddleCenterBias,
|
getMiddleCenterBias,
|
||||||
|
GridCellKind,
|
||||||
ProvideEditorCallback,
|
ProvideEditorCallback,
|
||||||
} from "@glideapps/glide-data-grid";
|
} from "@glideapps/glide-data-grid";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -11,7 +12,7 @@ import { usePriceField } from "../../PriceField/usePriceField";
|
||||||
interface MoneyCellProps {
|
interface MoneyCellProps {
|
||||||
readonly kind: "money-cell";
|
readonly kind: "money-cell";
|
||||||
readonly currency: string;
|
readonly currency: string;
|
||||||
readonly value: number | null;
|
readonly value: number | string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MoneyCell = CustomCell<MoneyCellProps>;
|
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",
|
isMatch: (c): c is MoneyCell => (c.data as any).kind === "money-cell",
|
||||||
draw: (args, cell) => {
|
draw: (args, cell) => {
|
||||||
const { ctx, theme, rect } = args;
|
const { ctx, theme, rect } = args;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import {
|
import {
|
||||||
CustomCell,
|
CustomCell,
|
||||||
CustomCellRenderer,
|
CustomRenderer,
|
||||||
getMiddleCenterBias,
|
getMiddleCenterBias,
|
||||||
|
GridCellKind,
|
||||||
ProvideEditorCallback,
|
ProvideEditorCallback,
|
||||||
} from "@glideapps/glide-data-grid";
|
} from "@glideapps/glide-data-grid";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -38,7 +39,8 @@ const NumberCellEdit: ReturnType<ProvideEditorCallback<NumberCell>> = ({
|
||||||
|
|
||||||
export const numberCellRenderer = (
|
export const numberCellRenderer = (
|
||||||
locale: Locale,
|
locale: Locale,
|
||||||
): CustomCellRenderer<NumberCell> => ({
|
): CustomRenderer<NumberCell> => ({
|
||||||
|
kind: GridCellKind.Custom,
|
||||||
isMatch: (c): c is NumberCell => (c.data as any).kind === "number-cell",
|
isMatch: (c): c is NumberCell => (c.data as any).kind === "number-cell",
|
||||||
draw: (args, cell) => {
|
draw: (args, cell) => {
|
||||||
const { ctx, theme, rect } = args;
|
const { ctx, theme, rect } = args;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import {
|
import {
|
||||||
CustomCell,
|
CustomCell,
|
||||||
CustomCellRenderer,
|
CustomRenderer,
|
||||||
getMiddleCenterBias,
|
getMiddleCenterBias,
|
||||||
|
GridCellKind,
|
||||||
TextCellEntry,
|
TextCellEntry,
|
||||||
} from "@glideapps/glide-data-grid";
|
} from "@glideapps/glide-data-grid";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -14,7 +15,8 @@ export interface ThumbnailCellProps {
|
||||||
|
|
||||||
export type ThumbnailCell = CustomCell<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 =>
|
isMatch: (cell: CustomCell): cell is ThumbnailCell =>
|
||||||
(cell.data as any).kind === "thumbnail-cell",
|
(cell.data as any).kind === "thumbnail-cell",
|
||||||
draw: (args, 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 {
|
export function booleanCell(value: boolean): GridCell {
|
||||||
return {
|
return {
|
||||||
...common,
|
...common,
|
||||||
|
@ -51,8 +69,12 @@ export function booleanCell(value: boolean): GridCell {
|
||||||
|
|
||||||
export function loadingCell(): GridCell {
|
export function loadingCell(): GridCell {
|
||||||
return {
|
return {
|
||||||
kind: GridCellKind.Loading,
|
kind: GridCellKind.Custom,
|
||||||
allowOverlay: true,
|
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 {
|
return {
|
||||||
...common,
|
...common,
|
||||||
|
...opts,
|
||||||
kind: GridCellKind.Custom,
|
kind: GridCellKind.Custom,
|
||||||
data: {
|
data: {
|
||||||
kind: "money-cell",
|
kind: "money-cell",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import useLocale from "@dashboard/hooks/useLocale";
|
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 { useMemo } from "react";
|
||||||
|
|
||||||
import { dropdownCellRenderer } from "./DropdownCell";
|
import { dropdownCellRenderer } from "./DropdownCell";
|
||||||
|
@ -7,19 +7,20 @@ import { moneyCellRenderer } from "./MoneyCell";
|
||||||
import { numberCellRenderer } from "./NumberCell";
|
import { numberCellRenderer } from "./NumberCell";
|
||||||
import { thumbnailCellRenderer } from "./ThumbnailCell";
|
import { thumbnailCellRenderer } from "./ThumbnailCell";
|
||||||
|
|
||||||
function useCells() {
|
export function useCustomCellRenderers() {
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
const value = useMemo(
|
const { customRenderers } = useExtraCells();
|
||||||
|
|
||||||
|
const renderers = useMemo(
|
||||||
() => [
|
() => [
|
||||||
moneyCellRenderer(),
|
moneyCellRenderer(),
|
||||||
numberCellRenderer(locale),
|
numberCellRenderer(locale),
|
||||||
dropdownCellRenderer,
|
dropdownCellRenderer,
|
||||||
thumbnailCellRenderer,
|
thumbnailCellRenderer,
|
||||||
|
...customRenderers,
|
||||||
],
|
],
|
||||||
[locale],
|
[customRenderers, locale],
|
||||||
);
|
);
|
||||||
|
|
||||||
return useCustomCells(value);
|
return renderers;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useCells;
|
|
|
@ -16,11 +16,29 @@ export function useColumnsDefault(
|
||||||
|
|
||||||
const onColumnMoved = useCallback(
|
const onColumnMoved = useCallback(
|
||||||
(startIndex: number, endIndex: number): void => {
|
(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 =>
|
setDisplayedColumns(old =>
|
||||||
addAtIndex(old[startIndex], removeAtIndex(old, startIndex), endIndex),
|
addAtIndex(old[startIndex], removeAtIndex(old, startIndex), endIndex),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[setDisplayedColumns],
|
[availableColumns, setDisplayedColumns],
|
||||||
);
|
);
|
||||||
const onColumnResize = useCallback(
|
const onColumnResize = useCallback(
|
||||||
(column: GridColumn, newSize: number) =>
|
(column: GridColumn, newSize: number) =>
|
||||||
|
@ -34,13 +52,22 @@ export function useColumnsDefault(
|
||||||
[setColumnState],
|
[setColumnState],
|
||||||
);
|
);
|
||||||
const onColumnsChange = useCallback(
|
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 => [
|
setDisplayedColumns(prevColumns => [
|
||||||
|
...emptyColumn,
|
||||||
|
...(isEmptyColumn
|
||||||
|
? [availableColumns[1].id]
|
||||||
|
: [availableColumns[0].id]),
|
||||||
...prevColumns.filter(column => picked.includes(column)),
|
...prevColumns.filter(column => picked.includes(column)),
|
||||||
...picked
|
...picked
|
||||||
.filter(column => !prevColumns.find(c => c === column))
|
.filter(column => !prevColumns.find(c => c === column))
|
||||||
.map(column => availableColumns.find(ac => ac.id === column).id),
|
.map(column => availableColumns.find(ac => ac.id === column).id),
|
||||||
]),
|
]);
|
||||||
|
},
|
||||||
[availableColumns, setDisplayedColumns],
|
[availableColumns, setDisplayedColumns],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -48,9 +75,10 @@ export function useColumnsDefault(
|
||||||
() => displayedColumns.map(id => columnState.find(ac => ac.id === id)),
|
() => displayedColumns.map(id => columnState.find(ac => ac.id === id)),
|
||||||
[displayedColumns, columnState],
|
[displayedColumns, columnState],
|
||||||
);
|
);
|
||||||
|
|
||||||
const columnChoices = useMemo(
|
const columnChoices = useMemo(
|
||||||
() =>
|
() =>
|
||||||
columns.map(({ id, title }) => ({
|
applyFilters(columns).map(({ id, title }) => ({
|
||||||
label: title,
|
label: title,
|
||||||
value: id,
|
value: id,
|
||||||
})),
|
})),
|
||||||
|
@ -58,14 +86,14 @@ export function useColumnsDefault(
|
||||||
);
|
);
|
||||||
const availableColumnsChoices = useMemo(
|
const availableColumnsChoices = useMemo(
|
||||||
() =>
|
() =>
|
||||||
availableColumns.map(({ id, title }) => ({
|
applyFilters(availableColumns).map(({ id, title }) => ({
|
||||||
label: title,
|
label: title,
|
||||||
value: id,
|
value: id,
|
||||||
})),
|
})),
|
||||||
[availableColumns],
|
[availableColumns],
|
||||||
);
|
);
|
||||||
const defaultColumns = useMemo(
|
const defaultColumns = useMemo(
|
||||||
() => availableColumns.map(({ id }) => id),
|
() => applyFilters(availableColumns).map(({ id }) => id),
|
||||||
[availableColumns],
|
[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 = () => {
|
export const useEmptyColumn = () => {
|
||||||
const { theme: currentTheme } = useTheme();
|
const { themeValues } = useTheme();
|
||||||
const theme = themes[currentTheme];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: "empty",
|
id: "empty",
|
||||||
title: "",
|
title: "",
|
||||||
width: 20,
|
width: 20,
|
||||||
themeOverride: {
|
themeOverride: {
|
||||||
accentColor: theme.colors.background.plain,
|
accentColor: themeValues.colors.background.plain,
|
||||||
accentLight: theme.colors.background.plain,
|
accentLight: themeValues.colors.background.plain,
|
||||||
bgHeaderHovered: theme.colors.background.plain,
|
bgHeaderHovered: themeValues.colors.background.plain,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,12 +31,10 @@ export const Money: React.FC<MoneyProps> = props => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount = formatMoneyAmount(money, locale);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={classes.root} {...rest}>
|
<span className={classes.root} {...rest}>
|
||||||
<span className={classes.currency}>{money.currency}</span>
|
<span className={classes.currency}>{money.currency}</span>
|
||||||
{amount}
|
{formatMoneyAmount(money, locale)}
|
||||||
</span>
|
</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 { Grow, Paper, Popper, Typography } from "@material-ui/core";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import { Pill } from "../Pill";
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
import useStyles from "./styles";
|
import useStyles from "./styles";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import CardTitle from "@dashboard/components/CardTitle";
|
import CardTitle from "@dashboard/components/CardTitle";
|
||||||
import Grid from "@dashboard/components/Grid";
|
import Grid from "@dashboard/components/Grid";
|
||||||
import Hr from "@dashboard/components/Hr";
|
import Hr from "@dashboard/components/Hr";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import {
|
import {
|
||||||
WebhookEventTypeAsyncEnum,
|
WebhookEventTypeAsyncEnum,
|
||||||
WebhookEventTypeSyncEnum,
|
WebhookEventTypeSyncEnum,
|
||||||
|
@ -17,7 +18,6 @@ import {
|
||||||
PageTab,
|
PageTab,
|
||||||
PageTabPanel,
|
PageTabPanel,
|
||||||
PageTabs,
|
PageTabs,
|
||||||
Pill,
|
|
||||||
useListWidths,
|
useListWidths,
|
||||||
} from "@saleor/macaw-ui";
|
} from "@saleor/macaw-ui";
|
||||||
import React, { Dispatch, SetStateAction, useState } from "react";
|
import React, { Dispatch, SetStateAction, useState } from "react";
|
||||||
|
|
|
@ -2,6 +2,7 @@ import CardTitle from "@dashboard/components/CardTitle";
|
||||||
import FormSpacer from "@dashboard/components/FormSpacer";
|
import FormSpacer from "@dashboard/components/FormSpacer";
|
||||||
import Hr from "@dashboard/components/Hr";
|
import Hr from "@dashboard/components/Hr";
|
||||||
import Link from "@dashboard/components/Link";
|
import Link from "@dashboard/components/Link";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import { WebhookErrorFragment } from "@dashboard/graphql";
|
import { WebhookErrorFragment } from "@dashboard/graphql";
|
||||||
import { commonMessages } from "@dashboard/intl";
|
import { commonMessages } from "@dashboard/intl";
|
||||||
import { getFormErrors } from "@dashboard/utils/errors";
|
import { getFormErrors } from "@dashboard/utils/errors";
|
||||||
|
@ -13,7 +14,6 @@ import {
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Button } from "@dashboard/components/Button";
|
import { Button } from "@dashboard/components/Button";
|
||||||
import CardTitle from "@dashboard/components/CardTitle";
|
import CardTitle from "@dashboard/components/CardTitle";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||||
import Skeleton from "@dashboard/components/Skeleton";
|
import Skeleton from "@dashboard/components/Skeleton";
|
||||||
import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper";
|
import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper";
|
||||||
|
@ -21,7 +22,7 @@ import {
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { DeleteIcon, IconButton, Pill } from "@saleor/macaw-ui";
|
import { DeleteIcon, IconButton } from "@saleor/macaw-ui";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Button } from "@dashboard/components/Button";
|
||||||
import CardTitle from "@dashboard/components/CardTitle";
|
import CardTitle from "@dashboard/components/CardTitle";
|
||||||
import { DateTime } from "@dashboard/components/Date";
|
import { DateTime } from "@dashboard/components/Date";
|
||||||
import Money from "@dashboard/components/Money";
|
import Money from "@dashboard/components/Money";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||||
import Skeleton from "@dashboard/components/Skeleton";
|
import Skeleton from "@dashboard/components/Skeleton";
|
||||||
import TableRowLink from "@dashboard/components/TableRowLink";
|
import TableRowLink from "@dashboard/components/TableRowLink";
|
||||||
|
@ -9,7 +10,7 @@ import { CustomerDetailsQuery } from "@dashboard/graphql";
|
||||||
import { orderUrl } from "@dashboard/orders/urls";
|
import { orderUrl } from "@dashboard/orders/urls";
|
||||||
import { RelayToFlat } from "@dashboard/types";
|
import { RelayToFlat } from "@dashboard/types";
|
||||||
import { Card, TableBody, TableCell, TableHead } from "@material-ui/core";
|
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 React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import {
|
import {
|
||||||
ExtendedGiftCard,
|
ExtendedGiftCard,
|
||||||
GiftCardBase,
|
GiftCardBase,
|
||||||
} from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types";
|
} from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages";
|
import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages";
|
||||||
|
|
||||||
interface GiftCardStatusChipProps<
|
interface GiftCardStatusChipProps<
|
||||||
T extends ExtendedGiftCard<GiftCardBase & { isActive: boolean }>
|
T extends ExtendedGiftCard<GiftCardBase & { isActive: boolean }>,
|
||||||
> {
|
> {
|
||||||
giftCard: T;
|
giftCard: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
function GiftCardStatusChip<
|
function GiftCardStatusChip<
|
||||||
T extends ExtendedGiftCard<GiftCardBase & { isActive: boolean }>
|
T extends ExtendedGiftCard<GiftCardBase & { isActive: boolean }>,
|
||||||
>({ giftCard }: GiftCardStatusChipProps<T>) {
|
>({ giftCard }: GiftCardStatusChipProps<T>) {
|
||||||
const { isExpired, isActive } = giftCard;
|
const { isExpired, isActive } = giftCard;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
42
src/misc.ts
42
src/misc.ts
|
@ -8,6 +8,7 @@ import {
|
||||||
} from "@dashboard/graphql";
|
} from "@dashboard/graphql";
|
||||||
import { Node, SlugNode } from "@dashboard/types";
|
import { Node, SlugNode } from "@dashboard/types";
|
||||||
import { ConfirmButtonTransitionState, ThemeType } from "@saleor/macaw-ui";
|
import { ConfirmButtonTransitionState, ThemeType } from "@saleor/macaw-ui";
|
||||||
|
import { DefaultTheme, ThemeTokensValues } from "@saleor/macaw-ui/next";
|
||||||
import uniqBy from "lodash/uniqBy";
|
import uniqBy from "lodash/uniqBy";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { IntlShape } from "react-intl";
|
import { IntlShape } from "react-intl";
|
||||||
|
@ -559,3 +560,44 @@ export const getByUnmatchingId =
|
||||||
|
|
||||||
export const findById = <T extends Node>(id: string, list?: T[]) =>
|
export const findById = <T extends Node>(id: string, list?: T[]) =>
|
||||||
list?.find(getById(id));
|
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 { DateTime } from "@dashboard/components/Date";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import { transformOrderStatus } from "@dashboard/misc";
|
import { transformOrderStatus } from "@dashboard/misc";
|
||||||
import { OrderSharedType } from "@dashboard/orders/types";
|
import { OrderSharedType } from "@dashboard/orders/types";
|
||||||
import { Typography } from "@material-ui/core";
|
import { Typography } from "@material-ui/core";
|
||||||
import { Skeleton } from "@material-ui/lab";
|
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 { Box } from "@saleor/macaw-ui/next";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
|
@ -3,6 +3,7 @@ import CardTitle from "@dashboard/components/CardTitle";
|
||||||
import EventTime from "@dashboard/components/EventTime";
|
import EventTime from "@dashboard/components/EventTime";
|
||||||
import Money, { formatMoney } from "@dashboard/components/Money";
|
import Money, { formatMoney } from "@dashboard/components/Money";
|
||||||
import OverflowTooltip from "@dashboard/components/OverflowTooltip";
|
import OverflowTooltip from "@dashboard/components/OverflowTooltip";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||||
import { OrderDetailsFragment } from "@dashboard/graphql/transactions";
|
import { OrderDetailsFragment } from "@dashboard/graphql/transactions";
|
||||||
import useLocale from "@dashboard/hooks/useLocale";
|
import useLocale from "@dashboard/hooks/useLocale";
|
||||||
|
@ -10,7 +11,7 @@ import { buttonMessages } from "@dashboard/intl";
|
||||||
import { getUserInitials, renderCollection } from "@dashboard/misc";
|
import { getUserInitials, renderCollection } from "@dashboard/misc";
|
||||||
import { orderGrantRefundEditUrl } from "@dashboard/orders/urls";
|
import { orderGrantRefundEditUrl } from "@dashboard/orders/urls";
|
||||||
import { Card, TableCell, TableRow } from "@material-ui/core";
|
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 React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { DateTime } from "@dashboard/components/Date";
|
import { DateTime } from "@dashboard/components/Date";
|
||||||
import Money from "@dashboard/components/Money";
|
import Money from "@dashboard/components/Money";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||||
import Skeleton from "@dashboard/components/Skeleton";
|
import Skeleton from "@dashboard/components/Skeleton";
|
||||||
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
||||||
|
@ -22,7 +23,7 @@ import {
|
||||||
TableHead,
|
TableHead,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { CSSProperties } from "@material-ui/styles";
|
import { CSSProperties } from "@material-ui/styles";
|
||||||
import { makeStyles, Pill } from "@saleor/macaw-ui";
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
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,
|
sort: OrderListUrlSortField.number,
|
||||||
},
|
},
|
||||||
params: {},
|
params: {},
|
||||||
|
currentTab: 0,
|
||||||
|
hasPresetsChanged: false,
|
||||||
|
onTabSave: () => undefined,
|
||||||
|
onTabUpdate: () => undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Orders / Order list", module)
|
storiesOf("Orders / Order list", module)
|
||||||
|
|
|
@ -4,18 +4,22 @@ import {
|
||||||
useExtensions,
|
useExtensions,
|
||||||
} from "@dashboard/apps/hooks/useExtensions";
|
} from "@dashboard/apps/hooks/useExtensions";
|
||||||
import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
|
import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
|
||||||
|
import { ListFilters } from "@dashboard/components/AppLayout/ListFilters";
|
||||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||||
import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect";
|
import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect";
|
||||||
import CardMenu from "@dashboard/components/CardMenu";
|
import CardMenu from "@dashboard/components/CardMenu";
|
||||||
import { useDevModeContext } from "@dashboard/components/DevModePanel/hooks";
|
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 { ListPageLayout } from "@dashboard/components/Layouts";
|
||||||
import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql";
|
import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql";
|
||||||
|
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||||
import { sectionNames } from "@dashboard/intl";
|
import { sectionNames } from "@dashboard/intl";
|
||||||
|
import { orderMessages } from "@dashboard/orders/messages";
|
||||||
import { DevModeQuery } from "@dashboard/orders/queries";
|
import { DevModeQuery } from "@dashboard/orders/queries";
|
||||||
import {
|
import {
|
||||||
OrderListUrlQueryParams,
|
OrderListUrlQueryParams,
|
||||||
OrderListUrlSortField,
|
OrderListUrlSortField,
|
||||||
|
orderUrl,
|
||||||
} from "@dashboard/orders/urls";
|
} from "@dashboard/orders/urls";
|
||||||
import { getFilterVariables } from "@dashboard/orders/views/OrderList/filters";
|
import { getFilterVariables } from "@dashboard/orders/views/OrderList/filters";
|
||||||
import {
|
import {
|
||||||
|
@ -27,11 +31,12 @@ import {
|
||||||
import { hasLimits, isLimitReached } from "@dashboard/utils/limits";
|
import { hasLimits, isLimitReached } from "@dashboard/utils/limits";
|
||||||
import { Card } from "@material-ui/core";
|
import { Card } from "@material-ui/core";
|
||||||
import { makeStyles } from "@saleor/macaw-ui";
|
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 { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import OrderLimitReached from "../OrderLimitReached";
|
import OrderLimitReached from "../OrderLimitReached";
|
||||||
import OrderList from "../OrderList";
|
import { OrderListDatagrid } from "../OrderListDatagrid";
|
||||||
import {
|
import {
|
||||||
createFilterStructure,
|
createFilterStructure,
|
||||||
OrderFilterKeys,
|
OrderFilterKeys,
|
||||||
|
@ -40,13 +45,16 @@ import {
|
||||||
|
|
||||||
export interface OrderListPageProps
|
export interface OrderListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
|
Omit<FilterPageProps<OrderFilterKeys, OrderListFilterOpts>, "onTabDelete">,
|
||||||
SortPage<OrderListUrlSortField> {
|
SortPage<OrderListUrlSortField> {
|
||||||
limits: RefreshLimitsQuery["shop"]["limits"];
|
limits: RefreshLimitsQuery["shop"]["limits"];
|
||||||
orders: RelayToFlat<OrderListQuery["orders"]>;
|
orders: RelayToFlat<OrderListQuery["orders"]>;
|
||||||
|
hasPresetsChanged: boolean;
|
||||||
onSettingsOpen: () => void;
|
onSettingsOpen: () => void;
|
||||||
onAdd: () => void;
|
onAdd: () => void;
|
||||||
params: OrderListUrlQueryParams;
|
params: OrderListUrlQueryParams;
|
||||||
|
onTabUpdate: (tabName: string) => void;
|
||||||
|
onTabDelete: (tabIndex: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -59,26 +67,30 @@ const useStyles = makeStyles(
|
||||||
);
|
);
|
||||||
|
|
||||||
const OrderListPage: React.FC<OrderListPageProps> = ({
|
const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
currentTab,
|
|
||||||
initialSearch,
|
initialSearch,
|
||||||
filterOpts,
|
filterOpts,
|
||||||
limits,
|
limits,
|
||||||
tabs,
|
|
||||||
onAdd,
|
onAdd,
|
||||||
onAll,
|
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onSettingsOpen,
|
onSettingsOpen,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
|
params,
|
||||||
onTabChange,
|
onTabChange,
|
||||||
onTabDelete,
|
onTabDelete,
|
||||||
onTabSave,
|
onTabSave,
|
||||||
params,
|
onTabUpdate,
|
||||||
|
tabs,
|
||||||
|
onAll,
|
||||||
|
currentTab,
|
||||||
|
hasPresetsChanged,
|
||||||
...listProps
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
const classes = useStyles({});
|
||||||
|
const navigate = useNavigator();
|
||||||
const filterStructure = createFilterStructure(intl, filterOpts);
|
const filterStructure = createFilterStructure(intl, filterOpts);
|
||||||
const limitsReached = isLimitReached(limits, "orders");
|
const limitsReached = isLimitReached(limits, "orders");
|
||||||
|
const [isFilterPresetOpen, setFilterPresetOpen] = useState(false);
|
||||||
|
|
||||||
const { ORDER_OVERVIEW_CREATE, ORDER_OVERVIEW_MORE_ACTIONS } = useExtensions(
|
const { ORDER_OVERVIEW_CREATE, ORDER_OVERVIEW_MORE_ACTIONS } = useExtensions(
|
||||||
extensionMountPoints.ORDER_LIST,
|
extensionMountPoints.ORDER_LIST,
|
||||||
|
@ -106,7 +118,40 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListPageLayout>
|
<ListPageLayout>
|
||||||
<TopNav title={intl.formatMessage(sectionNames.orders)}>
|
<TopNav
|
||||||
|
title={intl.formatMessage(sectionNames.orders)}
|
||||||
|
withoutBorder
|
||||||
|
isAlignToRight={false}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
__flex={1}
|
||||||
|
display="flex"
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<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 && (
|
{!!onSettingsOpen && (
|
||||||
<CardMenu
|
<CardMenu
|
||||||
className={classes.settings}
|
className={classes.settings}
|
||||||
|
@ -158,31 +203,28 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
</TopNav>
|
</TopNav>
|
||||||
{limitsReached && <OrderLimitReached />}
|
{limitsReached && <OrderLimitReached />}
|
||||||
<Card>
|
<Card>
|
||||||
<FilterBar
|
<ListFilters
|
||||||
currentTab={currentTab}
|
|
||||||
initialSearch={initialSearch}
|
initialSearch={initialSearch}
|
||||||
onAll={onAll}
|
|
||||||
onFilterChange={onFilterChange}
|
onFilterChange={onFilterChange}
|
||||||
onSearchChange={onSearchChange}
|
onSearchChange={onSearchChange}
|
||||||
onTabChange={onTabChange}
|
|
||||||
onTabDelete={onTabDelete}
|
|
||||||
onTabSave={onTabSave}
|
|
||||||
tabs={tabs}
|
|
||||||
allTabLabel={intl.formatMessage({
|
|
||||||
id: "WRkCFt",
|
|
||||||
defaultMessage: "All Orders",
|
|
||||||
description: "tab name",
|
|
||||||
})}
|
|
||||||
filterStructure={filterStructure}
|
filterStructure={filterStructure}
|
||||||
searchPlaceholder={intl.formatMessage({
|
searchPlaceholder={intl.formatMessage({
|
||||||
id: "wTHjt3",
|
id: "wTHjt3",
|
||||||
defaultMessage: "Search Orders...",
|
defaultMessage: "Search Orders...",
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<OrderList {...listProps} />
|
<OrderListDatagrid
|
||||||
|
{...listProps}
|
||||||
|
hasRowHover={!isFilterPresetOpen}
|
||||||
|
onRowClick={id => {
|
||||||
|
navigate(orderUrl(id));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</ListPageLayout>
|
</ListPageLayout>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import CardTitle from "@dashboard/components/CardTitle";
|
||||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||||
import { Hr } from "@dashboard/components/Hr";
|
import { Hr } from "@dashboard/components/Hr";
|
||||||
import Money from "@dashboard/components/Money";
|
import Money from "@dashboard/components/Money";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import Skeleton from "@dashboard/components/Skeleton";
|
import Skeleton from "@dashboard/components/Skeleton";
|
||||||
import {
|
import {
|
||||||
OrderAction,
|
OrderAction,
|
||||||
|
@ -11,7 +12,6 @@ import {
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
} from "@dashboard/graphql";
|
} from "@dashboard/graphql";
|
||||||
import { Card, CardContent } from "@material-ui/core";
|
import { Card, CardContent } from "@material-ui/core";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
|
@ -27,6 +27,23 @@ jest.mock("@saleor/macaw-ui", () => ({
|
||||||
ResponsiveTable: jest.fn(() => <></>),
|
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", () => ({
|
jest.mock("@dashboard/hooks/useFlags", () => ({
|
||||||
useFlags: jest.fn(() => ({ orderTransactions: { enabled: false } })),
|
useFlags: jest.fn(() => ({ orderTransactions: { enabled: false } })),
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Button } from "@dashboard/components/Button";
|
import { Button } from "@dashboard/components/Button";
|
||||||
import CardTitle from "@dashboard/components/CardTitle";
|
import CardTitle from "@dashboard/components/CardTitle";
|
||||||
import { Hr } from "@dashboard/components/Hr";
|
import { Hr } from "@dashboard/components/Hr";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import Skeleton from "@dashboard/components/Skeleton";
|
import Skeleton from "@dashboard/components/Skeleton";
|
||||||
import {
|
import {
|
||||||
OrderAction,
|
OrderAction,
|
||||||
|
@ -12,7 +13,6 @@ import {
|
||||||
orderSendRefundUrl,
|
orderSendRefundUrl,
|
||||||
} from "@dashboard/orders/urls";
|
} from "@dashboard/orders/urls";
|
||||||
import { Card, CardContent, Typography } from "@material-ui/core";
|
import { Card, CardContent, Typography } from "@material-ui/core";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import { TransactionEventStatus } from "@dashboard/orders/types";
|
import { TransactionEventStatus } from "@dashboard/orders/types";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,10 @@ export const orderMessages = defineMessages({
|
||||||
defaultMessage: "Order #{orderNumber}",
|
defaultMessage: "Order #{orderNumber}",
|
||||||
description: "page header",
|
description: "page header",
|
||||||
},
|
},
|
||||||
|
filterPresetsAll: {
|
||||||
|
defaultMessage: "All orders",
|
||||||
|
id: "lNZuWl",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const transactionEventTypeMap = defineMessages<TransactionEventType>({
|
export const transactionEventTypeMap = defineMessages<TransactionEventType>({
|
||||||
|
|
|
@ -18,14 +18,19 @@ import usePaginator, {
|
||||||
PaginatorContext,
|
PaginatorContext,
|
||||||
} from "@dashboard/hooks/usePaginator";
|
} from "@dashboard/hooks/usePaginator";
|
||||||
import { useSortRedirects } from "@dashboard/hooks/useSortRedirects";
|
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 { ListViews } from "@dashboard/types";
|
||||||
|
import { prepareQs } from "@dashboard/utils/filters/qs";
|
||||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||||
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
|
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
|
||||||
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
||||||
import { mapEdgesToItems, mapNodeToChoice } from "@dashboard/utils/maps";
|
import { mapEdgesToItems, mapNodeToChoice } from "@dashboard/utils/maps";
|
||||||
import { getSortParams } from "@dashboard/utils/sort";
|
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 { useIntl } from "react-intl";
|
||||||
|
|
||||||
import OrderListPage from "../../components/OrderListPage/OrderListPage";
|
import OrderListPage from "../../components/OrderListPage/OrderListPage";
|
||||||
|
@ -39,13 +44,12 @@ import {
|
||||||
} from "../../urls";
|
} from "../../urls";
|
||||||
import {
|
import {
|
||||||
deleteFilterTab,
|
deleteFilterTab,
|
||||||
getActiveFilters,
|
|
||||||
getFilterOpts,
|
getFilterOpts,
|
||||||
getFilterQueryParam,
|
getFilterQueryParam,
|
||||||
getFiltersCurrentTab,
|
|
||||||
getFilterTabs,
|
getFilterTabs,
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab,
|
saveFilterTab,
|
||||||
|
updateFilterTab,
|
||||||
} from "./filters";
|
} from "./filters";
|
||||||
import { DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort";
|
import { DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
|
@ -60,6 +64,8 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
ListViews.ORDER_LIST,
|
ListViews.ORDER_LIST,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [tabIndexToDelete, setTabIndexToDelete] = useState<number | null>(null);
|
||||||
|
|
||||||
usePaginationReset(orderListUrl, params, settings.rowNumber);
|
usePaginationReset(orderListUrl, params, settings.rowNumber);
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -91,7 +97,8 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab = getFiltersCurrentTab(params, tabs);
|
const currentTab =
|
||||||
|
params.activeTab !== undefined ? parseInt(params.activeTab, 10) : undefined;
|
||||||
|
|
||||||
const [changeFilters, resetFilters, handleSearchChange] =
|
const [changeFilters, resetFilters, handleSearchChange] =
|
||||||
createFilterHandlers({
|
createFilterHandlers({
|
||||||
|
@ -99,6 +106,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
getFilterQueryParam,
|
getFilterQueryParam,
|
||||||
navigate,
|
navigate,
|
||||||
params,
|
params,
|
||||||
|
keepActiveTab: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [openModal, closeModal] = createDialogActionHandlers<
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
|
@ -106,21 +114,62 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
OrderListUrlQueryParams
|
OrderListUrlQueryParams
|
||||||
>(navigate, orderListUrl, params);
|
>(navigate, orderListUrl, params);
|
||||||
|
|
||||||
const handleTabChange = (tab: number) =>
|
const hasPresetsChanged = () => {
|
||||||
navigate(
|
const activeTab = tabs[currentTab - 1];
|
||||||
orderListUrl({
|
const { paresedQs } = prepareQs(location.search);
|
||||||
activeTab: tab.toString(),
|
|
||||||
...getFilterTabs()[tab - 1].data,
|
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 = () => {
|
const handleFilterTabDelete = () => {
|
||||||
deleteFilterTab(currentTab);
|
deleteFilterTab(tabIndexToDelete);
|
||||||
|
|
||||||
|
// When deleting the current tab, navigate to the All orders tab
|
||||||
|
if (tabIndexToDelete === currentTab) {
|
||||||
navigate(orderListUrl());
|
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) => {
|
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);
|
handleTabChange(tabs.length + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,13 +218,18 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
onSearchChange={handleSearchChange}
|
onSearchChange={handleSearchChange}
|
||||||
onFilterChange={changeFilters}
|
onFilterChange={changeFilters}
|
||||||
onTabSave={() => openModal("save-search")}
|
onTabSave={() => openModal("save-search")}
|
||||||
onTabDelete={() => openModal("delete-search")}
|
onTabDelete={(tabIndex: number) => {
|
||||||
|
setTabIndexToDelete(tabIndex);
|
||||||
|
openModal("delete-search");
|
||||||
|
}}
|
||||||
onTabChange={handleTabChange}
|
onTabChange={handleTabChange}
|
||||||
|
onTabUpdate={hanleFilterTabUpdate}
|
||||||
initialSearch={params.query || ""}
|
initialSearch={params.query || ""}
|
||||||
tabs={getFilterTabs().map(tab => tab.name)}
|
tabs={getFilterTabs().map(tab => tab.name)}
|
||||||
onAll={resetFilters}
|
onAll={resetFilters}
|
||||||
onSettingsOpen={() => navigate(orderSettingsPath)}
|
onSettingsOpen={() => navigate(orderSettingsPath)}
|
||||||
params={params}
|
params={params}
|
||||||
|
hasPresetsChanged={hasPresetsChanged()}
|
||||||
/>
|
/>
|
||||||
<SaveFilterTabDialog
|
<SaveFilterTabDialog
|
||||||
open={params.action === "save-search"}
|
open={params.action === "save-search"}
|
||||||
|
@ -188,7 +242,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
confirmButtonState="default"
|
confirmButtonState="default"
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
onSubmit={handleFilterTabDelete}
|
onSubmit={handleFilterTabDelete}
|
||||||
tabName={getStringOrPlaceholder(tabs[currentTab - 1]?.name)}
|
tabName={tabs[tabIndexToDelete - 1]?.name ?? "..."}
|
||||||
/>
|
/>
|
||||||
{!noChannel && (
|
{!noChannel && (
|
||||||
<ChannelPickerDialog
|
<ChannelPickerDialog
|
||||||
|
|
|
@ -35,7 +35,7 @@ import {
|
||||||
OrderListUrlQueryParams,
|
OrderListUrlQueryParams,
|
||||||
} from "../../urls";
|
} from "../../urls";
|
||||||
|
|
||||||
export const ORDER_FILTERS_KEY = "orderFilters";
|
export const ORDER_FILTERS_KEY = "orderFiltersPresets";
|
||||||
|
|
||||||
export function getFilterOpts(
|
export function getFilterOpts(
|
||||||
params: OrderListUrlFilters,
|
params: OrderListUrlFilters,
|
||||||
|
@ -199,8 +199,12 @@ export function getFilterQueryParam(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const { deleteFilterTab, getFilterTabs, saveFilterTab } =
|
export const {
|
||||||
createFilterTabUtils<OrderListUrlFilters>(ORDER_FILTERS_KEY);
|
deleteFilterTab,
|
||||||
|
getFilterTabs,
|
||||||
|
saveFilterTab,
|
||||||
|
updateFilterTab,
|
||||||
|
} = createFilterTabUtils<string>(ORDER_FILTERS_KEY);
|
||||||
|
|
||||||
export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } =
|
export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } =
|
||||||
createFilterUtils<OrderListUrlQueryParams, OrderListUrlFilters>({
|
createFilterUtils<OrderListUrlQueryParams, OrderListUrlFilters>({
|
||||||
|
|
|
@ -21,6 +21,5 @@ export function getSortQueryField(sort: OrderListUrlSortField): OrderSortField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSortQueryVariables = createGetSortQueryVariables(
|
export const getSortQueryVariables =
|
||||||
getSortQueryField,
|
createGetSortQueryVariables(getSortQueryField);
|
||||||
);
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Checkbox from "@dashboard/components/Checkbox";
|
import Checkbox from "@dashboard/components/Checkbox";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||||
import Skeleton from "@dashboard/components/Skeleton";
|
import Skeleton from "@dashboard/components/Skeleton";
|
||||||
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
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 { ListActions, ListProps, SortPage } from "@dashboard/types";
|
||||||
import { getArrowDirection } from "@dashboard/utils/sort";
|
import { getArrowDirection } from "@dashboard/utils/sort";
|
||||||
import { Card, TableBody, TableCell, TableFooter } from "@material-ui/core";
|
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 React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import { PluginBaseFragment } from "@dashboard/graphql";
|
import { PluginBaseFragment } from "@dashboard/graphql";
|
||||||
import { isPluginGlobal } from "@dashboard/plugins/views/utils";
|
import { isPluginGlobal } from "@dashboard/plugins/views/utils";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||||
|
import { Pill } from "@dashboard/components/Pill";
|
||||||
import { PluginBaseFragment } from "@dashboard/graphql";
|
import { PluginBaseFragment } from "@dashboard/graphql";
|
||||||
import { CardContent, Typography } from "@material-ui/core";
|
import { CardContent, Typography } from "@material-ui/core";
|
||||||
import { Pill } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@ interface GlobalConfigPluginPopupBodyProps {
|
||||||
plugin: PluginBaseFragment;
|
plugin: PluginBaseFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GlobalConfigPluginPopupBody: React.FC<GlobalConfigPluginPopupBodyProps> = ({
|
const GlobalConfigPluginPopupBody: React.FC<
|
||||||
plugin,
|
GlobalConfigPluginPopupBodyProps
|
||||||
}) => {
|
> = ({ plugin }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const { active } = plugin.globalConfiguration;
|
const { active } = plugin.globalConfiguration;
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
SearchAvailableInGridAttributesQuery,
|
SearchAvailableInGridAttributesQuery,
|
||||||
} from "@dashboard/graphql";
|
} from "@dashboard/graphql";
|
||||||
import useLocale from "@dashboard/hooks/useLocale";
|
import useLocale from "@dashboard/hooks/useLocale";
|
||||||
import { buttonMessages } from "@dashboard/intl";
|
|
||||||
import { ProductListUrlSortField } from "@dashboard/products/urls";
|
import { ProductListUrlSortField } from "@dashboard/products/urls";
|
||||||
import { canBeSorted } from "@dashboard/products/views/ProductList/sort";
|
import { canBeSorted } from "@dashboard/products/views/ProductList/sort";
|
||||||
import { useSearchProductTypes } from "@dashboard/searches/useProductTypeSearch";
|
import { useSearchProductTypes } from "@dashboard/searches/useProductTypeSearch";
|
||||||
|
@ -27,10 +26,9 @@ import {
|
||||||
} from "@dashboard/types";
|
} from "@dashboard/types";
|
||||||
import { addAtIndex, removeAtIndex } from "@dashboard/utils/lists";
|
import { addAtIndex, removeAtIndex } from "@dashboard/utils/lists";
|
||||||
import { GridColumn, Item } from "@glideapps/glide-data-grid";
|
import { GridColumn, Item } from "@glideapps/glide-data-grid";
|
||||||
import { Button } from "@saleor/macaw-ui";
|
|
||||||
import { Box } from "@saleor/macaw-ui/next";
|
import { Box } from "@saleor/macaw-ui/next";
|
||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useCallback, useMemo } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { isAttributeColumnValue } from "../ProductListPage/utils";
|
import { isAttributeColumnValue } from "../ProductListPage/utils";
|
||||||
import { useColumnPickerColumns } from "./hooks/useColumnPickerColumns";
|
import { useColumnPickerColumns } from "./hooks/useColumnPickerColumns";
|
||||||
|
@ -87,6 +85,7 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
||||||
const searchProductType = useSearchProductTypes();
|
const searchProductType = useSearchProductTypes();
|
||||||
const datagrid = useDatagridChangeState();
|
const datagrid = useDatagridChangeState();
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
|
const productsLength = getProductRowsLength(disabled, products, disabled);
|
||||||
const gridAttributesFromSettings = useMemo(
|
const gridAttributesFromSettings = useMemo(
|
||||||
() => settings.columns.filter(isAttributeColumnValue),
|
() => settings.columns.filter(isAttributeColumnValue),
|
||||||
[settings.columns],
|
[settings.columns],
|
||||||
|
@ -160,14 +159,12 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
||||||
gridAttributes,
|
gridAttributes,
|
||||||
gridAttributesFromSettings,
|
gridAttributesFromSettings,
|
||||||
selectedChannelId,
|
selectedChannelId,
|
||||||
loading,
|
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
columns,
|
columns,
|
||||||
gridAttributes,
|
gridAttributes,
|
||||||
gridAttributesFromSettings,
|
gridAttributesFromSettings,
|
||||||
intl,
|
intl,
|
||||||
loading,
|
|
||||||
locale,
|
locale,
|
||||||
products,
|
products,
|
||||||
searchProductType,
|
searchProductType,
|
||||||
|
@ -225,10 +222,11 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box __marginTop={-1}>
|
<Box __marginTop={productsLength > 0 ? -1 : 0}>
|
||||||
<DatagridChangeStateContext.Provider value={datagrid}>
|
<DatagridChangeStateContext.Provider value={datagrid}>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
readonly
|
readonly
|
||||||
|
loading={loading}
|
||||||
rowMarkers="none"
|
rowMarkers="none"
|
||||||
columnSelect="single"
|
columnSelect="single"
|
||||||
freezeColumns={2}
|
freezeColumns={2}
|
||||||
|
@ -243,12 +241,8 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
|
||||||
getCellContent={getCellContent}
|
getCellContent={getCellContent}
|
||||||
getCellError={() => false}
|
getCellError={() => false}
|
||||||
menuItems={() => []}
|
menuItems={() => []}
|
||||||
rows={getProductRowsLength(disabled, products)}
|
rows={productsLength}
|
||||||
selectionActions={(indexes, { removeRows }) => (
|
selectionActions={() => null}
|
||||||
<Button variant="tertiary" onClick={() => removeRows(indexes)}>
|
|
||||||
<FormattedMessage {...buttonMessages.delete} />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
fullScreenTitle={intl.formatMessage(messages.products)}
|
fullScreenTitle={intl.formatMessage(messages.products)}
|
||||||
onRowClick={handleRowClick}
|
onRowClick={handleRowClick}
|
||||||
renderColumnPicker={defaultProps => (
|
renderColumnPicker={defaultProps => (
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { messages } from "@dashboard/components/ChannelsAvailabilityDropdown/mes
|
||||||
import { getChannelAvailabilityLabel } from "@dashboard/components/ChannelsAvailabilityDropdown/utils";
|
import { getChannelAvailabilityLabel } from "@dashboard/components/ChannelsAvailabilityDropdown/utils";
|
||||||
import {
|
import {
|
||||||
dropdownCell,
|
dropdownCell,
|
||||||
loadingCell,
|
|
||||||
readonlyTextCell,
|
readonlyTextCell,
|
||||||
thumbnailCell,
|
thumbnailCell,
|
||||||
} from "@dashboard/components/Datagrid/customCells/cells";
|
} from "@dashboard/components/Datagrid/customCells/cells";
|
||||||
|
@ -18,8 +17,10 @@ import { getMoneyRange } from "@dashboard/components/MoneyRange";
|
||||||
import { ProductListColumns } from "@dashboard/config";
|
import { ProductListColumns } from "@dashboard/config";
|
||||||
import { GridAttributesQuery, ProductListQuery } from "@dashboard/graphql";
|
import { GridAttributesQuery, ProductListQuery } from "@dashboard/graphql";
|
||||||
import { commonMessages } from "@dashboard/intl";
|
import { commonMessages } from "@dashboard/intl";
|
||||||
|
import { getDatagridRowDataIndex, isFirstColumn } from "@dashboard/misc";
|
||||||
import { ProductListUrlSortField } from "@dashboard/products/urls";
|
import { ProductListUrlSortField } from "@dashboard/products/urls";
|
||||||
import { RelayToFlat, Sort } from "@dashboard/types";
|
import { RelayToFlat, Sort } from "@dashboard/types";
|
||||||
|
import { getColumnSortDirectionIcon } from "@dashboard/utils/columns/getColumnSortDirectionIcon";
|
||||||
import { Item } from "@glideapps/glide-data-grid";
|
import { Item } from "@glideapps/glide-data-grid";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { IntlShape } from "react-intl";
|
import { IntlShape } from "react-intl";
|
||||||
|
@ -50,7 +51,7 @@ export function getColumns({
|
||||||
id: "name",
|
id: "name",
|
||||||
title: intl.formatMessage(commonMessages.product),
|
title: intl.formatMessage(commonMessages.product),
|
||||||
width: 300,
|
width: 300,
|
||||||
icon: getColumnSortIconName(sort, ProductListUrlSortField.name),
|
icon: getColumnSortDirectionIcon(sort, ProductListUrlSortField.name),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "productType",
|
id: "productType",
|
||||||
|
@ -105,7 +106,7 @@ export function toAttributeColumnData(
|
||||||
width: 200,
|
width: 200,
|
||||||
icon:
|
icon:
|
||||||
attributeId === activeAttributeSortId &&
|
attributeId === activeAttributeSortId &&
|
||||||
getColumnSortIconName(sort, ProductListUrlSortField.attribute),
|
getColumnSortDirectionIcon(sort, ProductListUrlSortField.attribute),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -134,7 +135,6 @@ interface GetCellContentProps {
|
||||||
gridAttributes: RelayToFlat<GridAttributesQuery["grid"]>;
|
gridAttributes: RelayToFlat<GridAttributesQuery["grid"]>;
|
||||||
gridAttributesFromSettings: ProductListColumns[];
|
gridAttributesFromSettings: ProductListColumns[];
|
||||||
selectedChannelId?: string;
|
selectedChannelId?: string;
|
||||||
loading?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createGetCellContent({
|
export function createGetCellContent({
|
||||||
|
@ -144,20 +144,15 @@ export function createGetCellContent({
|
||||||
locale,
|
locale,
|
||||||
products,
|
products,
|
||||||
selectedChannelId,
|
selectedChannelId,
|
||||||
loading,
|
|
||||||
}: GetCellContentProps) {
|
}: GetCellContentProps) {
|
||||||
return (
|
return (
|
||||||
[column, row]: Item,
|
[column, row]: Item,
|
||||||
{ changes, getChangeIndex, added, removed }: GetCellContentOpts,
|
{ changes, getChangeIndex, added, removed }: GetCellContentOpts,
|
||||||
) => {
|
) => {
|
||||||
if (column === -1) {
|
if (isFirstColumn(column)) {
|
||||||
return readonlyTextCell("");
|
return readonlyTextCell("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return loadingCell();
|
|
||||||
}
|
|
||||||
|
|
||||||
const columnId = columns[column]?.id;
|
const columnId = columns[column]?.id;
|
||||||
|
|
||||||
if (!columnId) {
|
if (!columnId) {
|
||||||
|
@ -167,7 +162,7 @@ export function createGetCellContent({
|
||||||
const change = changes.current[getChangeIndex(columnId, row)]?.data;
|
const change = changes.current[getChangeIndex(columnId, row)]?.data;
|
||||||
const rowData = added.includes(row)
|
const rowData = added.includes(row)
|
||||||
? undefined
|
? undefined
|
||||||
: products[row + removed.filter(r => r <= row).length];
|
: products[getDatagridRowDataIndex(row, removed)];
|
||||||
|
|
||||||
const channel = rowData?.channelListings?.find(
|
const channel = rowData?.channelListings?.find(
|
||||||
listing => listing.channel.id === selectedChannelId,
|
listing => listing.channel.id === selectedChannelId,
|
||||||
|
@ -364,7 +359,12 @@ export function getColumnMetadata(column: string) {
|
||||||
export function getProductRowsLength(
|
export function getProductRowsLength(
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
product?: RelayToFlat<ProductListQuery["products"]>,
|
product?: RelayToFlat<ProductListQuery["products"]>,
|
||||||
|
loading?: boolean,
|
||||||
) {
|
) {
|
||||||
|
if (loading) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (product?.length) {
|
if (product?.length) {
|
||||||
return product.length;
|
return product.length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const ProductListTiles: React.FC<ProductListTilesProps> = ({
|
||||||
const renderContent = useCallback(() => {
|
const renderContent = useCallback(() => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Box display="flex" justifyContent="center" height="100%" marginY={12}>
|
<Box display="flex" justifyContent="center" marginY={12}>
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { ExternalAppProvider } from "@dashboard/apps/components/ExternalAppConte
|
||||||
import { Provider as DateProvider } from "@dashboard/components/Date/DateContext";
|
import { Provider as DateProvider } from "@dashboard/components/Date/DateContext";
|
||||||
import { Locale, RawLocaleProvider } from "@dashboard/components/Locale";
|
import { Locale, RawLocaleProvider } from "@dashboard/components/Locale";
|
||||||
import { TimezoneProvider } from "@dashboard/components/Timezone";
|
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 React from "react";
|
||||||
import { IntlProvider } from "react-intl";
|
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")}>
|
<DateProvider value={+new Date("2018-08-07T14:30:44+00:00")}>
|
||||||
<TimezoneProvider value="America/New_York">
|
<TimezoneProvider value="America/New_York">
|
||||||
|
<LegacyThemeProvider>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<ExternalAppProvider>{children}</ExternalAppProvider>
|
<ExternalAppProvider>{children}</ExternalAppProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
</LegacyThemeProvider>
|
||||||
</TimezoneProvider>
|
</TimezoneProvider>
|
||||||
</DateProvider>
|
</DateProvider>
|
||||||
</RawLocaleProvider>
|
</RawLocaleProvider>
|
||||||
|
|
Loading…
Reference in a new issue