Update from saleor/master
This commit is contained in:
parent
9551b50351
commit
bd211a6720
262 changed files with 35477 additions and 13429 deletions
|
@ -10,14 +10,14 @@ import Typography from "@material-ui/core/Typography";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import SVG from "react-inlinesvg";
|
import SVG from "react-inlinesvg";
|
||||||
|
|
||||||
import * as backgroundArt from "@assets/images/login-background.svg";
|
import backgroundArt from "@assets/images/login-background.svg";
|
||||||
import * as saleorDarkLogo from "@assets/images/logo-dark.svg";
|
import saleorDarkLogo from "@assets/images/logo-dark.svg";
|
||||||
import * as saleorLightLogo from "@assets/images/logo-light.svg";
|
import saleorLightLogo from "@assets/images/logo-light.svg";
|
||||||
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
|
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
|
||||||
import Form from "@saleor/components/Form";
|
import Form from "@saleor/components/Form";
|
||||||
import { FormSpacer } from "@saleor/components/FormSpacer";
|
import { FormSpacer } from "@saleor/components/FormSpacer";
|
||||||
import useTheme from "@saleor/hooks/useTheme";
|
import useTheme from "@saleor/hooks/useTheme";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
email: string;
|
email: string;
|
||||||
|
|
|
@ -16,6 +16,9 @@ export const fragmentUser = gql`
|
||||||
code
|
code
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
avatar {
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,11 @@ export interface TokenAuth_tokenCreate_user_permissions {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TokenAuth_tokenCreate_user_avatar {
|
||||||
|
__typename: "Image";
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TokenAuth_tokenCreate_user {
|
export interface TokenAuth_tokenCreate_user {
|
||||||
__typename: "User";
|
__typename: "User";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -29,6 +34,7 @@ export interface TokenAuth_tokenCreate_user {
|
||||||
isStaff: boolean;
|
isStaff: boolean;
|
||||||
note: string | null;
|
note: string | null;
|
||||||
permissions: (TokenAuth_tokenCreate_user_permissions | null)[] | null;
|
permissions: (TokenAuth_tokenCreate_user_permissions | null)[] | null;
|
||||||
|
avatar: TokenAuth_tokenCreate_user_avatar | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenAuth_tokenCreate {
|
export interface TokenAuth_tokenCreate {
|
||||||
|
|
|
@ -14,6 +14,11 @@ export interface User_permissions {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface User_avatar {
|
||||||
|
__typename: "Image";
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
__typename: "User";
|
__typename: "User";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -23,4 +28,5 @@ export interface User {
|
||||||
isStaff: boolean;
|
isStaff: boolean;
|
||||||
note: string | null;
|
note: string | null;
|
||||||
permissions: (User_permissions | null)[] | null;
|
permissions: (User_permissions | null)[] | null;
|
||||||
|
avatar: User_avatar | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@ export interface VerifyToken_tokenVerify_user_permissions {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VerifyToken_tokenVerify_user_avatar {
|
||||||
|
__typename: "Image";
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface VerifyToken_tokenVerify_user {
|
export interface VerifyToken_tokenVerify_user {
|
||||||
__typename: "User";
|
__typename: "User";
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -23,6 +28,7 @@ export interface VerifyToken_tokenVerify_user {
|
||||||
isStaff: boolean;
|
isStaff: boolean;
|
||||||
note: string | null;
|
note: string | null;
|
||||||
permissions: (VerifyToken_tokenVerify_user_permissions | null)[] | null;
|
permissions: (VerifyToken_tokenVerify_user_permissions | null)[] | null;
|
||||||
|
avatar: VerifyToken_tokenVerify_user_avatar | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VerifyToken_tokenVerify {
|
export interface VerifyToken_tokenVerify {
|
||||||
|
|
|
@ -18,9 +18,9 @@ import Checkbox from "@saleor/components/Checkbox";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { renderCollection } from "../../../misc";
|
import { renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "../../../types";
|
import { ListActions, ListProps } from "@saleor/types";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -67,11 +67,14 @@ interface CategoryListProps
|
||||||
onAdd?();
|
onAdd?();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const numberOfColumns = 4;
|
||||||
|
|
||||||
const CategoryList = withStyles(styles, { name: "CategoryList" })(
|
const CategoryList = withStyles(styles, { name: "CategoryList" })(
|
||||||
({
|
({
|
||||||
categories,
|
categories,
|
||||||
classes,
|
classes,
|
||||||
disabled,
|
disabled,
|
||||||
|
settings,
|
||||||
isRoot,
|
isRoot,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
isChecked,
|
isChecked,
|
||||||
|
@ -82,6 +85,7 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
|
||||||
onAdd,
|
onAdd,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
onRowClick
|
onRowClick
|
||||||
}: CategoryListProps) => (
|
}: CategoryListProps) => (
|
||||||
<Card>
|
<Card>
|
||||||
|
@ -97,6 +101,7 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
|
||||||
)}
|
)}
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={categories}
|
items={categories}
|
||||||
|
@ -116,9 +121,11 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={4}
|
colSpan={numberOfColumns}
|
||||||
|
settings={settings}
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
hasPreviousPage={
|
hasPreviousPage={
|
||||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||||
}
|
}
|
||||||
|
@ -144,6 +151,7 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(category.id)}
|
onChange={() => toggle(category.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -173,7 +181,7 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
|
||||||
},
|
},
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={4}>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
{isRoot
|
{isRoot
|
||||||
? i18n.t("No categories found")
|
? i18n.t("No categories found")
|
||||||
: i18n.t("No subcategories found")}
|
: i18n.t("No subcategories found")}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import React from "react";
|
||||||
|
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { ListActions, PageListProps } from "../../../types";
|
import { ListActions, PageListProps } from "@saleor/types";
|
||||||
import CategoryList from "../CategoryList";
|
import CategoryList from "../CategoryList";
|
||||||
|
|
||||||
export interface CategoryTableProps extends PageListProps, ListActions {
|
export interface CategoryTableProps extends PageListProps, ListActions {
|
||||||
|
@ -24,9 +24,11 @@ export interface CategoryTableProps extends PageListProps, ListActions {
|
||||||
export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
|
export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
|
||||||
categories,
|
categories,
|
||||||
disabled,
|
disabled,
|
||||||
|
settings,
|
||||||
onAdd,
|
onAdd,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
isChecked,
|
isChecked,
|
||||||
|
@ -46,9 +48,11 @@ export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
|
||||||
onAdd={onAdd}
|
onAdd={onAdd}
|
||||||
onRowClick={onRowClick}
|
onRowClick={onRowClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
settings={settings}
|
||||||
isRoot={true}
|
isRoot={true}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
onPreviousPage={onPreviousPage}
|
onPreviousPage={onPreviousPage}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
isChecked={isChecked}
|
isChecked={isChecked}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
|
|
@ -40,6 +40,10 @@ export const CategoryProductsCard: React.StatelessComponent<
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ProductList
|
<ProductList
|
||||||
|
settings={{
|
||||||
|
columns: ["isPublished", "price", "productType"],
|
||||||
|
rowNumber: undefined
|
||||||
|
}}
|
||||||
products={products}
|
products={products}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
|
|
|
@ -5,13 +5,14 @@ import React from "react";
|
||||||
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import usePaginator, {
|
import usePaginator, {
|
||||||
createPaginationState
|
createPaginationState
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import { PAGINATE_BY } from "../../config";
|
import i18n from "@saleor/i18n";
|
||||||
import i18n from "../../i18n";
|
import { getMutationState, maybe } from "@saleor/misc";
|
||||||
import { getMutationState, maybe } from "../../misc";
|
import { ListViews } from "@saleor/types";
|
||||||
import { CategoryListPage } from "../components/CategoryListPage/CategoryListPage";
|
import { CategoryListPage } from "../components/CategoryListPage/CategoryListPage";
|
||||||
import { TypedCategoryBulkDeleteMutation } from "../mutations";
|
import { TypedCategoryBulkDeleteMutation } from "../mutations";
|
||||||
import { TypedRootCategoriesQuery } from "../queries";
|
import { TypedRootCategoriesQuery } from "../queries";
|
||||||
|
@ -35,8 +36,10 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
|
||||||
const { isSelected, listElements, toggle, toggleAll, reset } = useBulkActions(
|
const { isSelected, listElements, toggle, toggleAll, reset } = useBulkActions(
|
||||||
params.ids
|
params.ids
|
||||||
);
|
);
|
||||||
|
const { updateListSettings, settings } = useListSettings(
|
||||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
ListViews.CATEGORY_LIST
|
||||||
|
);
|
||||||
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
return (
|
return (
|
||||||
<TypedRootCategoriesQuery displayLoader variables={paginationState}>
|
<TypedRootCategoriesQuery displayLoader variables={paginationState}>
|
||||||
{({ data, loading, refetch }) => {
|
{({ data, loading, refetch }) => {
|
||||||
|
@ -73,11 +76,13 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
|
||||||
() => data.categories.edges.map(edge => edge.node),
|
() => data.categories.edges.map(edge => edge.node),
|
||||||
[]
|
[]
|
||||||
)}
|
)}
|
||||||
|
settings={settings}
|
||||||
onAdd={() => navigate(categoryAddUrl())}
|
onAdd={() => navigate(categoryAddUrl())}
|
||||||
onRowClick={id => () => navigate(categoryUrl(id))}
|
onRowClick={id => () => navigate(categoryUrl(id))}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onNextPage={loadNextPage}
|
onNextPage={loadNextPage}
|
||||||
onPreviousPage={loadPreviousPage}
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
isChecked={isSelected}
|
isChecked={isSelected}
|
||||||
selected={listElements.length}
|
selected={listElements.length}
|
||||||
|
|
|
@ -17,9 +17,9 @@ import Skeleton from "@saleor/components/Skeleton";
|
||||||
import StatusLabel from "@saleor/components/StatusLabel";
|
import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "../../../types";
|
import { ListActions, ListProps } from "@saleor/types";
|
||||||
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
@ -50,13 +50,17 @@ interface CollectionListProps
|
||||||
collections: CollectionList_collections_edges_node[];
|
collections: CollectionList_collections_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const numberOfColumns = 5;
|
||||||
|
|
||||||
const CollectionList = withStyles(styles, { name: "CollectionList" })(
|
const CollectionList = withStyles(styles, { name: "CollectionList" })(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
collections,
|
collections,
|
||||||
disabled,
|
disabled,
|
||||||
|
settings,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
isChecked,
|
isChecked,
|
||||||
|
@ -68,6 +72,7 @@ const CollectionList = withStyles(styles, { name: "CollectionList" })(
|
||||||
<Card>
|
<Card>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={collections}
|
items={collections}
|
||||||
|
@ -89,9 +94,11 @@ const CollectionList = withStyles(styles, { name: "CollectionList" })(
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={5}
|
colSpan={numberOfColumns}
|
||||||
|
settings={settings}
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
hasPreviousPage={
|
hasPreviousPage={
|
||||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||||
}
|
}
|
||||||
|
@ -116,6 +123,7 @@ const CollectionList = withStyles(styles, { name: "CollectionList" })(
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(collection.id)}
|
onChange={() => toggle(collection.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -151,7 +159,7 @@ const CollectionList = withStyles(styles, { name: "CollectionList" })(
|
||||||
},
|
},
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={3}>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
{i18n.t("No collections found")}
|
{i18n.t("No collections found")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
@ -4,8 +4,8 @@ import React from "react";
|
||||||
|
|
||||||
import { Container } from "@saleor/components/Container";
|
import { Container } from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { ListActions, PageListProps } from "../../../types";
|
import { ListActions, PageListProps } from "@saleor/types";
|
||||||
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
||||||
import CollectionList from "../CollectionList/CollectionList";
|
import CollectionList from "../CollectionList/CollectionList";
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,9 @@ import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Checkbox from "@saleor/components/Checkbox";
|
import Checkbox from "@saleor/components/Checkbox";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import StatusLabel from "@saleor/components/StatusLabel";
|
import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
import TableCellAvatar, {
|
||||||
|
AVATAR_MARGIN
|
||||||
|
} from "@saleor/components/TableCellAvatar";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
|
@ -29,12 +31,27 @@ import { CollectionDetails_collection } from "../../types/CollectionDetails";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
iconCell: {
|
colActions: {
|
||||||
"&:last-child": {
|
"&:last-child": {
|
||||||
paddingRight: 0
|
paddingRight: 0
|
||||||
},
|
},
|
||||||
width: 48 + theme.spacing.unit / 2
|
width: 48 + theme.spacing.unit / 2
|
||||||
},
|
},
|
||||||
|
colName: {
|
||||||
|
width: "auto"
|
||||||
|
},
|
||||||
|
colNameLabel: {
|
||||||
|
marginLeft: AVATAR_MARGIN
|
||||||
|
},
|
||||||
|
colPublished: {
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
colType: {
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
tableLayout: "fixed"
|
||||||
|
},
|
||||||
tableRow: {
|
tableRow: {
|
||||||
cursor: "pointer"
|
cursor: "pointer"
|
||||||
}
|
}
|
||||||
|
@ -48,6 +65,8 @@ export interface CollectionProductsProps
|
||||||
onProductUnassign: (id: string, event: React.MouseEvent<any>) => void;
|
onProductUnassign: (id: string, event: React.MouseEvent<any>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const numberOfColumns = 5;
|
||||||
|
|
||||||
const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })(
|
const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
|
@ -89,24 +108,32 @@ const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })(
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Table>
|
<Table className={classes.table}>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={maybe(() => collection.products.edges.map(edge => edge.node))}
|
items={maybe(() => collection.products.edges.map(edge => edge.node))}
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell>{i18n.t("Name", { context: "table header" })}</TableCell>
|
<TableCell className={classes.colName}>
|
||||||
<TableCell>{i18n.t("Type", { context: "table header" })}</TableCell>
|
<span className={classes.colNameLabel}>
|
||||||
<TableCell>
|
{i18n.t("Name", { context: "table header" })}
|
||||||
|
</span>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colType}>
|
||||||
|
{i18n.t("Type", { context: "table header" })}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colPublished}>
|
||||||
{i18n.t("Published", { context: "table header" })}
|
{i18n.t("Published", { context: "table header" })}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell className={classes.colActions} />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={6}
|
colSpan={numberOfColumns}
|
||||||
hasNextPage={maybe(() => pageInfo.hasNextPage)}
|
hasNextPage={maybe(() => pageInfo.hasNextPage)}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
hasPreviousPage={maybe(() => pageInfo.hasPreviousPage)}
|
hasPreviousPage={maybe(() => pageInfo.hasPreviousPage)}
|
||||||
|
@ -132,22 +159,23 @@ const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })(
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(product.id)}
|
onChange={() => toggle(product.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCellAvatar
|
<TableCellAvatar
|
||||||
|
className={classes.colName}
|
||||||
thumbnail={maybe(() => product.thumbnail.url)}
|
thumbnail={maybe(() => product.thumbnail.url)}
|
||||||
/>
|
>
|
||||||
<TableCell>
|
|
||||||
{maybe<React.ReactNode>(() => product.name, <Skeleton />)}
|
{maybe<React.ReactNode>(() => product.name, <Skeleton />)}
|
||||||
</TableCell>
|
</TableCellAvatar>
|
||||||
<TableCell>
|
<TableCell className={classes.colType}>
|
||||||
{maybe<React.ReactNode>(
|
{maybe<React.ReactNode>(
|
||||||
() => product.productType.name,
|
() => product.productType.name,
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell className={classes.colPublished}>
|
||||||
{maybe(
|
{maybe(
|
||||||
() => (
|
() => (
|
||||||
<StatusLabel
|
<StatusLabel
|
||||||
|
@ -162,7 +190,7 @@ const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })(
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.iconCell}>
|
<TableCell className={classes.colActions}>
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={!product}
|
disabled={!product}
|
||||||
onClick={event => onProductUnassign(product.id, event)}
|
onClick={event => onProductUnassign(product.id, event)}
|
||||||
|
@ -176,7 +204,9 @@ const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })(
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell />
|
<TableCell />
|
||||||
<TableCell colSpan={6}>{i18n.t("No products found")}</TableCell>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
|
{i18n.t("No products found")}
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -22,6 +22,46 @@ export const collections: CollectionList_collections_edges_node[] = [
|
||||||
__typename: "ProductCountableConnection",
|
__typename: "ProductCountableConnection",
|
||||||
totalCount: 4
|
totalCount: 4
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: "Collection",
|
||||||
|
id: "Q29sbGVjdGlvbjoz",
|
||||||
|
isPublished: true,
|
||||||
|
name: "Vintage vibes",
|
||||||
|
products: {
|
||||||
|
__typename: "ProductCountableConnection",
|
||||||
|
totalCount: 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: "Collection",
|
||||||
|
id: "Q29sbGVjdGlvbjoa",
|
||||||
|
isPublished: true,
|
||||||
|
name: "Merry Christmas",
|
||||||
|
products: {
|
||||||
|
__typename: "ProductCountableConnection",
|
||||||
|
totalCount: 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: "Collection",
|
||||||
|
id: "Q29sbGVjdGlvbjob",
|
||||||
|
isPublished: true,
|
||||||
|
name: "80s Miami",
|
||||||
|
products: {
|
||||||
|
__typename: "ProductCountableConnection",
|
||||||
|
totalCount: 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: "Collection",
|
||||||
|
id: "Q29sbGVjdGlvbjoc",
|
||||||
|
isPublished: true,
|
||||||
|
name: "Yellow Submarine 2019",
|
||||||
|
products: {
|
||||||
|
__typename: "ProductCountableConnection",
|
||||||
|
totalCount: 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
export const collection: (
|
export const collection: (
|
||||||
|
|
|
@ -287,14 +287,12 @@ export const CollectionDetails: React.StatelessComponent<
|
||||||
open={params.action === "assign"}
|
open={params.action === "assign"}
|
||||||
onFetch={search}
|
onFetch={search}
|
||||||
loading={result.loading}
|
loading={result.loading}
|
||||||
onClose={() => navigate(collectionUrl(id), true, true)}
|
onClose={closeModal}
|
||||||
onSubmit={formData =>
|
onSubmit={products =>
|
||||||
assignProduct.mutate({
|
assignProduct.mutate({
|
||||||
...paginationState,
|
...paginationState,
|
||||||
collectionId: id,
|
collectionId: id,
|
||||||
productIds: formData.products.map(
|
productIds: products.map(product => product.id)
|
||||||
product => product.id
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
products={maybe(() =>
|
products={maybe(() =>
|
||||||
|
|
|
@ -6,14 +6,15 @@ import React from "react";
|
||||||
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import usePaginator, {
|
import usePaginator, {
|
||||||
createPaginationState
|
createPaginationState
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import { PAGINATE_BY } from "../../config";
|
import i18n from "@saleor/i18n";
|
||||||
import i18n from "../../i18n";
|
import { getMutationState, maybe } from "@saleor/misc";
|
||||||
import { getMutationState, maybe } from "../../misc";
|
import { ListViews } from "@saleor/types";
|
||||||
import CollectionListPage from "../components/CollectionListPage/CollectionListPage";
|
import CollectionListPage from "../components/CollectionListPage/CollectionListPage";
|
||||||
import {
|
import {
|
||||||
TypedCollectionBulkDelete,
|
TypedCollectionBulkDelete,
|
||||||
|
@ -43,6 +44,9 @@ export const CollectionList: React.StatelessComponent<CollectionListProps> = ({
|
||||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||||
params.ids
|
params.ids
|
||||||
);
|
);
|
||||||
|
const { updateListSettings, settings } = useListSettings(
|
||||||
|
ListViews.COLLECTION_LIST
|
||||||
|
);
|
||||||
|
|
||||||
const closeModal = () =>
|
const closeModal = () =>
|
||||||
navigate(
|
navigate(
|
||||||
|
@ -62,7 +66,7 @@ export const CollectionList: React.StatelessComponent<CollectionListProps> = ({
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
return (
|
return (
|
||||||
<TypedCollectionListQuery displayLoader variables={paginationState}>
|
<TypedCollectionListQuery displayLoader variables={paginationState}>
|
||||||
{({ data, loading, refetch }) => {
|
{({ data, loading, refetch }) => {
|
||||||
|
@ -129,8 +133,10 @@ export const CollectionList: React.StatelessComponent<CollectionListProps> = ({
|
||||||
collections={maybe(() =>
|
collections={maybe(() =>
|
||||||
data.collections.edges.map(edge => edge.node)
|
data.collections.edges.map(edge => edge.node)
|
||||||
)}
|
)}
|
||||||
|
settings={settings}
|
||||||
onNextPage={loadNextPage}
|
onNextPage={loadNextPage}
|
||||||
onPreviousPage={loadPreviousPage}
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
onRowClick={id => () => navigate(collectionUrl(id))}
|
onRowClick={id => () => navigate(collectionUrl(id))}
|
||||||
toolbar={
|
toolbar={
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
withStyles,
|
withStyles,
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
|
|
|
@ -7,12 +7,13 @@ import {
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { AddressTypeInput } from "../../customers/types";
|
import { AddressTypeInput } from "@saleor/customers/types";
|
||||||
import i18n from "../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { maybe } from "../../misc";
|
import { FormErrors } from "@saleor/types";
|
||||||
import { FormErrors } from "../../types";
|
|
||||||
import FormSpacer from "../FormSpacer";
|
import FormSpacer from "../FormSpacer";
|
||||||
import SingleAutocompleteSelectField from "../SingleAutocompleteSelectField";
|
import SingleAutocompleteSelectField, {
|
||||||
|
SingleAutocompleteChoiceType
|
||||||
|
} from "../SingleAutocompleteSelectField";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -24,24 +25,25 @@ const styles = (theme: Theme) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
interface AddressEditProps extends WithStyles<typeof styles> {
|
interface AddressEditProps extends WithStyles<typeof styles> {
|
||||||
countries?: Array<{
|
countries: SingleAutocompleteChoiceType[];
|
||||||
code: string;
|
countryDisplayValue: string;
|
||||||
label: string;
|
|
||||||
}>;
|
|
||||||
data: AddressTypeInput;
|
data: AddressTypeInput;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
errors: FormErrors<keyof AddressTypeInput>;
|
errors: FormErrors<keyof AddressTypeInput>;
|
||||||
onChange(event: React.ChangeEvent<any>);
|
onChange(event: React.ChangeEvent<any>);
|
||||||
|
onCountryChange(event: React.ChangeEvent<any>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddressEdit = withStyles(styles, { name: "AddressEdit" })(
|
const AddressEdit = withStyles(styles, { name: "AddressEdit" })(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
countries,
|
countries,
|
||||||
|
countryDisplayValue,
|
||||||
data,
|
data,
|
||||||
disabled,
|
disabled,
|
||||||
errors,
|
errors,
|
||||||
onChange
|
onChange,
|
||||||
|
onCountryChange
|
||||||
}: AddressEditProps) => (
|
}: AddressEditProps) => (
|
||||||
<>
|
<>
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
|
@ -152,16 +154,14 @@ const AddressEdit = withStyles(styles, { name: "AddressEdit" })(
|
||||||
<div>
|
<div>
|
||||||
<SingleAutocompleteSelectField
|
<SingleAutocompleteSelectField
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
displayValue={countryDisplayValue}
|
||||||
error={!!errors.country}
|
error={!!errors.country}
|
||||||
helperText={errors.country}
|
helperText={errors.country}
|
||||||
label={i18n.t("Country")}
|
label={i18n.t("Country")}
|
||||||
name="country"
|
name="country"
|
||||||
onChange={onChange}
|
onChange={onCountryChange}
|
||||||
value={data.country}
|
value={data.country}
|
||||||
choices={maybe(
|
choices={countries}
|
||||||
() => countries.map(c => ({ ...c, value: c.code })),
|
|
||||||
[]
|
|
||||||
)}
|
|
||||||
InputProps={{
|
InputProps={{
|
||||||
autoComplete: "off"
|
autoComplete: "off"
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import Avatar from "@material-ui/core/Avatar";
|
||||||
import Chip from "@material-ui/core/Chip";
|
import Chip from "@material-ui/core/Chip";
|
||||||
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
||||||
import Grow from "@material-ui/core/Grow";
|
import Grow from "@material-ui/core/Grow";
|
||||||
|
import Hidden from "@material-ui/core/Hidden";
|
||||||
import LinearProgress from "@material-ui/core/LinearProgress";
|
import LinearProgress from "@material-ui/core/LinearProgress";
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
import Menu from "@material-ui/core/MenuList";
|
import Menu from "@material-ui/core/MenuList";
|
||||||
|
@ -12,23 +14,25 @@ import {
|
||||||
withStyles,
|
withStyles,
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import SVG from "react-inlinesvg";
|
import SVG from "react-inlinesvg";
|
||||||
import { RouteComponentProps, withRouter } from "react-router";
|
import { RouteComponentProps, withRouter } from "react-router";
|
||||||
|
|
||||||
import * as saleorDarkLogo from "@assets/images/logo-dark.svg";
|
import saleorDarkLogoSmall from "@assets/logo-dark-small.svg";
|
||||||
import * as saleorLightLogo from "@assets/images/logo-light.svg";
|
import saleorDarkLogo from "@assets/logo-dark.svg";
|
||||||
|
import menuArrowIcon from "@assets/menu-arrow-icon.svg";
|
||||||
import AppProgressProvider from "@saleor/components/AppProgress";
|
import AppProgressProvider from "@saleor/components/AppProgress";
|
||||||
|
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useTheme from "@saleor/hooks/useTheme";
|
import useTheme from "@saleor/hooks/useTheme";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
import useUser from "@saleor/hooks/useUser";
|
||||||
import i18n from "../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import ArrowDropdown from "../../icons/ArrowDropdown";
|
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
|
||||||
import Container from "../Container";
|
import Container from "../Container";
|
||||||
import AppActionContext from "./AppActionContext";
|
import AppActionContext from "./AppActionContext";
|
||||||
import AppHeaderContext from "./AppHeaderContext";
|
import AppHeaderContext from "./AppHeaderContext";
|
||||||
import { appLoaderHeight, drawerWidth } from "./consts";
|
import { appLoaderHeight, drawerWidth, drawerWidthExpanded } from "./consts";
|
||||||
import MenuList from "./MenuList";
|
import MenuList from "./MenuList";
|
||||||
import menuStructure from "./menuStructure";
|
import menuStructure from "./menuStructure";
|
||||||
import ResponsiveDrawer from "./ResponsiveDrawer";
|
import ResponsiveDrawer from "./ResponsiveDrawer";
|
||||||
|
@ -51,9 +55,18 @@ const styles = (theme: Theme) =>
|
||||||
transition: theme.transitions.duration.standard + "ms"
|
transition: theme.transitions.duration.standard + "ms"
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
display: "flex",
|
[theme.breakpoints.down("sm")]: {
|
||||||
flexDirection: "column",
|
paddingLeft: 0
|
||||||
minHeight: `calc(100vh - ${appLoaderHeight}px)`
|
},
|
||||||
|
paddingLeft: drawerWidthExpanded,
|
||||||
|
transition: "padding-left 0.5s ease",
|
||||||
|
width: "100%"
|
||||||
|
},
|
||||||
|
contentToggle: {
|
||||||
|
[theme.breakpoints.down("sm")]: {
|
||||||
|
paddingLeft: 0
|
||||||
|
},
|
||||||
|
paddingLeft: drawerWidth
|
||||||
},
|
},
|
||||||
darkThemeSwitch: {
|
darkThemeSwitch: {
|
||||||
marginRight: theme.spacing.unit * 2
|
marginRight: theme.spacing.unit * 2
|
||||||
|
@ -67,20 +80,72 @@ const styles = (theme: Theme) =>
|
||||||
hide: {
|
hide: {
|
||||||
opacity: 0
|
opacity: 0
|
||||||
},
|
},
|
||||||
|
isMenuSmall: {
|
||||||
|
"& path": {
|
||||||
|
fill: theme.palette.primary.main
|
||||||
|
},
|
||||||
|
"& span": {
|
||||||
|
margin: "0 8px"
|
||||||
|
},
|
||||||
|
"& svg": {
|
||||||
|
marginTop: 12,
|
||||||
|
transform: "rotate(180deg)"
|
||||||
|
},
|
||||||
|
"&:hover": {
|
||||||
|
background: "#E6F3F3"
|
||||||
|
},
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
border: `solid 1px #EAEAEA`,
|
||||||
|
borderRadius: "50%",
|
||||||
|
cursor: "pointer",
|
||||||
|
height: 32,
|
||||||
|
position: "absolute",
|
||||||
|
right: -16,
|
||||||
|
top: 65,
|
||||||
|
transition: `background ${theme.transitions.duration.shorter}ms`,
|
||||||
|
width: 32,
|
||||||
|
zIndex: 99
|
||||||
|
},
|
||||||
|
isMenuSmallDark: {
|
||||||
|
"&:hover": {
|
||||||
|
background: `linear-gradient(0deg, rgba(25, 195, 190, 0.1), rgba(25, 195, 190, 0.1)), ${
|
||||||
|
theme.palette.background.paper
|
||||||
|
}`
|
||||||
|
},
|
||||||
|
border: `solid 1px #252728`,
|
||||||
|
transition: `background ${theme.transitions.duration.shorter}ms`
|
||||||
|
},
|
||||||
|
isMenuSmallHide: {
|
||||||
|
"& svg": {
|
||||||
|
transform: "rotate(0deg)"
|
||||||
|
}
|
||||||
|
},
|
||||||
logo: {
|
logo: {
|
||||||
"& svg": {
|
"& svg": {
|
||||||
height: "100%"
|
height: "100%",
|
||||||
|
margin: "20px 50px"
|
||||||
},
|
},
|
||||||
|
background: theme.palette.secondary.main,
|
||||||
display: "block",
|
display: "block",
|
||||||
height: 28
|
height: 80
|
||||||
|
},
|
||||||
|
logoDark: {
|
||||||
|
"& path": {
|
||||||
|
fill: theme.palette.common.white
|
||||||
|
},
|
||||||
|
background: theme.palette.primary.main
|
||||||
|
},
|
||||||
|
logoSmall: {
|
||||||
|
"& svg": {
|
||||||
|
margin: "0px 25px"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
marginTop: theme.spacing.unit * 4
|
background: theme.palette.background.paper,
|
||||||
|
height: "100vh",
|
||||||
|
padding: 25
|
||||||
},
|
},
|
||||||
menuIcon: {
|
menuIcon: {
|
||||||
[theme.breakpoints.up("md")]: {
|
|
||||||
display: "none"
|
|
||||||
},
|
|
||||||
"& span": {
|
"& span": {
|
||||||
"&:nth-child(1)": {
|
"&:nth-child(1)": {
|
||||||
top: 15
|
top: 15
|
||||||
|
@ -101,6 +166,9 @@ const styles = (theme: Theme) =>
|
||||||
transition: ".25s ease-in-out",
|
transition: ".25s ease-in-out",
|
||||||
width: "60%"
|
width: "60%"
|
||||||
},
|
},
|
||||||
|
[theme.breakpoints.up("md")]: {
|
||||||
|
display: "none"
|
||||||
|
},
|
||||||
background: theme.palette.background.paper,
|
background: theme.palette.background.paper,
|
||||||
borderRadius: "50%",
|
borderRadius: "50%",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
|
@ -109,7 +177,7 @@ const styles = (theme: Theme) =>
|
||||||
marginRight: theme.spacing.unit * 2,
|
marginRight: theme.spacing.unit * 2,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
transform: "rotate(0deg)",
|
transform: "rotate(0deg)",
|
||||||
transition: ".2s ease-in-out",
|
transition: `${theme.transitions.duration.shorter}ms ease-in-out`,
|
||||||
width: 42
|
width: 42
|
||||||
},
|
},
|
||||||
menuIconDark: {
|
menuIconDark: {
|
||||||
|
@ -135,15 +203,16 @@ const styles = (theme: Theme) =>
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
zIndex: 1999
|
zIndex: 1999
|
||||||
},
|
},
|
||||||
|
menuSmall: {
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
height: "100vh",
|
||||||
|
padding: 25
|
||||||
|
},
|
||||||
popover: {
|
popover: {
|
||||||
zIndex: 1
|
zIndex: 1
|
||||||
},
|
},
|
||||||
root: {
|
root: {
|
||||||
[theme.breakpoints.down("sm")]: {
|
width: `100%`
|
||||||
gridTemplateColumns: "1fr"
|
|
||||||
},
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: `${drawerWidth}px 1fr`
|
|
||||||
},
|
},
|
||||||
rotate: {
|
rotate: {
|
||||||
transform: "rotate(180deg)"
|
transform: "rotate(180deg)"
|
||||||
|
@ -153,7 +222,7 @@ const styles = (theme: Theme) =>
|
||||||
padding: 0
|
padding: 0
|
||||||
},
|
},
|
||||||
background: theme.palette.background.paper,
|
background: theme.palette.background.paper,
|
||||||
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 4}px`
|
padding: `0 ${theme.spacing.unit * 4}px`
|
||||||
},
|
},
|
||||||
spacer: {
|
spacer: {
|
||||||
flex: 1
|
flex: 1
|
||||||
|
@ -163,8 +232,8 @@ const styles = (theme: Theme) =>
|
||||||
display: "flex"
|
display: "flex"
|
||||||
},
|
},
|
||||||
userChip: {
|
userChip: {
|
||||||
backgroundColor: theme.palette.common.white,
|
backgroundColor: theme.palette.background.paper,
|
||||||
border: `1px solid ${theme.palette.grey[200]}`
|
color: theme.palette.text.primary
|
||||||
},
|
},
|
||||||
userMenuContainer: {
|
userMenuContainer: {
|
||||||
position: "relative"
|
position: "relative"
|
||||||
|
@ -191,7 +260,7 @@ interface AppLayoutProps {
|
||||||
const AppLayout = withStyles(styles, {
|
const AppLayout = withStyles(styles, {
|
||||||
name: "AppLayout"
|
name: "AppLayout"
|
||||||
})(
|
})(
|
||||||
withRouter<AppLayoutProps & RouteComponentProps<any>>(
|
withRouter<AppLayoutProps & RouteComponentProps<any>, any>(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
children,
|
children,
|
||||||
|
@ -200,6 +269,7 @@ const AppLayout = withStyles(styles, {
|
||||||
WithStyles<typeof styles> &
|
WithStyles<typeof styles> &
|
||||||
RouteComponentProps<any>) => {
|
RouteComponentProps<any>) => {
|
||||||
const { isDark, toggleTheme } = useTheme();
|
const { isDark, toggleTheme } = useTheme();
|
||||||
|
const [isMenuSmall, setMenuSmall] = useLocalStorage("isMenuSmall", false);
|
||||||
const [isDrawerOpened, setDrawerState] = React.useState(false);
|
const [isDrawerOpened, setDrawerState] = React.useState(false);
|
||||||
const [isMenuOpened, setMenuState] = React.useState(false);
|
const [isMenuOpened, setMenuState] = React.useState(false);
|
||||||
const appActionAnchor = React.useRef<HTMLDivElement>();
|
const appActionAnchor = React.useRef<HTMLDivElement>();
|
||||||
|
@ -223,6 +293,10 @@ const AppLayout = withStyles(styles, {
|
||||||
navigate(url);
|
navigate(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleIsMenuSmall = () => {
|
||||||
|
setMenuSmall(!isMenuSmall);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppProgressProvider>
|
<AppProgressProvider>
|
||||||
{({ isProgress }) => (
|
{({ isProgress }) => (
|
||||||
|
@ -239,14 +313,37 @@ const AppLayout = withStyles(styles, {
|
||||||
<ResponsiveDrawer
|
<ResponsiveDrawer
|
||||||
onClose={() => setDrawerState(false)}
|
onClose={() => setDrawerState(false)}
|
||||||
open={isDrawerOpened}
|
open={isDrawerOpened}
|
||||||
|
small={!isMenuSmall}
|
||||||
>
|
>
|
||||||
<SVG
|
<div
|
||||||
className={classes.logo}
|
className={classNames(classes.logo, {
|
||||||
src={isDark ? saleorDarkLogo : saleorLightLogo}
|
[classes.logoSmall]: isMenuSmall,
|
||||||
/>
|
[classes.logoDark]: isDark
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<SVG
|
||||||
|
src={
|
||||||
|
isMenuSmall ? saleorDarkLogoSmall : saleorDarkLogo
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Hidden smDown>
|
||||||
|
<div
|
||||||
|
className={classNames(classes.isMenuSmall, {
|
||||||
|
[classes.isMenuSmallHide]: isMenuSmall,
|
||||||
|
[classes.isMenuSmallDark]: isDark
|
||||||
|
})}
|
||||||
|
onClick={handleIsMenuSmall}
|
||||||
|
>
|
||||||
|
<SVG src={menuArrowIcon} />
|
||||||
|
</div>
|
||||||
|
</Hidden>
|
||||||
<MenuList
|
<MenuList
|
||||||
className={classes.menu}
|
className={
|
||||||
|
isMenuSmall ? classes.menuSmall : classes.menu
|
||||||
|
}
|
||||||
menuItems={menuStructure}
|
menuItems={menuStructure}
|
||||||
|
isMenuSmall={!isMenuSmall}
|
||||||
location={location.pathname}
|
location={location.pathname}
|
||||||
user={user}
|
user={user}
|
||||||
renderConfigure={true}
|
renderConfigure={true}
|
||||||
|
@ -254,7 +351,11 @@ const AppLayout = withStyles(styles, {
|
||||||
/>
|
/>
|
||||||
</ResponsiveDrawer>
|
</ResponsiveDrawer>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.content}>
|
<div
|
||||||
|
className={classNames(classes.content, {
|
||||||
|
[classes.contentToggle]: isMenuSmall
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<Container>
|
<Container>
|
||||||
<div className={classes.header}>
|
<div className={classes.header}>
|
||||||
|
@ -283,6 +384,11 @@ const AppLayout = withStyles(styles, {
|
||||||
ref={anchor}
|
ref={anchor}
|
||||||
>
|
>
|
||||||
<Chip
|
<Chip
|
||||||
|
avatar={
|
||||||
|
user.avatar && (
|
||||||
|
<Avatar alt="user" src={user.avatar.url} />
|
||||||
|
)
|
||||||
|
}
|
||||||
className={classes.userChip}
|
className={classes.userChip}
|
||||||
label={
|
label={
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -7,8 +7,11 @@ import {
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import SVG from "react-inlinesvg";
|
||||||
import { matchPath } from "react-router";
|
import { matchPath } from "react-router";
|
||||||
|
|
||||||
|
import useTheme from "@saleor/hooks/useTheme";
|
||||||
|
import configureIcon from "@assets/images/menu-configure-icon.svg";
|
||||||
import { User } from "../../auth/types/User";
|
import { User } from "../../auth/types/User";
|
||||||
import { configurationMenu, configurationMenuUrl } from "../../configuration";
|
import { configurationMenu, configurationMenuUrl } from "../../configuration";
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
|
@ -19,6 +22,44 @@ import { IMenuItem } from "./menuStructure";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
menuIcon: {
|
||||||
|
"& svg": {
|
||||||
|
height: 32,
|
||||||
|
width: 32
|
||||||
|
},
|
||||||
|
display: "inline-block",
|
||||||
|
position: "relative",
|
||||||
|
top: 8
|
||||||
|
},
|
||||||
|
menuIconDark: {
|
||||||
|
"& path": {
|
||||||
|
fill: theme.palette.common.white
|
||||||
|
}
|
||||||
|
},
|
||||||
|
menuIsActive: {
|
||||||
|
boxShadow: "0px 0px 12px 1px rgba(0,0,0,0.2)"
|
||||||
|
},
|
||||||
|
menuItemHover: {
|
||||||
|
"& path": {
|
||||||
|
transition: "fill 0.5s ease"
|
||||||
|
},
|
||||||
|
"&:hover": {
|
||||||
|
"& path": {
|
||||||
|
fill: theme.palette.primary.main
|
||||||
|
},
|
||||||
|
"&:before": {
|
||||||
|
borderLeft: `solid 2px ${theme.palette.primary.main}`,
|
||||||
|
content: "''",
|
||||||
|
height: 33,
|
||||||
|
left: -25,
|
||||||
|
position: "absolute",
|
||||||
|
top: 8
|
||||||
|
},
|
||||||
|
color: theme.palette.primary.main
|
||||||
|
},
|
||||||
|
cursor: "pointer",
|
||||||
|
position: "relative"
|
||||||
|
},
|
||||||
menuList: {
|
menuList: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
@ -28,24 +69,33 @@ const styles = (theme: Theme) =>
|
||||||
paddingBottom: theme.spacing.unit * 3
|
paddingBottom: theme.spacing.unit * 3
|
||||||
},
|
},
|
||||||
menuListItem: {
|
menuListItem: {
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.primary.main
|
|
||||||
},
|
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
display: "block",
|
display: "block",
|
||||||
marginTop: theme.spacing.unit * 2,
|
marginBottom: theme.spacing.unit * 5,
|
||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
transition: theme.transitions.duration.standard + "ms"
|
transition: theme.transitions.duration.standard + "ms"
|
||||||
},
|
},
|
||||||
menuListItemActive: {
|
menuListItemActive: {
|
||||||
"&:before": {
|
"& $menuListItemText": {
|
||||||
background: theme.palette.primary.main,
|
color: theme.palette.primary.main
|
||||||
|
},
|
||||||
|
"& path": {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
fill: theme.palette.primary.main
|
||||||
|
}
|
||||||
|
},
|
||||||
|
menuListItemOpen: {
|
||||||
|
"&:after": {
|
||||||
|
borderBottom: `10px solid transparent`,
|
||||||
|
borderLeft: `10px solid ${theme.palette.background.paper}`,
|
||||||
|
borderTop: `10px solid transparent`,
|
||||||
content: "''",
|
content: "''",
|
||||||
height: "100%",
|
height: 0,
|
||||||
left: -32,
|
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
width: 5
|
right: -35,
|
||||||
|
top: 15,
|
||||||
|
width: 0
|
||||||
},
|
},
|
||||||
position: "relative"
|
position: "relative"
|
||||||
},
|
},
|
||||||
|
@ -54,123 +104,231 @@ const styles = (theme: Theme) =>
|
||||||
color: theme.palette.primary.main
|
color: theme.palette.primary.main
|
||||||
},
|
},
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
|
display: "inline-block",
|
||||||
fontSize: "1rem",
|
fontSize: "1rem",
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
|
opacity: 1,
|
||||||
|
paddingLeft: 16,
|
||||||
textTransform: "uppercase",
|
textTransform: "uppercase",
|
||||||
transition: theme.transitions.duration.standard + "ms"
|
transition: `opacity ${theme.transitions.duration.shorter}ms ease 0.1s`
|
||||||
},
|
},
|
||||||
menuListNested: {
|
menuListItemTextHide: {
|
||||||
"& $menuListItemActive": {
|
opacity: 0,
|
||||||
"& $menuListItemText": {
|
position: "absolute",
|
||||||
color: theme.palette.primary.main
|
transition: `opacity ${theme.transitions.duration.shorter}ms ease`
|
||||||
},
|
},
|
||||||
"&:before": {
|
subMenu: {
|
||||||
borderRadius: "100%",
|
padding: "0 15px"
|
||||||
height: 8,
|
},
|
||||||
marginLeft: 9,
|
subMenuDrawer: {
|
||||||
marginTop: 7,
|
background: "#000",
|
||||||
width: 8
|
cursor: "pointer",
|
||||||
}
|
height: "100vh",
|
||||||
},
|
left: 0,
|
||||||
"& $menuListItemText": {
|
opacity: 0.2,
|
||||||
textTransform: "none"
|
position: "absolute",
|
||||||
},
|
top: 0,
|
||||||
marginLeft: theme.spacing.unit * 3
|
width: 0,
|
||||||
|
zIndex: -2
|
||||||
|
},
|
||||||
|
subMenuDrawerOpen: {
|
||||||
|
width: `100vw`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface MenuListProps {
|
interface MenuListProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
menuItems: IMenuItem[];
|
menuItems: IMenuItem[];
|
||||||
|
isMenuSmall: boolean;
|
||||||
location: string;
|
location: string;
|
||||||
user: User;
|
user: User;
|
||||||
renderConfigure: boolean;
|
renderConfigure: boolean;
|
||||||
onMenuItemClick: (url: string, event: React.MouseEvent<any>) => void;
|
onMenuItemClick: (url: string, event: React.MouseEvent<any>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IActiveSubMenu {
|
||||||
|
isActive: boolean;
|
||||||
|
label: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
const MenuList = withStyles(styles, { name: "MenuList" })(
|
const MenuList = withStyles(styles, { name: "MenuList" })(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
className,
|
className,
|
||||||
menuItems,
|
menuItems,
|
||||||
|
isMenuSmall,
|
||||||
location,
|
location,
|
||||||
user,
|
user,
|
||||||
renderConfigure,
|
renderConfigure,
|
||||||
onMenuItemClick
|
onMenuItemClick
|
||||||
}: MenuListProps & WithStyles<typeof styles>) => (
|
}: MenuListProps & WithStyles<typeof styles>) => {
|
||||||
<div className={className}>
|
const { isDark } = useTheme();
|
||||||
{/* FIXME: this .split("?")[0] looks gross */}
|
const [activeSubMenu, setActiveSubMenu] = React.useState<IActiveSubMenu>({
|
||||||
{menuItems.map(menuItem => {
|
isActive: false,
|
||||||
const isActive = (menuItem: IMenuItem) =>
|
label: null
|
||||||
location.split("?")[0] === orderDraftListUrl().split("?")[0] &&
|
});
|
||||||
menuItem.url.split("?")[0] === orderListUrl().split("?")[0]
|
|
||||||
? false
|
|
||||||
: !!matchPath(location.split("?")[0], {
|
|
||||||
exact: menuItem.url.split("?")[0] === "/",
|
|
||||||
path: menuItem.url.split("?")[0]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
const handleSubMenu = itemLabel => {
|
||||||
menuItem.permission &&
|
setActiveSubMenu({
|
||||||
!user.permissions.map(perm => perm.code).includes(menuItem.permission)
|
isActive:
|
||||||
) {
|
itemLabel === activeSubMenu.label ? !activeSubMenu.isActive : true,
|
||||||
return null;
|
label: itemLabel
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (!menuItem.url) {
|
const closeSubMenu = (menuItemUrl, event) => {
|
||||||
const isAnyChildActive = menuItem.children.reduce(
|
setActiveSubMenu({
|
||||||
(acc, child) => acc || isActive(child),
|
isActive: false,
|
||||||
false
|
label: null
|
||||||
);
|
});
|
||||||
|
if (menuItemUrl && event) {
|
||||||
|
onMenuItemClick(menuItemUrl, event);
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(className, {
|
||||||
|
[classes.menuIsActive]: activeSubMenu.isActive
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{/* FIXME: this .split("?")[0] looks gross */}
|
||||||
|
{menuItems.map(menuItem => {
|
||||||
|
const isActive = (menuItem: IMenuItem) =>
|
||||||
|
location.split("?")[0] === orderDraftListUrl().split("?")[0] &&
|
||||||
|
menuItem.url.split("?")[0] === orderListUrl().split("?")[0]
|
||||||
|
? false
|
||||||
|
: !!matchPath(location.split("?")[0], {
|
||||||
|
exact: menuItem.url.split("?")[0] === "/",
|
||||||
|
path: menuItem.url.split("?")[0]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
menuItem.permission &&
|
||||||
|
!user.permissions
|
||||||
|
.map(perm => perm.code)
|
||||||
|
.includes(menuItem.permission)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!menuItem.url) {
|
||||||
|
const isAnyChildActive = menuItem.children.reduce(
|
||||||
|
(acc, child) => acc || isActive(child),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(classes.menuListItem, {
|
||||||
|
[classes.menuListItemActive]: isAnyChildActive
|
||||||
|
})}
|
||||||
|
key={menuItem.label}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={classNames(classes.menuItemHover, {
|
||||||
|
[classes.menuListItemOpen]:
|
||||||
|
menuItem.ariaLabel === activeSubMenu.label &&
|
||||||
|
activeSubMenu.isActive
|
||||||
|
})}
|
||||||
|
onClick={() => handleSubMenu(menuItem.ariaLabel)}
|
||||||
|
>
|
||||||
|
<SVG
|
||||||
|
className={classNames(classes.menuIcon, {
|
||||||
|
[classes.menuIconDark]: isDark
|
||||||
|
})}
|
||||||
|
src={menuItem.icon}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
aria-label={menuItem.ariaLabel}
|
||||||
|
className={classNames(classes.menuListItemText, {
|
||||||
|
[classes.menuListItemTextHide]: !isMenuSmall
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{menuItem.label}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<MenuNested
|
||||||
|
activeItem={activeSubMenu}
|
||||||
|
closeSubMenu={setActiveSubMenu}
|
||||||
|
menuItem={menuItem}
|
||||||
|
onMenuItemClick={onMenuItemClick}
|
||||||
|
handleSubMenu={handleSubMenu}
|
||||||
|
title={menuItem.label}
|
||||||
|
icon={menuItem.icon}
|
||||||
|
ariaLabel={menuItem.ariaLabel}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
onClick={event => closeSubMenu(null, event)}
|
||||||
|
className={classNames(classes.subMenuDrawer, {
|
||||||
|
[classes.subMenuDrawerOpen]: activeSubMenu.isActive
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuNested
|
<a
|
||||||
classes={classes}
|
className={classNames(classes.menuListItem, {
|
||||||
isAnyChildActive={isAnyChildActive}
|
[classes.menuListItemActive]: isActive(menuItem)
|
||||||
location={location}
|
})}
|
||||||
menuItem={menuItem}
|
href={createHref(menuItem.url)}
|
||||||
onMenuItemClick={onMenuItemClick}
|
onClick={event => closeSubMenu(menuItem.url, event)}
|
||||||
user={user}
|
|
||||||
key={menuItem.label}
|
key={menuItem.label}
|
||||||
/>
|
>
|
||||||
|
<div className={classes.menuItemHover}>
|
||||||
|
<SVG
|
||||||
|
className={classNames(classes.menuIcon, {
|
||||||
|
[classes.menuIconDark]: isDark
|
||||||
|
})}
|
||||||
|
src={menuItem.icon}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
aria-label={menuItem.ariaLabel}
|
||||||
|
className={classNames(classes.menuListItemText, {
|
||||||
|
[classes.menuListItemTextHide]: !isMenuSmall
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{menuItem.label}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
);
|
);
|
||||||
}
|
})}
|
||||||
|
{renderConfigure &&
|
||||||
return (
|
configurationMenu.filter(menuItem =>
|
||||||
<a
|
user.permissions
|
||||||
className={classNames(classes.menuListItem, {
|
.map(perm => perm.code)
|
||||||
[classes.menuListItemActive]: isActive(menuItem)
|
.includes(menuItem.permission)
|
||||||
})}
|
).length > 0 && (
|
||||||
href={createHref(menuItem.url)}
|
<a
|
||||||
onClick={event => onMenuItemClick(menuItem.url, event)}
|
className={classes.menuListItem}
|
||||||
key={menuItem.label}
|
href={createHref(configurationMenuUrl)}
|
||||||
>
|
onClick={event => onMenuItemClick(configurationMenuUrl, event)}
|
||||||
<Typography
|
|
||||||
aria-label={menuItem.ariaLabel}
|
|
||||||
className={classes.menuListItemText}
|
|
||||||
>
|
>
|
||||||
{menuItem.label}
|
<div className={classes.menuItemHover}>
|
||||||
</Typography>
|
<SVG
|
||||||
</a>
|
className={classNames(classes.menuIcon, {
|
||||||
);
|
[classes.menuIconDark]: isDark
|
||||||
})}
|
})}
|
||||||
{renderConfigure &&
|
src={configureIcon}
|
||||||
configurationMenu.filter(menuItem =>
|
/>
|
||||||
user.permissions.map(perm => perm.code).includes(menuItem.permission)
|
<Typography
|
||||||
).length > 0 && (
|
aria-label="configure"
|
||||||
<a
|
className={classNames(classes.menuListItemText, {
|
||||||
className={classes.menuListItem}
|
[classes.menuListItemTextHide]: !isMenuSmall
|
||||||
href={createHref(configurationMenuUrl)}
|
})}
|
||||||
onClick={event => onMenuItemClick(configurationMenuUrl, event)}
|
>
|
||||||
>
|
{i18n.t("Configure")}
|
||||||
<Typography
|
</Typography>
|
||||||
aria-label="configure"
|
</div>
|
||||||
className={classes.menuListItemText}
|
</a>
|
||||||
>
|
)}
|
||||||
{i18n.t("Configure")}
|
</div>
|
||||||
</Typography>
|
);
|
||||||
</a>
|
}
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
export default MenuList;
|
export default MenuList;
|
||||||
|
|
|
@ -1,61 +1,195 @@
|
||||||
|
import Hidden from "@material-ui/core/Hidden";
|
||||||
|
import {
|
||||||
|
createStyles,
|
||||||
|
Theme,
|
||||||
|
withStyles,
|
||||||
|
WithStyles
|
||||||
|
} from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import SVG from "react-inlinesvg";
|
||||||
|
|
||||||
import { User } from "../../auth/types/User";
|
import menuArrowIcon from "@assets/images/menu-arrow-icon.svg";
|
||||||
import MenuList from "./MenuList";
|
import useTheme from "@saleor/hooks/useTheme";
|
||||||
|
import { createHref } from "@saleor/misc";
|
||||||
|
import { drawerWidthExpanded } from "./consts";
|
||||||
|
import { IActiveSubMenu } from "./MenuList";
|
||||||
import { IMenuItem } from "./menuStructure";
|
import { IMenuItem } from "./menuStructure";
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
menuListNested: {
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
height: "100vh",
|
||||||
|
position: "absolute",
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
transition: `right ${theme.transitions.duration.shorter}ms ease`,
|
||||||
|
width: 300,
|
||||||
|
zIndex: -1
|
||||||
|
},
|
||||||
|
menuListNestedClose: {
|
||||||
|
"& svg": {
|
||||||
|
fill: theme.palette.primary.main,
|
||||||
|
left: 7,
|
||||||
|
position: "relative",
|
||||||
|
top: -2
|
||||||
|
},
|
||||||
|
border: `solid 1px #EAEAEA`,
|
||||||
|
borderRadius: "100%",
|
||||||
|
cursor: "pointer",
|
||||||
|
height: 32,
|
||||||
|
position: "absolute",
|
||||||
|
right: 32,
|
||||||
|
top: 35,
|
||||||
|
transform: "rotate(180deg)",
|
||||||
|
width: 32
|
||||||
|
},
|
||||||
|
menuListNestedCloseDark: {
|
||||||
|
border: `solid 1px #252728`
|
||||||
|
},
|
||||||
|
menuListNestedHide: {
|
||||||
|
opacity: 0
|
||||||
|
},
|
||||||
|
menuListNestedIcon: {
|
||||||
|
"& path": {
|
||||||
|
fill: "initial"
|
||||||
|
},
|
||||||
|
"& svg": { height: 32, position: "relative", top: 7, width: 32 }
|
||||||
|
},
|
||||||
|
menuListNestedIconDark: {
|
||||||
|
"& path": {
|
||||||
|
fill: theme.palette.common.white
|
||||||
|
}
|
||||||
|
},
|
||||||
|
menuListNestedItem: {
|
||||||
|
"&:hover": {
|
||||||
|
"& p": {
|
||||||
|
color: theme.palette.primary.main
|
||||||
|
}
|
||||||
|
},
|
||||||
|
display: "block",
|
||||||
|
marginBottom: theme.spacing.unit * 2,
|
||||||
|
padding: "0px 30px",
|
||||||
|
textDecoration: "none"
|
||||||
|
},
|
||||||
|
menuListNestedOpen: {
|
||||||
|
[theme.breakpoints.down("sm")]: {
|
||||||
|
right: 0,
|
||||||
|
width: drawerWidthExpanded,
|
||||||
|
zIndex: 2
|
||||||
|
},
|
||||||
|
right: -300,
|
||||||
|
zIndex: -1
|
||||||
|
},
|
||||||
|
subHeader: {
|
||||||
|
borderBottom: "solid 1px #EAEAEA",
|
||||||
|
margin: "30px",
|
||||||
|
marginBottom: 39,
|
||||||
|
paddingBottom: 22
|
||||||
|
},
|
||||||
|
subHeaderDark: {
|
||||||
|
borderBottom: "solid 1px #252728"
|
||||||
|
},
|
||||||
|
subHeaderTitle: {
|
||||||
|
[theme.breakpoints.up("md")]: {
|
||||||
|
paddingLeft: 0
|
||||||
|
},
|
||||||
|
display: "inline",
|
||||||
|
paddingLeft: 10
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export interface MenuNestedProps {
|
export interface MenuNestedProps {
|
||||||
classes: Record<
|
activeItem: IActiveSubMenu;
|
||||||
| "menuListItem"
|
ariaLabel: string;
|
||||||
| "menuListItemActive"
|
closeSubMenu: ({ isActive, label }: IActiveSubMenu) => void;
|
||||||
| "menuListItemText"
|
icon: string;
|
||||||
| "menuListNested",
|
|
||||||
string
|
|
||||||
>;
|
|
||||||
isAnyChildActive: boolean;
|
|
||||||
menuItem: IMenuItem;
|
menuItem: IMenuItem;
|
||||||
location: string;
|
title: string;
|
||||||
user: User;
|
handleSubMenu: (itemLabel: string) => void;
|
||||||
onMenuItemClick: (url: string, event: React.MouseEvent<any>) => void;
|
onMenuItemClick: (url: string, event: React.MouseEvent<any>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MenuNested: React.FC<MenuNestedProps> = ({
|
const MenuNested = withStyles(styles, { name: "MenuNested" })(
|
||||||
classes,
|
({
|
||||||
isAnyChildActive,
|
activeItem,
|
||||||
location,
|
ariaLabel,
|
||||||
menuItem,
|
classes,
|
||||||
onMenuItemClick,
|
closeSubMenu,
|
||||||
user
|
icon,
|
||||||
}) => {
|
menuItem,
|
||||||
const [isOpened, setOpenStatus] = React.useState(false);
|
onMenuItemClick,
|
||||||
|
title
|
||||||
return (
|
}: MenuNestedProps & WithStyles<typeof styles>) => {
|
||||||
<div
|
const menuItems = menuItem.children;
|
||||||
onClick={() => setOpenStatus(!isOpened)}
|
const { isDark } = useTheme();
|
||||||
className={classNames(classes.menuListItem, {
|
const closeMenu = (menuItemUrl, event) => {
|
||||||
[classes.menuListItemActive]: isAnyChildActive
|
onMenuItemClick(menuItemUrl, event);
|
||||||
})}
|
closeSubMenu({
|
||||||
>
|
isActive: false,
|
||||||
<Typography
|
label: null
|
||||||
aria-label={menuItem.ariaLabel}
|
});
|
||||||
className={classes.menuListItemText}
|
event.stopPropagation();
|
||||||
>
|
event.preventDefault();
|
||||||
{menuItem.label}
|
};
|
||||||
</Typography>
|
return (
|
||||||
{isOpened && (
|
<>
|
||||||
<div className={classes.menuListNested}>
|
<div
|
||||||
<MenuList
|
className={classNames(classes.menuListNested, {
|
||||||
menuItems={menuItem.children}
|
[classes.menuListNestedOpen]:
|
||||||
location={location}
|
activeItem.label === ariaLabel && activeItem.isActive
|
||||||
user={user}
|
})}
|
||||||
renderConfigure={false}
|
>
|
||||||
onMenuItemClick={onMenuItemClick}
|
<Typography
|
||||||
/>
|
className={classNames(classes.subHeader, {
|
||||||
|
[classes.subHeaderDark]: isDark
|
||||||
|
})}
|
||||||
|
variant="h5"
|
||||||
|
>
|
||||||
|
<Hidden mdUp>
|
||||||
|
<SVG
|
||||||
|
className={classNames(classes.menuListNestedIcon, {
|
||||||
|
[classes.menuListNestedIconDark]: isDark
|
||||||
|
})}
|
||||||
|
src={icon}
|
||||||
|
/>
|
||||||
|
</Hidden>
|
||||||
|
<div className={classes.subHeaderTitle}>{title}</div>
|
||||||
|
<Hidden mdUp>
|
||||||
|
<div
|
||||||
|
className={classNames(classes.menuListNestedClose, {
|
||||||
|
[classes.menuListNestedCloseDark]: isDark
|
||||||
|
})}
|
||||||
|
onClick={() =>
|
||||||
|
closeSubMenu({
|
||||||
|
isActive: false,
|
||||||
|
label: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SVG src={menuArrowIcon} />
|
||||||
|
</div>
|
||||||
|
</Hidden>
|
||||||
|
</Typography>
|
||||||
|
{menuItems.map(item => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className={classNames(classes.menuListNestedItem)}
|
||||||
|
href={createHref(item.url)}
|
||||||
|
onClick={event => closeMenu(item.url, event)}
|
||||||
|
key={item.label}
|
||||||
|
>
|
||||||
|
<Typography aria-label={item.ariaLabel}>
|
||||||
|
{item.label}
|
||||||
|
</Typography>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
};
|
);
|
||||||
export default MenuNested;
|
export default MenuNested;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { drawerWidth } from "./consts";
|
import { drawerWidth, drawerWidthExpanded } from "./consts";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -15,31 +15,38 @@ const styles = (theme: Theme) =>
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
border: "none",
|
border: "none",
|
||||||
height: "100vh",
|
height: "100vh",
|
||||||
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 4}px`,
|
overflow: "visible",
|
||||||
|
padding: 0,
|
||||||
position: "fixed" as "fixed",
|
position: "fixed" as "fixed",
|
||||||
|
transition: "width 0.2s ease",
|
||||||
|
width: drawerWidthExpanded
|
||||||
|
},
|
||||||
|
drawerDesktopSmall: {
|
||||||
|
overflow: "visible",
|
||||||
|
transition: "width 0.2s ease",
|
||||||
width: drawerWidth
|
width: drawerWidth
|
||||||
},
|
},
|
||||||
drawerMobile: {
|
drawerMobile: {
|
||||||
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 4}px`,
|
width: drawerWidthExpanded
|
||||||
width: drawerWidth,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ResponsiveDrawerProps extends WithStyles<typeof styles> {
|
interface ResponsiveDrawerProps extends WithStyles<typeof styles> {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
small: boolean;
|
||||||
onClose?();
|
onClose?();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResponsiveDrawer = withStyles(styles, { name: "ResponsiveDrawer" })(
|
const ResponsiveDrawer = withStyles(styles, { name: "ResponsiveDrawer" })(
|
||||||
({ children, classes, onClose, open }: ResponsiveDrawerProps) => (
|
({ children, classes, onClose, open, small }: ResponsiveDrawerProps) => (
|
||||||
<>
|
<>
|
||||||
<Hidden smDown>
|
<Hidden smDown>
|
||||||
<Drawer
|
<Drawer
|
||||||
variant="persistent"
|
variant="persistent"
|
||||||
open
|
open
|
||||||
classes={{
|
classes={{
|
||||||
paper: classes.drawerDesktop
|
paper: small ? classes.drawerDesktop : classes.drawerDesktopSmall
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export const drawerWidth = 256;
|
export const drawerWidthExpanded = 256;
|
||||||
|
export const drawerWidth = 80;
|
||||||
export const navigationBarHeight = 64;
|
export const navigationBarHeight = 64;
|
||||||
export const appLoaderHeight = 4;
|
export const appLoaderHeight = 4;
|
||||||
|
|
|
@ -8,9 +8,17 @@ import { productListUrl } from "../../products/urls";
|
||||||
import { languageListUrl } from "../../translations/urls";
|
import { languageListUrl } from "../../translations/urls";
|
||||||
import { PermissionEnum } from "../../types/globalTypes";
|
import { PermissionEnum } from "../../types/globalTypes";
|
||||||
|
|
||||||
|
import catalogIcon from "@assets/images/menu-catalog-icon.svg";
|
||||||
|
import customerIcon from "@assets/images/menu-customers-icon.svg";
|
||||||
|
import discountsIcon from "@assets/images/menu-discounts-icon.svg";
|
||||||
|
import homeIcon from "@assets/images/menu-home-icon.svg";
|
||||||
|
import ordersIcon from "@assets/images/menu-orders-icon.svg";
|
||||||
|
import translationIcon from "@assets/images/menu-translation-icon.svg";
|
||||||
|
|
||||||
export interface IMenuItem {
|
export interface IMenuItem {
|
||||||
ariaLabel: string;
|
ariaLabel: string;
|
||||||
children?: IMenuItem[];
|
children?: IMenuItem[];
|
||||||
|
icon?: any;
|
||||||
label: string;
|
label: string;
|
||||||
permission?: PermissionEnum;
|
permission?: PermissionEnum;
|
||||||
url?: string;
|
url?: string;
|
||||||
|
@ -19,6 +27,7 @@ export interface IMenuItem {
|
||||||
const menuStructure: IMenuItem[] = [
|
const menuStructure: IMenuItem[] = [
|
||||||
{
|
{
|
||||||
ariaLabel: "home",
|
ariaLabel: "home",
|
||||||
|
icon: homeIcon,
|
||||||
label: i18n.t("Home", { context: "Menu label" }),
|
label: i18n.t("Home", { context: "Menu label" }),
|
||||||
url: "/"
|
url: "/"
|
||||||
},
|
},
|
||||||
|
@ -41,6 +50,7 @@ const menuStructure: IMenuItem[] = [
|
||||||
url: collectionListUrl()
|
url: collectionListUrl()
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
icon: catalogIcon,
|
||||||
label: i18n.t("Catalog", { context: "Menu label" }),
|
label: i18n.t("Catalog", { context: "Menu label" }),
|
||||||
permission: PermissionEnum.MANAGE_PRODUCTS
|
permission: PermissionEnum.MANAGE_PRODUCTS
|
||||||
},
|
},
|
||||||
|
@ -60,11 +70,13 @@ const menuStructure: IMenuItem[] = [
|
||||||
url: orderDraftListUrl()
|
url: orderDraftListUrl()
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
icon: ordersIcon,
|
||||||
label: i18n.t("Orders", { context: "Menu label" }),
|
label: i18n.t("Orders", { context: "Menu label" }),
|
||||||
permission: PermissionEnum.MANAGE_ORDERS
|
permission: PermissionEnum.MANAGE_ORDERS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ariaLabel: "customers",
|
ariaLabel: "customers",
|
||||||
|
icon: customerIcon,
|
||||||
label: i18n.t("Customers", { context: "Menu label" }),
|
label: i18n.t("Customers", { context: "Menu label" }),
|
||||||
permission: PermissionEnum.MANAGE_USERS,
|
permission: PermissionEnum.MANAGE_USERS,
|
||||||
url: customerListUrl()
|
url: customerListUrl()
|
||||||
|
@ -84,11 +96,13 @@ const menuStructure: IMenuItem[] = [
|
||||||
url: voucherListUrl()
|
url: voucherListUrl()
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
icon: discountsIcon,
|
||||||
label: i18n.t("Discounts", { context: "Menu label" }),
|
label: i18n.t("Discounts", { context: "Menu label" }),
|
||||||
permission: PermissionEnum.MANAGE_DISCOUNTS
|
permission: PermissionEnum.MANAGE_DISCOUNTS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ariaLabel: "translations",
|
ariaLabel: "translations",
|
||||||
|
icon: translationIcon,
|
||||||
label: i18n.t("Translations", { context: "Menu label" }),
|
label: i18n.t("Translations", { context: "Menu label" }),
|
||||||
permission: PermissionEnum.MANAGE_TRANSLATIONS,
|
permission: PermissionEnum.MANAGE_TRANSLATIONS,
|
||||||
url: languageListUrl
|
url: languageListUrl
|
||||||
|
|
|
@ -15,8 +15,8 @@ import React from "react";
|
||||||
import ConfirmButton, {
|
import ConfirmButton, {
|
||||||
ConfirmButtonTransitionState
|
ConfirmButtonTransitionState
|
||||||
} from "@saleor/components/ConfirmButton";
|
} from "@saleor/components/ConfirmButton";
|
||||||
import Form from "@saleor/components/Form";
|
|
||||||
import FormSpacer from "@saleor/components/FormSpacer";
|
import FormSpacer from "@saleor/components/FormSpacer";
|
||||||
|
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||||
import { SearchCategories_categories_edges_node } from "../../containers/SearchCategories/types/SearchCategories";
|
import { SearchCategories_categories_edges_node } from "../../containers/SearchCategories/types/SearchCategories";
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
import Checkbox from "../Checkbox";
|
import Checkbox from "../Checkbox";
|
||||||
|
@ -50,13 +50,28 @@ interface AssignCategoriesDialogProps extends WithStyles<typeof styles> {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onFetch: (value: string) => void;
|
onFetch: (value: string) => void;
|
||||||
onSubmit: (data: FormData) => void;
|
onSubmit: (data: SearchCategories_categories_edges_node[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCategoryAssign(
|
||||||
|
product: SearchCategories_categories_edges_node,
|
||||||
|
isSelected: boolean,
|
||||||
|
selectedCategories: SearchCategories_categories_edges_node[],
|
||||||
|
setSelectedCategories: (
|
||||||
|
data: SearchCategories_categories_edges_node[]
|
||||||
|
) => void
|
||||||
|
) {
|
||||||
|
if (isSelected) {
|
||||||
|
setSelectedCategories(
|
||||||
|
selectedCategories.filter(
|
||||||
|
selectedProduct => selectedProduct.id !== product.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setSelectedCategories([...selectedCategories, product]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialForm: FormData = {
|
|
||||||
categories: [],
|
|
||||||
query: ""
|
|
||||||
};
|
|
||||||
const AssignCategoriesDialog = withStyles(styles, {
|
const AssignCategoriesDialog = withStyles(styles, {
|
||||||
name: "AssignCategoriesDialog"
|
name: "AssignCategoriesDialog"
|
||||||
})(
|
})(
|
||||||
|
@ -69,104 +84,93 @@ const AssignCategoriesDialog = withStyles(styles, {
|
||||||
onClose,
|
onClose,
|
||||||
onFetch,
|
onFetch,
|
||||||
onSubmit
|
onSubmit
|
||||||
}: AssignCategoriesDialogProps) => (
|
}: AssignCategoriesDialogProps) => {
|
||||||
<Dialog
|
const [query, onQueryChange] = useSearchQuery(onFetch);
|
||||||
open={open}
|
const [selectedCategories, setSelectedCategories] = React.useState<
|
||||||
onClose={onClose}
|
SearchCategories_categories_edges_node[]
|
||||||
classes={{ paper: classes.overflow }}
|
>([]);
|
||||||
fullWidth
|
|
||||||
maxWidth="sm"
|
|
||||||
>
|
|
||||||
<Form initial={initialForm} onSubmit={onSubmit}>
|
|
||||||
{({ data, change }) => (
|
|
||||||
<>
|
|
||||||
<DialogTitle>{i18n.t("Assign Categories")}</DialogTitle>
|
|
||||||
<DialogContent className={classes.overflow}>
|
|
||||||
<TextField
|
|
||||||
name="query"
|
|
||||||
value={data.query}
|
|
||||||
onChange={event => change(event, () => onFetch(data.query))}
|
|
||||||
label={i18n.t("Search Categories", {
|
|
||||||
context: "product search input label"
|
|
||||||
})}
|
|
||||||
placeholder={i18n.t(
|
|
||||||
"Search by product name, attribute, product type etc...",
|
|
||||||
{
|
|
||||||
context: "product search input placeholder"
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
fullWidth
|
|
||||||
InputProps={{
|
|
||||||
autoComplete: "off",
|
|
||||||
endAdornment: loading && <CircularProgress size={16} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FormSpacer />
|
|
||||||
<Table>
|
|
||||||
<TableBody>
|
|
||||||
{categories &&
|
|
||||||
categories.map(category => {
|
|
||||||
const isChecked = !!data.categories.find(
|
|
||||||
selectedCategories =>
|
|
||||||
selectedCategories.id === category.id
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
const handleSubmit = () => onSubmit(selectedCategories);
|
||||||
<TableRow key={category.id}>
|
|
||||||
<TableCell
|
return (
|
||||||
padding="checkbox"
|
<Dialog
|
||||||
className={classes.checkboxCell}
|
open={open}
|
||||||
>
|
onClose={onClose}
|
||||||
<Checkbox
|
classes={{ paper: classes.overflow }}
|
||||||
checked={isChecked}
|
fullWidth
|
||||||
onChange={() =>
|
maxWidth="sm"
|
||||||
isChecked
|
>
|
||||||
? change({
|
<DialogTitle>{i18n.t("Assign Categories")}</DialogTitle>
|
||||||
target: {
|
<DialogContent className={classes.overflow}>
|
||||||
name: "categories",
|
<TextField
|
||||||
value: data.categories.filter(
|
name="query"
|
||||||
selectedCategories =>
|
value={query}
|
||||||
selectedCategories.id !==
|
onChange={onQueryChange}
|
||||||
category.id
|
label={i18n.t("Search Categories", {
|
||||||
)
|
context: "category search input label"
|
||||||
}
|
})}
|
||||||
} as any)
|
placeholder={i18n.t("Search by category name, etc...", {
|
||||||
: change({
|
context: "category search input placeholder"
|
||||||
target: {
|
})}
|
||||||
name: "categories",
|
fullWidth
|
||||||
value: [...data.categories, category]
|
InputProps={{
|
||||||
}
|
autoComplete: "off",
|
||||||
} as any)
|
endAdornment: loading && <CircularProgress size={16} />
|
||||||
}
|
}}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
<FormSpacer />
|
||||||
<TableCell className={classes.wideCell}>
|
<Table>
|
||||||
{category.name}
|
<TableBody>
|
||||||
</TableCell>
|
{categories &&
|
||||||
</TableRow>
|
categories.map(category => {
|
||||||
);
|
const isSelected = !!selectedCategories.find(
|
||||||
})}
|
selectedCategories => selectedCategories.id === category.id
|
||||||
</TableBody>
|
);
|
||||||
</Table>
|
|
||||||
</DialogContent>
|
return (
|
||||||
<DialogActions>
|
<TableRow key={category.id}>
|
||||||
<Button onClick={onClose}>
|
<TableCell
|
||||||
{i18n.t("Cancel", { context: "button" })}
|
padding="checkbox"
|
||||||
</Button>
|
className={classes.checkboxCell}
|
||||||
<ConfirmButton
|
>
|
||||||
transitionState={confirmButtonState}
|
<Checkbox
|
||||||
color="primary"
|
checked={isSelected}
|
||||||
variant="contained"
|
onChange={() =>
|
||||||
type="submit"
|
handleCategoryAssign(
|
||||||
>
|
category,
|
||||||
{i18n.t("Assign categories", { context: "button" })}
|
isSelected,
|
||||||
</ConfirmButton>
|
selectedCategories,
|
||||||
</DialogActions>
|
setSelectedCategories
|
||||||
</>
|
)
|
||||||
)}
|
}
|
||||||
</Form>
|
/>
|
||||||
</Dialog>
|
</TableCell>
|
||||||
)
|
<TableCell className={classes.wideCell}>
|
||||||
|
{category.name}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onClose}>
|
||||||
|
{i18n.t("Cancel", { context: "button" })}
|
||||||
|
</Button>
|
||||||
|
<ConfirmButton
|
||||||
|
transitionState={confirmButtonState}
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
type="submit"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
{i18n.t("Assign categories", { context: "button" })}
|
||||||
|
</ConfirmButton>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
AssignCategoriesDialog.displayName = "AssignCategoriesDialog";
|
AssignCategoriesDialog.displayName = "AssignCategoriesDialog";
|
||||||
export default AssignCategoriesDialog;
|
export default AssignCategoriesDialog;
|
||||||
|
|
|
@ -12,13 +12,13 @@ import TableRow from "@material-ui/core/TableRow";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||||
|
import i18n from "@saleor/i18n";
|
||||||
import { SearchCollections_collections_edges_node } from "../../containers/SearchCollections/types/SearchCollections";
|
import { SearchCollections_collections_edges_node } from "../../containers/SearchCollections/types/SearchCollections";
|
||||||
import i18n from "../../i18n";
|
|
||||||
import Checkbox from "../Checkbox";
|
import Checkbox from "../Checkbox";
|
||||||
import ConfirmButton, {
|
import ConfirmButton, {
|
||||||
ConfirmButtonTransitionState
|
ConfirmButtonTransitionState
|
||||||
} from "../ConfirmButton/ConfirmButton";
|
} from "../ConfirmButton/ConfirmButton";
|
||||||
import Form from "../Form";
|
|
||||||
import FormSpacer from "../FormSpacer";
|
import FormSpacer from "../FormSpacer";
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
|
@ -50,13 +50,28 @@ interface AssignCollectionDialogProps extends WithStyles<typeof styles> {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onFetch: (value: string) => void;
|
onFetch: (value: string) => void;
|
||||||
onSubmit: (data: FormData) => void;
|
onSubmit: (data: SearchCollections_collections_edges_node[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCollectionAssign(
|
||||||
|
product: SearchCollections_collections_edges_node,
|
||||||
|
isSelected: boolean,
|
||||||
|
selectedCollections: SearchCollections_collections_edges_node[],
|
||||||
|
setSelectedCollections: (
|
||||||
|
data: SearchCollections_collections_edges_node[]
|
||||||
|
) => void
|
||||||
|
) {
|
||||||
|
if (isSelected) {
|
||||||
|
setSelectedCollections(
|
||||||
|
selectedCollections.filter(
|
||||||
|
selectedProduct => selectedProduct.id !== product.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setSelectedCollections([...selectedCollections, product]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialForm: FormData = {
|
|
||||||
collections: [],
|
|
||||||
query: ""
|
|
||||||
};
|
|
||||||
const AssignCollectionDialog = withStyles(styles, {
|
const AssignCollectionDialog = withStyles(styles, {
|
||||||
name: "AssignCollectionDialog"
|
name: "AssignCollectionDialog"
|
||||||
})(
|
})(
|
||||||
|
@ -69,104 +84,94 @@ const AssignCollectionDialog = withStyles(styles, {
|
||||||
onClose,
|
onClose,
|
||||||
onFetch,
|
onFetch,
|
||||||
onSubmit
|
onSubmit
|
||||||
}: AssignCollectionDialogProps) => (
|
}: AssignCollectionDialogProps) => {
|
||||||
<Dialog
|
const [query, onQueryChange] = useSearchQuery(onFetch);
|
||||||
onClose={onClose}
|
const [selectedCollections, setSelectedCollections] = React.useState<
|
||||||
open={open}
|
SearchCollections_collections_edges_node[]
|
||||||
classes={{ paper: classes.overflow }}
|
>([]);
|
||||||
fullWidth
|
|
||||||
maxWidth="sm"
|
|
||||||
>
|
|
||||||
<Form initial={initialForm} onSubmit={onSubmit}>
|
|
||||||
{({ data, change }) => (
|
|
||||||
<>
|
|
||||||
<DialogTitle>{i18n.t("Assign Collection")}</DialogTitle>
|
|
||||||
<DialogContent className={classes.overflow}>
|
|
||||||
<TextField
|
|
||||||
name="query"
|
|
||||||
value={data.query}
|
|
||||||
onChange={event => change(event, () => onFetch(data.query))}
|
|
||||||
label={i18n.t("Search Collection", {
|
|
||||||
context: "product search input label"
|
|
||||||
})}
|
|
||||||
placeholder={i18n.t(
|
|
||||||
"Search by product name, attribute, product type etc...",
|
|
||||||
{
|
|
||||||
context: "product search input placeholder"
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
fullWidth
|
|
||||||
InputProps={{
|
|
||||||
autoComplete: "off",
|
|
||||||
endAdornment: loading && <CircularProgress size={16} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FormSpacer />
|
|
||||||
<Table>
|
|
||||||
<TableBody>
|
|
||||||
{collections &&
|
|
||||||
collections.map(category => {
|
|
||||||
const isChecked = !!data.collections.find(
|
|
||||||
selectedCollection =>
|
|
||||||
selectedCollection.id === category.id
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
const handleSubmit = () => onSubmit(selectedCollections);
|
||||||
<TableRow key={category.id}>
|
|
||||||
<TableCell
|
return (
|
||||||
padding="checkbox"
|
<Dialog
|
||||||
className={classes.checkboxCell}
|
onClose={onClose}
|
||||||
>
|
open={open}
|
||||||
<Checkbox
|
classes={{ paper: classes.overflow }}
|
||||||
checked={isChecked}
|
fullWidth
|
||||||
onChange={() =>
|
maxWidth="sm"
|
||||||
isChecked
|
>
|
||||||
? change({
|
<DialogTitle>{i18n.t("Assign Collection")}</DialogTitle>
|
||||||
target: {
|
<DialogContent className={classes.overflow}>
|
||||||
name: "collections",
|
<TextField
|
||||||
value: data.collections.filter(
|
name="query"
|
||||||
selectedCollection =>
|
value={query}
|
||||||
selectedCollection.id !==
|
onChange={onQueryChange}
|
||||||
category.id
|
label={i18n.t("Search Collection", {
|
||||||
)
|
context: "product search input label"
|
||||||
}
|
})}
|
||||||
} as any)
|
placeholder={i18n.t("Search by collection name, etc...", {
|
||||||
: change({
|
context: "product search input placeholder"
|
||||||
target: {
|
})}
|
||||||
name: "collections",
|
fullWidth
|
||||||
value: [...data.collections, category]
|
InputProps={{
|
||||||
}
|
autoComplete: "off",
|
||||||
} as any)
|
endAdornment: loading && <CircularProgress size={16} />
|
||||||
}
|
}}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
<FormSpacer />
|
||||||
<TableCell className={classes.wideCell}>
|
<Table>
|
||||||
{category.name}
|
<TableBody>
|
||||||
</TableCell>
|
{collections &&
|
||||||
</TableRow>
|
collections.map(collection => {
|
||||||
);
|
const isSelected = !!selectedCollections.find(
|
||||||
})}
|
selectedCollection =>
|
||||||
</TableBody>
|
selectedCollection.id === collection.id
|
||||||
</Table>
|
);
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
return (
|
||||||
<Button onClick={onClose}>
|
<TableRow key={collection.id}>
|
||||||
{i18n.t("Cancel", { context: "button" })}
|
<TableCell
|
||||||
</Button>
|
padding="checkbox"
|
||||||
<ConfirmButton
|
className={classes.checkboxCell}
|
||||||
transitionState={confirmButtonState}
|
>
|
||||||
color="primary"
|
<Checkbox
|
||||||
variant="contained"
|
checked={isSelected}
|
||||||
type="submit"
|
onChange={() =>
|
||||||
>
|
handleCollectionAssign(
|
||||||
{i18n.t("Assign collections", { context: "button" })}
|
collection,
|
||||||
</ConfirmButton>
|
isSelected,
|
||||||
</DialogActions>
|
selectedCollections,
|
||||||
</>
|
setSelectedCollections
|
||||||
)}
|
)
|
||||||
</Form>
|
}
|
||||||
</Dialog>
|
/>
|
||||||
)
|
</TableCell>
|
||||||
|
<TableCell className={classes.wideCell}>
|
||||||
|
{collection.name}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onClose}>
|
||||||
|
{i18n.t("Cancel", { context: "button" })}
|
||||||
|
</Button>
|
||||||
|
<ConfirmButton
|
||||||
|
transitionState={confirmButtonState}
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
type="submit"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
{i18n.t("Assign collections", { context: "button" })}
|
||||||
|
</ConfirmButton>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
AssignCollectionDialog.displayName = "AssignCollectionDialog";
|
AssignCollectionDialog.displayName = "AssignCollectionDialog";
|
||||||
export default AssignCollectionDialog;
|
export default AssignCollectionDialog;
|
||||||
|
|
|
@ -15,12 +15,12 @@ import React from "react";
|
||||||
import ConfirmButton, {
|
import ConfirmButton, {
|
||||||
ConfirmButtonTransitionState
|
ConfirmButtonTransitionState
|
||||||
} from "@saleor/components/ConfirmButton";
|
} from "@saleor/components/ConfirmButton";
|
||||||
import Form from "@saleor/components/Form";
|
|
||||||
import FormSpacer from "@saleor/components/FormSpacer";
|
import FormSpacer from "@saleor/components/FormSpacer";
|
||||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||||
|
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||||
|
import i18n from "@saleor/i18n";
|
||||||
|
import { maybe } from "@saleor/misc";
|
||||||
import { SearchProducts_products_edges_node } from "../../containers/SearchProducts/types/SearchProducts";
|
import { SearchProducts_products_edges_node } from "../../containers/SearchProducts/types/SearchProducts";
|
||||||
import i18n from "../../i18n";
|
|
||||||
import { maybe } from "../../misc";
|
|
||||||
import Checkbox from "../Checkbox";
|
import Checkbox from "../Checkbox";
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
|
@ -40,25 +40,41 @@ const styles = createStyles({
|
||||||
overflow: {
|
overflow: {
|
||||||
overflowY: "visible"
|
overflowY: "visible"
|
||||||
},
|
},
|
||||||
|
scrollArea: {
|
||||||
|
overflowY: "scroll"
|
||||||
|
},
|
||||||
wideCell: {
|
wideCell: {
|
||||||
width: "100%"
|
width: "100%"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface AssignProductDialogProps extends WithStyles<typeof styles> {
|
export interface AssignProductDialogProps {
|
||||||
confirmButtonState: ConfirmButtonTransitionState;
|
confirmButtonState: ConfirmButtonTransitionState;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
products: SearchProducts_products_edges_node[];
|
products: SearchProducts_products_edges_node[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onFetch: (value: string) => void;
|
onFetch: (value: string) => void;
|
||||||
onSubmit: (data: FormData) => void;
|
onSubmit: (data: SearchProducts_products_edges_node[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleProductAssign(
|
||||||
|
product: SearchProducts_products_edges_node,
|
||||||
|
isSelected: boolean,
|
||||||
|
selectedProducts: SearchProducts_products_edges_node[],
|
||||||
|
setSelectedProducts: (data: SearchProducts_products_edges_node[]) => void
|
||||||
|
) {
|
||||||
|
if (isSelected) {
|
||||||
|
setSelectedProducts(
|
||||||
|
selectedProducts.filter(
|
||||||
|
selectedProduct => selectedProduct.id !== product.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setSelectedProducts([...selectedProducts, product]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialForm: FormData = {
|
|
||||||
products: [],
|
|
||||||
query: ""
|
|
||||||
};
|
|
||||||
const AssignProductDialog = withStyles(styles, {
|
const AssignProductDialog = withStyles(styles, {
|
||||||
name: "AssignProductDialog"
|
name: "AssignProductDialog"
|
||||||
})(
|
})(
|
||||||
|
@ -71,106 +87,102 @@ const AssignProductDialog = withStyles(styles, {
|
||||||
onClose,
|
onClose,
|
||||||
onFetch,
|
onFetch,
|
||||||
onSubmit
|
onSubmit
|
||||||
}: AssignProductDialogProps) => (
|
}: AssignProductDialogProps & WithStyles<typeof styles>) => {
|
||||||
<Dialog
|
const [query, onQueryChange] = useSearchQuery(onFetch);
|
||||||
onClose={onClose}
|
const [selectedProducts, setSelectedProducts] = React.useState<
|
||||||
open={open}
|
SearchProducts_products_edges_node[]
|
||||||
classes={{ paper: classes.overflow }}
|
>([]);
|
||||||
fullWidth
|
|
||||||
maxWidth="sm"
|
|
||||||
>
|
|
||||||
<Form initial={initialForm} onSubmit={onSubmit}>
|
|
||||||
{({ data, change }) => (
|
|
||||||
<>
|
|
||||||
<DialogTitle>{i18n.t("Assign Product")}</DialogTitle>
|
|
||||||
<DialogContent className={classes.overflow}>
|
|
||||||
<TextField
|
|
||||||
name="query"
|
|
||||||
value={data.query}
|
|
||||||
onChange={event => change(event, () => onFetch(data.query))}
|
|
||||||
label={i18n.t("Search Products", {
|
|
||||||
context: "product search input label"
|
|
||||||
})}
|
|
||||||
placeholder={i18n.t(
|
|
||||||
"Search by product name, attribute, product type etc...",
|
|
||||||
{
|
|
||||||
context: "product search input placeholder"
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
fullWidth
|
|
||||||
InputProps={{
|
|
||||||
autoComplete: "off",
|
|
||||||
endAdornment: loading && <CircularProgress size={16} />
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FormSpacer />
|
|
||||||
<Table>
|
|
||||||
<TableBody>
|
|
||||||
{products &&
|
|
||||||
products.map(product => {
|
|
||||||
const isChecked = !!data.products.find(
|
|
||||||
selectedProduct => selectedProduct.id === product.id
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
const handleSubmit = () => onSubmit(selectedProducts);
|
||||||
<TableRow key={product.id}>
|
|
||||||
<TableCellAvatar
|
return (
|
||||||
className={classes.avatar}
|
<Dialog
|
||||||
thumbnail={maybe(() => product.thumbnail.url)}
|
onClose={onClose}
|
||||||
|
open={open}
|
||||||
|
classes={{ paper: classes.overflow }}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
|
>
|
||||||
|
<DialogTitle>{i18n.t("Assign Product")}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<TextField
|
||||||
|
name="query"
|
||||||
|
value={query}
|
||||||
|
onChange={onQueryChange}
|
||||||
|
label={i18n.t("Search Products", {
|
||||||
|
context: "product search input label"
|
||||||
|
})}
|
||||||
|
placeholder={i18n.t(
|
||||||
|
"Search by product name, attribute, product type etc...",
|
||||||
|
{
|
||||||
|
context: "product search input placeholder"
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
autoComplete: "off",
|
||||||
|
endAdornment: loading && <CircularProgress size={16} />
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormSpacer />
|
||||||
|
<div className={classes.scrollArea}>
|
||||||
|
<Table>
|
||||||
|
<TableBody>
|
||||||
|
{products &&
|
||||||
|
products.map(product => {
|
||||||
|
const isSelected = selectedProducts.some(
|
||||||
|
selectedProduct => selectedProduct.id === product.id
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={product.id}>
|
||||||
|
<TableCellAvatar
|
||||||
|
className={classes.avatar}
|
||||||
|
thumbnail={maybe(() => product.thumbnail.url)}
|
||||||
|
/>
|
||||||
|
<TableCell className={classes.wideCell}>
|
||||||
|
{product.name}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
padding="checkbox"
|
||||||
|
className={classes.checkboxCell}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={isSelected}
|
||||||
|
onChange={() =>
|
||||||
|
handleProductAssign(
|
||||||
|
product,
|
||||||
|
isSelected,
|
||||||
|
selectedProducts,
|
||||||
|
setSelectedProducts
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<TableCell className={classes.wideCell}>
|
</TableCell>
|
||||||
{product.name}
|
</TableRow>
|
||||||
</TableCell>
|
);
|
||||||
<TableCell
|
})}
|
||||||
padding="checkbox"
|
</TableBody>
|
||||||
className={classes.checkboxCell}
|
</Table>
|
||||||
>
|
</div>
|
||||||
<Checkbox
|
</DialogContent>
|
||||||
checked={isChecked}
|
<DialogActions>
|
||||||
onChange={() =>
|
<Button onClick={onClose}>
|
||||||
isChecked
|
{i18n.t("Cancel", { context: "button" })}
|
||||||
? change({
|
</Button>
|
||||||
target: {
|
<ConfirmButton
|
||||||
name: "products",
|
transitionState={confirmButtonState}
|
||||||
value: data.products.filter(
|
color="primary"
|
||||||
selectedProduct =>
|
variant="contained"
|
||||||
selectedProduct.id !== product.id
|
type="submit"
|
||||||
)
|
onClick={handleSubmit}
|
||||||
}
|
>
|
||||||
} as any)
|
{i18n.t("Assign products", { context: "button" })}
|
||||||
: change({
|
</ConfirmButton>
|
||||||
target: {
|
</DialogActions>
|
||||||
name: "products",
|
</Dialog>
|
||||||
value: [...data.products, product]
|
);
|
||||||
}
|
}
|
||||||
} as any)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={onClose}>
|
|
||||||
{i18n.t("Cancel", { context: "button" })}
|
|
||||||
</Button>
|
|
||||||
<ConfirmButton
|
|
||||||
transitionState={confirmButtonState}
|
|
||||||
color="primary"
|
|
||||||
variant="contained"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
{i18n.t("Assign products", { context: "button" })}
|
|
||||||
</ConfirmButton>
|
|
||||||
</DialogActions>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
AssignProductDialog.displayName = "AssignProductDialog";
|
AssignProductDialog.displayName = "AssignProductDialog";
|
||||||
export default AssignProductDialog;
|
export default AssignProductDialog;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
@ -32,7 +32,7 @@ const styles = (theme: Theme) =>
|
||||||
lineHeight: 1
|
lineHeight: 1
|
||||||
},
|
},
|
||||||
toolbar: {
|
toolbar: {
|
||||||
marginRight: -theme.spacing.unit * 2
|
marginRight: -theme.spacing.unit
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { stopPropagation } from "../../misc";
|
|
||||||
|
|
||||||
export type CheckboxProps = Omit<
|
export type CheckboxProps = Omit<
|
||||||
MuiCheckboxProps,
|
MuiCheckboxProps,
|
||||||
|
@ -19,7 +18,9 @@ export type CheckboxProps = Omit<
|
||||||
| "indeterminateIcon"
|
| "indeterminateIcon"
|
||||||
| "classes"
|
| "classes"
|
||||||
| "onChange"
|
| "onChange"
|
||||||
|
| "onClick"
|
||||||
> & {
|
> & {
|
||||||
|
disableClickPropagation?: boolean;
|
||||||
onChange?: (event: React.ChangeEvent<any>) => void;
|
onChange?: (event: React.ChangeEvent<any>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,7 +46,6 @@ const styles = (theme: Theme) =>
|
||||||
},
|
},
|
||||||
"&:before": {
|
"&:before": {
|
||||||
background: "rgba(0, 0, 0, 0)",
|
background: "rgba(0, 0, 0, 0)",
|
||||||
borderRadius: 2,
|
|
||||||
content: '""',
|
content: '""',
|
||||||
height: 8,
|
height: 8,
|
||||||
left: 2,
|
left: 2,
|
||||||
|
@ -56,7 +56,6 @@ const styles = (theme: Theme) =>
|
||||||
},
|
},
|
||||||
WebkitAppearance: "none",
|
WebkitAppearance: "none",
|
||||||
border: `1px solid ${theme.palette.grey[500]}`,
|
border: `1px solid ${theme.palette.grey[500]}`,
|
||||||
borderRadius: 4,
|
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
height: 14,
|
height: 14,
|
||||||
|
@ -84,21 +83,31 @@ const Checkbox = withStyles(styles, { name: "Checkbox" })(
|
||||||
className,
|
className,
|
||||||
classes,
|
classes,
|
||||||
disabled,
|
disabled,
|
||||||
|
disableClickPropagation,
|
||||||
indeterminate,
|
indeterminate,
|
||||||
onChange,
|
onChange,
|
||||||
onClick,
|
|
||||||
value,
|
value,
|
||||||
name,
|
name,
|
||||||
...props
|
...props
|
||||||
}: CheckboxProps & WithStyles<typeof styles>) => {
|
}: CheckboxProps & WithStyles<typeof styles>) => {
|
||||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||||
|
const handleClick = React.useCallback(
|
||||||
|
disableClickPropagation
|
||||||
|
? event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
inputRef.current.click();
|
||||||
|
}
|
||||||
|
: () => inputRef.current.click(),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
{...props}
|
{...props}
|
||||||
centerRipple
|
centerRipple
|
||||||
className={classNames(classes.root, className)}
|
className={classNames(classes.root, className)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={stopPropagation(() => inputRef.current.click())}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
className={classNames(classes.box, {
|
className={classNames(classes.box, {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
withStyles,
|
withStyles,
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const ControlledCheckbox: React.StatelessComponent<
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={checked}
|
checked={checked}
|
||||||
name={name}
|
name={name}
|
||||||
onClick={() => onChange({ target: { name, value: !checked } })}
|
disableClickPropagation
|
||||||
onChange={() => onChange({ target: { name, value: !checked } })}
|
onChange={() => onChange({ target: { name, value: !checked } })}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
label: {
|
label: {
|
||||||
marginLeft: theme.spacing.unit * 2
|
marginLeft: theme.spacing.unit * 2
|
||||||
|
},
|
||||||
|
labelText: {
|
||||||
|
fontSize: 14
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,7 +18,7 @@ interface ControlledSwitchProps extends WithStyles<typeof styles> {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
label: string | React.ReactNode;
|
label: string | React.ReactNode;
|
||||||
name: string;
|
name: string;
|
||||||
secondLabel?: string;
|
secondLabel?: string | React.ReactNode;
|
||||||
uncheckedLabel?: string | React.ReactNode;
|
uncheckedLabel?: string | React.ReactNode;
|
||||||
onChange?(event: React.ChangeEvent<any>);
|
onChange?(event: React.ChangeEvent<any>);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +49,17 @@ export const ControlledSwitch = withStyles(styles, {
|
||||||
}
|
}
|
||||||
label={
|
label={
|
||||||
<div className={classes.label}>
|
<div className={classes.label}>
|
||||||
{uncheckedLabel ? (checked ? label : uncheckedLabel) : label}
|
{uncheckedLabel ? (
|
||||||
|
checked ? (
|
||||||
|
label
|
||||||
|
) : (
|
||||||
|
uncheckedLabel
|
||||||
|
)
|
||||||
|
) : typeof label === "string" ? (
|
||||||
|
<span className={classes.labelText}>{label}</span>
|
||||||
|
) : (
|
||||||
|
label
|
||||||
|
)}
|
||||||
<div>{secondLabel ? secondLabel : null}</div>
|
<div>{secondLabel ? secondLabel : null}</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import * as renderer from "react-test-renderer";
|
import renderer from "react-test-renderer";
|
||||||
|
|
||||||
import { TimezoneProvider } from "../Timezone";
|
import { TimezoneProvider } from "../Timezone";
|
||||||
import Date from "./Date";
|
import Date from "./Date";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
import * as moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import useDateLocalize from "@saleor/hooks/useDateLocalize";
|
import useDateLocalize from "@saleor/hooks/useDateLocalize";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
import * as moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactMoment from "react-moment";
|
import ReactMoment from "react-moment";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { RawDraftContentState } from "draft-js";
|
import { RawDraftContentState } from "draft-js";
|
||||||
import * as draftToHtml from "draftjs-to-html";
|
import draftToHtml from "draftjs-to-html";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
interface DraftRendererProps {
|
interface DraftRendererProps {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
/* tslint:disable:no-submodule-imports */
|
/* tslint:disable:no-submodule-imports */
|
||||||
import * as Dropzone from "react-dropzone/dist/index";
|
import Dropzone from "react-dropzone/dist/index";
|
||||||
export default Dropzone;
|
export default Dropzone;
|
||||||
|
|
|
@ -9,10 +9,10 @@ import {
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
import TableCell from "@material-ui/core/TableCell";
|
||||||
import TextField, { TextFieldProps } from "@material-ui/core/TextField";
|
import TextField, { TextFieldProps } from "@material-ui/core/TextField";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import Form from "@saleor/components/Form";
|
import useForm from "@saleor/hooks/useForm";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -66,48 +66,39 @@ export const EditableTableCell = withStyles(styles, {
|
||||||
value,
|
value,
|
||||||
onConfirm
|
onConfirm
|
||||||
}: EditableTableCellProps) => {
|
}: EditableTableCellProps) => {
|
||||||
const [opened, setOpenStatus] = React.useState(focused);
|
|
||||||
const enable = () => setOpenStatus(true);
|
|
||||||
const disable = () => setOpenStatus(false);
|
|
||||||
|
|
||||||
const handleConfirm = (data: { value: string }) => {
|
const handleConfirm = (data: { value: string }) => {
|
||||||
disable();
|
disable();
|
||||||
onConfirm(data.value);
|
onConfirm(data.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [opened, setOpenStatus] = React.useState(focused);
|
||||||
|
const { change, data } = useForm({ value }, [], handleConfirm);
|
||||||
|
const enable = () => setOpenStatus(true);
|
||||||
|
const disable = () => setOpenStatus(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableCell className={classNames(classes.container, className)}>
|
<TableCell className={classNames(classes.container, className)}>
|
||||||
{opened && <div className={classes.overlay} onClick={disable} />}
|
{opened && <div className={classes.overlay} onClick={disable} />}
|
||||||
<Form initial={{ value }} onSubmit={handleConfirm} useForm={false}>
|
<Typography variant="caption" onClick={enable} className={classes.text}>
|
||||||
{({ change, data }) => (
|
{value || defaultValue}
|
||||||
<>
|
</Typography>
|
||||||
<Typography
|
{opened && (
|
||||||
variant="caption"
|
<div className={classes.root}>
|
||||||
onClick={enable}
|
<Card className={classes.card}>
|
||||||
className={classes.text}
|
<CardContent>
|
||||||
>
|
<TextField
|
||||||
{value || defaultValue}
|
name="value"
|
||||||
</Typography>
|
autoFocus
|
||||||
{opened && (
|
fullWidth
|
||||||
<div className={classes.root}>
|
onChange={change}
|
||||||
<Card className={classes.card}>
|
value={data.value}
|
||||||
<CardContent>
|
variant="standard"
|
||||||
<TextField
|
{...InputProps}
|
||||||
name="value"
|
/>
|
||||||
autoFocus
|
</CardContent>
|
||||||
fullWidth
|
</Card>
|
||||||
onChange={change}
|
</div>
|
||||||
value={data.value}
|
)}
|
||||||
variant="standard"
|
|
||||||
{...InputProps}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Typography from "@material-ui/core/Typography";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import SVG from "react-inlinesvg";
|
import SVG from "react-inlinesvg";
|
||||||
|
|
||||||
import * as notFoundImage from "@assets/images/what.svg";
|
import notFoundImage from "@assets/images/what.svg";
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
|
|
||||||
export interface ErrorPageProps extends WithStyles<typeof styles> {
|
export interface ErrorPageProps extends WithStyles<typeof styles> {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
withStyles,
|
withStyles,
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
|
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { FilterContent } from ".";
|
import { FilterContent } from ".";
|
||||||
|
|
|
@ -47,7 +47,7 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [menuValue, setMenuValue] = React.useState<string>("");
|
const [menuValue, setMenuValue] = React.useState<string>("");
|
||||||
const [filterValue, setFilterValue] = React.useState<string | string[]>("");
|
const [filterValue, setFilterValue] = React.useState<string | string[]>("");
|
||||||
const classes = useStyles();
|
const classes = useStyles({});
|
||||||
|
|
||||||
const activeMenu = menuValue
|
const activeMenu = menuValue
|
||||||
? getMenuItemByValue(filters, menuValue)
|
? getMenuItemByValue(filters, menuValue)
|
||||||
|
|
|
@ -41,7 +41,7 @@ const FilterElement: React.FC<FilterElementProps> = ({
|
||||||
onChange,
|
onChange,
|
||||||
value
|
value
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles({});
|
||||||
|
|
||||||
if (filter.data.type === FieldType.date) {
|
if (filter.data.type === FieldType.date) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -41,6 +41,7 @@ const FilterBar: React.FC<FilterBarProps> = ({
|
||||||
<FilterTab
|
<FilterTab
|
||||||
onClick={() => onTabChange(tabIndex + 1)}
|
onClick={() => onTabChange(tabIndex + 1)}
|
||||||
label={tab.name}
|
label={tab.name}
|
||||||
|
key={tabIndex}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{isCustom && (
|
{isCustom && (
|
||||||
|
|
|
@ -1,170 +1,42 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { UserError } from "../../types";
|
|
||||||
|
|
||||||
export interface FormProps<T extends {}> {
|
import useForm, { UseFormResult } from "@saleor/hooks/useForm";
|
||||||
children: (props: {
|
import { UserError } from "@saleor/types";
|
||||||
data: T;
|
|
||||||
hasChanged: boolean;
|
export interface FormProps<T> {
|
||||||
errors: { [key: string]: string };
|
children: (props: UseFormResult<T>) => React.ReactNode;
|
||||||
change(event: React.ChangeEvent<any>, cb?: () => void);
|
confirmLeave?: boolean;
|
||||||
reset();
|
|
||||||
submit(event?: React.FormEvent<any>);
|
|
||||||
}) => React.ReactElement<any>;
|
|
||||||
errors?: UserError[];
|
errors?: UserError[];
|
||||||
initial?: T;
|
initial?: T;
|
||||||
confirmLeave?: boolean;
|
|
||||||
useForm?: boolean;
|
|
||||||
resetOnSubmit?: boolean;
|
resetOnSubmit?: boolean;
|
||||||
onSubmit?(data: T);
|
onSubmit?: (data: T) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormComponentProps<T extends {}> extends FormProps<T> {
|
function Form<T>(props: FormProps<T>) {
|
||||||
hasChanged: boolean;
|
const { children, errors, initial, resetOnSubmit, onSubmit } = props;
|
||||||
toggleFormChangeState: () => void;
|
const renderProps = useForm(initial, errors, onSubmit);
|
||||||
}
|
|
||||||
|
|
||||||
interface FormState<T extends {}> {
|
function handleSubmit(event?: React.FormEvent<any>, cb?: () => void) {
|
||||||
initial: T;
|
const { reset, submit } = renderProps;
|
||||||
fields: T;
|
|
||||||
hasChanged: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FormComponent<T extends {} = {}> extends React.Component<
|
|
||||||
FormComponentProps<T>,
|
|
||||||
FormState<T>
|
|
||||||
> {
|
|
||||||
static getDerivedStateFromProps<T extends {} = {}>(
|
|
||||||
nextProps: FormComponentProps<T>,
|
|
||||||
prevState: FormState<T>
|
|
||||||
): FormState<T> {
|
|
||||||
const changedFields = Object.keys(nextProps.initial).filter(
|
|
||||||
nextFieldName =>
|
|
||||||
JSON.stringify(nextProps.initial[nextFieldName]) !==
|
|
||||||
JSON.stringify(prevState.initial[nextFieldName])
|
|
||||||
);
|
|
||||||
if (changedFields.length > 0) {
|
|
||||||
const swapFields = changedFields.reduce((prev, curr) => {
|
|
||||||
prev[curr] = nextProps.initial[curr];
|
|
||||||
return prev;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
return {
|
|
||||||
fields: {
|
|
||||||
...(prevState.fields as any),
|
|
||||||
...swapFields
|
|
||||||
},
|
|
||||||
hasChanged: false,
|
|
||||||
initial: {
|
|
||||||
...(prevState.initial as any),
|
|
||||||
...swapFields
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
state: FormState<T> = {
|
|
||||||
fields: this.props.initial,
|
|
||||||
hasChanged: false,
|
|
||||||
initial: this.props.initial
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
const { hasChanged, confirmLeave, toggleFormChangeState } = this.props;
|
|
||||||
if (this.state.hasChanged !== hasChanged && confirmLeave) {
|
|
||||||
toggleFormChangeState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { hasChanged, confirmLeave, toggleFormChangeState } = this.props;
|
|
||||||
if (this.state.hasChanged !== hasChanged && confirmLeave) {
|
|
||||||
toggleFormChangeState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
const { hasChanged, confirmLeave, toggleFormChangeState } = this.props;
|
|
||||||
if (hasChanged && confirmLeave) {
|
|
||||||
toggleFormChangeState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleChange = (event: React.ChangeEvent<any>, cb?: () => void) => {
|
|
||||||
const { target } = event;
|
|
||||||
if (!(target.name in this.state.fields)) {
|
|
||||||
console.error(`Unknown form field: ${target.name}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
fields: {
|
|
||||||
...(this.state.fields as any),
|
|
||||||
[target.name]: target.value
|
|
||||||
},
|
|
||||||
hasChanged: true
|
|
||||||
},
|
|
||||||
typeof cb === "function" ? cb : undefined
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleKeyDown = (event: React.KeyboardEvent<any>) => {
|
|
||||||
switch (event.keyCode) {
|
|
||||||
// Enter
|
|
||||||
case 13:
|
|
||||||
this.props.onSubmit(this.state.fields);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSubmit = (event?: React.FormEvent<any>, cb?: () => void) => {
|
|
||||||
const { resetOnSubmit, onSubmit } = this.props;
|
|
||||||
if (event) {
|
if (event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
if (onSubmit !== undefined) {
|
|
||||||
onSubmit(this.state.fields);
|
|
||||||
}
|
|
||||||
if (cb) {
|
if (cb) {
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resetOnSubmit) {
|
if (resetOnSubmit) {
|
||||||
this.setState({
|
reset();
|
||||||
fields: this.state.initial
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
submit();
|
||||||
const { children, errors, useForm = true } = this.props;
|
|
||||||
|
|
||||||
const contents = children({
|
|
||||||
change: this.handleChange,
|
|
||||||
data: this.state.fields,
|
|
||||||
errors: errors
|
|
||||||
? errors.reduce(
|
|
||||||
(prev, curr) => ({
|
|
||||||
...prev,
|
|
||||||
[curr.field.split(":")[0]]: curr.message
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
: {},
|
|
||||||
hasChanged: this.state.hasChanged,
|
|
||||||
reset: () =>
|
|
||||||
this.setState({
|
|
||||||
fields: this.state.initial
|
|
||||||
}),
|
|
||||||
submit: this.handleSubmit
|
|
||||||
});
|
|
||||||
|
|
||||||
return useForm ? (
|
|
||||||
<form onSubmit={this.handleSubmit}>{contents}</form>
|
|
||||||
) : (
|
|
||||||
<div onKeyDown={this.handleKeyDown}>{contents}</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return <form onSubmit={handleSubmit}>{children(renderProps)}</form>;
|
||||||
}
|
}
|
||||||
export default FormComponent;
|
Form.displayName = "Form";
|
||||||
|
|
||||||
|
export default Form;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
export * from "./Form";
|
export * from "./Form";
|
||||||
|
export { default } from "./Form";
|
||||||
export { default as FormActions } from "./FormActions";
|
export { default as FormActions } from "./FormActions";
|
||||||
export * from "./FormActions";
|
export * from "./FormActions";
|
||||||
export * from "./FormContext";
|
|
||||||
export { default } from "./FormContext";
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import Typography, { TypographyProps } from "@material-ui/core/Typography";
|
import Typography, { TypographyProps } from "@material-ui/core/Typography";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import {
|
import {
|
||||||
|
@ -8,27 +9,77 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import CloseIcon from "@material-ui/icons/Close";
|
||||||
import Downshift, { ControllerStateAndHelpers } from "downshift";
|
import Downshift, { ControllerStateAndHelpers } from "downshift";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { compareTwoStrings } from "string-similarity";
|
||||||
|
|
||||||
import i18n from "../../i18n";
|
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||||
import ArrowDropdownIcon from "../../icons/ArrowDropdown";
|
import Checkbox from "@saleor/components/Checkbox";
|
||||||
import Debounce, { DebounceProps } from "../Debounce";
|
import Debounce, { DebounceProps } from "@saleor/components/Debounce";
|
||||||
|
import i18n from "@saleor/i18n";
|
||||||
|
import ArrowDropdownIcon from "@saleor/icons/ArrowDropdown";
|
||||||
|
import Hr from "../Hr";
|
||||||
|
|
||||||
interface ChoiceType {
|
export interface MultiAutocompleteChoiceType {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
checkbox: {
|
||||||
|
height: 24,
|
||||||
|
width: 20
|
||||||
|
},
|
||||||
chip: {
|
chip: {
|
||||||
margin: `${theme.spacing.unit / 2}px ${theme.spacing.unit / 2}px`
|
width: "100%"
|
||||||
|
},
|
||||||
|
chipClose: {
|
||||||
|
height: 32,
|
||||||
|
padding: 0,
|
||||||
|
width: 32
|
||||||
|
},
|
||||||
|
chipContainer: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
marginTop: theme.spacing.unit
|
||||||
|
},
|
||||||
|
chipInner: {
|
||||||
|
"& svg": {
|
||||||
|
color: theme.palette.primary.contrastText
|
||||||
|
},
|
||||||
|
alignItems: "center",
|
||||||
|
background: fade(theme.palette.primary.main, 0.6),
|
||||||
|
borderRadius: 24,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
margin: `${theme.spacing.unit}px 0`,
|
||||||
|
paddingLeft: theme.spacing.unit * 2,
|
||||||
|
paddingRight: theme.spacing.unit
|
||||||
|
},
|
||||||
|
chipLabel: {
|
||||||
|
color: theme.palette.primary.contrastText
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
position: "relative"
|
position: "relative"
|
||||||
},
|
},
|
||||||
|
hr: {
|
||||||
|
margin: `${theme.spacing.unit}px 0`
|
||||||
|
},
|
||||||
|
menuItem: {
|
||||||
|
display: "grid",
|
||||||
|
gridColumnGap: theme.spacing.unit + "px",
|
||||||
|
gridTemplateColumns: "20px 1fr",
|
||||||
|
height: "auto",
|
||||||
|
whiteSpace: "normal"
|
||||||
|
},
|
||||||
|
menuItemLabel: {
|
||||||
|
overflowWrap: "break-word"
|
||||||
|
},
|
||||||
paper: {
|
paper: {
|
||||||
left: 0,
|
left: 0,
|
||||||
marginTop: theme.spacing.unit,
|
marginTop: theme.spacing.unit,
|
||||||
|
@ -39,39 +90,32 @@ const styles = (theme: Theme) =>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface MultiAutocompleteSelectFieldChildrenFunc {
|
export interface MultiAutocompleteSelectFieldProps {
|
||||||
deleteItem: (item: ChoiceType) => void;
|
allowCustomValues?: boolean;
|
||||||
items: ChoiceType[];
|
displayValues: MultiAutocompleteChoiceType[];
|
||||||
}
|
|
||||||
export type MultiAutocompleteSelectFieldChildren = (
|
|
||||||
props: MultiAutocompleteSelectFieldChildrenFunc
|
|
||||||
) => React.ReactNode;
|
|
||||||
|
|
||||||
export interface MultiAutocompleteSelectFieldProps
|
|
||||||
extends WithStyles<typeof styles> {
|
|
||||||
name: string;
|
name: string;
|
||||||
children: MultiAutocompleteSelectFieldChildren;
|
choices: MultiAutocompleteChoiceType[];
|
||||||
choices: ChoiceType[];
|
value: string[];
|
||||||
value?: ChoiceType[];
|
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
helperText?: string;
|
helperText?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
fetchChoices(value: string);
|
fetchChoices?: (value: string) => void;
|
||||||
onChange(event);
|
onChange: (event: React.ChangeEvent<any>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DebounceAutocomplete: React.ComponentType<
|
const DebounceAutocomplete: React.ComponentType<
|
||||||
DebounceProps<string>
|
DebounceProps<string>
|
||||||
> = Debounce;
|
> = Debounce;
|
||||||
|
|
||||||
export const MultiAutocompleteSelectField = withStyles(styles, {
|
export const MultiAutocompleteSelectFieldComponent = withStyles(styles, {
|
||||||
name: "MultiAutocompleteSelectField"
|
name: "MultiAutocompleteSelectField"
|
||||||
})(
|
})(
|
||||||
({
|
({
|
||||||
children,
|
allowCustomValues,
|
||||||
choices,
|
choices,
|
||||||
classes,
|
classes,
|
||||||
|
displayValues,
|
||||||
helperText,
|
helperText,
|
||||||
label,
|
label,
|
||||||
loading,
|
loading,
|
||||||
|
@ -80,102 +124,200 @@ export const MultiAutocompleteSelectField = withStyles(styles, {
|
||||||
value,
|
value,
|
||||||
fetchChoices,
|
fetchChoices,
|
||||||
onChange
|
onChange
|
||||||
}: MultiAutocompleteSelectFieldProps) => {
|
}: MultiAutocompleteSelectFieldProps & WithStyles<typeof styles>) => {
|
||||||
const handleSelect = (
|
const handleSelect = (
|
||||||
item: ChoiceType,
|
item: string,
|
||||||
{ reset }: ControllerStateAndHelpers
|
downshiftOpts?: ControllerStateAndHelpers
|
||||||
) => {
|
) => {
|
||||||
reset({ inputValue: "" });
|
if (downshiftOpts) {
|
||||||
onChange({ target: { name, value: [...value, item] } });
|
downshiftOpts.reset({ inputValue: "" });
|
||||||
|
}
|
||||||
|
onChange({
|
||||||
|
target: { name, value: item }
|
||||||
|
} as any);
|
||||||
};
|
};
|
||||||
const handleDelete = (item: ChoiceType) => {
|
const suggestions = choices.filter(choice => !value.includes(choice.value));
|
||||||
const newValue = value.slice();
|
|
||||||
newValue.splice(
|
|
||||||
value.findIndex(listItem => listItem.value === item.value),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
onChange({ target: { name, value: newValue } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredChoices = choices.filter(
|
|
||||||
suggestion => value.map(v => v.value).indexOf(suggestion.value) === -1
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DebounceAutocomplete debounceFn={fetchChoices}>
|
<>
|
||||||
{debounce => (
|
<Downshift
|
||||||
<Downshift
|
onInputValueChange={fetchChoices}
|
||||||
selectedItem={value}
|
onSelect={handleSelect}
|
||||||
itemToString={item => (item ? item.label : "")}
|
itemToString={() => ""}
|
||||||
onSelect={handleSelect}
|
>
|
||||||
onInputValueChange={value => debounce(value)}
|
{({
|
||||||
>
|
getInputProps,
|
||||||
{({
|
getItemProps,
|
||||||
getInputProps,
|
isOpen,
|
||||||
getItemProps,
|
toggleMenu,
|
||||||
isOpen,
|
highlightedIndex,
|
||||||
selectedItem,
|
inputValue
|
||||||
toggleMenu,
|
}) => (
|
||||||
closeMenu,
|
<div className={classes.container}>
|
||||||
openMenu,
|
<TextField
|
||||||
highlightedIndex
|
InputProps={{
|
||||||
}) => {
|
...getInputProps({
|
||||||
return (
|
placeholder
|
||||||
<div className={classes.container}>
|
}),
|
||||||
<TextField
|
endAdornment: (
|
||||||
InputProps={{
|
<div>
|
||||||
...getInputProps({
|
{loading ? (
|
||||||
placeholder
|
<CircularProgress size={20} />
|
||||||
}),
|
) : (
|
||||||
endAdornment: (
|
<ArrowDropdownIcon onClick={toggleMenu} />
|
||||||
<div>
|
)}
|
||||||
{loading ? (
|
</div>
|
||||||
<CircularProgress size={20} />
|
),
|
||||||
) : (
|
id: undefined,
|
||||||
<ArrowDropdownIcon onClick={toggleMenu} />
|
onClick: toggleMenu
|
||||||
)}
|
}}
|
||||||
</div>
|
helperText={helperText}
|
||||||
),
|
label={label}
|
||||||
id: undefined,
|
fullWidth={true}
|
||||||
onBlur: closeMenu,
|
/>
|
||||||
onFocus: openMenu
|
{isOpen && (!!inputValue || !!choices.length) && (
|
||||||
}}
|
<Paper className={classes.paper} square>
|
||||||
helperText={helperText}
|
{choices.length > 0 ||
|
||||||
label={label}
|
displayValues.length > 0 ||
|
||||||
fullWidth={true}
|
allowCustomValues ? (
|
||||||
/>
|
<>
|
||||||
{isOpen && (
|
{displayValues.map(value => (
|
||||||
<Paper className={classes.paper} square>
|
<MenuItem
|
||||||
{!loading && filteredChoices.length > 0
|
className={classes.menuItem}
|
||||||
? filteredChoices.map((suggestion, index) => (
|
key={value.value}
|
||||||
<MenuItem
|
selected={true}
|
||||||
key={suggestion.value}
|
component="div"
|
||||||
selected={highlightedIndex === index}
|
{...getItemProps({
|
||||||
component="div"
|
item: value.value
|
||||||
{...getItemProps({ item: suggestion })}
|
})}
|
||||||
>
|
>
|
||||||
{suggestion.label}
|
<Checkbox
|
||||||
</MenuItem>
|
className={classes.checkbox}
|
||||||
))
|
checked={true}
|
||||||
: !loading && (
|
disableRipple
|
||||||
<MenuItem disabled={true} component="div">
|
/>
|
||||||
{i18n.t("No results found")}
|
<span className={classes.menuItemLabel}>
|
||||||
</MenuItem>
|
{value.label}
|
||||||
)}
|
</span>
|
||||||
</Paper>
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
{displayValues.length > 0 && suggestions.length > 0 && (
|
||||||
|
<Hr className={classes.hr} />
|
||||||
|
)}
|
||||||
|
{suggestions.map((suggestion, index) => (
|
||||||
|
<MenuItem
|
||||||
|
className={classes.menuItem}
|
||||||
|
key={suggestion.value}
|
||||||
|
selected={highlightedIndex === index + value.length}
|
||||||
|
component="div"
|
||||||
|
{...getItemProps({
|
||||||
|
item: suggestion.value
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={value.includes(suggestion.value)}
|
||||||
|
className={classes.checkbox}
|
||||||
|
disableRipple
|
||||||
|
/>
|
||||||
|
<span className={classes.menuItemLabel}>
|
||||||
|
{suggestion.label}
|
||||||
|
</span>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
{allowCustomValues &&
|
||||||
|
inputValue &&
|
||||||
|
!choices.find(
|
||||||
|
choice =>
|
||||||
|
choice.label.toLowerCase() ===
|
||||||
|
inputValue.toLowerCase()
|
||||||
|
) && (
|
||||||
|
<MenuItem
|
||||||
|
className={classes.menuItem}
|
||||||
|
key={"customValue"}
|
||||||
|
component="div"
|
||||||
|
{...getItemProps({
|
||||||
|
item: inputValue
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className={classes.menuItemLabel}>
|
||||||
|
{i18n.t("Add new value: {{ value }}", {
|
||||||
|
context: "add custom option",
|
||||||
|
value: inputValue
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
!loading && (
|
||||||
|
<MenuItem disabled={true} component="div">
|
||||||
|
{i18n.t("No results found")}
|
||||||
|
</MenuItem>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
{children({
|
</Paper>
|
||||||
deleteItem: handleDelete,
|
)}
|
||||||
items: selectedItem
|
</div>
|
||||||
})}
|
)}
|
||||||
</div>
|
</Downshift>
|
||||||
);
|
<div className={classes.chipContainer}>
|
||||||
}}
|
{displayValues.map(value => (
|
||||||
</Downshift>
|
<div className={classes.chip} key={value.value}>
|
||||||
|
<div className={classes.chipInner}>
|
||||||
|
<Typography className={classes.chipLabel} variant="caption">
|
||||||
|
{value.label}
|
||||||
|
</Typography>
|
||||||
|
<IconButton
|
||||||
|
className={classes.chipClose}
|
||||||
|
onClick={() => handleSelect(value.value)}
|
||||||
|
>
|
||||||
|
<CloseIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const MultiAutocompleteSelectField: React.FC<
|
||||||
|
MultiAutocompleteSelectFieldProps
|
||||||
|
> = ({ choices, fetchChoices, ...props }) => {
|
||||||
|
const [query, setQuery] = React.useState("");
|
||||||
|
if (fetchChoices) {
|
||||||
|
return (
|
||||||
|
<DebounceAutocomplete debounceFn={fetchChoices}>
|
||||||
|
{debounceFn => (
|
||||||
|
<MultiAutocompleteSelectFieldComponent
|
||||||
|
choices={choices}
|
||||||
|
{...props}
|
||||||
|
fetchChoices={debounceFn}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</DebounceAutocomplete>
|
</DebounceAutocomplete>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
const sortedChoices = choices.sort((a, b) => {
|
||||||
|
const ratingA = compareTwoStrings(query, a.label);
|
||||||
|
const ratingB = compareTwoStrings(query, b.label);
|
||||||
|
if (ratingA > ratingB) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ratingA < ratingB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MultiAutocompleteSelectFieldComponent
|
||||||
|
fetchChoices={q => setQuery(q || "")}
|
||||||
|
choices={sortedChoices}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
MultiAutocompleteSelectField.displayName = "MultiAutocompleteSelectField";
|
MultiAutocompleteSelectField.displayName = "MultiAutocompleteSelectField";
|
||||||
export default MultiAutocompleteSelectField;
|
export default MultiAutocompleteSelectField;
|
||||||
|
|
|
@ -9,8 +9,8 @@ import Typography from "@material-ui/core/Typography";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import SVG from "react-inlinesvg";
|
import SVG from "react-inlinesvg";
|
||||||
|
|
||||||
import * as notFoundImage from "@assets/images/not-found-404.svg";
|
import notFoundImage from "@assets/images/not-found-404.svg";
|
||||||
import i18n from "../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
|
|
@ -12,6 +12,9 @@ import Skeleton from "../Skeleton";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
root: {
|
||||||
|
display: "flex"
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
|
@ -35,7 +38,7 @@ const PageHeader = withStyles(styles)(
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{children}
|
<div className={classes.root}>{children}</div>
|
||||||
</ExtendedPageHeader>
|
</ExtendedPageHeader>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,11 +11,15 @@ import TableFooter from "@material-ui/core/TableFooter";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
import TableCellAvatar, {
|
||||||
|
AVATAR_MARGIN
|
||||||
|
} from "@saleor/components/TableCellAvatar";
|
||||||
|
import { ProductListColumns } from "@saleor/config";
|
||||||
|
import i18n from "@saleor/i18n";
|
||||||
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
|
import { ListActions, ListProps } from "@saleor/types";
|
||||||
|
import { isSelected } from "@saleor/utils/lists";
|
||||||
import { CategoryDetails_category_products_edges_node } from "../../categories/types/CategoryDetails";
|
import { CategoryDetails_category_products_edges_node } from "../../categories/types/CategoryDetails";
|
||||||
import i18n from "../../i18n";
|
|
||||||
import { maybe, renderCollection } from "../../misc";
|
|
||||||
import { ListActions, ListProps } from "../../types";
|
|
||||||
import Checkbox from "../Checkbox";
|
import Checkbox from "../Checkbox";
|
||||||
import Money from "../Money";
|
import Money from "../Money";
|
||||||
import Skeleton from "../Skeleton";
|
import Skeleton from "../Skeleton";
|
||||||
|
@ -27,7 +31,7 @@ const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
[theme.breakpoints.up("lg")]: {
|
[theme.breakpoints.up("lg")]: {
|
||||||
colName: {
|
colName: {
|
||||||
width: 430
|
width: "auto"
|
||||||
},
|
},
|
||||||
colPrice: {
|
colPrice: {
|
||||||
width: 200
|
width: 200
|
||||||
|
@ -39,12 +43,14 @@ const styles = (theme: Theme) =>
|
||||||
width: 200
|
width: 200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
avatarCell: {
|
colFill: {
|
||||||
paddingLeft: theme.spacing.unit * 2,
|
padding: 0,
|
||||||
paddingRight: 0,
|
width: "100%"
|
||||||
width: theme.spacing.unit * 5
|
|
||||||
},
|
},
|
||||||
colName: {},
|
colName: {},
|
||||||
|
colNameHeader: {
|
||||||
|
marginLeft: AVATAR_MARGIN
|
||||||
|
},
|
||||||
colPrice: {
|
colPrice: {
|
||||||
textAlign: "right"
|
textAlign: "right"
|
||||||
},
|
},
|
||||||
|
@ -53,6 +59,12 @@ const styles = (theme: Theme) =>
|
||||||
link: {
|
link: {
|
||||||
cursor: "pointer"
|
cursor: "pointer"
|
||||||
},
|
},
|
||||||
|
table: {
|
||||||
|
tableLayout: "fixed"
|
||||||
|
},
|
||||||
|
tableContainer: {
|
||||||
|
overflowX: "scroll"
|
||||||
|
},
|
||||||
textLeft: {
|
textLeft: {
|
||||||
textAlign: "left"
|
textAlign: "left"
|
||||||
},
|
},
|
||||||
|
@ -62,7 +74,7 @@ const styles = (theme: Theme) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ProductListProps
|
interface ProductListProps
|
||||||
extends ListProps,
|
extends ListProps<ProductListColumns>,
|
||||||
ListActions,
|
ListActions,
|
||||||
WithStyles<typeof styles> {
|
WithStyles<typeof styles> {
|
||||||
products: CategoryDetails_category_products_edges_node[];
|
products: CategoryDetails_category_products_edges_node[];
|
||||||
|
@ -71,6 +83,7 @@ interface ProductListProps
|
||||||
export const ProductList = withStyles(styles, { name: "ProductList" })(
|
export const ProductList = withStyles(styles, { name: "ProductList" })(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
|
settings,
|
||||||
disabled,
|
disabled,
|
||||||
isChecked,
|
isChecked,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
|
@ -81,114 +94,158 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
|
||||||
toolbar,
|
toolbar,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
onRowClick
|
onRowClick
|
||||||
}: ProductListProps) => (
|
}: ProductListProps) => {
|
||||||
<Table>
|
const displayColumn = React.useCallback(
|
||||||
<TableHead
|
(column: ProductListColumns) =>
|
||||||
selected={selected}
|
isSelected(column, settings.columns, (a, b) => a === b),
|
||||||
disabled={disabled}
|
[settings.columns]
|
||||||
items={products}
|
);
|
||||||
toggleAll={toggleAll}
|
const numberOfColumns = 2 + settings.columns.length;
|
||||||
toolbar={toolbar}
|
|
||||||
>
|
|
||||||
<TableCell />
|
|
||||||
<TableCell className={classes.colName}>
|
|
||||||
{i18n.t("Name", { context: "object" })}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className={classes.colType}>
|
|
||||||
{i18n.t("Type", { context: "object" })}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className={classes.colPublished}>
|
|
||||||
{i18n.t("Published", { context: "object" })}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className={classes.colPrice}>
|
|
||||||
{i18n.t("Price", { context: "object" })}
|
|
||||||
</TableCell>
|
|
||||||
</TableHead>
|
|
||||||
<TableFooter>
|
|
||||||
<TableRow>
|
|
||||||
<TablePagination
|
|
||||||
colSpan={6}
|
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
|
||||||
onNextPage={onNextPage}
|
|
||||||
hasPreviousPage={
|
|
||||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
|
||||||
}
|
|
||||||
onPreviousPage={onPreviousPage}
|
|
||||||
/>
|
|
||||||
</TableRow>
|
|
||||||
</TableFooter>
|
|
||||||
<TableBody>
|
|
||||||
{renderCollection(
|
|
||||||
products,
|
|
||||||
product => {
|
|
||||||
const isSelected = product ? isChecked(product.id) : false;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<div className={classes.tableContainer}>
|
||||||
selected={isSelected}
|
<Table className={classes.table}>
|
||||||
hover={!!product}
|
<col />
|
||||||
key={product ? product.id : "skeleton"}
|
<col className={classes.colName} />
|
||||||
onClick={product && onRowClick(product.id)}
|
{displayColumn("productType") && <col className={classes.colType} />}
|
||||||
className={classes.link}
|
{displayColumn("isPublished") && (
|
||||||
>
|
<col className={classes.colPublished} />
|
||||||
<TableCell padding="checkbox">
|
)}
|
||||||
<Checkbox
|
{displayColumn("price") && <col className={classes.colPrice} />}
|
||||||
checked={isSelected}
|
<TableHead
|
||||||
disabled={disabled}
|
colSpan={numberOfColumns}
|
||||||
onChange={() => toggle(product.id)}
|
selected={selected}
|
||||||
/>
|
disabled={disabled}
|
||||||
</TableCell>
|
items={products}
|
||||||
<TableCellAvatar
|
toggleAll={toggleAll}
|
||||||
thumbnail={maybe(() => product.thumbnail.url)}
|
toolbar={toolbar}
|
||||||
/>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCell className={classes.colName}>
|
||||||
{product ? product.name : <Skeleton />}
|
<span className={classes.colNameHeader}>
|
||||||
</TableCell>
|
{i18n.t("Name", { context: "object" })}
|
||||||
<TableCell className={classes.colType}>
|
</span>
|
||||||
{product && product.productType ? (
|
</TableCell>
|
||||||
product.productType.name
|
{displayColumn("productType") && (
|
||||||
) : (
|
<TableCell className={classes.colType}>
|
||||||
<Skeleton />
|
{i18n.t("Type", { context: "object" })}
|
||||||
)}
|
</TableCell>
|
||||||
</TableCell>
|
)}
|
||||||
<TableCell className={classes.colPublished}>
|
{displayColumn("isPublished") && (
|
||||||
{product && maybe(() => product.isAvailable !== undefined) ? (
|
<TableCell className={classes.colPublished}>
|
||||||
<StatusLabel
|
{i18n.t("Published", { context: "object" })}
|
||||||
label={
|
</TableCell>
|
||||||
product.isAvailable
|
)}
|
||||||
? i18n.t("Published", { context: "product status" })
|
{displayColumn("price") && (
|
||||||
: i18n.t("Not published", {
|
<TableCell className={classes.colPrice}>
|
||||||
context: "product status"
|
{i18n.t("Price", { context: "object" })}
|
||||||
})
|
</TableCell>
|
||||||
}
|
)}
|
||||||
status={product.isAvailable ? "success" : "error"}
|
</TableHead>
|
||||||
/>
|
<TableFooter>
|
||||||
) : (
|
|
||||||
<Skeleton />
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className={classes.colPrice}>
|
|
||||||
{maybe(() => product.basePrice) &&
|
|
||||||
maybe(() => product.basePrice.amount) !== undefined &&
|
|
||||||
maybe(() => product.basePrice.currency) !== undefined ? (
|
|
||||||
<Money money={product.basePrice} />
|
|
||||||
) : (
|
|
||||||
<Skeleton />
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
() => (
|
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={6}>{i18n.t("No products found")}</TableCell>
|
<TablePagination
|
||||||
|
colSpan={numberOfColumns}
|
||||||
|
settings={settings}
|
||||||
|
hasNextPage={
|
||||||
|
pageInfo && !disabled ? pageInfo.hasNextPage : false
|
||||||
|
}
|
||||||
|
onNextPage={onNextPage}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
|
hasPreviousPage={
|
||||||
|
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||||
|
}
|
||||||
|
onPreviousPage={onPreviousPage}
|
||||||
|
/>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
</TableFooter>
|
||||||
)}
|
<TableBody>
|
||||||
</TableBody>
|
{renderCollection(
|
||||||
</Table>
|
products,
|
||||||
)
|
product => {
|
||||||
|
const isSelected = product ? isChecked(product.id) : false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
selected={isSelected}
|
||||||
|
hover={!!product}
|
||||||
|
key={product ? product.id : "skeleton"}
|
||||||
|
onClick={product && onRowClick(product.id)}
|
||||||
|
className={classes.link}
|
||||||
|
>
|
||||||
|
<TableCell padding="checkbox">
|
||||||
|
<Checkbox
|
||||||
|
checked={isSelected}
|
||||||
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
|
onChange={() => toggle(product.id)}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCellAvatar
|
||||||
|
className={classes.colName}
|
||||||
|
thumbnail={maybe(() => product.thumbnail.url)}
|
||||||
|
>
|
||||||
|
{product ? product.name : <Skeleton />}
|
||||||
|
</TableCellAvatar>
|
||||||
|
{displayColumn("productType") && (
|
||||||
|
<TableCell className={classes.colType}>
|
||||||
|
{product && product.productType ? (
|
||||||
|
product.productType.name
|
||||||
|
) : (
|
||||||
|
<Skeleton />
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
|
{displayColumn("isPublished") && (
|
||||||
|
<TableCell className={classes.colPublished}>
|
||||||
|
{product &&
|
||||||
|
maybe(() => product.isAvailable !== undefined) ? (
|
||||||
|
<StatusLabel
|
||||||
|
label={
|
||||||
|
product.isAvailable
|
||||||
|
? i18n.t("Published", {
|
||||||
|
context: "product status"
|
||||||
|
})
|
||||||
|
: i18n.t("Not published", {
|
||||||
|
context: "product status"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
status={product.isAvailable ? "success" : "error"}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Skeleton />
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
|
{displayColumn("price") && (
|
||||||
|
<TableCell className={classes.colPrice}>
|
||||||
|
{maybe(() => product.basePrice) &&
|
||||||
|
maybe(() => product.basePrice.amount) !== undefined &&
|
||||||
|
maybe(() => product.basePrice.currency) !==
|
||||||
|
undefined ? (
|
||||||
|
<Money money={product.basePrice} />
|
||||||
|
) : (
|
||||||
|
<Skeleton />
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
() => (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={numberOfColumns}>
|
||||||
|
{i18n.t("No products found")}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductList.displayName = "ProductList";
|
ProductList.displayName = "ProductList";
|
||||||
export default ProductList;
|
export default ProductList;
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { RawDraftContentState } from "draft-js";
|
import { RawDraftContentState } from "draft-js";
|
||||||
import {
|
import {
|
||||||
BLOCK_TYPE,
|
BLOCK_TYPE,
|
||||||
|
@ -14,6 +14,7 @@ import {
|
||||||
ENTITY_TYPE,
|
ENTITY_TYPE,
|
||||||
INLINE_STYLE
|
INLINE_STYLE
|
||||||
} from "draftail";
|
} from "draftail";
|
||||||
|
import isEqual from "lodash-es/isEqual";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import BoldIcon from "../../icons/BoldIcon";
|
import BoldIcon from "../../icons/BoldIcon";
|
||||||
|
@ -29,6 +30,7 @@ import UnorderedListIcon from "../../icons/UnorderedListIcon";
|
||||||
|
|
||||||
// import ImageEntity from "./ImageEntity";
|
// import ImageEntity from "./ImageEntity";
|
||||||
// import ImageSource from "./ImageSource";
|
// import ImageSource from "./ImageSource";
|
||||||
|
import { ChangeEvent } from "@saleor/hooks/useForm";
|
||||||
import LinkEntity from "./LinkEntity";
|
import LinkEntity from "./LinkEntity";
|
||||||
import LinkSource from "./LinkSource";
|
import LinkSource from "./LinkSource";
|
||||||
|
|
||||||
|
@ -45,22 +47,6 @@ export interface RichTextEditorProps {
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
"@keyframes focus": {
|
|
||||||
from: {
|
|
||||||
transform: "scaleX(0) scaleY(1)"
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
transform: "scaleX(1) scaleY(1)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@keyframes hover": {
|
|
||||||
from: {
|
|
||||||
transform: "scaleX(1) scaleY(0)"
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
transform: "scaleX(1) scaleY(1)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: {
|
error: {
|
||||||
color: theme.palette.error.main
|
color: theme.palette.error.main
|
||||||
},
|
},
|
||||||
|
@ -68,21 +54,13 @@ const styles = (theme: Theme) =>
|
||||||
marginTop: theme.spacing.unit * 0.75
|
marginTop: theme.spacing.unit * 0.75
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
"&:hover": {
|
position: "relative"
|
||||||
borderBottomColor: theme.palette.primary.main
|
|
||||||
},
|
|
||||||
backgroundColor: theme.overrides.MuiFilledInput.root.backgroundColor,
|
|
||||||
borderBottom: `1px rgba(0, 0, 0, 0) solid`,
|
|
||||||
borderTopLeftRadius: 4,
|
|
||||||
borderTopRightRadius: 4,
|
|
||||||
padding: "27px 12px 10px",
|
|
||||||
position: "relative",
|
|
||||||
transition: theme.transitions.duration.shortest + "ms"
|
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
fontSize: theme.typography.caption.fontSize,
|
fontSize: theme.typography.caption.fontSize,
|
||||||
marginBottom: theme.spacing.unit * 2,
|
left: 12,
|
||||||
marginTop: -21
|
position: "absolute",
|
||||||
|
top: 9
|
||||||
},
|
},
|
||||||
linkIcon: {
|
linkIcon: {
|
||||||
marginTop: 2
|
marginTop: 2
|
||||||
|
@ -97,8 +75,6 @@ const styles = (theme: Theme) =>
|
||||||
color: theme.palette.primary.light
|
color: theme.palette.primary.light
|
||||||
},
|
},
|
||||||
"&:after": {
|
"&:after": {
|
||||||
animationDuration: theme.transitions.duration.shortest + "ms",
|
|
||||||
animationFillMode: "both",
|
|
||||||
background: theme.palette.getContrastText(
|
background: theme.palette.getContrastText(
|
||||||
theme.palette.background.default
|
theme.palette.background.default
|
||||||
),
|
),
|
||||||
|
@ -120,16 +96,19 @@ const styles = (theme: Theme) =>
|
||||||
"& .Draftail": {
|
"& .Draftail": {
|
||||||
"&-Editor": {
|
"&-Editor": {
|
||||||
"&--focus": {
|
"&--focus": {
|
||||||
"& .DraftEditor": {
|
boxShadow: `inset 0px 0px 0px 2px ${theme.palette.primary.main}`
|
||||||
"&-editorContainer": {
|
},
|
||||||
"&:after": {
|
"&:hover": {
|
||||||
animationName: "focus !important",
|
borderColor: theme.palette.primary.main
|
||||||
background: theme.palette.primary.main,
|
},
|
||||||
transform: "scaleX(0) scaleY(1)"
|
border: `1px ${
|
||||||
}
|
theme.overrides.MuiOutlinedInput.root.borderColor
|
||||||
}
|
} solid`,
|
||||||
}
|
borderTopLeftRadius: 4,
|
||||||
}
|
borderTopRightRadius: 4,
|
||||||
|
padding: "27px 12px 10px",
|
||||||
|
position: "relative",
|
||||||
|
transition: theme.transitions.duration.shortest + "ms"
|
||||||
},
|
},
|
||||||
"&-Toolbar": {
|
"&-Toolbar": {
|
||||||
"&Button": {
|
"&Button": {
|
||||||
|
@ -190,6 +169,7 @@ const styles = (theme: Theme) =>
|
||||||
display: "inline-flex",
|
display: "inline-flex",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
marginBottom: theme.spacing.unit,
|
marginBottom: theme.spacing.unit,
|
||||||
|
marginTop: 10,
|
||||||
[theme.breakpoints.down(460)]: {
|
[theme.breakpoints.down(460)]: {
|
||||||
width: "min-content"
|
width: "min-content"
|
||||||
}
|
}
|
||||||
|
@ -205,24 +185,7 @@ const styles = (theme: Theme) =>
|
||||||
"&$error": {
|
"&$error": {
|
||||||
"& .Draftail": {
|
"& .Draftail": {
|
||||||
"&-Editor": {
|
"&-Editor": {
|
||||||
"& .DraftEditor": {
|
borderColor: theme.palette.error.main
|
||||||
"&-editorContainer": {
|
|
||||||
"&:after": {
|
|
||||||
animationName: "none",
|
|
||||||
background: theme.palette.error.main,
|
|
||||||
transform: "scaleX(1) scaleY(1)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"&--focus": {
|
|
||||||
"& .DraftEditor": {
|
|
||||||
"&-editorContainer": {
|
|
||||||
"&:after": {
|
|
||||||
animationName: "none !important"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,6 +203,23 @@ const styles = (theme: Theme) =>
|
||||||
marginLeft: 10
|
marginLeft: 10
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleSave(
|
||||||
|
value: any,
|
||||||
|
initial: any,
|
||||||
|
name: string,
|
||||||
|
onChange: (event: ChangeEvent) => void
|
||||||
|
) {
|
||||||
|
if (value && !isEqual(value, initial)) {
|
||||||
|
onChange({
|
||||||
|
target: {
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const RichTextEditor = withStyles(styles, { name: "RichTextEditor" })(
|
const RichTextEditor = withStyles(styles, { name: "RichTextEditor" })(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
|
@ -267,14 +247,7 @@ const RichTextEditor = withStyles(styles, { name: "RichTextEditor" })(
|
||||||
rawContentState={
|
rawContentState={
|
||||||
initial && Object.keys(initial).length > 0 ? initial : null
|
initial && Object.keys(initial).length > 0 ? initial : null
|
||||||
}
|
}
|
||||||
onSave={value =>
|
onSave={value => handleSave(value, initial, name, onChange)}
|
||||||
onChange({
|
|
||||||
target: {
|
|
||||||
name,
|
|
||||||
value
|
|
||||||
}
|
|
||||||
} as any)
|
|
||||||
}
|
|
||||||
blockTypes={[
|
blockTypes={[
|
||||||
{
|
{
|
||||||
icon: <HeaderOne />,
|
icon: <HeaderOne />,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import useScroll from "@saleor/hooks/useScroll";
|
import useWindowScroll from "@saleor/hooks/useWindowScroll";
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
import { maybe } from "../../misc";
|
import { maybe } from "../../misc";
|
||||||
import AppActionContext from "../AppLayout/AppActionContext";
|
import AppActionContext from "../AppLayout/AppActionContext";
|
||||||
|
@ -82,7 +82,7 @@ export const SaveButtonBar = withStyles(styles, { name: "SaveButtonBar" })(
|
||||||
onSave,
|
onSave,
|
||||||
...props
|
...props
|
||||||
}: SaveButtonBarProps) => {
|
}: SaveButtonBarProps) => {
|
||||||
const scrollPosition = useScroll();
|
const scrollPosition = useWindowScroll();
|
||||||
const scrolledToBottom =
|
const scrolledToBottom =
|
||||||
scrollPosition.y + window.innerHeight >= document.body.scrollHeight;
|
scrollPosition.y + window.innerHeight >= document.body.scrollHeight;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
|
@ -43,6 +43,9 @@ const styles = (theme: Theme) =>
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
labelContainer: {
|
labelContainer: {
|
||||||
|
"& span": {
|
||||||
|
paddingRight: 30
|
||||||
|
},
|
||||||
display: "flex"
|
display: "flex"
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
|
@ -128,7 +131,6 @@ const SeoForm = withStyles(styles, { name: "SeoForm" })(
|
||||||
)}
|
)}
|
||||||
value={title.slice(0, 69)}
|
value={title.slice(0, 69)}
|
||||||
disabled={loading || disabled}
|
disabled={loading || disabled}
|
||||||
InputLabelProps={{ shrink: true }}
|
|
||||||
placeholder={titlePlaceholder}
|
placeholder={titlePlaceholder}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
@ -158,7 +160,6 @@ const SeoForm = withStyles(styles, { name: "SeoForm" })(
|
||||||
fullWidth
|
fullWidth
|
||||||
multiline
|
multiline
|
||||||
placeholder={descriptionPlaceholder}
|
placeholder={descriptionPlaceholder}
|
||||||
InputLabelProps={{ shrink: true }}
|
|
||||||
rows={10}
|
rows={10}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Omit } from "@material-ui/core";
|
import { Omit } from "@material-ui/core";
|
||||||
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
import { InputProps } from "@material-ui/core/Input";
|
import { InputProps } from "@material-ui/core/Input";
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
|
@ -9,10 +10,12 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
import Downshift from "downshift";
|
import Downshift from "downshift";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { compareTwoStrings } from "string-similarity";
|
import { compareTwoStrings } from "string-similarity";
|
||||||
|
|
||||||
|
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
import ArrowDropdownIcon from "../../icons/ArrowDropdown";
|
import ArrowDropdownIcon from "../../icons/ArrowDropdown";
|
||||||
import Debounce, { DebounceProps } from "../Debounce";
|
import Debounce, { DebounceProps } from "../Debounce";
|
||||||
|
@ -23,6 +26,10 @@ const styles = (theme: Theme) =>
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
position: "relative"
|
position: "relative"
|
||||||
},
|
},
|
||||||
|
menuItem: {
|
||||||
|
height: "auto",
|
||||||
|
whiteSpace: "normal"
|
||||||
|
},
|
||||||
paper: {
|
paper: {
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
@ -34,26 +41,26 @@ const styles = (theme: Theme) =>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export interface SingleAutocompleteChoiceType {
|
||||||
|
label: string;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
export interface SingleAutocompleteSelectFieldProps {
|
export interface SingleAutocompleteSelectFieldProps {
|
||||||
error?: boolean;
|
error?: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
choices: Array<{
|
displayValue: string;
|
||||||
label: string;
|
emptyOption?: boolean;
|
||||||
value: any;
|
choices: SingleAutocompleteChoiceType[];
|
||||||
}>;
|
value?: string;
|
||||||
value?: {
|
|
||||||
label: string;
|
|
||||||
value: any;
|
|
||||||
};
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
custom?: boolean;
|
allowCustomValues?: boolean;
|
||||||
helperText?: string;
|
helperText?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
InputProps?: InputProps;
|
InputProps?: InputProps;
|
||||||
fetchChoices?(value: string);
|
fetchChoices?: (value: string) => void;
|
||||||
onChange(event);
|
onChange: (event: React.ChangeEvent<any>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SingleAutocompleteSelectFieldState {
|
interface SingleAutocompleteSelectFieldState {
|
||||||
|
@ -73,8 +80,10 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, {
|
||||||
({
|
({
|
||||||
choices,
|
choices,
|
||||||
classes,
|
classes,
|
||||||
custom,
|
allowCustomValues,
|
||||||
disabled,
|
disabled,
|
||||||
|
displayValue,
|
||||||
|
emptyOption,
|
||||||
error,
|
error,
|
||||||
helperText,
|
helperText,
|
||||||
label,
|
label,
|
||||||
|
@ -86,16 +95,24 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, {
|
||||||
fetchChoices,
|
fetchChoices,
|
||||||
onChange
|
onChange
|
||||||
}: SingleAutocompleteSelectFieldProps & WithStyles<typeof styles>) => {
|
}: SingleAutocompleteSelectFieldProps & WithStyles<typeof styles>) => {
|
||||||
const handleChange = item => onChange({ target: { name, value: item } });
|
const [prevDisplayValue] = useStateFromProps(displayValue);
|
||||||
|
const handleChange = item =>
|
||||||
|
onChange({
|
||||||
|
target: {
|
||||||
|
name,
|
||||||
|
value: item
|
||||||
|
}
|
||||||
|
} as any);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DebounceAutocomplete debounceFn={fetchChoices}>
|
<DebounceAutocomplete debounceFn={fetchChoices}>
|
||||||
{debounceFn => (
|
{debounceFn => (
|
||||||
<Downshift
|
<Downshift
|
||||||
selectedItem={value}
|
defaultInputValue={displayValue}
|
||||||
itemToString={item => (item ? item.label : "")}
|
itemToString={() => displayValue}
|
||||||
onSelect={handleChange}
|
|
||||||
onInputValueChange={value => debounceFn(value)}
|
onInputValueChange={value => debounceFn(value)}
|
||||||
|
onSelect={handleChange}
|
||||||
|
selectedItem={value}
|
||||||
>
|
>
|
||||||
{({
|
{({
|
||||||
getInputProps,
|
getInputProps,
|
||||||
|
@ -106,13 +123,18 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, {
|
||||||
toggleMenu,
|
toggleMenu,
|
||||||
openMenu,
|
openMenu,
|
||||||
closeMenu,
|
closeMenu,
|
||||||
highlightedIndex
|
highlightedIndex,
|
||||||
|
reset
|
||||||
}) => {
|
}) => {
|
||||||
const isCustom =
|
const isCustomValueSelected =
|
||||||
choices && selectedItem
|
choices && selectedItem
|
||||||
? choices.filter(c => c.value === selectedItem.value)
|
? choices.filter(c => c.value === selectedItem).length === 0
|
||||||
.length === 0
|
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
|
if (prevDisplayValue !== displayValue) {
|
||||||
|
reset({ inputValue: displayValue });
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.container}>
|
<div className={classes.container}>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -122,9 +144,13 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, {
|
||||||
placeholder
|
placeholder
|
||||||
}),
|
}),
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
<ArrowDropdownIcon
|
<div>
|
||||||
onClick={disabled ? undefined : toggleMenu}
|
{loading ? (
|
||||||
/>
|
<CircularProgress size={20} />
|
||||||
|
) : (
|
||||||
|
<ArrowDropdownIcon onClick={toggleMenu} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
),
|
),
|
||||||
error,
|
error,
|
||||||
id: undefined,
|
id: undefined,
|
||||||
|
@ -136,36 +162,66 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, {
|
||||||
label={label}
|
label={label}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
/>
|
/>
|
||||||
{isOpen && (
|
{isOpen && (!!inputValue || !!choices.length) && (
|
||||||
<Paper className={classes.paper} square>
|
<Paper className={classes.paper} square>
|
||||||
{loading ? (
|
{choices.length > 0 || allowCustomValues ? (
|
||||||
<MenuItem disabled={true} component="div">
|
|
||||||
{i18n.t("Loading...")}
|
|
||||||
</MenuItem>
|
|
||||||
) : choices.length > 0 || custom ? (
|
|
||||||
<>
|
<>
|
||||||
{choices.map((suggestion, index) => (
|
{emptyOption && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={JSON.stringify(suggestion)}
|
className={classes.menuItem}
|
||||||
selected={highlightedIndex === index}
|
|
||||||
component="div"
|
|
||||||
{...getItemProps({ item: suggestion })}
|
|
||||||
>
|
|
||||||
{suggestion.label}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
{custom && (
|
|
||||||
<MenuItem
|
|
||||||
key={"customValue"}
|
|
||||||
selected={isCustom}
|
|
||||||
component="div"
|
component="div"
|
||||||
{...getItemProps({
|
{...getItemProps({
|
||||||
item: { label: inputValue, value: inputValue }
|
item: ""
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{i18n.t("Add custom value")}
|
<Typography color="textSecondary">
|
||||||
|
{i18n.t("None")}
|
||||||
|
</Typography>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
{choices.map((suggestion, index) => {
|
||||||
|
const choiceIndex = index + (emptyOption ? 1 : 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
className={classes.menuItem}
|
||||||
|
key={JSON.stringify(suggestion)}
|
||||||
|
selected={
|
||||||
|
highlightedIndex === choiceIndex ||
|
||||||
|
selectedItem === suggestion.value
|
||||||
|
}
|
||||||
|
component="div"
|
||||||
|
{...getItemProps({
|
||||||
|
index: choiceIndex,
|
||||||
|
item: suggestion.value
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{suggestion.label}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{allowCustomValues &&
|
||||||
|
!!inputValue &&
|
||||||
|
!choices.find(
|
||||||
|
choice =>
|
||||||
|
choice.label.toLowerCase() ===
|
||||||
|
inputValue.toLowerCase()
|
||||||
|
) && (
|
||||||
|
<MenuItem
|
||||||
|
className={classes.menuItem}
|
||||||
|
key={"customValue"}
|
||||||
|
selected={isCustomValueSelected}
|
||||||
|
component="div"
|
||||||
|
{...getItemProps({
|
||||||
|
item: inputValue
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{i18n.t("Add new value: {{ value }}", {
|
||||||
|
context: "add custom option",
|
||||||
|
value: inputValue
|
||||||
|
})}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<MenuItem disabled={true} component="div">
|
<MenuItem disabled={true} component="div">
|
||||||
|
|
|
@ -17,6 +17,11 @@ const styles = (theme: Theme) =>
|
||||||
opacity: 1
|
opacity: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
primary: {
|
||||||
|
"&$skeleton": {
|
||||||
|
background: theme.palette.primary.main
|
||||||
|
}
|
||||||
|
},
|
||||||
skeleton: {
|
skeleton: {
|
||||||
animation: "skeleton-animation .75s linear infinite forwards alternate",
|
animation: "skeleton-animation .75s linear infinite forwards alternate",
|
||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
|
@ -29,12 +34,18 @@ const styles = (theme: Theme) =>
|
||||||
|
|
||||||
interface SkeletonProps extends WithStyles<typeof styles> {
|
interface SkeletonProps extends WithStyles<typeof styles> {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
primary?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Skeleton = withStyles(styles, { name: "Skeleton" })(
|
const Skeleton = withStyles(styles, { name: "Skeleton" })(
|
||||||
({ className, classes, style }: SkeletonProps) => (
|
({ className, classes, primary, style }: SkeletonProps) => (
|
||||||
<span className={classNames(classes.skeleton, className)} style={style}>
|
<span
|
||||||
|
className={classNames(classes.skeleton, className, {
|
||||||
|
[classes.primary]: primary
|
||||||
|
})}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
‌
|
‌
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import Typography, { TypographyProps } from "@material-ui/core/Typography";
|
import Typography, { TypographyProps } from "@material-ui/core/Typography";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const styles = (theme: Theme) => {
|
const styles = (theme: Theme) => {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
|
|
@ -7,11 +7,13 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
import TableCell from "@material-ui/core/TableCell";
|
||||||
import Cached from "@material-ui/icons/Cached";
|
import Cached from "@material-ui/icons/Cached";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import Image from "../../icons/Image";
|
import Image from "../../icons/Image";
|
||||||
|
|
||||||
|
export const AVATAR_MARGIN = 56;
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
avatar: {
|
avatar: {
|
||||||
|
@ -19,8 +21,17 @@ const styles = (theme: Theme) =>
|
||||||
border: `1px solid ${theme.overrides.MuiCard.root.borderColor}`,
|
border: `1px solid ${theme.overrides.MuiCard.root.borderColor}`,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
color: "#bdbdbd",
|
color: "#bdbdbd",
|
||||||
|
display: "inline-flex",
|
||||||
padding: theme.spacing.unit / 2
|
padding: theme.spacing.unit / 2
|
||||||
},
|
},
|
||||||
|
children: {
|
||||||
|
alignSelf: "center",
|
||||||
|
marginLeft: theme.spacing.unit * 2
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
alignItems: "center",
|
||||||
|
display: "flex"
|
||||||
|
},
|
||||||
root: {
|
root: {
|
||||||
paddingRight: theme.spacing.unit * 3,
|
paddingRight: theme.spacing.unit * 3,
|
||||||
width: "1%"
|
width: "1%"
|
||||||
|
@ -31,25 +42,35 @@ interface TableCellAvatarProps extends WithStyles<typeof styles> {
|
||||||
className?: string;
|
className?: string;
|
||||||
thumbnail?: string;
|
thumbnail?: string;
|
||||||
avatarProps?: string;
|
avatarProps?: string;
|
||||||
|
children?: React.ReactNode | React.ReactNodeArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableCellAvatar = withStyles(styles, { name: "TableCellAvatar" })(
|
const TableCellAvatar = withStyles(styles, { name: "TableCellAvatar" })(
|
||||||
({ classes, className, thumbnail, avatarProps }: TableCellAvatarProps) => (
|
({
|
||||||
|
classes,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
thumbnail,
|
||||||
|
avatarProps
|
||||||
|
}: TableCellAvatarProps) => (
|
||||||
<TableCell className={classNames(classes.root, className)}>
|
<TableCell className={classNames(classes.root, className)}>
|
||||||
{thumbnail === undefined ? (
|
<div className={classes.content}>
|
||||||
<Avatar className={classNames(classes.avatar, avatarProps)}>
|
{thumbnail === undefined ? (
|
||||||
<Cached color="primary" />
|
<Avatar className={classNames(classes.avatar, avatarProps)}>
|
||||||
</Avatar>
|
<Cached color="primary" />
|
||||||
) : thumbnail === null ? (
|
</Avatar>
|
||||||
<Avatar className={classNames(classes.avatar, avatarProps)}>
|
) : thumbnail === null ? (
|
||||||
<Image color="primary" />
|
<Avatar className={classNames(classes.avatar, avatarProps)}>
|
||||||
</Avatar>
|
<Image color="primary" />
|
||||||
) : (
|
</Avatar>
|
||||||
<Avatar
|
) : (
|
||||||
className={classNames(classes.avatar, avatarProps)}
|
<Avatar
|
||||||
src={thumbnail}
|
className={classNames(classes.avatar, avatarProps)}
|
||||||
/>
|
src={thumbnail}
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
|
<span className={classes.children}>{children}</span>
|
||||||
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -27,7 +27,7 @@ const useInputStyles = makeStyles({
|
||||||
});
|
});
|
||||||
|
|
||||||
const Search: React.FC<TextFieldProps> = props => {
|
const Search: React.FC<TextFieldProps> = props => {
|
||||||
const classes = useInputStyles();
|
const classes = useInputStyles({});
|
||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import MuiTableHead, {
|
||||||
} from "@material-ui/core/TableHead";
|
} from "@material-ui/core/TableHead";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { Node } from "../../types";
|
import { Node } from "../../types";
|
||||||
|
@ -20,7 +20,9 @@ import i18n from "../../i18n";
|
||||||
import Checkbox from "../Checkbox";
|
import Checkbox from "../Checkbox";
|
||||||
|
|
||||||
export interface TableHeadProps extends MuiTableHeadProps {
|
export interface TableHeadProps extends MuiTableHeadProps {
|
||||||
|
colSpan: number;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
dragRows?: boolean;
|
||||||
selected: number;
|
selected: number;
|
||||||
items: Node[];
|
items: Node[];
|
||||||
toolbar: React.ReactNode | React.ReactNodeArray;
|
toolbar: React.ReactNode | React.ReactNodeArray;
|
||||||
|
@ -34,7 +36,7 @@ const styles = (theme: Theme) =>
|
||||||
},
|
},
|
||||||
checkboxPartialSelect: {
|
checkboxPartialSelect: {
|
||||||
"&:after": {
|
"&:after": {
|
||||||
background: "#fff",
|
background: theme.palette.common.white,
|
||||||
content: "''",
|
content: "''",
|
||||||
height: 2,
|
height: 2,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
|
@ -50,6 +52,10 @@ const styles = (theme: Theme) =>
|
||||||
height: 47,
|
height: 47,
|
||||||
marginRight: -theme.spacing.unit * 2
|
marginRight: -theme.spacing.unit * 2
|
||||||
},
|
},
|
||||||
|
dragRows: {
|
||||||
|
padding: 0,
|
||||||
|
width: 52
|
||||||
|
},
|
||||||
padding: {
|
padding: {
|
||||||
"&:last-child": {
|
"&:last-child": {
|
||||||
padding: 0
|
padding: 0
|
||||||
|
@ -57,7 +63,6 @@ const styles = (theme: Theme) =>
|
||||||
},
|
},
|
||||||
root: {
|
root: {
|
||||||
backgroundColor: fade(theme.palette.primary.main, 0.05),
|
backgroundColor: fade(theme.palette.primary.main, 0.05),
|
||||||
borderBottom: "1px solid rgba(224, 224, 224, 1)",
|
|
||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
paddingRight: 24
|
paddingRight: 24
|
||||||
},
|
},
|
||||||
|
@ -77,7 +82,9 @@ const TableHead = withStyles(styles, {
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
children,
|
children,
|
||||||
|
colSpan,
|
||||||
disabled,
|
disabled,
|
||||||
|
dragRows,
|
||||||
items,
|
items,
|
||||||
selected,
|
selected,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
|
@ -87,13 +94,21 @@ const TableHead = withStyles(styles, {
|
||||||
return (
|
return (
|
||||||
<MuiTableHead {...muiTableHeadProps}>
|
<MuiTableHead {...muiTableHeadProps}>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell
|
{dragRows && (items === undefined || items.length > 0) && (
|
||||||
padding="checkbox"
|
<TableCell
|
||||||
className={classNames({
|
className={classNames({
|
||||||
[classes.checkboxSelected]: selected
|
[classes.checkboxSelected]: selected
|
||||||
})}
|
})}
|
||||||
>
|
/>
|
||||||
{items && items.length > 0 ? (
|
)}
|
||||||
|
{(items === undefined || items.length > 0) && (
|
||||||
|
<TableCell
|
||||||
|
padding="checkbox"
|
||||||
|
className={classNames({
|
||||||
|
[classes.checkboxSelected]: selected,
|
||||||
|
[classes.dragRows]: dragRows
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
className={classNames({
|
className={classNames({
|
||||||
[classes.checkboxPartialSelect]:
|
[classes.checkboxPartialSelect]:
|
||||||
|
@ -103,11 +118,14 @@ const TableHead = withStyles(styles, {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={() => toggleAll(items, selected)}
|
onChange={() => toggleAll(items, selected)}
|
||||||
/>
|
/>
|
||||||
) : null}
|
</TableCell>
|
||||||
</TableCell>
|
)}
|
||||||
{selected ? (
|
{selected ? (
|
||||||
<>
|
<>
|
||||||
<TableCell className={classNames(classes.root)} colSpan={50}>
|
<TableCell
|
||||||
|
className={classNames(classes.root)}
|
||||||
|
colSpan={colSpan - 1}
|
||||||
|
>
|
||||||
<div className={classes.container}>
|
<div className={classes.container}>
|
||||||
{selected && (
|
{selected && (
|
||||||
<Typography>
|
<Typography>
|
||||||
|
|
|
@ -11,6 +11,9 @@ import TableCell from "@material-ui/core/TableCell";
|
||||||
import Toolbar from "@material-ui/core/Toolbar";
|
import Toolbar from "@material-ui/core/Toolbar";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import RowNumberSelect from "@saleor/components/RowNumberSelect";
|
||||||
|
import { maybe } from "@saleor/misc";
|
||||||
|
import { ListSettings } from "../../types";
|
||||||
import TablePaginationActions from "./TablePaginationActions";
|
import TablePaginationActions from "./TablePaginationActions";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
@ -50,6 +53,7 @@ const styles = (theme: Theme) =>
|
||||||
toolbar: {
|
toolbar: {
|
||||||
height: 56,
|
height: 56,
|
||||||
minHeight: 56,
|
minHeight: 56,
|
||||||
|
paddingLeft: 2,
|
||||||
paddingRight: 2
|
paddingRight: 2
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -59,11 +63,13 @@ interface TablePaginationProps extends WithStyles<typeof styles> {
|
||||||
backIconButtonProps?: Partial<IconButtonProps>;
|
backIconButtonProps?: Partial<IconButtonProps>;
|
||||||
colSpan: number;
|
colSpan: number;
|
||||||
component?: string | typeof TableCell;
|
component?: string | typeof TableCell;
|
||||||
|
settings?: ListSettings;
|
||||||
hasNextPage: boolean;
|
hasNextPage: boolean;
|
||||||
hasPreviousPage: boolean;
|
hasPreviousPage: boolean;
|
||||||
nextIconButtonProps?: Partial<IconButtonProps>;
|
nextIconButtonProps?: Partial<IconButtonProps>;
|
||||||
onNextPage(event);
|
onNextPage(event);
|
||||||
onPreviousPage(event);
|
onPreviousPage(event);
|
||||||
|
onUpdateListSettings?(key: keyof ListSettings, value: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TablePagination = withStyles(styles, { name: "TablePagination" })(
|
const TablePagination = withStyles(styles, { name: "TablePagination" })(
|
||||||
|
@ -73,11 +79,13 @@ const TablePagination = withStyles(styles, { name: "TablePagination" })(
|
||||||
classes,
|
classes,
|
||||||
colSpan: colSpanProp,
|
colSpan: colSpanProp,
|
||||||
component: Component,
|
component: Component,
|
||||||
|
settings,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
hasPreviousPage,
|
hasPreviousPage,
|
||||||
nextIconButtonProps,
|
nextIconButtonProps,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
...other
|
...other
|
||||||
}: TablePaginationProps) => {
|
}: TablePaginationProps) => {
|
||||||
let colSpan;
|
let colSpan;
|
||||||
|
@ -89,7 +97,15 @@ const TablePagination = withStyles(styles, { name: "TablePagination" })(
|
||||||
return (
|
return (
|
||||||
<Component className={classes.root} colSpan={colSpan} {...other}>
|
<Component className={classes.root} colSpan={colSpan} {...other}>
|
||||||
<Toolbar className={classes.toolbar}>
|
<Toolbar className={classes.toolbar}>
|
||||||
<div className={classes.spacer} />
|
<div className={classes.spacer}>
|
||||||
|
{maybe(() => settings.rowNumber) && (
|
||||||
|
<RowNumberSelect
|
||||||
|
choices={[20, 30, 50, 100]}
|
||||||
|
settings={settings}
|
||||||
|
onChange={onUpdateListSettings}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<Actions
|
<Actions
|
||||||
backIconButtonProps={backIconButtonProps}
|
backIconButtonProps={backIconButtonProps}
|
||||||
hasNextPage={hasNextPage}
|
hasNextPage={hasNextPage}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||||
import ArrowLeft from "@material-ui/icons/ArrowLeft";
|
import ArrowLeft from "@material-ui/icons/ArrowLeft";
|
||||||
import ArrowRight from "@material-ui/icons/ArrowRight";
|
import ArrowRight from "@material-ui/icons/ArrowRight";
|
||||||
import useTheme from "@saleor/hooks/useTheme";
|
import useTheme from "@saleor/hooks/useTheme";
|
||||||
import * as classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
|
|
@ -78,7 +78,7 @@ const TextFieldWithChoice = withStyles(styles, {
|
||||||
<Typography component="span" variant="caption">
|
<Typography component="span" variant="caption">
|
||||||
{ChoiceProps.label}
|
{ChoiceProps.label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<DropdownIcon />
|
{ChoiceProps.values ? <DropdownIcon /> : null}
|
||||||
</div>
|
</div>
|
||||||
<Popper
|
<Popper
|
||||||
open={menuOpen}
|
open={menuOpen}
|
||||||
|
|
|
@ -14,17 +14,21 @@ const dark: IThemeColors = {
|
||||||
},
|
},
|
||||||
error: "#C22D74",
|
error: "#C22D74",
|
||||||
font: {
|
font: {
|
||||||
|
button: "#202124",
|
||||||
default: "#FCFCFC",
|
default: "#FCFCFC",
|
||||||
gray: "#9E9D9D"
|
gray: "#9E9D9D",
|
||||||
|
textButton: "#FFFFFF"
|
||||||
},
|
},
|
||||||
gray: {
|
gray: {
|
||||||
default: "#202124",
|
default: "#202124",
|
||||||
disabled: "rgba(32, 33, 36, 0.6)"
|
disabled: "rgba(32, 33, 36, 0.6)"
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
|
border: "#9d9d9d",
|
||||||
default: "#25262A",
|
default: "#25262A",
|
||||||
disabled: "#292A2D",
|
disabled: "none",
|
||||||
focused: "#25262A"
|
text: "#FCFCFC",
|
||||||
|
textHover: "#616161"
|
||||||
},
|
},
|
||||||
paperBorder: "#252728",
|
paperBorder: "#252728",
|
||||||
primary: "#13BEBB",
|
primary: "#13BEBB",
|
||||||
|
@ -38,20 +42,24 @@ const light: IThemeColors = {
|
||||||
},
|
},
|
||||||
error: "#C22D74",
|
error: "#C22D74",
|
||||||
font: {
|
font: {
|
||||||
|
button: "#FFFFFF",
|
||||||
default: "#3D3D3D",
|
default: "#3D3D3D",
|
||||||
gray: "#616161"
|
gray: "#616161",
|
||||||
|
textButton: "#06847B"
|
||||||
},
|
},
|
||||||
gray: {
|
gray: {
|
||||||
default: "#C8C8C8",
|
default: "#C8C8C8",
|
||||||
disabled: "rgba(216, 216, 216, 0.3)"
|
disabled: "rgba(216, 216, 216, 0.3)"
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
default: "#F1F6F6",
|
border: "#616161",
|
||||||
|
default: "#FFFFFF",
|
||||||
disabled: "#EAEAEA",
|
disabled: "#EAEAEA",
|
||||||
focused: "#DCEBEB"
|
text: "#3D3D3D",
|
||||||
|
textHover: "#616161"
|
||||||
},
|
},
|
||||||
paperBorder: "#EAEAEA",
|
paperBorder: "#EAEAEA",
|
||||||
primary: "#13BEBB",
|
primary: "#06847B",
|
||||||
secondary: "#21125E"
|
secondary: "#21125E"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Avatar from "@material-ui/core/Avatar";
|
import Avatar from "@material-ui/core/Avatar";
|
||||||
import Card from "@material-ui/core/Card";
|
import Card from "@material-ui/core/Card";
|
||||||
import CardContent from "@material-ui/core/CardContent";
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
import * as colors from "@material-ui/core/colors";
|
import colors from "@material-ui/core/colors";
|
||||||
import {
|
import {
|
||||||
createStyles,
|
createStyles,
|
||||||
Theme,
|
Theme,
|
||||||
|
@ -10,7 +10,7 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import PersonIcon from "@material-ui/icons/Person";
|
import PersonIcon from "@material-ui/icons/Person";
|
||||||
import * as CRC from "crc-32";
|
import CRC from "crc-32";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { DateTime } from "../Date";
|
import { DateTime } from "../Date";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { SearchQueryVariables } from "./containers/BaseSearch";
|
import { SearchQueryVariables } from "./containers/BaseSearch";
|
||||||
|
import { ListSettings, ListViews } from "./types";
|
||||||
|
|
||||||
export const APP_MOUNT_URI = process.env.APP_MOUNT_URI || "/";
|
export const APP_MOUNT_URI = process.env.APP_MOUNT_URI || "/";
|
||||||
export const API_URI = process.env.API_URI || "/graphql/";
|
export const API_URI = process.env.API_URI || "/graphql/";
|
||||||
|
@ -10,3 +11,58 @@ export const DEFAULT_INITIAL_SEARCH_DATA: SearchQueryVariables = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PAGINATE_BY = 20;
|
export const PAGINATE_BY = 20;
|
||||||
|
|
||||||
|
export type ProductListColumns = "productType" | "isPublished" | "price";
|
||||||
|
export interface AppListViewSettings {
|
||||||
|
[ListViews.CATEGORY_LIST]: ListSettings;
|
||||||
|
[ListViews.COLLECTION_LIST]: ListSettings;
|
||||||
|
[ListViews.CUSTOMER_LIST]: ListSettings;
|
||||||
|
[ListViews.DRAFT_LIST]: ListSettings;
|
||||||
|
[ListViews.NAVIGATION_LIST]: ListSettings;
|
||||||
|
[ListViews.ORDER_LIST]: ListSettings;
|
||||||
|
[ListViews.PAGES_LIST]: ListSettings;
|
||||||
|
[ListViews.PRODUCT_LIST]: ListSettings<ProductListColumns>;
|
||||||
|
[ListViews.SALES_LIST]: ListSettings;
|
||||||
|
[ListViews.SHIPPING_METHODS_LIST]: ListSettings;
|
||||||
|
[ListViews.STAFF_MEMBERS_LIST]: ListSettings;
|
||||||
|
[ListViews.VOUCHER_LIST]: ListSettings;
|
||||||
|
}
|
||||||
|
export const defaultListSettings: AppListViewSettings = {
|
||||||
|
[ListViews.CATEGORY_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.COLLECTION_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.CUSTOMER_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.DRAFT_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.NAVIGATION_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.ORDER_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.PAGES_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.PRODUCT_LIST]: {
|
||||||
|
columns: ["isPublished", "price", "productType"],
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.SALES_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.SHIPPING_METHODS_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.STAFF_MEMBERS_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
},
|
||||||
|
[ListViews.VOUCHER_LIST]: {
|
||||||
|
rowNumber: PAGINATE_BY
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,28 +1,36 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import { attributeListUrl } from "@saleor/attributes/urls";
|
||||||
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
import useUser from "@saleor/hooks/useUser";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import i18n from "@saleor/i18n";
|
||||||
import i18n from "../i18n";
|
import Navigation from "@saleor/icons/Navigation";
|
||||||
import Navigation from "../icons/Navigation";
|
import Pages from "@saleor/icons/Pages";
|
||||||
import Pages from "../icons/Pages";
|
import ProductTypes from "@saleor/icons/ProductTypes";
|
||||||
import ProductTypes from "../icons/ProductTypes";
|
import ShippingMethods from "@saleor/icons/ShippingMethods";
|
||||||
import ShippingMethods from "../icons/ShippingMethods";
|
import SiteSettings from "@saleor/icons/SiteSettings";
|
||||||
import SiteSettings from "../icons/SiteSettings";
|
import StaffMembers from "@saleor/icons/StaffMembers";
|
||||||
import StaffMembers from "../icons/StaffMembers";
|
import Taxes from "@saleor/icons/Taxes";
|
||||||
import Taxes from "../icons/Taxes";
|
import { maybe } from "@saleor/misc";
|
||||||
import { maybe } from "../misc";
|
import { menuListUrl } from "@saleor/navigation/urls";
|
||||||
import { menuListUrl } from "../navigation/urls";
|
import { pageListUrl } from "@saleor/pages/urls";
|
||||||
import { pageListUrl } from "../pages/urls";
|
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
||||||
import { productTypeListUrl } from "../productTypes/urls";
|
import { shippingZonesListUrl } from "@saleor/shipping/urls";
|
||||||
import { shippingZonesListUrl } from "../shipping/urls";
|
import { siteSettingsUrl } from "@saleor/siteSettings/urls";
|
||||||
import { siteSettingsUrl } from "../siteSettings/urls";
|
import { staffListUrl } from "@saleor/staff/urls";
|
||||||
import { staffListUrl } from "../staff/urls";
|
import { taxSection } from "@saleor/taxes/urls";
|
||||||
import { taxSection } from "../taxes/urls";
|
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||||
import { PermissionEnum } from "../types/globalTypes";
|
|
||||||
import ConfigurationPage, { MenuItem } from "./ConfigurationPage";
|
import ConfigurationPage, { MenuItem } from "./ConfigurationPage";
|
||||||
|
|
||||||
export const configurationMenu: MenuItem[] = [
|
export const configurationMenu: MenuItem[] = [
|
||||||
|
{
|
||||||
|
description: i18n.t("Determine attributes used to create product types"),
|
||||||
|
icon: <ProductTypes fontSize="inherit" viewBox="0 0 44 44" />,
|
||||||
|
permission: PermissionEnum.MANAGE_PRODUCTS,
|
||||||
|
title: i18n.t("Attributes"),
|
||||||
|
url: attributeListUrl()
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: i18n.t("Define types of products you sell"),
|
description: i18n.t("Define types of products you sell"),
|
||||||
icon: <ProductTypes fontSize="inherit" viewBox="0 0 44 44" />,
|
icon: <ProductTypes fontSize="inherit" viewBox="0 0 44 44" />,
|
||||||
|
|
|
@ -12,9 +12,11 @@ import ConfirmButton, {
|
||||||
ConfirmButtonTransitionState
|
ConfirmButtonTransitionState
|
||||||
} from "@saleor/components/ConfirmButton";
|
} from "@saleor/components/ConfirmButton";
|
||||||
import Form from "@saleor/components/Form";
|
import Form from "@saleor/components/Form";
|
||||||
import i18n from "../../../i18n";
|
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||||
import { maybe } from "../../../misc";
|
import i18n from "@saleor/i18n";
|
||||||
import { UserError } from "../../../types";
|
import { maybe } from "@saleor/misc";
|
||||||
|
import { UserError } from "@saleor/types";
|
||||||
|
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||||
import { AddressTypeInput } from "../../types";
|
import { AddressTypeInput } from "../../types";
|
||||||
import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses";
|
import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses";
|
||||||
|
|
||||||
|
@ -50,14 +52,14 @@ const CustomerAddressDialog = withStyles(styles, {})(
|
||||||
onClose,
|
onClose,
|
||||||
onConfirm
|
onConfirm
|
||||||
}: CustomerAddressDialogProps & WithStyles<typeof styles>) => {
|
}: CustomerAddressDialogProps & WithStyles<typeof styles>) => {
|
||||||
|
const [countryDisplayName, setCountryDisplayName] = useStateFromProps(
|
||||||
|
maybe(() => address.country.country, "")
|
||||||
|
);
|
||||||
const initialForm: AddressTypeInput = {
|
const initialForm: AddressTypeInput = {
|
||||||
city: maybe(() => address.city, ""),
|
city: maybe(() => address.city, ""),
|
||||||
cityArea: maybe(() => address.cityArea, ""),
|
cityArea: maybe(() => address.cityArea, ""),
|
||||||
companyName: maybe(() => address.companyName, ""),
|
companyName: maybe(() => address.companyName, ""),
|
||||||
country: {
|
country: maybe(() => address.country.code, ""),
|
||||||
label: maybe(() => address.country.country, ""),
|
|
||||||
value: maybe(() => address.country.code, "")
|
|
||||||
},
|
|
||||||
countryArea: maybe(() => address.countryArea, ""),
|
countryArea: maybe(() => address.countryArea, ""),
|
||||||
firstName: maybe(() => address.firstName, ""),
|
firstName: maybe(() => address.firstName, ""),
|
||||||
lastName: maybe(() => address.lastName, ""),
|
lastName: maybe(() => address.lastName, ""),
|
||||||
|
@ -66,6 +68,16 @@ const CustomerAddressDialog = withStyles(styles, {})(
|
||||||
streetAddress1: maybe(() => address.streetAddress1, ""),
|
streetAddress1: maybe(() => address.streetAddress1, ""),
|
||||||
streetAddress2: maybe(() => address.streetAddress2, "")
|
streetAddress2: maybe(() => address.streetAddress2, "")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const countryChoices = maybe(
|
||||||
|
() =>
|
||||||
|
countries.map(country => ({
|
||||||
|
label: country.label,
|
||||||
|
value: country.code
|
||||||
|
})),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
@ -75,38 +87,48 @@ const CustomerAddressDialog = withStyles(styles, {})(
|
||||||
maxWidth="sm"
|
maxWidth="sm"
|
||||||
>
|
>
|
||||||
<Form initial={initialForm} errors={errors} onSubmit={onConfirm}>
|
<Form initial={initialForm} errors={errors} onSubmit={onConfirm}>
|
||||||
{({ change, data, errors, submit }) => (
|
{({ change, data, errors, submit }) => {
|
||||||
<>
|
const handleCountrySelect = createSingleAutocompleteSelectHandler(
|
||||||
<DialogTitle>
|
change,
|
||||||
{variant === "create"
|
setCountryDisplayName,
|
||||||
? i18n.t("Add Address")
|
countryChoices
|
||||||
: i18n.t("Edit Address")}
|
);
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent className={classes.overflow}>
|
return (
|
||||||
<AddressEdit
|
<>
|
||||||
countries={countries}
|
<DialogTitle>
|
||||||
data={data}
|
{variant === "create"
|
||||||
errors={errors}
|
? i18n.t("Add Address")
|
||||||
onChange={change}
|
: i18n.t("Edit Address")}
|
||||||
/>
|
</DialogTitle>
|
||||||
</DialogContent>
|
<DialogContent className={classes.overflow}>
|
||||||
<DialogActions>
|
<AddressEdit
|
||||||
<Button onClick={onClose}>
|
countries={countryChoices}
|
||||||
{i18n.t("Cancel", { context: "button" })}
|
data={data}
|
||||||
</Button>
|
countryDisplayValue={countryDisplayName}
|
||||||
<ConfirmButton
|
errors={errors}
|
||||||
transitionState={confirmButtonState}
|
onChange={change}
|
||||||
color="primary"
|
onCountryChange={handleCountrySelect}
|
||||||
variant="contained"
|
/>
|
||||||
onClick={submit}
|
</DialogContent>
|
||||||
type="submit"
|
<DialogActions>
|
||||||
>
|
<Button onClick={onClose}>
|
||||||
{i18n.t("Save Address", { context: "button" })}
|
{i18n.t("Cancel", { context: "button" })}
|
||||||
<AddIcon />
|
</Button>
|
||||||
</ConfirmButton>
|
<ConfirmButton
|
||||||
</DialogActions>
|
transitionState={confirmButtonState}
|
||||||
</>
|
color="primary"
|
||||||
)}
|
variant="contained"
|
||||||
|
onClick={submit}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{i18n.t("Save Address", { context: "button" })}
|
||||||
|
<AddIcon />
|
||||||
|
</ConfirmButton>
|
||||||
|
</DialogActions>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
</Form>
|
</Form>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,10 +7,10 @@ import React from "react";
|
||||||
import AddressEdit from "@saleor/components/AddressEdit";
|
import AddressEdit from "@saleor/components/AddressEdit";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import { FormSpacer } from "@saleor/components/FormSpacer";
|
import { FormSpacer } from "@saleor/components/FormSpacer";
|
||||||
|
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
import { FormErrors } from "../../../types";
|
import { FormErrors } from "../../../types";
|
||||||
import { AddressTypeInput } from "../../types";
|
import { AddressTypeInput } from "../../types";
|
||||||
import { CustomerCreateData_shop_countries } from "../../types/CustomerCreateData";
|
|
||||||
|
|
||||||
const styles = createStyles({
|
const styles = createStyles({
|
||||||
overflow: {
|
overflow: {
|
||||||
|
@ -19,11 +19,13 @@ const styles = createStyles({
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface CustomerCreateAddressProps extends WithStyles<typeof styles> {
|
export interface CustomerCreateAddressProps extends WithStyles<typeof styles> {
|
||||||
countries: CustomerCreateData_shop_countries[];
|
countries: SingleAutocompleteChoiceType[];
|
||||||
|
countryDisplayName: string;
|
||||||
data: AddressTypeInput;
|
data: AddressTypeInput;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
errors: FormErrors<keyof AddressTypeInput>;
|
errors: FormErrors<keyof AddressTypeInput>;
|
||||||
onChange(event: React.ChangeEvent<any>);
|
onChange(event: React.ChangeEvent<any>);
|
||||||
|
onCountryChange(event: React.ChangeEvent<any>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomerCreateAddress = withStyles(styles, {
|
const CustomerCreateAddress = withStyles(styles, {
|
||||||
|
@ -32,10 +34,12 @@ const CustomerCreateAddress = withStyles(styles, {
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
countries,
|
countries,
|
||||||
|
countryDisplayName,
|
||||||
data,
|
data,
|
||||||
disabled,
|
disabled,
|
||||||
errors,
|
errors,
|
||||||
onChange
|
onChange,
|
||||||
|
onCountryChange
|
||||||
}: CustomerCreateAddressProps) => (
|
}: CustomerCreateAddressProps) => (
|
||||||
<Card className={classes.overflow}>
|
<Card className={classes.overflow}>
|
||||||
<CardTitle title={i18n.t("Primary address")} />
|
<CardTitle title={i18n.t("Primary address")} />
|
||||||
|
@ -45,14 +49,13 @@ const CustomerCreateAddress = withStyles(styles, {
|
||||||
</Typography>
|
</Typography>
|
||||||
<FormSpacer />
|
<FormSpacer />
|
||||||
<AddressEdit
|
<AddressEdit
|
||||||
countries={countries.map(country => ({
|
countries={countries}
|
||||||
code: country.code,
|
|
||||||
label: country.country
|
|
||||||
}))}
|
|
||||||
data={data}
|
data={data}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
countryDisplayValue={countryDisplayName}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
onCountryChange={onCountryChange}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import Form from "@saleor/components/Form";
|
||||||
import Grid from "@saleor/components/Grid";
|
import Grid from "@saleor/components/Grid";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||||
|
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
import { UserError } from "../../../types";
|
import { UserError } from "../../../types";
|
||||||
import { AddressTypeInput } from "../../types";
|
import { AddressTypeInput } from "../../types";
|
||||||
|
@ -27,10 +28,7 @@ const initialForm: CustomerCreatePageFormData = {
|
||||||
city: "",
|
city: "",
|
||||||
cityArea: "",
|
cityArea: "",
|
||||||
companyName: "",
|
companyName: "",
|
||||||
country: {
|
country: "",
|
||||||
label: "",
|
|
||||||
value: ""
|
|
||||||
},
|
|
||||||
countryArea: "",
|
countryArea: "",
|
||||||
customerFirstName: "",
|
customerFirstName: "",
|
||||||
customerLastName: "",
|
customerLastName: "",
|
||||||
|
@ -60,46 +58,69 @@ const CustomerCreatePage: React.StatelessComponent<CustomerCreatePageProps> = ({
|
||||||
saveButtonBar,
|
saveButtonBar,
|
||||||
onBack,
|
onBack,
|
||||||
onSubmit
|
onSubmit
|
||||||
}: CustomerCreatePageProps) => (
|
}: CustomerCreatePageProps) => {
|
||||||
<Form initial={initialForm} onSubmit={onSubmit} errors={errors} confirmLeave>
|
const [countryDisplayName, setCountryDisplayName] = React.useState("");
|
||||||
{({ change, data, errors: formErrors, hasChanged, submit }) => (
|
const countryChoices = countries.map(country => ({
|
||||||
<Container>
|
label: country.country,
|
||||||
<AppHeader onBack={onBack}>{i18n.t("Customers")}</AppHeader>
|
value: country.code
|
||||||
<PageHeader title={i18n.t("Add customer")} />
|
}));
|
||||||
<Grid>
|
|
||||||
<div>
|
return (
|
||||||
<CustomerCreateDetails
|
<Form
|
||||||
data={data}
|
initial={initialForm}
|
||||||
disabled={disabled}
|
onSubmit={onSubmit}
|
||||||
errors={formErrors}
|
errors={errors}
|
||||||
onChange={change}
|
confirmLeave
|
||||||
|
>
|
||||||
|
{({ change, data, errors: formErrors, hasChanged, submit }) => {
|
||||||
|
const handleCountrySelect = createSingleAutocompleteSelectHandler(
|
||||||
|
change,
|
||||||
|
setCountryDisplayName,
|
||||||
|
countryChoices
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<AppHeader onBack={onBack}>{i18n.t("Customers")}</AppHeader>
|
||||||
|
<PageHeader title={i18n.t("Add customer")} />
|
||||||
|
<Grid>
|
||||||
|
<div>
|
||||||
|
<CustomerCreateDetails
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
<CardSpacer />
|
||||||
|
<CustomerCreateAddress
|
||||||
|
countries={countryChoices}
|
||||||
|
countryDisplayName={countryDisplayName}
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
onCountryChange={handleCountrySelect}
|
||||||
|
/>
|
||||||
|
<CardSpacer />
|
||||||
|
<CustomerCreateNote
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
<SaveButtonBar
|
||||||
|
disabled={disabled || !hasChanged}
|
||||||
|
state={saveButtonBar}
|
||||||
|
onSave={submit}
|
||||||
|
onCancel={onBack}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
</Container>
|
||||||
<CustomerCreateAddress
|
);
|
||||||
countries={countries}
|
}}
|
||||||
data={data}
|
</Form>
|
||||||
disabled={disabled}
|
);
|
||||||
errors={formErrors}
|
};
|
||||||
onChange={change}
|
|
||||||
/>
|
|
||||||
<CardSpacer />
|
|
||||||
<CustomerCreateNote
|
|
||||||
data={data}
|
|
||||||
disabled={disabled}
|
|
||||||
errors={formErrors}
|
|
||||||
onChange={change}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
|
||||||
<SaveButtonBar
|
|
||||||
disabled={disabled || !hasChanged}
|
|
||||||
state={saveButtonBar}
|
|
||||||
onSave={submit}
|
|
||||||
onCancel={onBack}
|
|
||||||
/>
|
|
||||||
</Container>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
CustomerCreatePage.displayName = "CustomerCreatePage";
|
CustomerCreatePage.displayName = "CustomerCreatePage";
|
||||||
export default CustomerCreatePage;
|
export default CustomerCreatePage;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import * as moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
|
|
|
@ -55,11 +55,11 @@ const CustomerDetailsPage: React.StatelessComponent<
|
||||||
<Form
|
<Form
|
||||||
errors={errors}
|
errors={errors}
|
||||||
initial={{
|
initial={{
|
||||||
email: maybe(() => customer.email),
|
email: maybe(() => customer.email, ""),
|
||||||
firstName: maybe(() => customer.firstName),
|
firstName: maybe(() => customer.firstName, ""),
|
||||||
isActive: maybe(() => customer.isActive, false),
|
isActive: maybe(() => customer.isActive, false),
|
||||||
lastName: maybe(() => customer.lastName),
|
lastName: maybe(() => customer.lastName, ""),
|
||||||
note: maybe(() => customer.note)
|
note: maybe(() => customer.note, "")
|
||||||
}}
|
}}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
confirmLeave
|
confirmLeave
|
||||||
|
|
|
@ -16,9 +16,9 @@ import Checkbox from "@saleor/components/Checkbox";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { getUserName, maybe, renderCollection } from "../../../misc";
|
import { getUserName, maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "../../../types";
|
import { ListActions, ListProps } from "@saleor/types";
|
||||||
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
@ -47,14 +47,18 @@ export interface CustomerListProps
|
||||||
customers: ListCustomers_customers_edges_node[];
|
customers: ListCustomers_customers_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const numberOfColumns = 4;
|
||||||
|
|
||||||
const CustomerList = withStyles(styles, { name: "CustomerList" })(
|
const CustomerList = withStyles(styles, { name: "CustomerList" })(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
|
settings,
|
||||||
disabled,
|
disabled,
|
||||||
customers,
|
customers,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
toolbar,
|
toolbar,
|
||||||
toggle,
|
toggle,
|
||||||
|
@ -65,6 +69,7 @@ const CustomerList = withStyles(styles, { name: "CustomerList" })(
|
||||||
<Card>
|
<Card>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={customers}
|
items={customers}
|
||||||
|
@ -84,9 +89,11 @@ const CustomerList = withStyles(styles, { name: "CustomerList" })(
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={4}
|
colSpan={numberOfColumns}
|
||||||
|
settings={settings}
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
hasPreviousPage={
|
hasPreviousPage={
|
||||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||||
}
|
}
|
||||||
|
@ -112,6 +119,7 @@ const CustomerList = withStyles(styles, { name: "CustomerList" })(
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(customer.id)}
|
onChange={() => toggle(customer.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -132,7 +140,7 @@ const CustomerList = withStyles(styles, { name: "CustomerList" })(
|
||||||
},
|
},
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={4}>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
{i18n.t("No customers found")}
|
{i18n.t("No customers found")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
@ -4,8 +4,8 @@ import React from "react";
|
||||||
|
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { ListActions, PageListProps } from "../../../types";
|
import { ListActions, PageListProps } from "@saleor/types";
|
||||||
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
import { ListCustomers_customers_edges_node } from "../../types/ListCustomers";
|
||||||
import CustomerList from "../CustomerList/CustomerList";
|
import CustomerList from "../CustomerList/CustomerList";
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,7 @@ export interface AddressTypeInput {
|
||||||
city: string;
|
city: string;
|
||||||
cityArea?: string;
|
cityArea?: string;
|
||||||
companyName?: string;
|
companyName?: string;
|
||||||
country: {
|
country: string;
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
countryArea?: string;
|
countryArea?: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
|
|
|
@ -133,6 +133,16 @@ const CustomerAddresses: React.FC<CustomerAddressesProps> = ({
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const countryChoices = maybe(
|
||||||
|
() =>
|
||||||
|
shop.countries.map(country => ({
|
||||||
|
code: country.code,
|
||||||
|
label: country.country
|
||||||
|
})),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle
|
<WindowTitle
|
||||||
|
@ -156,14 +166,7 @@ const CustomerAddresses: React.FC<CustomerAddressesProps> = ({
|
||||||
<CustomerAddressDialog
|
<CustomerAddressDialog
|
||||||
address={undefined}
|
address={undefined}
|
||||||
confirmButtonState={createAddressTransitionState}
|
confirmButtonState={createAddressTransitionState}
|
||||||
countries={maybe(
|
countries={countryChoices}
|
||||||
() =>
|
|
||||||
shop.countries.map(country => ({
|
|
||||||
code: country.code,
|
|
||||||
label: country.country
|
|
||||||
})),
|
|
||||||
[]
|
|
||||||
)}
|
|
||||||
errors={maybe(
|
errors={maybe(
|
||||||
() =>
|
() =>
|
||||||
createCustomerAddressOpts.data.addressCreate
|
createCustomerAddressOpts.data.addressCreate
|
||||||
|
@ -173,14 +176,11 @@ const CustomerAddresses: React.FC<CustomerAddressesProps> = ({
|
||||||
open={params.action === "add"}
|
open={params.action === "add"}
|
||||||
variant="create"
|
variant="create"
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
onConfirm={formData =>
|
onConfirm={input =>
|
||||||
createCustomerAddress({
|
createCustomerAddress({
|
||||||
variables: {
|
variables: {
|
||||||
id,
|
id,
|
||||||
input: {
|
input
|
||||||
...formData,
|
|
||||||
country: formData.country.value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ const CustomerAddresses: React.FC<CustomerAddressesProps> = ({
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
confirmButtonState={updateAddressTransitionState}
|
confirmButtonState={updateAddressTransitionState}
|
||||||
countries={[]}
|
countries={countryChoices}
|
||||||
errors={maybe(
|
errors={maybe(
|
||||||
() =>
|
() =>
|
||||||
updateCustomerAddressOpts.data.addressUpdate
|
updateCustomerAddressOpts.data.addressUpdate
|
||||||
|
@ -202,14 +202,11 @@ const CustomerAddresses: React.FC<CustomerAddressesProps> = ({
|
||||||
open={params.action === "edit"}
|
open={params.action === "edit"}
|
||||||
variant="edit"
|
variant="edit"
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
onConfirm={formData =>
|
onConfirm={input =>
|
||||||
updateCustomerAddress({
|
updateCustomerAddress({
|
||||||
variables: {
|
variables: {
|
||||||
id: params.id,
|
id: params.id,
|
||||||
input: {
|
input
|
||||||
...formData,
|
|
||||||
country: formData.country.value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,14 +67,8 @@ export const CustomerCreate: React.StatelessComponent<{}> = () => {
|
||||||
createCustomer({
|
createCustomer({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
defaultBillingAddress: {
|
defaultBillingAddress: address,
|
||||||
...address,
|
defaultShippingAddress: address,
|
||||||
country: address.country.value
|
|
||||||
},
|
|
||||||
defaultShippingAddress: {
|
|
||||||
...address,
|
|
||||||
country: address.country.value
|
|
||||||
},
|
|
||||||
email: formData.email,
|
email: formData.email,
|
||||||
firstName: formData.customerFirstName,
|
firstName: formData.customerFirstName,
|
||||||
lastName: formData.customerLastName,
|
lastName: formData.customerLastName,
|
||||||
|
|
|
@ -5,14 +5,15 @@ import React from "react";
|
||||||
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import usePaginator, {
|
import usePaginator, {
|
||||||
createPaginationState
|
createPaginationState
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import { PAGINATE_BY } from "../../config";
|
import i18n from "@saleor/i18n";
|
||||||
import i18n from "../../i18n";
|
import { getMutationState, maybe } from "@saleor/misc";
|
||||||
import { getMutationState, maybe } from "../../misc";
|
import { ListViews } from "@saleor/types";
|
||||||
import CustomerListPage from "../components/CustomerListPage";
|
import CustomerListPage from "../components/CustomerListPage";
|
||||||
import { TypedBulkRemoveCustomers } from "../mutations";
|
import { TypedBulkRemoveCustomers } from "../mutations";
|
||||||
import { TypedCustomerListQuery } from "../queries";
|
import { TypedCustomerListQuery } from "../queries";
|
||||||
|
@ -37,6 +38,9 @@ export const CustomerList: React.StatelessComponent<CustomerListProps> = ({
|
||||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||||
params.ids
|
params.ids
|
||||||
);
|
);
|
||||||
|
const { updateListSettings, settings } = useListSettings(
|
||||||
|
ListViews.CUSTOMER_LIST
|
||||||
|
);
|
||||||
|
|
||||||
const closeModal = () =>
|
const closeModal = () =>
|
||||||
navigate(
|
navigate(
|
||||||
|
@ -48,7 +52,7 @@ export const CustomerList: React.StatelessComponent<CustomerListProps> = ({
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedCustomerListQuery displayLoader variables={paginationState}>
|
<TypedCustomerListQuery displayLoader variables={paginationState}>
|
||||||
|
@ -87,11 +91,13 @@ export const CustomerList: React.StatelessComponent<CustomerListProps> = ({
|
||||||
customers={maybe(() =>
|
customers={maybe(() =>
|
||||||
data.customers.edges.map(edge => edge.node)
|
data.customers.edges.map(edge => edge.node)
|
||||||
)}
|
)}
|
||||||
|
settings={settings}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
onAdd={() => navigate(customerAddUrl)}
|
onAdd={() => navigate(customerAddUrl)}
|
||||||
onNextPage={loadNextPage}
|
onNextPage={loadNextPage}
|
||||||
onPreviousPage={loadPreviousPage}
|
onPreviousPage={loadPreviousPage}
|
||||||
|
onUpdateListSettings={updateListSettings}
|
||||||
onRowClick={id => () => navigate(customerUrl(id))}
|
onRowClick={id => () => navigate(customerUrl(id))}
|
||||||
toolbar={
|
toolbar={
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|
|
@ -50,6 +50,9 @@ const styles = (theme: Theme) =>
|
||||||
width: "60%"
|
width: "60%"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const numberOfColumns = 4;
|
||||||
|
|
||||||
const DiscountCategories = withStyles(styles, {
|
const DiscountCategories = withStyles(styles, {
|
||||||
name: "DiscountCategories"
|
name: "DiscountCategories"
|
||||||
})(
|
})(
|
||||||
|
@ -71,9 +74,7 @@ const DiscountCategories = withStyles(styles, {
|
||||||
}: DiscountCategoriesProps & WithStyles<typeof styles>) => (
|
}: DiscountCategoriesProps & WithStyles<typeof styles>) => (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
title={i18n.t("Categories assigned to {{ saleName }}", {
|
title={i18n.t("Eligible Categories")}
|
||||||
saleName: maybe(() => sale.name)
|
|
||||||
})}
|
|
||||||
toolbar={
|
toolbar={
|
||||||
<Button color="primary" onClick={onCategoryAssign}>
|
<Button color="primary" onClick={onCategoryAssign}>
|
||||||
{i18n.t("Assign categories")}
|
{i18n.t("Assign categories")}
|
||||||
|
@ -82,6 +83,7 @@ const DiscountCategories = withStyles(styles, {
|
||||||
/>
|
/>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={maybe(() => sale.categories.edges.map(edge => edge.node))}
|
items={maybe(() => sale.categories.edges.map(edge => edge.node))}
|
||||||
|
@ -89,7 +91,6 @@ const DiscountCategories = withStyles(styles, {
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
<TableCell />
|
|
||||||
<TableCell className={classes.wideColumn}>
|
<TableCell className={classes.wideColumn}>
|
||||||
{i18n.t("Category name")}
|
{i18n.t("Category name")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -102,7 +103,7 @@ const DiscountCategories = withStyles(styles, {
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={4}
|
colSpan={numberOfColumns}
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
hasPreviousPage={
|
hasPreviousPage={
|
||||||
|
@ -130,6 +131,7 @@ const DiscountCategories = withStyles(styles, {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(category.id)}
|
onChange={() => toggle(category.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -158,7 +160,7 @@ const DiscountCategories = withStyles(styles, {
|
||||||
},
|
},
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={4}>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
{i18n.t("No categories found")}
|
{i18n.t("No categories found")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
@ -50,6 +50,9 @@ const styles = (theme: Theme) =>
|
||||||
width: "60%"
|
width: "60%"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const numberOfColumns = 4;
|
||||||
|
|
||||||
const DiscountCollections = withStyles(styles, {
|
const DiscountCollections = withStyles(styles, {
|
||||||
name: "DiscountCollections"
|
name: "DiscountCollections"
|
||||||
})(
|
})(
|
||||||
|
@ -71,9 +74,7 @@ const DiscountCollections = withStyles(styles, {
|
||||||
}: DiscountCollectionsProps & WithStyles<typeof styles>) => (
|
}: DiscountCollectionsProps & WithStyles<typeof styles>) => (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
title={i18n.t("Collections assigned to {{ saleName }}", {
|
title={i18n.t("Eligible Collections")}
|
||||||
saleName: maybe(() => sale.name)
|
|
||||||
})}
|
|
||||||
toolbar={
|
toolbar={
|
||||||
<Button color="primary" onClick={onCollectionAssign}>
|
<Button color="primary" onClick={onCollectionAssign}>
|
||||||
{i18n.t("Assign collections")}
|
{i18n.t("Assign collections")}
|
||||||
|
@ -82,6 +83,7 @@ const DiscountCollections = withStyles(styles, {
|
||||||
/>
|
/>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={maybe(() => sale.collections.edges.map(edge => edge.node))}
|
items={maybe(() => sale.collections.edges.map(edge => edge.node))}
|
||||||
|
@ -94,11 +96,12 @@ const DiscountCollections = withStyles(styles, {
|
||||||
<TableCell className={classes.textRight}>
|
<TableCell className={classes.textRight}>
|
||||||
{i18n.t("Products")}
|
{i18n.t("Products")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={4}
|
colSpan={numberOfColumns}
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
hasPreviousPage={
|
hasPreviousPage={
|
||||||
|
@ -125,6 +128,7 @@ const DiscountCollections = withStyles(styles, {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(collection.id)}
|
onChange={() => toggle(collection.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -156,7 +160,7 @@ const DiscountCollections = withStyles(styles, {
|
||||||
},
|
},
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={4}>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
{i18n.t("No collections found")}
|
{i18n.t("No collections found")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
@ -19,7 +19,9 @@ import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Checkbox from "@saleor/components/Checkbox";
|
import Checkbox from "@saleor/components/Checkbox";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import StatusLabel from "@saleor/components/StatusLabel";
|
import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
import TableCellAvatar, {
|
||||||
|
AVATAR_MARGIN
|
||||||
|
} from "@saleor/components/TableCellAvatar";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
|
@ -36,28 +38,34 @@ export interface SaleProductsProps extends ListProps, ListActions {
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
[theme.breakpoints.up("lg")]: {
|
colActions: {
|
||||||
colName: {},
|
|
||||||
colPublished: {
|
|
||||||
width: 150
|
|
||||||
},
|
|
||||||
colType: {
|
|
||||||
width: 200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
colName: {},
|
|
||||||
colPublished: {},
|
|
||||||
colType: {},
|
|
||||||
iconCell: {
|
|
||||||
"&:last-child": {
|
"&:last-child": {
|
||||||
paddingRight: 0
|
paddingRight: 0
|
||||||
},
|
},
|
||||||
width: 48 + theme.spacing.unit / 2
|
width: 48 + theme.spacing.unit / 2
|
||||||
},
|
},
|
||||||
|
colName: {
|
||||||
|
width: "auto"
|
||||||
|
},
|
||||||
|
colNameLabel: {
|
||||||
|
marginLeft: AVATAR_MARGIN
|
||||||
|
},
|
||||||
|
colPublished: {
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
colType: {
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
tableLayout: "fixed"
|
||||||
|
},
|
||||||
tableRow: {
|
tableRow: {
|
||||||
cursor: "pointer"
|
cursor: "pointer"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const numberOfColumns = 5;
|
||||||
|
|
||||||
const DiscountProducts = withStyles(styles, {
|
const DiscountProducts = withStyles(styles, {
|
||||||
name: "DiscountProducts"
|
name: "DiscountProducts"
|
||||||
})(
|
})(
|
||||||
|
@ -79,9 +87,7 @@ const DiscountProducts = withStyles(styles, {
|
||||||
}: SaleProductsProps & WithStyles<typeof styles>) => (
|
}: SaleProductsProps & WithStyles<typeof styles>) => (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
title={i18n.t("Products assigned to {{ saleName }}", {
|
title={i18n.t("Eligible Products")}
|
||||||
saleName: maybe(() => sale.name)
|
|
||||||
})}
|
|
||||||
toolbar={
|
toolbar={
|
||||||
<Button color="primary" onClick={onProductAssign}>
|
<Button color="primary" onClick={onProductAssign}>
|
||||||
{i18n.t("Assign products")}
|
{i18n.t("Assign products")}
|
||||||
|
@ -90,16 +96,17 @@ const DiscountProducts = withStyles(styles, {
|
||||||
/>
|
/>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={maybe(() => sale.products.edges.map(edge => edge.node))}
|
items={maybe(() => sale.products.edges.map(edge => edge.node))}
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell />
|
|
||||||
<TableCell />
|
|
||||||
<TableCell className={classes.colName}>
|
<TableCell className={classes.colName}>
|
||||||
{i18n.t("Product name")}
|
<span className={classes.colNameLabel}>
|
||||||
|
{i18n.t("Product name")}
|
||||||
|
</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.colType}>
|
<TableCell className={classes.colType}>
|
||||||
{i18n.t("Product Type")}
|
{i18n.t("Product Type")}
|
||||||
|
@ -112,7 +119,7 @@ const DiscountProducts = withStyles(styles, {
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={6}
|
colSpan={numberOfColumns}
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
hasPreviousPage={
|
hasPreviousPage={
|
||||||
|
@ -139,15 +146,16 @@ const DiscountProducts = withStyles(styles, {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(product.id)}
|
onChange={() => toggle(product.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCellAvatar
|
<TableCellAvatar
|
||||||
|
className={classes.colName}
|
||||||
thumbnail={maybe(() => product.thumbnail.url)}
|
thumbnail={maybe(() => product.thumbnail.url)}
|
||||||
/>
|
>
|
||||||
<TableCell className={classes.colName}>
|
|
||||||
{maybe<React.ReactNode>(() => product.name, <Skeleton />)}
|
{maybe<React.ReactNode>(() => product.name, <Skeleton />)}
|
||||||
</TableCell>
|
</TableCellAvatar>
|
||||||
<TableCell className={classes.colType}>
|
<TableCell className={classes.colType}>
|
||||||
{maybe<React.ReactNode>(
|
{maybe<React.ReactNode>(
|
||||||
() => product.productType.name,
|
() => product.productType.name,
|
||||||
|
@ -170,7 +178,7 @@ const DiscountProducts = withStyles(styles, {
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.iconCell}>
|
<TableCell className={classes.colActions}>
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={!product || disabled}
|
disabled={!product || disabled}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
|
@ -186,7 +194,9 @@ const DiscountProducts = withStyles(styles, {
|
||||||
},
|
},
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={6}>{i18n.t("No products found")}</TableCell>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
|
{i18n.t("No products found")}
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -19,10 +19,10 @@ import Percent from "@saleor/components/Percent";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "../../../types";
|
import { ListActions, ListProps } from "@saleor/types";
|
||||||
import { SaleType } from "../../../types/globalTypes";
|
import { SaleType } from "@saleor/types/globalTypes";
|
||||||
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
||||||
|
|
||||||
export interface SaleListProps extends ListProps, ListActions {
|
export interface SaleListProps extends ListProps, ListActions {
|
||||||
|
@ -59,15 +59,19 @@ const styles = (theme: Theme) =>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const numberOfColumns = 5;
|
||||||
|
|
||||||
const SaleList = withStyles(styles, {
|
const SaleList = withStyles(styles, {
|
||||||
name: "SaleList"
|
name: "SaleList"
|
||||||
})(
|
})(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
|
settings,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
disabled,
|
disabled,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
sales,
|
sales,
|
||||||
|
@ -80,6 +84,7 @@ const SaleList = withStyles(styles, {
|
||||||
<Card>
|
<Card>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={sales}
|
items={sales}
|
||||||
|
@ -110,9 +115,11 @@ const SaleList = withStyles(styles, {
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={5}
|
colSpan={numberOfColumns}
|
||||||
|
settings={settings}
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
hasPreviousPage={
|
hasPreviousPage={
|
||||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||||
}
|
}
|
||||||
|
@ -138,6 +145,7 @@ const SaleList = withStyles(styles, {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(sale.id)}
|
onChange={() => toggle(sale.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -184,7 +192,9 @@ const SaleList = withStyles(styles, {
|
||||||
},
|
},
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={5}>{i18n.t("No sales found")}</TableCell>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
|
{i18n.t("No sales found")}
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import React from "react";
|
||||||
|
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { ListActions, PageListProps } from "../../../types";
|
import { ListActions, PageListProps } from "@saleor/types";
|
||||||
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
import { SaleList_sales_edges_node } from "../../types/SaleList";
|
||||||
import SaleList from "../SaleList";
|
import SaleList from "../SaleList";
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,9 @@ const SalePricing = withStyles(styles, {
|
||||||
label={i18n.t("Start Date")}
|
label={i18n.t("Start Date")}
|
||||||
value={data.startDate}
|
value={data.startDate}
|
||||||
type="date"
|
type="date"
|
||||||
|
InputLabelProps={{
|
||||||
|
shrink: true
|
||||||
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -107,6 +110,9 @@ const SalePricing = withStyles(styles, {
|
||||||
label={i18n.t("End Date")}
|
label={i18n.t("End Date")}
|
||||||
value={data.endDate}
|
value={data.endDate}
|
||||||
type="date"
|
type="date"
|
||||||
|
InputLabelProps={{
|
||||||
|
shrink: true
|
||||||
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
@ -11,22 +11,33 @@ import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
import { UserError } from "../../../types";
|
import { UserError } from "../../../types";
|
||||||
import {
|
import {
|
||||||
VoucherDiscountValueType,
|
DiscountValueTypeEnum,
|
||||||
VoucherType
|
VoucherTypeEnum
|
||||||
} from "../../../types/globalTypes";
|
} from "../../../types/globalTypes";
|
||||||
|
import { RequirementsPicker } from "../../types";
|
||||||
|
import VoucherDates from "../VoucherDates";
|
||||||
import VoucherInfo from "../VoucherInfo";
|
import VoucherInfo from "../VoucherInfo";
|
||||||
import VoucherOptions from "../VoucherOptions";
|
import VoucherLimits from "../VoucherLimits";
|
||||||
|
import VoucherRequirements from "../VoucherRequirements";
|
||||||
|
import VoucherTypes from "../VoucherTypes";
|
||||||
|
|
||||||
|
import VoucherValue from "../VoucherValue";
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
|
applyOncePerCustomer: boolean;
|
||||||
applyOncePerOrder: boolean;
|
applyOncePerOrder: boolean;
|
||||||
code: string;
|
code: string;
|
||||||
discountType: VoucherDiscountValueType;
|
discountType: DiscountValueTypeEnum;
|
||||||
endDate: string;
|
endDate: string;
|
||||||
minAmountSpent: number;
|
endTime: string;
|
||||||
name: string;
|
hasEndDate: boolean;
|
||||||
|
hasUsageLimit: boolean;
|
||||||
|
minAmountSpent: string;
|
||||||
|
minCheckoutItemsQuantity: string;
|
||||||
|
requirementsPicker: RequirementsPicker;
|
||||||
startDate: string;
|
startDate: string;
|
||||||
type: VoucherType;
|
startTime: string;
|
||||||
usageLimit: number;
|
type: VoucherTypeEnum;
|
||||||
|
usageLimit: string;
|
||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,15 +59,21 @@ const VoucherCreatePage: React.StatelessComponent<VoucherCreatePageProps> = ({
|
||||||
onSubmit
|
onSubmit
|
||||||
}) => {
|
}) => {
|
||||||
const initialForm: FormData = {
|
const initialForm: FormData = {
|
||||||
|
applyOncePerCustomer: false,
|
||||||
applyOncePerOrder: false,
|
applyOncePerOrder: false,
|
||||||
code: "",
|
code: "",
|
||||||
discountType: VoucherDiscountValueType.FIXED,
|
discountType: DiscountValueTypeEnum.FIXED,
|
||||||
endDate: "",
|
endDate: "",
|
||||||
minAmountSpent: 0,
|
endTime: "",
|
||||||
name: "",
|
hasEndDate: false,
|
||||||
|
hasUsageLimit: false,
|
||||||
|
minAmountSpent: "0",
|
||||||
|
minCheckoutItemsQuantity: "0",
|
||||||
|
requirementsPicker: RequirementsPicker.NONE,
|
||||||
startDate: "",
|
startDate: "",
|
||||||
type: VoucherType.VALUE,
|
startTime: "",
|
||||||
usageLimit: 0,
|
type: VoucherTypeEnum.ENTIRE_ORDER,
|
||||||
|
usageLimit: "0",
|
||||||
value: 0
|
value: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,11 +89,28 @@ const VoucherCreatePage: React.StatelessComponent<VoucherCreatePageProps> = ({
|
||||||
data={data}
|
data={data}
|
||||||
errors={formErrors}
|
errors={formErrors}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
variant="create"
|
|
||||||
onChange={change}
|
onChange={change}
|
||||||
|
variant="create"
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<VoucherOptions
|
<VoucherTypes
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
{data.discountType.toString() !== "SHIPPING" ? (
|
||||||
|
<VoucherValue
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
defaultCurrency={defaultCurrency}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
variant="create"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<CardSpacer />
|
||||||
|
<VoucherRequirements
|
||||||
data={data}
|
data={data}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
defaultCurrency={defaultCurrency}
|
defaultCurrency={defaultCurrency}
|
||||||
|
@ -84,6 +118,21 @@ const VoucherCreatePage: React.StatelessComponent<VoucherCreatePageProps> = ({
|
||||||
onChange={change}
|
onChange={change}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
|
<VoucherLimits
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
defaultCurrency={defaultCurrency}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
<CardSpacer />
|
||||||
|
<VoucherDates
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
defaultCurrency={defaultCurrency}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
<SaveButtonBar
|
<SaveButtonBar
|
||||||
|
|
|
@ -11,26 +11,32 @@ import Grid from "@saleor/components/Grid";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||||
import { Tab, TabContainer } from "@saleor/components/Tab";
|
import { Tab, TabContainer } from "@saleor/components/Tab";
|
||||||
|
import { RequirementsPicker } from "@saleor/discounts/types";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
import { maybe } from "../../../misc";
|
import { maybe, splitDateTime } from "../../../misc";
|
||||||
import { ListProps, TabListActions, UserError } from "../../../types";
|
import { ListProps, TabListActions, UserError } from "../../../types";
|
||||||
import {
|
import {
|
||||||
VoucherDiscountValueType,
|
DiscountValueTypeEnum,
|
||||||
VoucherType
|
VoucherTypeEnum
|
||||||
} from "../../../types/globalTypes";
|
} from "../../../types/globalTypes";
|
||||||
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
||||||
import DiscountCategories from "../DiscountCategories";
|
import DiscountCategories from "../DiscountCategories";
|
||||||
import DiscountCollections from "../DiscountCollections";
|
import DiscountCollections from "../DiscountCollections";
|
||||||
import DiscountProducts from "../DiscountProducts";
|
import DiscountProducts from "../DiscountProducts";
|
||||||
|
import VoucherDates from "../VoucherDates";
|
||||||
import VoucherInfo from "../VoucherInfo";
|
import VoucherInfo from "../VoucherInfo";
|
||||||
import VoucherOptions from "../VoucherOptions";
|
import VoucherLimits from "../VoucherLimits";
|
||||||
|
import VoucherRequirements from "../VoucherRequirements";
|
||||||
import VoucherSummary from "../VoucherSummary";
|
import VoucherSummary from "../VoucherSummary";
|
||||||
|
import VoucherTypes from "../VoucherTypes";
|
||||||
|
import VoucherValue from "../VoucherValue";
|
||||||
|
|
||||||
export enum VoucherDetailsPageTab {
|
export enum VoucherDetailsPageTab {
|
||||||
categories = "categories",
|
categories = "categories",
|
||||||
collections = "collections",
|
collections = "collections",
|
||||||
products = "products"
|
products = "products"
|
||||||
}
|
}
|
||||||
|
|
||||||
export function voucherDetailsPageTab(tab: string): VoucherDetailsPageTab {
|
export function voucherDetailsPageTab(tab: string): VoucherDetailsPageTab {
|
||||||
return tab === VoucherDetailsPageTab.products
|
return tab === VoucherDetailsPageTab.products
|
||||||
? VoucherDetailsPageTab.products
|
? VoucherDetailsPageTab.products
|
||||||
|
@ -40,15 +46,21 @@ export function voucherDetailsPageTab(tab: string): VoucherDetailsPageTab {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
|
applyOncePerCustomer: boolean;
|
||||||
applyOncePerOrder: boolean;
|
applyOncePerOrder: boolean;
|
||||||
code: string;
|
code: string;
|
||||||
discountType: VoucherDiscountValueType;
|
discountType: DiscountValueTypeEnum;
|
||||||
endDate: string;
|
endDate: string;
|
||||||
minAmountSpent: number;
|
endTime: string;
|
||||||
name: string;
|
hasEndDate: boolean;
|
||||||
|
hasUsageLimit: boolean;
|
||||||
|
minAmountSpent: string;
|
||||||
|
minCheckoutItemsQuantity: string;
|
||||||
|
requirementsPicker: RequirementsPicker;
|
||||||
startDate: string;
|
startDate: string;
|
||||||
type: VoucherType;
|
startTime: string;
|
||||||
usageLimit: number;
|
type: VoucherTypeEnum;
|
||||||
|
usageLimit: string;
|
||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,19 +128,37 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
|
||||||
collectionListToolbar,
|
collectionListToolbar,
|
||||||
productListToolbar
|
productListToolbar
|
||||||
}) => {
|
}) => {
|
||||||
|
let requirementsPickerInitValue;
|
||||||
|
if (maybe(() => voucher.minAmountSpent.amount) > 0) {
|
||||||
|
requirementsPickerInitValue = RequirementsPicker.ORDER;
|
||||||
|
} else if (maybe(() => voucher.minCheckoutItemsQuantity) > 0) {
|
||||||
|
requirementsPickerInitValue = RequirementsPicker.ITEM;
|
||||||
|
} else {
|
||||||
|
requirementsPickerInitValue = RequirementsPicker.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
const initialForm: FormData = {
|
const initialForm: FormData = {
|
||||||
|
applyOncePerCustomer: maybe(() => voucher.applyOncePerCustomer, false),
|
||||||
applyOncePerOrder: maybe(() => voucher.applyOncePerOrder, false),
|
applyOncePerOrder: maybe(() => voucher.applyOncePerOrder, false),
|
||||||
code: maybe(() => voucher.code, ""),
|
code: maybe(() => voucher.code, ""),
|
||||||
discountType: maybe(
|
discountType: maybe(
|
||||||
() => voucher.discountValueType,
|
() => voucher.discountValueType,
|
||||||
VoucherDiscountValueType.FIXED
|
DiscountValueTypeEnum.FIXED
|
||||||
),
|
),
|
||||||
endDate: maybe(() => voucher.endDate, ""),
|
endDate: splitDateTime(maybe(() => voucher.endDate, "")).date,
|
||||||
minAmountSpent: maybe(() => voucher.minAmountSpent.amount, 0),
|
endTime: splitDateTime(maybe(() => voucher.endDate, "")).time,
|
||||||
name: maybe(() => voucher.name, ""),
|
hasEndDate: maybe(() => !!voucher.endDate),
|
||||||
startDate: maybe(() => voucher.startDate, ""),
|
hasUsageLimit: maybe(() => !!voucher.usageLimit),
|
||||||
type: maybe(() => voucher.type, VoucherType.VALUE),
|
minAmountSpent: maybe(() => voucher.minAmountSpent.amount.toString(), "0"),
|
||||||
usageLimit: maybe(() => voucher.usageLimit || 0, 0),
|
minCheckoutItemsQuantity: maybe(
|
||||||
|
() => voucher.minCheckoutItemsQuantity.toString(),
|
||||||
|
"0"
|
||||||
|
),
|
||||||
|
requirementsPicker: requirementsPickerInitValue,
|
||||||
|
startDate: splitDateTime(maybe(() => voucher.startDate, "")).date,
|
||||||
|
startTime: splitDateTime(maybe(() => voucher.startDate, "")).time,
|
||||||
|
type: maybe(() => voucher.type, VoucherTypeEnum.ENTIRE_ORDER),
|
||||||
|
usageLimit: maybe(() => voucher.usageLimit.toString(), "0"),
|
||||||
value: maybe(() => voucher.discountValue, 0)
|
value: maybe(() => voucher.discountValue, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,28 +167,37 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
|
||||||
{({ change, data, errors: formErrors, hasChanged, submit }) => (
|
{({ change, data, errors: formErrors, hasChanged, submit }) => (
|
||||||
<Container>
|
<Container>
|
||||||
<AppHeader onBack={onBack}>{i18n.t("Vouchers")}</AppHeader>
|
<AppHeader onBack={onBack}>{i18n.t("Vouchers")}</AppHeader>
|
||||||
<PageHeader title={maybe(() => voucher.name)} />
|
<PageHeader title={maybe(() => voucher.code)} />
|
||||||
<Grid>
|
<Grid>
|
||||||
<div>
|
<div>
|
||||||
<VoucherInfo
|
<VoucherInfo
|
||||||
data={data}
|
data={data}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
errors={formErrors}
|
errors={formErrors}
|
||||||
variant="update"
|
|
||||||
onChange={change}
|
onChange={change}
|
||||||
|
variant="update"
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<VoucherOptions
|
<VoucherTypes
|
||||||
data={data}
|
data={data}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
defaultCurrency={defaultCurrency}
|
|
||||||
errors={formErrors}
|
errors={formErrors}
|
||||||
onChange={change}
|
onChange={change}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
{data.type === VoucherType.CATEGORY ||
|
{data.discountType.toString() !== "SHIPPING" ? (
|
||||||
data.type === VoucherType.COLLECTION ||
|
<VoucherValue
|
||||||
data.type === VoucherType.PRODUCT ? (
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
defaultCurrency={defaultCurrency}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
variant="update"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<CardSpacer />
|
||||||
|
{data.type === VoucherTypeEnum.SPECIFIC_PRODUCT &&
|
||||||
|
data.discountType.toString() !== "SHIPPING" ? (
|
||||||
<>
|
<>
|
||||||
<TabContainer>
|
<TabContainer>
|
||||||
<CategoriesTab
|
<CategoriesTab
|
||||||
|
@ -246,16 +285,16 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : data.type === VoucherType.SHIPPING ? (
|
) : null}
|
||||||
|
<CardSpacer />
|
||||||
|
{data.discountType.toString() === "SHIPPING" ? (
|
||||||
<CountryList
|
<CountryList
|
||||||
countries={maybe(() => voucher.countries)}
|
countries={maybe(() => voucher.countries)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
emptyText={i18n.t("Voucher applies to all countries")}
|
emptyText={i18n.t("Voucher applies to all countries")}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
{i18n.t("Countries assigned to {{ voucherName }}", {
|
{i18n.t("Countries")}
|
||||||
voucherName: maybe(() => voucher.name)
|
|
||||||
})}
|
|
||||||
<Typography variant="caption">
|
<Typography variant="caption">
|
||||||
{i18n.t("Vouchers limited to these countries")}
|
{i18n.t("Vouchers limited to these countries")}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -265,6 +304,30 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
|
||||||
onCountryUnassign={onCountryUnassign}
|
onCountryUnassign={onCountryUnassign}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
<CardSpacer />
|
||||||
|
<VoucherRequirements
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
defaultCurrency={defaultCurrency}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
<CardSpacer />
|
||||||
|
<VoucherLimits
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
defaultCurrency={defaultCurrency}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
<CardSpacer />
|
||||||
|
<VoucherDates
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
defaultCurrency={defaultCurrency}
|
||||||
|
errors={formErrors}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<VoucherSummary
|
<VoucherSummary
|
||||||
|
|
|
@ -1,101 +1,63 @@
|
||||||
import Card from "@material-ui/core/Card";
|
import Card from "@material-ui/core/Card";
|
||||||
import CardContent from "@material-ui/core/CardContent";
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
import {
|
|
||||||
createStyles,
|
|
||||||
Theme,
|
|
||||||
withStyles,
|
|
||||||
WithStyles
|
|
||||||
} from "@material-ui/core/styles";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import FormSpacer from "@saleor/components/FormSpacer";
|
|
||||||
import SingleSelectField from "@saleor/components/SingleSelectField";
|
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
|
import { generateCode } from "../../../misc";
|
||||||
import { FormErrors } from "../../../types";
|
import { FormErrors } from "../../../types";
|
||||||
import { VoucherType } from "../../../types/globalTypes";
|
|
||||||
import { translateVoucherTypes } from "../../translations";
|
|
||||||
import { FormData } from "../VoucherDetailsPage";
|
import { FormData } from "../VoucherDetailsPage";
|
||||||
|
|
||||||
interface VoucherInfoProps {
|
interface VoucherInfoProps {
|
||||||
data: FormData;
|
data: FormData;
|
||||||
errors: FormErrors<"name" | "code" | "type">;
|
errors: FormErrors<"code">;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
variant: "create" | "update";
|
variant: "create" | "update";
|
||||||
onChange: (event: React.ChangeEvent<any>) => void;
|
onChange: (event: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const VoucherInfo = ({
|
||||||
createStyles({
|
data,
|
||||||
nameInput: {
|
disabled,
|
||||||
gridColumnEnd: "span 2"
|
errors,
|
||||||
},
|
variant,
|
||||||
root: {
|
onChange
|
||||||
display: "grid",
|
}: VoucherInfoProps) => {
|
||||||
gridColumnGap: theme.spacing.unit * 2 + "px",
|
const onGenerateCode = () =>
|
||||||
gridTemplateColumns: "1fr 1fr"
|
onChange({
|
||||||
}
|
target: {
|
||||||
});
|
name: "code",
|
||||||
|
value: generateCode(10)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const VoucherInfo = withStyles(styles, {
|
return (
|
||||||
name: "VoucherInfo"
|
<Card>
|
||||||
})(
|
<CardTitle
|
||||||
({
|
title={i18n.t("General Information")}
|
||||||
classes,
|
toolbar={
|
||||||
data,
|
variant === "create" && (
|
||||||
disabled,
|
<Button color="primary" onClick={onGenerateCode}>
|
||||||
errors,
|
{i18n.t("Generate Code")}
|
||||||
variant,
|
</Button>
|
||||||
onChange
|
)
|
||||||
}: VoucherInfoProps & WithStyles<typeof styles>) => {
|
}
|
||||||
const translatedVoucherTypes = translateVoucherTypes();
|
/>
|
||||||
const voucherTypeChoices = Object.values(VoucherType).map(type => ({
|
<CardContent>
|
||||||
label: translatedVoucherTypes[type],
|
<TextField
|
||||||
value: type
|
disabled={variant === "update" || disabled}
|
||||||
}));
|
error={!!errors.code}
|
||||||
|
fullWidth
|
||||||
return (
|
helperText={errors.code}
|
||||||
<Card>
|
name={"code" as keyof FormData}
|
||||||
<CardTitle title={i18n.t("General Information")} />
|
label={i18n.t("Discount Code")}
|
||||||
<CardContent>
|
value={data.code}
|
||||||
<TextField
|
onChange={onChange}
|
||||||
className={classes.nameInput}
|
/>
|
||||||
disabled={disabled}
|
</CardContent>
|
||||||
error={!!errors.name}
|
</Card>
|
||||||
fullWidth
|
);
|
||||||
helperText={errors.name}
|
};
|
||||||
name={"name" as keyof FormData}
|
|
||||||
label={i18n.t("Name")}
|
|
||||||
value={data.name}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<FormSpacer />
|
|
||||||
<div className={classes.root}>
|
|
||||||
<TextField
|
|
||||||
disabled={disabled}
|
|
||||||
error={!!errors.code}
|
|
||||||
fullWidth
|
|
||||||
helperText={errors.code}
|
|
||||||
name={"code" as keyof FormData}
|
|
||||||
label={i18n.t("Discount Code")}
|
|
||||||
value={data.code}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<SingleSelectField
|
|
||||||
choices={voucherTypeChoices}
|
|
||||||
disabled={disabled || variant === "update"}
|
|
||||||
error={!!errors.type}
|
|
||||||
hint={errors.type}
|
|
||||||
name={"type" as keyof FormData}
|
|
||||||
label={i18n.t("Type of Discount")}
|
|
||||||
value={data.type}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
export default VoucherInfo;
|
export default VoucherInfo;
|
||||||
|
|
|
@ -19,10 +19,10 @@ import Percent from "@saleor/components/Percent";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "../../../types";
|
import { ListActions, ListProps } from "@saleor/types";
|
||||||
import { VoucherDiscountValueType } from "../../../types/globalTypes";
|
import { DiscountValueTypeEnum } from "@saleor/types/globalTypes";
|
||||||
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
||||||
|
|
||||||
export interface VoucherListProps extends ListProps, ListActions {
|
export interface VoucherListProps extends ListProps, ListActions {
|
||||||
|
@ -74,15 +74,19 @@ const styles = (theme: Theme) =>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const numberOfColumns = 7;
|
||||||
|
|
||||||
const VoucherList = withStyles(styles, {
|
const VoucherList = withStyles(styles, {
|
||||||
name: "VoucherList"
|
name: "VoucherList"
|
||||||
})(
|
})(
|
||||||
({
|
({
|
||||||
classes,
|
classes,
|
||||||
|
settings,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
disabled,
|
disabled,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
vouchers,
|
vouchers,
|
||||||
|
@ -95,6 +99,7 @@ const VoucherList = withStyles(styles, {
|
||||||
<Card>
|
<Card>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead
|
<TableHead
|
||||||
|
colSpan={numberOfColumns}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
items={vouchers}
|
items={vouchers}
|
||||||
|
@ -102,7 +107,7 @@ const VoucherList = withStyles(styles, {
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCell className={classes.colName}>
|
||||||
{i18n.t("Name", {
|
{i18n.t("Code", {
|
||||||
context: "voucher list table header"
|
context: "voucher list table header"
|
||||||
})}
|
})}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -135,9 +140,11 @@ const VoucherList = withStyles(styles, {
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
colSpan={7}
|
colSpan={numberOfColumns}
|
||||||
|
settings={settings}
|
||||||
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
hasPreviousPage={
|
hasPreviousPage={
|
||||||
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
|
||||||
}
|
}
|
||||||
|
@ -163,11 +170,12 @@ const VoucherList = withStyles(styles, {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
disableClickPropagation
|
||||||
onChange={() => toggle(voucher.id)}
|
onChange={() => toggle(voucher.id)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.colName}>
|
<TableCell className={classes.colName}>
|
||||||
{maybe<React.ReactNode>(() => voucher.name, <Skeleton />)}
|
{maybe<React.ReactNode>(() => voucher.code, <Skeleton />)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.colMinSpent}>
|
<TableCell className={classes.colMinSpent}>
|
||||||
{voucher && voucher.minAmountSpent ? (
|
{voucher && voucher.minAmountSpent ? (
|
||||||
|
@ -202,7 +210,7 @@ const VoucherList = withStyles(styles, {
|
||||||
voucher.discountValueType &&
|
voucher.discountValueType &&
|
||||||
voucher.discountValue ? (
|
voucher.discountValue ? (
|
||||||
voucher.discountValueType ===
|
voucher.discountValueType ===
|
||||||
VoucherDiscountValueType.FIXED ? (
|
DiscountValueTypeEnum.FIXED ? (
|
||||||
<Money
|
<Money
|
||||||
money={{
|
money={{
|
||||||
amount: voucher.discountValue,
|
amount: voucher.discountValue,
|
||||||
|
@ -230,7 +238,9 @@ const VoucherList = withStyles(styles, {
|
||||||
},
|
},
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={7}>{i18n.t("No vouchers found")}</TableCell>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
|
{i18n.t("No vouchers found")}
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import React from "react";
|
||||||
|
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "@saleor/i18n";
|
||||||
import { ListActions, PageListProps } from "../../../types";
|
import { ListActions, PageListProps } from "@saleor/types";
|
||||||
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
||||||
import VoucherList from "../VoucherList";
|
import VoucherList from "../VoucherList";
|
||||||
|
|
||||||
|
@ -17,9 +17,11 @@ export interface VoucherListPageProps extends PageListProps, ListActions {
|
||||||
const VoucherListPage: React.StatelessComponent<VoucherListPageProps> = ({
|
const VoucherListPage: React.StatelessComponent<VoucherListPageProps> = ({
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
disabled,
|
disabled,
|
||||||
|
settings,
|
||||||
onAdd,
|
onAdd,
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
vouchers,
|
vouchers,
|
||||||
|
@ -38,9 +40,11 @@ const VoucherListPage: React.StatelessComponent<VoucherListPageProps> = ({
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<VoucherList
|
<VoucherList
|
||||||
defaultCurrency={defaultCurrency}
|
defaultCurrency={defaultCurrency}
|
||||||
|
settings={settings}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onNextPage={onNextPage}
|
onNextPage={onNextPage}
|
||||||
onPreviousPage={onPreviousPage}
|
onPreviousPage={onPreviousPage}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
onRowClick={onRowClick}
|
onRowClick={onRowClick}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
vouchers={vouchers}
|
vouchers={vouchers}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import Percent from "@saleor/components/Percent";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
import { maybe } from "../../../misc";
|
import { maybe } from "../../../misc";
|
||||||
import { VoucherDiscountValueType } from "../../../types/globalTypes";
|
import { DiscountValueTypeEnum } from "../../../types/globalTypes";
|
||||||
import { translateVoucherTypes } from "../../translations";
|
import { translateVoucherTypes } from "../../translations";
|
||||||
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ const VoucherSummary: React.StatelessComponent<VoucherSummaryProps> = ({
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("Summary")} />
|
<CardTitle title={i18n.t("Summary")} />
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="caption">{i18n.t("Name")}</Typography>
|
<Typography variant="caption">{i18n.t("Code")}</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
{maybe<React.ReactNode>(() => voucher.name, <Skeleton />)}
|
{maybe<React.ReactNode>(() => voucher.code, <Skeleton />)}
|
||||||
</Typography>
|
</Typography>
|
||||||
<FormSpacer />
|
<FormSpacer />
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ const VoucherSummary: React.StatelessComponent<VoucherSummaryProps> = ({
|
||||||
<Typography>
|
<Typography>
|
||||||
{maybe<React.ReactNode>(
|
{maybe<React.ReactNode>(
|
||||||
() =>
|
() =>
|
||||||
voucher.discountValueType === VoucherDiscountValueType.FIXED ? (
|
voucher.discountValueType === DiscountValueTypeEnum.FIXED ? (
|
||||||
<Money
|
<Money
|
||||||
money={{
|
money={{
|
||||||
amount: voucher.discountValue,
|
amount: voucher.discountValue,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||||
import {
|
import {
|
||||||
|
DiscountValueTypeEnum,
|
||||||
SaleType,
|
SaleType,
|
||||||
VoucherDiscountValueType,
|
VoucherTypeEnum
|
||||||
VoucherType
|
|
||||||
} from "../types/globalTypes";
|
} from "../types/globalTypes";
|
||||||
import { SaleDetails_sale } from "./types/SaleDetails";
|
import { SaleDetails_sale } from "./types/SaleDetails";
|
||||||
import { SaleList_sales_edges_node } from "./types/SaleList";
|
import { SaleList_sales_edges_node } from "./types/SaleList";
|
||||||
|
@ -60,6 +60,7 @@ export const saleList: SaleList_sales_edges_node[] = [
|
||||||
export const voucherList: VoucherList_vouchers_edges_node[] = [
|
export const voucherList: VoucherList_vouchers_edges_node[] = [
|
||||||
{
|
{
|
||||||
__typename: "Voucher" as "Voucher",
|
__typename: "Voucher" as "Voucher",
|
||||||
|
code: "FREE2019",
|
||||||
countries: [
|
countries: [
|
||||||
{
|
{
|
||||||
__typename: "CountryDisplay",
|
__typename: "CountryDisplay",
|
||||||
|
@ -68,19 +69,20 @@ export const voucherList: VoucherList_vouchers_edges_node[] = [
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
discountValue: 100,
|
discountValue: 100,
|
||||||
discountValueType: "PERCENTAGE" as VoucherDiscountValueType,
|
discountValueType: "PERCENTAGE" as DiscountValueTypeEnum,
|
||||||
endDate: null,
|
endDate: null,
|
||||||
id: "Vm91Y2hlcjox",
|
id: "Vm91Y2hlcjox",
|
||||||
minAmountSpent: null,
|
minAmountSpent: null,
|
||||||
name: "Free shipping",
|
minCheckoutItemsQuantity: null,
|
||||||
startDate: "2019-01-03",
|
startDate: "2019-01-03",
|
||||||
usageLimit: null
|
usageLimit: null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
__typename: "Voucher" as "Voucher",
|
__typename: "Voucher" as "Voucher",
|
||||||
|
code: "FREE2020",
|
||||||
countries: [],
|
countries: [],
|
||||||
discountValue: 25,
|
discountValue: 25,
|
||||||
discountValueType: "FIXED" as VoucherDiscountValueType,
|
discountValueType: "FIXED" as DiscountValueTypeEnum,
|
||||||
endDate: null,
|
endDate: null,
|
||||||
id: "Vm91Y2hlcjoy",
|
id: "Vm91Y2hlcjoy",
|
||||||
minAmountSpent: {
|
minAmountSpent: {
|
||||||
|
@ -88,7 +90,7 @@ export const voucherList: VoucherList_vouchers_edges_node[] = [
|
||||||
amount: 200,
|
amount: 200,
|
||||||
currency: "USD"
|
currency: "USD"
|
||||||
},
|
},
|
||||||
name: "Big order discount",
|
minCheckoutItemsQuantity: 0,
|
||||||
startDate: "2019-01-03",
|
startDate: "2019-01-03",
|
||||||
usageLimit: 150
|
usageLimit: 150
|
||||||
}
|
}
|
||||||
|
@ -241,6 +243,7 @@ export const sale: SaleDetails_sale = {
|
||||||
|
|
||||||
export const voucherDetails: VoucherDetails_voucher = {
|
export const voucherDetails: VoucherDetails_voucher = {
|
||||||
__typename: "Voucher",
|
__typename: "Voucher",
|
||||||
|
applyOncePerCustomer: false,
|
||||||
applyOncePerOrder: false,
|
applyOncePerOrder: false,
|
||||||
categories: {
|
categories: {
|
||||||
__typename: "CategoryCountableConnection",
|
__typename: "CategoryCountableConnection",
|
||||||
|
@ -275,7 +278,7 @@ export const voucherDetails: VoucherDetails_voucher = {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
discountValue: 25,
|
discountValue: 25,
|
||||||
discountValueType: VoucherDiscountValueType.FIXED,
|
discountValueType: DiscountValueTypeEnum.FIXED,
|
||||||
endDate: null,
|
endDate: null,
|
||||||
id: "Vm91Y2hlcjoy",
|
id: "Vm91Y2hlcjoy",
|
||||||
minAmountSpent: {
|
minAmountSpent: {
|
||||||
|
@ -283,7 +286,7 @@ export const voucherDetails: VoucherDetails_voucher = {
|
||||||
amount: 200,
|
amount: 200,
|
||||||
currency: "USD"
|
currency: "USD"
|
||||||
},
|
},
|
||||||
name: "Big order discount",
|
minCheckoutItemsQuantity: 0,
|
||||||
products: {
|
products: {
|
||||||
__typename: "ProductCountableConnection",
|
__typename: "ProductCountableConnection",
|
||||||
edges: [],
|
edges: [],
|
||||||
|
@ -297,7 +300,7 @@ export const voucherDetails: VoucherDetails_voucher = {
|
||||||
totalCount: 0
|
totalCount: 0
|
||||||
},
|
},
|
||||||
startDate: "2018-11-27",
|
startDate: "2018-11-27",
|
||||||
type: VoucherType.VALUE,
|
type: VoucherTypeEnum.ENTIRE_ORDER,
|
||||||
usageLimit: null,
|
usageLimit: null,
|
||||||
used: 0
|
used: 0
|
||||||
};
|
};
|
||||||
|
|
|
@ -81,7 +81,7 @@ export const saleDetailsFragment = gql`
|
||||||
export const voucherFragment = gql`
|
export const voucherFragment = gql`
|
||||||
fragment VoucherFragment on Voucher {
|
fragment VoucherFragment on Voucher {
|
||||||
id
|
id
|
||||||
name
|
code
|
||||||
startDate
|
startDate
|
||||||
endDate
|
endDate
|
||||||
usageLimit
|
usageLimit
|
||||||
|
@ -95,6 +95,7 @@ export const voucherFragment = gql`
|
||||||
currency
|
currency
|
||||||
amount
|
amount
|
||||||
}
|
}
|
||||||
|
minCheckoutItemsQuantity
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -108,6 +109,7 @@ export const voucherDetailsFragment = gql`
|
||||||
usageLimit
|
usageLimit
|
||||||
used
|
used
|
||||||
applyOncePerOrder
|
applyOncePerOrder
|
||||||
|
applyOncePerCustomer
|
||||||
products(after: $after, before: $before, first: $first, last: $last) {
|
products(after: $after, before: $before, first: $first, last: $last) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import i18n from "../i18n";
|
import i18n from "../i18n";
|
||||||
import { VoucherType } from "../types/globalTypes";
|
import { VoucherTypeEnum } from "../types/globalTypes";
|
||||||
|
|
||||||
export const translateVoucherTypes = () => ({
|
export const translateVoucherTypes = () => ({
|
||||||
[VoucherType.CATEGORY]: i18n.t("Selected Categories"),
|
[VoucherTypeEnum.SHIPPING]: i18n.t("Shipment"),
|
||||||
[VoucherType.COLLECTION]: i18n.t("Selected Collections"),
|
[VoucherTypeEnum.ENTIRE_ORDER]: i18n.t("Entire order"),
|
||||||
[VoucherType.PRODUCT]: i18n.t("Selected Products"),
|
[VoucherTypeEnum.SPECIFIC_PRODUCT]: i18n.t("Specific Products")
|
||||||
[VoucherType.SHIPPING]: i18n.t("Shipment"),
|
|
||||||
[VoucherType.VALUE]: i18n.t("All Products")
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { CatalogueInput, VoucherDiscountValueType, VoucherType } from "./../../types/globalTypes";
|
import { CatalogueInput, DiscountValueTypeEnum, VoucherTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL mutation operation: VoucherCataloguesAdd
|
// GraphQL mutation operation: VoucherCataloguesAdd
|
||||||
|
@ -133,18 +133,19 @@ export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_categories {
|
||||||
export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher {
|
export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher {
|
||||||
__typename: "Voucher";
|
__typename: "Voucher";
|
||||||
id: string;
|
id: string;
|
||||||
name: string | null;
|
code: string;
|
||||||
startDate: any;
|
startDate: any;
|
||||||
endDate: any | null;
|
endDate: any | null;
|
||||||
usageLimit: number | null;
|
usageLimit: number | null;
|
||||||
discountValueType: VoucherDiscountValueType;
|
discountValueType: DiscountValueTypeEnum;
|
||||||
discountValue: number;
|
discountValue: number;
|
||||||
countries: (VoucherCataloguesAdd_voucherCataloguesAdd_voucher_countries | null)[] | null;
|
countries: (VoucherCataloguesAdd_voucherCataloguesAdd_voucher_countries | null)[] | null;
|
||||||
minAmountSpent: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_minAmountSpent | null;
|
minAmountSpent: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_minAmountSpent | null;
|
||||||
type: VoucherType;
|
minCheckoutItemsQuantity: number | null;
|
||||||
code: string;
|
type: VoucherTypeEnum;
|
||||||
used: number;
|
used: number;
|
||||||
applyOncePerOrder: boolean;
|
applyOncePerOrder: boolean;
|
||||||
|
applyOncePerCustomer: boolean;
|
||||||
products: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_products | null;
|
products: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_products | null;
|
||||||
collections: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_collections | null;
|
collections: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_collections | null;
|
||||||
categories: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_categories | null;
|
categories: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_categories | null;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { CatalogueInput, VoucherDiscountValueType, VoucherType } from "./../../types/globalTypes";
|
import { CatalogueInput, DiscountValueTypeEnum, VoucherTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL mutation operation: VoucherCataloguesRemove
|
// GraphQL mutation operation: VoucherCataloguesRemove
|
||||||
|
@ -133,18 +133,19 @@ export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_categor
|
||||||
export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher {
|
export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher {
|
||||||
__typename: "Voucher";
|
__typename: "Voucher";
|
||||||
id: string;
|
id: string;
|
||||||
name: string | null;
|
code: string;
|
||||||
startDate: any;
|
startDate: any;
|
||||||
endDate: any | null;
|
endDate: any | null;
|
||||||
usageLimit: number | null;
|
usageLimit: number | null;
|
||||||
discountValueType: VoucherDiscountValueType;
|
discountValueType: DiscountValueTypeEnum;
|
||||||
discountValue: number;
|
discountValue: number;
|
||||||
countries: (VoucherCataloguesRemove_voucherCataloguesRemove_voucher_countries | null)[] | null;
|
countries: (VoucherCataloguesRemove_voucherCataloguesRemove_voucher_countries | null)[] | null;
|
||||||
minAmountSpent: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_minAmountSpent | null;
|
minAmountSpent: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_minAmountSpent | null;
|
||||||
type: VoucherType;
|
minCheckoutItemsQuantity: number | null;
|
||||||
code: string;
|
type: VoucherTypeEnum;
|
||||||
used: number;
|
used: number;
|
||||||
applyOncePerOrder: boolean;
|
applyOncePerOrder: boolean;
|
||||||
|
applyOncePerCustomer: boolean;
|
||||||
products: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_products | null;
|
products: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_products | null;
|
||||||
collections: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_collections | null;
|
collections: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_collections | null;
|
||||||
categories: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_categories | null;
|
categories: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_categories | null;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { VoucherInput, VoucherDiscountValueType } from "./../../types/globalTypes";
|
import { VoucherInput, DiscountValueTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL mutation operation: VoucherCreate
|
// GraphQL mutation operation: VoucherCreate
|
||||||
|
@ -29,14 +29,15 @@ export interface VoucherCreate_voucherCreate_voucher_minAmountSpent {
|
||||||
export interface VoucherCreate_voucherCreate_voucher {
|
export interface VoucherCreate_voucherCreate_voucher {
|
||||||
__typename: "Voucher";
|
__typename: "Voucher";
|
||||||
id: string;
|
id: string;
|
||||||
name: string | null;
|
code: string;
|
||||||
startDate: any;
|
startDate: any;
|
||||||
endDate: any | null;
|
endDate: any | null;
|
||||||
usageLimit: number | null;
|
usageLimit: number | null;
|
||||||
discountValueType: VoucherDiscountValueType;
|
discountValueType: DiscountValueTypeEnum;
|
||||||
discountValue: number;
|
discountValue: number;
|
||||||
countries: (VoucherCreate_voucherCreate_voucher_countries | null)[] | null;
|
countries: (VoucherCreate_voucherCreate_voucher_countries | null)[] | null;
|
||||||
minAmountSpent: VoucherCreate_voucherCreate_voucher_minAmountSpent | null;
|
minAmountSpent: VoucherCreate_voucherCreate_voucher_minAmountSpent | null;
|
||||||
|
minCheckoutItemsQuantity: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VoucherCreate_voucherCreate {
|
export interface VoucherCreate_voucherCreate {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { VoucherDiscountValueType, VoucherType } from "./../../types/globalTypes";
|
import { DiscountValueTypeEnum, VoucherTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: VoucherDetails
|
// GraphQL query operation: VoucherDetails
|
||||||
|
@ -127,18 +127,19 @@ export interface VoucherDetails_voucher_categories {
|
||||||
export interface VoucherDetails_voucher {
|
export interface VoucherDetails_voucher {
|
||||||
__typename: "Voucher";
|
__typename: "Voucher";
|
||||||
id: string;
|
id: string;
|
||||||
name: string | null;
|
code: string;
|
||||||
startDate: any;
|
startDate: any;
|
||||||
endDate: any | null;
|
endDate: any | null;
|
||||||
usageLimit: number | null;
|
usageLimit: number | null;
|
||||||
discountValueType: VoucherDiscountValueType;
|
discountValueType: DiscountValueTypeEnum;
|
||||||
discountValue: number;
|
discountValue: number;
|
||||||
countries: (VoucherDetails_voucher_countries | null)[] | null;
|
countries: (VoucherDetails_voucher_countries | null)[] | null;
|
||||||
minAmountSpent: VoucherDetails_voucher_minAmountSpent | null;
|
minAmountSpent: VoucherDetails_voucher_minAmountSpent | null;
|
||||||
type: VoucherType;
|
minCheckoutItemsQuantity: number | null;
|
||||||
code: string;
|
type: VoucherTypeEnum;
|
||||||
used: number;
|
used: number;
|
||||||
applyOncePerOrder: boolean;
|
applyOncePerOrder: boolean;
|
||||||
|
applyOncePerCustomer: boolean;
|
||||||
products: VoucherDetails_voucher_products | null;
|
products: VoucherDetails_voucher_products | null;
|
||||||
collections: VoucherDetails_voucher_collections | null;
|
collections: VoucherDetails_voucher_collections | null;
|
||||||
categories: VoucherDetails_voucher_categories | null;
|
categories: VoucherDetails_voucher_categories | null;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { VoucherDiscountValueType, VoucherType } from "./../../types/globalTypes";
|
import { DiscountValueTypeEnum, VoucherTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL fragment: VoucherDetailsFragment
|
// GraphQL fragment: VoucherDetailsFragment
|
||||||
|
@ -127,18 +127,19 @@ export interface VoucherDetailsFragment_categories {
|
||||||
export interface VoucherDetailsFragment {
|
export interface VoucherDetailsFragment {
|
||||||
__typename: "Voucher";
|
__typename: "Voucher";
|
||||||
id: string;
|
id: string;
|
||||||
name: string | null;
|
code: string;
|
||||||
startDate: any;
|
startDate: any;
|
||||||
endDate: any | null;
|
endDate: any | null;
|
||||||
usageLimit: number | null;
|
usageLimit: number | null;
|
||||||
discountValueType: VoucherDiscountValueType;
|
discountValueType: DiscountValueTypeEnum;
|
||||||
discountValue: number;
|
discountValue: number;
|
||||||
countries: (VoucherDetailsFragment_countries | null)[] | null;
|
countries: (VoucherDetailsFragment_countries | null)[] | null;
|
||||||
minAmountSpent: VoucherDetailsFragment_minAmountSpent | null;
|
minAmountSpent: VoucherDetailsFragment_minAmountSpent | null;
|
||||||
type: VoucherType;
|
minCheckoutItemsQuantity: number | null;
|
||||||
code: string;
|
type: VoucherTypeEnum;
|
||||||
used: number;
|
used: number;
|
||||||
applyOncePerOrder: boolean;
|
applyOncePerOrder: boolean;
|
||||||
|
applyOncePerCustomer: boolean;
|
||||||
products: VoucherDetailsFragment_products | null;
|
products: VoucherDetailsFragment_products | null;
|
||||||
collections: VoucherDetailsFragment_collections | null;
|
collections: VoucherDetailsFragment_collections | null;
|
||||||
categories: VoucherDetailsFragment_categories | null;
|
categories: VoucherDetailsFragment_categories | null;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { VoucherDiscountValueType } from "./../../types/globalTypes";
|
import { DiscountValueTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL fragment: VoucherFragment
|
// GraphQL fragment: VoucherFragment
|
||||||
|
@ -23,12 +23,13 @@ export interface VoucherFragment_minAmountSpent {
|
||||||
export interface VoucherFragment {
|
export interface VoucherFragment {
|
||||||
__typename: "Voucher";
|
__typename: "Voucher";
|
||||||
id: string;
|
id: string;
|
||||||
name: string | null;
|
code: string;
|
||||||
startDate: any;
|
startDate: any;
|
||||||
endDate: any | null;
|
endDate: any | null;
|
||||||
usageLimit: number | null;
|
usageLimit: number | null;
|
||||||
discountValueType: VoucherDiscountValueType;
|
discountValueType: DiscountValueTypeEnum;
|
||||||
discountValue: number;
|
discountValue: number;
|
||||||
countries: (VoucherFragment_countries | null)[] | null;
|
countries: (VoucherFragment_countries | null)[] | null;
|
||||||
minAmountSpent: VoucherFragment_minAmountSpent | null;
|
minAmountSpent: VoucherFragment_minAmountSpent | null;
|
||||||
|
minCheckoutItemsQuantity: number | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { VoucherDiscountValueType } from "./../../types/globalTypes";
|
import { DiscountValueTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: VoucherList
|
// GraphQL query operation: VoucherList
|
||||||
|
@ -23,14 +23,15 @@ export interface VoucherList_vouchers_edges_node_minAmountSpent {
|
||||||
export interface VoucherList_vouchers_edges_node {
|
export interface VoucherList_vouchers_edges_node {
|
||||||
__typename: "Voucher";
|
__typename: "Voucher";
|
||||||
id: string;
|
id: string;
|
||||||
name: string | null;
|
code: string;
|
||||||
startDate: any;
|
startDate: any;
|
||||||
endDate: any | null;
|
endDate: any | null;
|
||||||
usageLimit: number | null;
|
usageLimit: number | null;
|
||||||
discountValueType: VoucherDiscountValueType;
|
discountValueType: DiscountValueTypeEnum;
|
||||||
discountValue: number;
|
discountValue: number;
|
||||||
countries: (VoucherList_vouchers_edges_node_countries | null)[] | null;
|
countries: (VoucherList_vouchers_edges_node_countries | null)[] | null;
|
||||||
minAmountSpent: VoucherList_vouchers_edges_node_minAmountSpent | null;
|
minAmountSpent: VoucherList_vouchers_edges_node_minAmountSpent | null;
|
||||||
|
minCheckoutItemsQuantity: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VoucherList_vouchers_edges {
|
export interface VoucherList_vouchers_edges {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue