From a2dd1a14047843f6fa945c18cc8cc022072e8063 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Fri, 9 Aug 2019 12:17:04 +0200 Subject: [PATCH 01/12] Add missing files --- .../AttributeBulkDeleteDialog.tsx | 40 ++ .../AttributeBulkDeleteDialog/index.ts | 2 + .../AttributeDeleteDialog.tsx | 45 ++ .../components/AttributeDeleteDialog/index.ts | 2 + .../AttributeDetails/AttributeDetails.tsx | 98 ++++ .../components/AttributeDetails/index.ts | 2 + .../AttributeList/AttributeList.tsx | 183 +++++++ .../components/AttributeList/index.ts | 2 + .../AttributeListPage/AttributeListPage.tsx | 33 ++ .../components/AttributeListPage/index.ts | 2 + .../AttributePage/AttributePage.tsx | 160 ++++++ .../components/AttributePage/index.ts | 2 + .../AttributeProperties.tsx | 112 ++++ .../components/AttributeProperties/index.ts | 2 + .../AttributeValueDeleteDialog.tsx | 52 ++ .../AttributeValueDeleteDialog/index.ts | 2 + .../AttributeValueEditDialog.tsx | 97 ++++ .../AttributeValueEditDialog/index.ts | 2 + .../AttributeValues/AttributeValues.tsx | 129 +++++ .../components/AttributeValues/index.ts | 2 + src/attributes/fixtures.ts | 512 ++++++++++++++++++ src/attributes/index.tsx | 55 ++ src/attributes/mutations.ts | 182 +++++++ src/attributes/queries.ts | 86 +++ src/attributes/types/AttributeBulkDelete.ts | 26 + src/attributes/types/AttributeCreate.ts | 52 ++ src/attributes/types/AttributeDelete.ts | 26 + src/attributes/types/AttributeDetails.ts | 40 ++ .../types/AttributeDetailsFragment.ts | 32 ++ src/attributes/types/AttributeFragment.ts | 17 + src/attributes/types/AttributeList.ts | 50 ++ src/attributes/types/AttributeUpdate.ts | 53 ++ src/attributes/types/AttributeValueCreate.ts | 53 ++ src/attributes/types/AttributeValueDelete.ts | 52 ++ src/attributes/types/AttributeValueReorder.ts | 41 ++ src/attributes/types/AttributeValueUpdate.ts | 53 ++ src/attributes/urls.ts | 38 ++ .../views/AttributeCreate/AttributeCreate.tsx | 206 +++++++ src/attributes/views/AttributeCreate/index.ts | 2 + .../AttributeDetails/AttributeDetails.tsx | 360 ++++++++++++ .../views/AttributeDetails/index.ts | 2 + .../views/AttributeList/AttributeList.tsx | 137 +++++ src/attributes/views/AttributeList/index.ts | 2 + src/components/ColumnPicker/ColumnPicker.tsx | 103 ++++ .../ColumnPicker/ColumnPickerButton.tsx | 67 +++ .../ColumnPicker/ColumnPickerContent.tsx | 128 +++++ src/components/ColumnPicker/index.ts | 4 + .../RadioGroupField/RadioGroupField.tsx | 95 ++++ src/components/RadioGroupField/index.ts | 2 + .../RowNumberSelect/RowNumberSelect.tsx | 71 +++ src/components/RowNumberSelect/index.ts | 2 + .../SortableTable/SortableHandle.tsx | 29 + .../SortableTable/SortableTableBody.tsx | 45 ++ .../SortableTable/SortableTableRow.tsx | 15 + src/components/SortableTable/index.ts | 5 + .../components/VoucherDates/VoucherDates.tsx | 103 ++++ .../components/VoucherDates/index.ts | 2 + .../VoucherLimits/VoucherLimits.tsx | 64 +++ .../components/VoucherLimits/index.ts | 2 + .../VoucherRequirements.tsx | 82 +++ .../components/VoucherRequirements/index.ts | 2 + .../components/VoucherTypes/VoucherTypes.tsx | 60 ++ .../components/VoucherTypes/index.ts | 2 + .../components/VoucherValue/VoucherValue.tsx | 112 ++++ .../components/VoucherValue/index.ts | 2 + src/discounts/types.ts | 5 + src/hooks/useElementScroll.ts | 34 ++ src/hooks/useForm.ts | 148 +++++ src/hooks/useFormset.ts | 51 ++ src/hooks/useListActions.ts | 47 ++ src/hooks/useListSettings.ts | 30 + src/hooks/useLocalStorage.ts | 21 + src/hooks/useModalDialogErrors/index.ts | 2 + .../useModalDialogErrors.test.ts | 44 ++ .../useModalDialogErrors.ts | 17 + src/hooks/useModalDialogOpen/index.ts | 2 + .../useModalDialogOpen.test.ts | 55 ++ .../useModalDialogOpen/useModalDialogOpen.ts | 26 + src/hooks/useMultiAutocomplete.ts | 28 + src/hooks/useSearchQuery.ts | 29 + src/hooks/useStateFromProps.ts | 35 ++ src/hooks/useWindowScroll.ts | 24 + .../AssignAttributeDialog.tsx | 192 +++++++ .../components/AssignAttributeDialog/index.ts | 2 + .../ProductTypeAttributeUnassignDialog.tsx | 49 ++ .../index.ts | 2 + ...ProductTypeBulkAttributeUnassignDialog.tsx | 49 ++ .../index.ts | 2 + .../ProductTypeDeleteDialog.tsx | 43 ++ .../ProductTypeDeleteDialog/index.ts | 2 + .../containers/SearchAttributes/index.tsx | 42 ++ .../types/SearchAttributes.ts | 50 ++ src/productTypes/types/AssignAttribute.ts | 74 +++ .../types/ProductTypeAttributeReorder.ts | 75 +++ src/productTypes/types/UnassignAttribute.ts | 72 +++ .../ProductAttributes/ProductAttributes.tsx | 214 ++++++++ .../components/ProductAttributes/index.ts | 2 + .../ProductListPage/ProductListPage.tsx | 133 +++++ .../components/ProductListPage/index.ts | 2 + src/products/utils/data.ts | 224 ++++++++ src/products/utils/handlers.ts | 117 ++++ .../views/ProductUpdate/ProductUpdate.tsx | 333 ++++++++++++ src/products/views/ProductUpdate/handlers.ts | 77 +++ src/products/views/ProductUpdate/index.ts | 1 + .../SiteSettingsAddress.tsx | 151 ++++++ .../components/SiteSettingsAddress/index.ts | 2 + .../attributes/AttributeBulkDeleteDialog.tsx | 19 + .../attributes/AttributeDeleteDialog.tsx | 19 + .../stories/attributes/AttributeListPage.tsx | 23 + .../stories/attributes/AttributePage.tsx | 54 ++ .../attributes/AttributeValueDeleteDialog.tsx | 20 + .../attributes/AttributeValueEditDialog.tsx | 31 ++ .../components/AssignAttributeDialog.tsx | 33 ++ .../components/AssignProductDialog.tsx | 23 + .../stories/components/ColumnPicker.tsx | 45 ++ .../ProductTypeAttributeUnassignDialog.tsx | 20 + ...ProductTypeBulkAttributeUnassignDialog.tsx | 20 + .../productTypes/ProductTypeDeleteDialog.tsx | 19 + .../stories/products/ProductListPage.tsx | 50 ++ src/utils/errors.ts | 14 + .../multiAutocompleteSelectChangeHandler.ts | 21 + src/utils/handlers/queryChangeHandler.ts | 12 + .../singleAutocompleteSelectChangeHandler.ts | 17 + src/utils/i18n.ts | 5 + .../lists/__snapshots__/lists.test.ts.snap | 65 +++ src/utils/lists/index.ts | 1 + src/utils/lists/lists.test.ts | 48 ++ src/utils/lists/lists.ts | 61 +++ src/utils/maps.ts | 12 + 129 files changed, 7183 insertions(+) create mode 100644 src/attributes/components/AttributeBulkDeleteDialog/AttributeBulkDeleteDialog.tsx create mode 100644 src/attributes/components/AttributeBulkDeleteDialog/index.ts create mode 100644 src/attributes/components/AttributeDeleteDialog/AttributeDeleteDialog.tsx create mode 100644 src/attributes/components/AttributeDeleteDialog/index.ts create mode 100644 src/attributes/components/AttributeDetails/AttributeDetails.tsx create mode 100644 src/attributes/components/AttributeDetails/index.ts create mode 100644 src/attributes/components/AttributeList/AttributeList.tsx create mode 100644 src/attributes/components/AttributeList/index.ts create mode 100644 src/attributes/components/AttributeListPage/AttributeListPage.tsx create mode 100644 src/attributes/components/AttributeListPage/index.ts create mode 100644 src/attributes/components/AttributePage/AttributePage.tsx create mode 100644 src/attributes/components/AttributePage/index.ts create mode 100644 src/attributes/components/AttributeProperties/AttributeProperties.tsx create mode 100644 src/attributes/components/AttributeProperties/index.ts create mode 100644 src/attributes/components/AttributeValueDeleteDialog/AttributeValueDeleteDialog.tsx create mode 100644 src/attributes/components/AttributeValueDeleteDialog/index.ts create mode 100644 src/attributes/components/AttributeValueEditDialog/AttributeValueEditDialog.tsx create mode 100644 src/attributes/components/AttributeValueEditDialog/index.ts create mode 100644 src/attributes/components/AttributeValues/AttributeValues.tsx create mode 100644 src/attributes/components/AttributeValues/index.ts create mode 100644 src/attributes/fixtures.ts create mode 100644 src/attributes/index.tsx create mode 100644 src/attributes/mutations.ts create mode 100644 src/attributes/queries.ts create mode 100644 src/attributes/types/AttributeBulkDelete.ts create mode 100644 src/attributes/types/AttributeCreate.ts create mode 100644 src/attributes/types/AttributeDelete.ts create mode 100644 src/attributes/types/AttributeDetails.ts create mode 100644 src/attributes/types/AttributeDetailsFragment.ts create mode 100644 src/attributes/types/AttributeFragment.ts create mode 100644 src/attributes/types/AttributeList.ts create mode 100644 src/attributes/types/AttributeUpdate.ts create mode 100644 src/attributes/types/AttributeValueCreate.ts create mode 100644 src/attributes/types/AttributeValueDelete.ts create mode 100644 src/attributes/types/AttributeValueReorder.ts create mode 100644 src/attributes/types/AttributeValueUpdate.ts create mode 100644 src/attributes/urls.ts create mode 100644 src/attributes/views/AttributeCreate/AttributeCreate.tsx create mode 100644 src/attributes/views/AttributeCreate/index.ts create mode 100644 src/attributes/views/AttributeDetails/AttributeDetails.tsx create mode 100644 src/attributes/views/AttributeDetails/index.ts create mode 100644 src/attributes/views/AttributeList/AttributeList.tsx create mode 100644 src/attributes/views/AttributeList/index.ts create mode 100644 src/components/ColumnPicker/ColumnPicker.tsx create mode 100644 src/components/ColumnPicker/ColumnPickerButton.tsx create mode 100644 src/components/ColumnPicker/ColumnPickerContent.tsx create mode 100644 src/components/ColumnPicker/index.ts create mode 100644 src/components/RadioGroupField/RadioGroupField.tsx create mode 100644 src/components/RadioGroupField/index.ts create mode 100644 src/components/RowNumberSelect/RowNumberSelect.tsx create mode 100644 src/components/RowNumberSelect/index.ts create mode 100644 src/components/SortableTable/SortableHandle.tsx create mode 100644 src/components/SortableTable/SortableTableBody.tsx create mode 100644 src/components/SortableTable/SortableTableRow.tsx create mode 100644 src/components/SortableTable/index.ts create mode 100644 src/discounts/components/VoucherDates/VoucherDates.tsx create mode 100644 src/discounts/components/VoucherDates/index.ts create mode 100644 src/discounts/components/VoucherLimits/VoucherLimits.tsx create mode 100644 src/discounts/components/VoucherLimits/index.ts create mode 100644 src/discounts/components/VoucherRequirements/VoucherRequirements.tsx create mode 100644 src/discounts/components/VoucherRequirements/index.ts create mode 100644 src/discounts/components/VoucherTypes/VoucherTypes.tsx create mode 100644 src/discounts/components/VoucherTypes/index.ts create mode 100644 src/discounts/components/VoucherValue/VoucherValue.tsx create mode 100644 src/discounts/components/VoucherValue/index.ts create mode 100644 src/discounts/types.ts create mode 100644 src/hooks/useElementScroll.ts create mode 100644 src/hooks/useForm.ts create mode 100644 src/hooks/useFormset.ts create mode 100644 src/hooks/useListActions.ts create mode 100644 src/hooks/useListSettings.ts create mode 100644 src/hooks/useLocalStorage.ts create mode 100644 src/hooks/useModalDialogErrors/index.ts create mode 100644 src/hooks/useModalDialogErrors/useModalDialogErrors.test.ts create mode 100644 src/hooks/useModalDialogErrors/useModalDialogErrors.ts create mode 100644 src/hooks/useModalDialogOpen/index.ts create mode 100644 src/hooks/useModalDialogOpen/useModalDialogOpen.test.ts create mode 100644 src/hooks/useModalDialogOpen/useModalDialogOpen.ts create mode 100644 src/hooks/useMultiAutocomplete.ts create mode 100644 src/hooks/useSearchQuery.ts create mode 100644 src/hooks/useStateFromProps.ts create mode 100644 src/hooks/useWindowScroll.ts create mode 100644 src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.tsx create mode 100644 src/productTypes/components/AssignAttributeDialog/index.ts create mode 100644 src/productTypes/components/ProductTypeAttributeUnassignDialog/ProductTypeAttributeUnassignDialog.tsx create mode 100644 src/productTypes/components/ProductTypeAttributeUnassignDialog/index.ts create mode 100644 src/productTypes/components/ProductTypeBulkAttributeUnassignDialog/ProductTypeBulkAttributeUnassignDialog.tsx create mode 100644 src/productTypes/components/ProductTypeBulkAttributeUnassignDialog/index.ts create mode 100644 src/productTypes/components/ProductTypeDeleteDialog/ProductTypeDeleteDialog.tsx create mode 100644 src/productTypes/components/ProductTypeDeleteDialog/index.ts create mode 100644 src/productTypes/containers/SearchAttributes/index.tsx create mode 100644 src/productTypes/containers/SearchAttributes/types/SearchAttributes.ts create mode 100644 src/productTypes/types/AssignAttribute.ts create mode 100644 src/productTypes/types/ProductTypeAttributeReorder.ts create mode 100644 src/productTypes/types/UnassignAttribute.ts create mode 100644 src/products/components/ProductAttributes/ProductAttributes.tsx create mode 100644 src/products/components/ProductAttributes/index.ts create mode 100644 src/products/components/ProductListPage/ProductListPage.tsx create mode 100644 src/products/components/ProductListPage/index.ts create mode 100644 src/products/utils/data.ts create mode 100644 src/products/utils/handlers.ts create mode 100644 src/products/views/ProductUpdate/ProductUpdate.tsx create mode 100644 src/products/views/ProductUpdate/handlers.ts create mode 100644 src/products/views/ProductUpdate/index.ts create mode 100644 src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx create mode 100644 src/siteSettings/components/SiteSettingsAddress/index.ts create mode 100644 src/storybook/stories/attributes/AttributeBulkDeleteDialog.tsx create mode 100644 src/storybook/stories/attributes/AttributeDeleteDialog.tsx create mode 100644 src/storybook/stories/attributes/AttributeListPage.tsx create mode 100644 src/storybook/stories/attributes/AttributePage.tsx create mode 100644 src/storybook/stories/attributes/AttributeValueDeleteDialog.tsx create mode 100644 src/storybook/stories/attributes/AttributeValueEditDialog.tsx create mode 100644 src/storybook/stories/components/AssignAttributeDialog.tsx create mode 100644 src/storybook/stories/components/AssignProductDialog.tsx create mode 100644 src/storybook/stories/components/ColumnPicker.tsx create mode 100644 src/storybook/stories/productTypes/ProductTypeAttributeUnassignDialog.tsx create mode 100644 src/storybook/stories/productTypes/ProductTypeBulkAttributeUnassignDialog.tsx create mode 100644 src/storybook/stories/productTypes/ProductTypeDeleteDialog.tsx create mode 100644 src/storybook/stories/products/ProductListPage.tsx create mode 100644 src/utils/errors.ts create mode 100644 src/utils/handlers/multiAutocompleteSelectChangeHandler.ts create mode 100644 src/utils/handlers/queryChangeHandler.ts create mode 100644 src/utils/handlers/singleAutocompleteSelectChangeHandler.ts create mode 100644 src/utils/i18n.ts create mode 100644 src/utils/lists/__snapshots__/lists.test.ts.snap create mode 100644 src/utils/lists/index.ts create mode 100644 src/utils/lists/lists.test.ts create mode 100644 src/utils/lists/lists.ts create mode 100644 src/utils/maps.ts diff --git a/src/attributes/components/AttributeBulkDeleteDialog/AttributeBulkDeleteDialog.tsx b/src/attributes/components/AttributeBulkDeleteDialog/AttributeBulkDeleteDialog.tsx new file mode 100644 index 000000000..bd40e2c51 --- /dev/null +++ b/src/attributes/components/AttributeBulkDeleteDialog/AttributeBulkDeleteDialog.tsx @@ -0,0 +1,40 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import i18n from "../../../i18n"; + +export interface AttributeBulkDeleteDialogProps { + confirmButtonState: ConfirmButtonTransitionState; + quantity: string; + open: boolean; + onConfirm: () => void; + onClose: () => void; +} + +const AttributeBulkDeleteDialog: React.StatelessComponent< + AttributeBulkDeleteDialogProps +> = ({ confirmButtonState, quantity, onClose, onConfirm, open }) => ( + + {{ quantity }} attributes?", + { + quantity + } + ) + }} + /> + +); +AttributeBulkDeleteDialog.displayName = "AttributeBulkDeleteDialog"; +export default AttributeBulkDeleteDialog; diff --git a/src/attributes/components/AttributeBulkDeleteDialog/index.ts b/src/attributes/components/AttributeBulkDeleteDialog/index.ts new file mode 100644 index 000000000..12ecc2424 --- /dev/null +++ b/src/attributes/components/AttributeBulkDeleteDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeBulkDeleteDialog'; +export * from './AttributeBulkDeleteDialog'; \ No newline at end of file diff --git a/src/attributes/components/AttributeDeleteDialog/AttributeDeleteDialog.tsx b/src/attributes/components/AttributeDeleteDialog/AttributeDeleteDialog.tsx new file mode 100644 index 000000000..b6e577d88 --- /dev/null +++ b/src/attributes/components/AttributeDeleteDialog/AttributeDeleteDialog.tsx @@ -0,0 +1,45 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import i18n from "@saleor/i18n"; + +export interface AttributeDeleteDialogProps { + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + onConfirm: () => void; + onClose: () => void; + name: string; +} + +const AttributeDeleteDialog: React.FC = ({ + name, + confirmButtonState, + onClose, + onConfirm, + open +}) => ( + + {{ name }}?", + { + name + } + ) + }} + /> + +); + +AttributeDeleteDialog.displayName = "AttributeDeleteDialog"; +export default AttributeDeleteDialog; diff --git a/src/attributes/components/AttributeDeleteDialog/index.ts b/src/attributes/components/AttributeDeleteDialog/index.ts new file mode 100644 index 000000000..d3f62c3df --- /dev/null +++ b/src/attributes/components/AttributeDeleteDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeDeleteDialog'; +export * from './AttributeDeleteDialog'; \ No newline at end of file diff --git a/src/attributes/components/AttributeDetails/AttributeDetails.tsx b/src/attributes/components/AttributeDetails/AttributeDetails.tsx new file mode 100644 index 000000000..038f0fa65 --- /dev/null +++ b/src/attributes/components/AttributeDetails/AttributeDetails.tsx @@ -0,0 +1,98 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import React from "react"; +import slugify from "slugify"; + +import CardTitle from "@saleor/components/CardTitle"; +import ControlledSwitch from "@saleor/components/ControlledSwitch"; +import FormSpacer from "@saleor/components/FormSpacer"; +import SingleSelectField from "@saleor/components/SingleSelectField"; +import i18n from "@saleor/i18n"; +import { FormErrors } from "@saleor/types"; +import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; +import { AttributePageFormData } from "../AttributePage"; + +export interface AttributeDetailsProps { + canChangeType: boolean; + data: AttributePageFormData; + disabled: boolean; + errors: FormErrors<"name" | "slug" | "inputType">; + onChange: (event: React.ChangeEvent) => void; +} + +const inputTypeChoices = [ + { + label: i18n.t("Dropdown"), + value: AttributeInputTypeEnum.DROPDOWN + }, + { + label: i18n.t("Multiple Select"), + value: AttributeInputTypeEnum.MULTISELECT + } +]; + +const AttributeDetails: React.FC = ({ + canChangeType, + data, + disabled, + errors, + onChange +}) => ( + + + + + + + + + + + + +); +AttributeDetails.displayName = "AttributeDetails"; +export default AttributeDetails; diff --git a/src/attributes/components/AttributeDetails/index.ts b/src/attributes/components/AttributeDetails/index.ts new file mode 100644 index 000000000..93e888b1e --- /dev/null +++ b/src/attributes/components/AttributeDetails/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeDetails'; +export * from './AttributeDetails'; \ No newline at end of file diff --git a/src/attributes/components/AttributeList/AttributeList.tsx b/src/attributes/components/AttributeList/AttributeList.tsx new file mode 100644 index 000000000..95dfc7cc4 --- /dev/null +++ b/src/attributes/components/AttributeList/AttributeList.tsx @@ -0,0 +1,183 @@ +import { Theme } from "@material-ui/core/styles"; +import Table from "@material-ui/core/Table"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableFooter from "@material-ui/core/TableFooter"; +import TableRow from "@material-ui/core/TableRow"; +import makeStyles from "@material-ui/styles/makeStyles"; +import React from "react"; + +import Checkbox from "@saleor/components/Checkbox"; +import Skeleton from "@saleor/components/Skeleton"; +import TableHead from "@saleor/components/TableHead"; +import TablePagination from "@saleor/components/TablePagination"; +import i18n from "@saleor/i18n"; +import { renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; +import { translateBoolean } from "@saleor/utils/i18n"; +import { AttributeList_attributes_edges_node } from "../../types/AttributeList"; + +export interface AttributeListProps extends ListProps, ListActions { + attributes: AttributeList_attributes_edges_node[]; +} + +const useStyles = makeStyles((theme: Theme) => ({ + [theme.breakpoints.up("lg")]: { + colFaceted: { + width: 150 + }, + colName: { + width: "auto" + }, + colSearchable: { + width: 150 + }, + colSlug: { + width: 200 + }, + colVisible: { + width: 150 + } + }, + colFaceted: { + textAlign: "center" + }, + colName: {}, + colSearchable: { + textAlign: "center" + }, + colSlug: {}, + colVisible: { + textAlign: "center" + }, + link: { + cursor: "pointer" + } +})); + +const numberOfColumns = 6; + +const AttributeList: React.StatelessComponent = ({ + attributes, + disabled, + isChecked, + onNextPage, + onPreviousPage, + onRowClick, + pageInfo, + selected, + toggle, + toggleAll, + toolbar +}) => { + const classes = useStyles({}); + + return ( + + + + {i18n.t("Attribute Code", { context: "attribute slug" })} + + + {i18n.t("Default Label", { context: "attribute name" })} + + + {i18n.t("Visible", { context: "attribute visibility" })} + + + {i18n.t("Searchable", { + context: "attribute can be searched in dashboard" + })} + + + {i18n.t("Use in faceted search", { + context: "attribute can be searched in storefront" + })} + + + + + + + + + {renderCollection( + attributes, + attribute => { + const isSelected = attribute ? isChecked(attribute.id) : false; + + return ( + + + toggle(attribute.id)} + /> + + + {attribute ? attribute.slug : } + + + {attribute ? attribute.name : } + + + {attribute ? ( + translateBoolean(attribute.visibleInStorefront) + ) : ( + + )} + + + {attribute ? ( + translateBoolean(attribute.filterableInDashboard) + ) : ( + + )} + + + {attribute ? ( + translateBoolean(attribute.filterableInStorefront) + ) : ( + + )} + + + ); + }, + () => ( + + + {i18n.t("No attributes found")} + + + ) + )} + +
+ ); +}; +AttributeList.displayName = "AttributeList"; +export default AttributeList; diff --git a/src/attributes/components/AttributeList/index.ts b/src/attributes/components/AttributeList/index.ts new file mode 100644 index 000000000..4a094ce5f --- /dev/null +++ b/src/attributes/components/AttributeList/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeList'; +export * from './AttributeList'; \ No newline at end of file diff --git a/src/attributes/components/AttributeListPage/AttributeListPage.tsx b/src/attributes/components/AttributeListPage/AttributeListPage.tsx new file mode 100644 index 000000000..4daa73528 --- /dev/null +++ b/src/attributes/components/AttributeListPage/AttributeListPage.tsx @@ -0,0 +1,33 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import AddIcon from "@material-ui/icons/Add"; +import React from "react"; + +import Container from "../../../components/Container"; +import PageHeader from "../../../components/PageHeader"; +import i18n from "../../../i18n"; +import { ListActions, PageListProps } from "../../../types"; +import { AttributeList_attributes_edges_node } from "../../types/AttributeList"; +import AttributeList from "../AttributeList/AttributeList"; + +export interface AttributeListPageProps extends PageListProps, ListActions { + attributes: AttributeList_attributes_edges_node[]; +} + +const AttributeListPage: React.FC = ({ + onAdd, + ...listProps +}) => ( + + + + + + + + +); +AttributeListPage.displayName = "AttributeListPage"; +export default AttributeListPage; diff --git a/src/attributes/components/AttributeListPage/index.ts b/src/attributes/components/AttributeListPage/index.ts new file mode 100644 index 000000000..e044052b6 --- /dev/null +++ b/src/attributes/components/AttributeListPage/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeListPage'; +export * from './AttributeListPage'; \ No newline at end of file diff --git a/src/attributes/components/AttributePage/AttributePage.tsx b/src/attributes/components/AttributePage/AttributePage.tsx new file mode 100644 index 000000000..6cc875c90 --- /dev/null +++ b/src/attributes/components/AttributePage/AttributePage.tsx @@ -0,0 +1,160 @@ +import React from "react"; +import slugify from "slugify"; + +import AppHeader from "@saleor/components/AppHeader"; +import CardSpacer from "@saleor/components/CardSpacer"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import Container from "@saleor/components/Container"; +import Form from "@saleor/components/Form"; +import Grid from "@saleor/components/Grid"; +import PageHeader from "@saleor/components/PageHeader"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import i18n from "@saleor/i18n"; +import { maybe } from "@saleor/misc"; +import { ReorderAction, UserError } from "@saleor/types"; +import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; +import { + AttributeDetailsFragment, + AttributeDetailsFragment_values +} from "../../types/AttributeDetailsFragment"; +import AttributeDetails from "../AttributeDetails"; +import AttributeProperties from "../AttributeProperties"; +import AttributeValues from "../AttributeValues"; + +export interface AttributePageProps { + attribute: AttributeDetailsFragment | null; + disabled: boolean; + errors: UserError[]; + saveButtonBarState: ConfirmButtonTransitionState; + values: AttributeDetailsFragment_values[]; + onBack: () => void; + onDelete: () => void; + onSubmit: (data: AttributePageFormData) => void; + onValueAdd: () => void; + onValueDelete: (id: string) => void; + onValueReorder: ReorderAction; + onValueUpdate: (id: string) => void; +} + +export interface AttributePageFormData { + filterableInDashboard: boolean; + inputType: AttributeInputTypeEnum; + filterableInStorefront: boolean; + name: string; + slug: string; + storefrontSearchPosition: string; + valueRequired: boolean; + visibleInStorefront: boolean; +} + +const AttributePage: React.FC = ({ + attribute, + disabled, + errors, + saveButtonBarState, + values, + onBack, + onDelete, + onSubmit, + onValueAdd, + onValueDelete, + onValueReorder, + onValueUpdate +}) => { + const initialForm: AttributePageFormData = + attribute === null + ? { + filterableInDashboard: true, + filterableInStorefront: true, + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "", + slug: "", + storefrontSearchPosition: "", + valueRequired: true, + visibleInStorefront: true + } + : { + filterableInDashboard: maybe( + () => attribute.filterableInDashboard, + true + ), + filterableInStorefront: maybe( + () => attribute.filterableInStorefront, + true + ), + inputType: maybe( + () => attribute.inputType, + AttributeInputTypeEnum.DROPDOWN + ), + name: maybe(() => attribute.name, ""), + slug: maybe(() => attribute.slug, ""), + storefrontSearchPosition: maybe( + () => attribute.storefrontSearchPosition.toString(), + "" + ), + valueRequired: maybe(() => attribute.valueRequired, true), + visibleInStorefront: maybe(() => attribute.visibleInStorefront, true) + }; + + const handleSubmit = (data: AttributePageFormData) => + onSubmit({ + ...data, + slug: data.slug || slugify(data.name).toLowerCase() + }); + + return ( +
+ {({ change, errors: formErrors, data, submit }) => ( + + {i18n.t("Attributes")} + attribute.name) + } + /> + +
+ + + +
+
+ +
+
+ +
+ )} +
+ ); +}; +AttributePage.displayName = "AttributePage"; +export default AttributePage; diff --git a/src/attributes/components/AttributePage/index.ts b/src/attributes/components/AttributePage/index.ts new file mode 100644 index 000000000..08880e9d8 --- /dev/null +++ b/src/attributes/components/AttributePage/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributePage'; +export * from './AttributePage'; \ No newline at end of file diff --git a/src/attributes/components/AttributeProperties/AttributeProperties.tsx b/src/attributes/components/AttributeProperties/AttributeProperties.tsx new file mode 100644 index 000000000..078d16882 --- /dev/null +++ b/src/attributes/components/AttributeProperties/AttributeProperties.tsx @@ -0,0 +1,112 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import React from "react"; + +import CardSpacer from "@saleor/components/CardSpacer"; +import CardTitle from "@saleor/components/CardTitle"; +import ControlledSwitch from "@saleor/components/ControlledSwitch"; +import FormSpacer from "@saleor/components/FormSpacer"; +import Hr from "@saleor/components/Hr"; +import i18n from "@saleor/i18n"; +import { FormErrors } from "@saleor/types"; +import { AttributePageFormData } from "../AttributePage"; + +export interface AttributePropertiesProps { + data: AttributePageFormData; + disabled: boolean; + errors: FormErrors<"storefrontSearchPosition">; + onChange: (event: React.ChangeEvent) => void; +} + +const AttributeProperties: React.FC = ({ + data, + errors, + disabled, + onChange +}) => ( + + + + {/* + {i18n.t("General Properties")} + +
+ + + {i18n.t("Variant Attribute")} + + {i18n.t( + "If enabled, you'll be able to use this attribute to create product variants" + )} + + + } + onChange={onChange} + /> */} + + + {i18n.t("Storefront Properties")} + +
+ + {data.filterableInStorefront && ( + + )} + + + + + + {i18n.t("Dashboard Properties")} + +
+ + + {i18n.t( + "If enabled, you’ll be able to use this attribute to filter products in product list." + )} + + } + onChange={onChange} + /> +
+
+); +AttributeProperties.displayName = "AttributeProperties"; +export default AttributeProperties; diff --git a/src/attributes/components/AttributeProperties/index.ts b/src/attributes/components/AttributeProperties/index.ts new file mode 100644 index 000000000..4f02c2478 --- /dev/null +++ b/src/attributes/components/AttributeProperties/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeProperties'; +export * from './AttributeProperties'; \ No newline at end of file diff --git a/src/attributes/components/AttributeValueDeleteDialog/AttributeValueDeleteDialog.tsx b/src/attributes/components/AttributeValueDeleteDialog/AttributeValueDeleteDialog.tsx new file mode 100644 index 000000000..3edd078ee --- /dev/null +++ b/src/attributes/components/AttributeValueDeleteDialog/AttributeValueDeleteDialog.tsx @@ -0,0 +1,52 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import i18n from "@saleor/i18n"; + +export interface AttributeValueDeleteDialogProps { + attributeName: string; + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + name: string; + useName?: boolean; + onConfirm: () => void; + onClose: () => void; +} + +const AttributeValueDeleteDialog: React.FC = ({ + attributeName, + name, + confirmButtonState, + useName, + onClose, + onConfirm, + open +}) => ( + + + {useName + ? i18n.t( + 'Are you sure you want to remove "{{ name }}" value? If you remove it you won’t be able to assign it to any of the products with "{{ attributeName }}" attribute.', + { + attributeName, + name + } + ) + : i18n.t('Are you sure you want to remove "{{ name }}" value?', { + name + })} + + +); + +AttributeValueDeleteDialog.displayName = "AttributeValueDeleteDialog"; +export default AttributeValueDeleteDialog; diff --git a/src/attributes/components/AttributeValueDeleteDialog/index.ts b/src/attributes/components/AttributeValueDeleteDialog/index.ts new file mode 100644 index 000000000..b58e72f50 --- /dev/null +++ b/src/attributes/components/AttributeValueDeleteDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeValueDeleteDialog'; +export * from './AttributeValueDeleteDialog'; \ No newline at end of file diff --git a/src/attributes/components/AttributeValueEditDialog/AttributeValueEditDialog.tsx b/src/attributes/components/AttributeValueEditDialog/AttributeValueEditDialog.tsx new file mode 100644 index 000000000..7bb83ca00 --- /dev/null +++ b/src/attributes/components/AttributeValueEditDialog/AttributeValueEditDialog.tsx @@ -0,0 +1,97 @@ +import Button from "@material-ui/core/Button"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import TextField from "@material-ui/core/TextField"; +import React from "react"; + +import ConfirmButton, { + ConfirmButtonTransitionState +} from "@saleor/components/ConfirmButton"; +import Form from "@saleor/components/Form"; +import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; +import i18n from "@saleor/i18n"; +import { maybe } from "@saleor/misc"; +import { UserError } from "@saleor/types"; +import { AttributeDetails_attribute_values } from "../../types/AttributeDetails"; + +export interface AttributeValueEditDialogFormData { + name: string; +} +export interface AttributeValueEditDialogProps { + attributeValue: AttributeDetails_attribute_values | null; + confirmButtonState: ConfirmButtonTransitionState; + disabled: boolean; + errors: UserError[]; + open: boolean; + onSubmit: (data: AttributeValueEditDialogFormData) => void; + onClose: () => void; +} + +const AttributeValueEditDialog: React.StatelessComponent< + AttributeValueEditDialogProps +> = ({ + attributeValue, + confirmButtonState, + disabled, + errors: apiErrors, + onClose, + onSubmit, + open +}) => { + const initialForm: AttributeValueEditDialogFormData = { + name: maybe(() => attributeValue.name, "") + }; + const errors = useModalDialogErrors(apiErrors, open); + + return ( + + + {attributeValue === null + ? i18n.t("Add Value", { + context: "add attribute value" + }) + : i18n.t("Edit Value", { + context: "edit attribute value" + })} + +
+ {({ change, data, errors: formErrors, submit }) => ( + <> + + + + + + + {i18n.t("Save")} + + + + )} +
+
+ ); +}; +AttributeValueEditDialog.displayName = "AttributeValueEditDialog"; +export default AttributeValueEditDialog; diff --git a/src/attributes/components/AttributeValueEditDialog/index.ts b/src/attributes/components/AttributeValueEditDialog/index.ts new file mode 100644 index 000000000..1dfc3d061 --- /dev/null +++ b/src/attributes/components/AttributeValueEditDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeValueEditDialog'; +export * from './AttributeValueEditDialog'; \ No newline at end of file diff --git a/src/attributes/components/AttributeValues/AttributeValues.tsx b/src/attributes/components/AttributeValues/AttributeValues.tsx new file mode 100644 index 000000000..4d6f3c444 --- /dev/null +++ b/src/attributes/components/AttributeValues/AttributeValues.tsx @@ -0,0 +1,129 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import IconButton from "@material-ui/core/IconButton"; +import { Theme } from "@material-ui/core/styles"; +import Table from "@material-ui/core/Table"; +import TableCell from "@material-ui/core/TableCell"; +import TableHead from "@material-ui/core/TableHead"; +import TableRow from "@material-ui/core/TableRow"; +import DeleteIcon from "@material-ui/icons/Delete"; +import makeStyles from "@material-ui/styles/makeStyles"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import Skeleton from "@saleor/components/Skeleton"; +import { + SortableTableBody, + SortableTableRow +} from "@saleor/components/SortableTable"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection, stopPropagation } from "@saleor/misc"; +import { ReorderAction } from "@saleor/types"; +import { AttributeDetailsFragment_values } from "../../types/AttributeDetailsFragment"; + +export interface AttributeValuesProps { + disabled: boolean; + values: AttributeDetailsFragment_values[]; + onValueAdd: () => void; + onValueDelete: (id: string) => void; + onValueReorder: ReorderAction; + onValueUpdate: (id: string) => void; +} + +const useStyles = makeStyles((theme: Theme) => ({ + columnAdmin: { + width: "50%" + }, + columnDrag: { + width: 48 + theme.spacing.unit * 1.5 + }, + columnStore: { + width: "50%" + }, + dragIcon: { + cursor: "grab" + }, + iconCell: { + "&:last-child": { + paddingRight: theme.spacing.unit + }, + width: 48 + theme.spacing.unit * 1.5 + }, + link: { + cursor: "pointer" + } +})); + +const AttributeValues: React.FC = ({ + disabled, + onValueAdd, + onValueDelete, + onValueReorder, + onValueUpdate, + values +}) => { + const classes = useStyles({}); + + return ( + + + {i18n.t("Add value", { context: "button" })} + + } + /> + + + + + + {i18n.t("Admin")} + + + {i18n.t("Default Store View")} + + + + + + {renderCollection( + values, + (value, valueIndex) => ( + onValueUpdate(value.id) : undefined} + key={maybe(() => value.id)} + index={valueIndex || 0} + > + + {maybe(() => value.slug) ? value.slug : } + + + {maybe(() => value.name) ? value.name : } + + + onValueDelete(value.id))} + > + + + + + ), + () => ( + + {i18n.t("No values found")} + + ) + )} + +
+
+ ); +}; +AttributeValues.displayName = "AttributeValues"; +export default AttributeValues; diff --git a/src/attributes/components/AttributeValues/index.ts b/src/attributes/components/AttributeValues/index.ts new file mode 100644 index 000000000..dae7f6baf --- /dev/null +++ b/src/attributes/components/AttributeValues/index.ts @@ -0,0 +1,2 @@ +export { default } from './AttributeValues'; +export * from './AttributeValues'; \ No newline at end of file diff --git a/src/attributes/fixtures.ts b/src/attributes/fixtures.ts new file mode 100644 index 000000000..b55cfa134 --- /dev/null +++ b/src/attributes/fixtures.ts @@ -0,0 +1,512 @@ +import { + AttributeInputTypeEnum, + AttributeValueType +} from "@saleor/types/globalTypes"; +import { AttributeList_attributes_edges_node } from "./types/AttributeList"; + +export const attribute = { + __typename: "Attribute" as "Attribute", + filterableInDashboard: false, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZTo5", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Author", + slug: "author", + storefrontSearchPosition: 2, + valueRequired: true, + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0", + name: "John Doe", + slug: "john-doe", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1", + name: "Milionare Pirate", + slug: "milionare-pirate", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true +}; + +export const attributes: AttributeList_attributes_edges_node[] = [ + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: false, + id: "UHJvZHVjdEF0dHJpYnV0ZTo5", + name: "Author", + slug: "author", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0", + name: "John Doe", + slug: "john-doe", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1", + name: "Milionare Pirate", + slug: "milionare-pirate", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: false, + id: "UHJvZHVjdEF0dHJpYnV0ZTo2", + name: "Box Size", + slug: "box-size", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE1", + name: "100g", + slug: "100g", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE2", + name: "250g", + slug: "250g", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE3", + name: "500g", + slug: "500g", + sortOrder: 2, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE4", + name: "1kg", + slug: "1kg", + sortOrder: 3, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: false + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: false, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZToz", + name: "Brand", + slug: "brand", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjY=", + name: "Saleor", + slug: "saleor", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: false + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZTo4", + name: "Candy Box Size", + slug: "candy-box-size", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIx", + name: "100g", + slug: "100g", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIy", + name: "250g", + slug: "250g", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIz", + name: "500g", + slug: "500g", + sortOrder: 2, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: false + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZTo1", + name: "Coffee Genre", + slug: "coffee-genre", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEz", + name: "Arabica", + slug: "arabica", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE0", + name: "Robusta", + slug: "robusta", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: false, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZToy", + name: "Collar", + slug: "collar", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM=", + name: "Round", + slug: "round", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjQ=", + name: "V-Neck", + slug: "v-neck", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjU=", + name: "Polo", + slug: "polo", + sortOrder: 2, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: false, + filterableInStorefront: false, + id: "UHJvZHVjdEF0dHJpYnV0ZTox", + name: "Color", + slug: "color", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE=", + name: "Blue", + slug: "blue", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI=", + name: "White", + slug: "white", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: false, + id: "UHJvZHVjdEF0dHJpYnV0ZToxMg==", + name: "Cover", + slug: "cover", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMw", + name: "Soft", + slug: "soft", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMx", + name: "Hard", + slug: "hard", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMy", + name: "Middle soft", + slug: "middle-soft", + sortOrder: 2, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjMz", + name: "Middle hard", + slug: "middle-hard", + sortOrder: 3, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM0", + name: "Middle", + slug: "middle", + sortOrder: 4, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjM1", + name: "Very hard", + slug: "very-hard", + sortOrder: 5, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: false + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZTo3", + name: "Flavor", + slug: "flavor", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjE5", + name: "Sour", + slug: "sour", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjIw", + name: "Sweet", + slug: "sweet", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: false, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZToxMQ==", + name: "Language", + slug: "language", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI4", + name: "English", + slug: "english", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI5", + name: "Pirate", + slug: "pirate", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZToxMA==", + name: "Publisher", + slug: "publisher", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI2", + name: "Mirumee Press", + slug: "mirumee-press", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI3", + name: "Saleor Publishing", + slug: "saleor-publishing", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true + } + }, + { + node: { + __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: true, + id: "UHJvZHVjdEF0dHJpYnV0ZTo0", + name: "Size", + slug: "size", + values: [ + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjc=", + name: "XS", + slug: "xs", + sortOrder: 0, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjg=", + name: "S", + slug: "s", + sortOrder: 1, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjk=", + name: "M", + slug: "m", + sortOrder: 2, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEw", + name: "L", + slug: "l", + sortOrder: 3, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEx", + name: "XL", + slug: "xl", + sortOrder: 4, + type: AttributeValueType.STRING, + value: "" + }, + { + __typename: "AttributeValue" as "AttributeValue", + id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjEy", + name: "XXL", + slug: "xxl", + sortOrder: 5, + type: AttributeValueType.STRING, + value: "" + } + ], + visibleInStorefront: true + } + } +].map(edge => edge.node); diff --git a/src/attributes/index.tsx b/src/attributes/index.tsx new file mode 100644 index 000000000..b98ce72ab --- /dev/null +++ b/src/attributes/index.tsx @@ -0,0 +1,55 @@ +import { parse as parseQs } from "qs"; +import React from "react"; +import { Route, RouteComponentProps, Switch } from "react-router-dom"; + +import { WindowTitle } from "../components/WindowTitle"; +import i18n from "../i18n"; +import { + attributeAddPath, + AttributeAddUrlQueryParams, + attributeListPath, + AttributeListUrlQueryParams, + attributePath, + AttributeUrlQueryParams +} from "./urls"; +import AttributeCreateComponent from "./views/AttributeCreate"; +import AttributeDetailsComponent from "./views/AttributeDetails"; +import AttributeListComponent from "./views/AttributeList"; + +const AttributeList: React.FC> = ({ location }) => { + const qs = parseQs(location.search.substr(1)); + const params: AttributeListUrlQueryParams = qs; + return ; +}; + +const AttributeCreate: React.FC> = ({ location }) => { + const qs = parseQs(location.search.substr(1)); + const params: AttributeAddUrlQueryParams = qs; + return ; +}; + +const AttributeDetails: React.FC> = ({ + location, + match +}) => { + const qs = parseQs(location.search.substr(1)); + const params: AttributeUrlQueryParams = qs; + return ( + + ); +}; + +export const AttributeSection: React.FC = () => ( + <> + + + + + + + +); +export default AttributeSection; diff --git a/src/attributes/mutations.ts b/src/attributes/mutations.ts new file mode 100644 index 000000000..b70363a7d --- /dev/null +++ b/src/attributes/mutations.ts @@ -0,0 +1,182 @@ +import gql from "graphql-tag"; + +import { TypedMutation } from "@saleor/mutations"; +import { attributeDetailsFragment } from "./queries"; +import { + AttributeBulkDelete, + AttributeBulkDeleteVariables +} from "./types/AttributeBulkDelete"; +import { + AttributeCreate, + AttributeCreateVariables +} from "./types/AttributeCreate"; +import { + AttributeDelete, + AttributeDeleteVariables +} from "./types/AttributeDelete"; +import { + AttributeUpdate, + AttributeUpdateVariables +} from "./types/AttributeUpdate"; +import { + AttributeValueCreate, + AttributeValueCreateVariables +} from "./types/AttributeValueCreate"; +import { + AttributeValueDelete, + AttributeValueDeleteVariables +} from "./types/AttributeValueDelete"; +import { + AttributeValueReorder, + AttributeValueReorderVariables +} from "./types/AttributeValueReorder"; +import { + AttributeValueUpdate, + AttributeValueUpdateVariables +} from "./types/AttributeValueUpdate"; + +const attributeBulkDelete = gql` + mutation AttributeBulkDelete($ids: [ID!]!) { + attributeBulkDelete(ids: $ids) { + errors { + field + message + } + } + } +`; +export const AttributeBulkDeleteMutation = TypedMutation< + AttributeBulkDelete, + AttributeBulkDeleteVariables +>(attributeBulkDelete); + +const attributeDelete = gql` + mutation AttributeDelete($id: ID!) { + attributeDelete(id: $id) { + errors { + field + message + } + } + } +`; +export const AttributeDeleteMutation = TypedMutation< + AttributeDelete, + AttributeDeleteVariables +>(attributeDelete); + +export const attributeUpdateMutation = gql` + ${attributeDetailsFragment} + mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) { + attributeUpdate(id: $id, input: $input) { + errors { + field + message + } + attribute { + ...AttributeDetailsFragment + } + } + } +`; +export const AttributeUpdateMutation = TypedMutation< + AttributeUpdate, + AttributeUpdateVariables +>(attributeUpdateMutation); + +const attributeValueDelete = gql` + ${attributeDetailsFragment} + mutation AttributeValueDelete($id: ID!) { + attributeValueDelete(id: $id) { + errors { + field + message + } + attribute { + ...AttributeDetailsFragment + } + } + } +`; +export const AttributeValueDeleteMutation = TypedMutation< + AttributeValueDelete, + AttributeValueDeleteVariables +>(attributeValueDelete); + +export const attributeValueUpdateMutation = gql` + ${attributeDetailsFragment} + mutation AttributeValueUpdate($id: ID!, $input: AttributeValueCreateInput!) { + attributeValueUpdate(id: $id, input: $input) { + errors { + field + message + } + attribute { + ...AttributeDetailsFragment + } + } + } +`; +export const AttributeValueUpdateMutation = TypedMutation< + AttributeValueUpdate, + AttributeValueUpdateVariables +>(attributeValueUpdateMutation); + +export const attributeValueCreateMutation = gql` + ${attributeDetailsFragment} + mutation AttributeValueCreate($id: ID!, $input: AttributeValueCreateInput!) { + attributeValueCreate(attribute: $id, input: $input) { + errors { + field + message + } + attribute { + ...AttributeDetailsFragment + } + } + } +`; +export const AttributeValueCreateMutation = TypedMutation< + AttributeValueCreate, + AttributeValueCreateVariables +>(attributeValueCreateMutation); + +export const attributeCreateMutation = gql` + ${attributeDetailsFragment} + mutation AttributeCreate($input: AttributeCreateInput!) { + attributeCreate(input: $input) { + errors { + field + message + } + attribute { + ...AttributeDetailsFragment + } + } + } +`; +export const AttributeCreateMutation = TypedMutation< + AttributeCreate, + AttributeCreateVariables +>(attributeCreateMutation); + +const attributeValueReorderMutation = gql` + mutation AttributeValueReorder($id: ID!, $move: ReorderInput!) { + attributeReorderValues(attributeId: $id, moves: [$move]) { + errors { + field + message + } + attribute { + id + values { + id + } + } + } + } +`; +export const AttributeValueReorderMutation = TypedMutation< + AttributeValueReorder, + AttributeValueReorderVariables +>(attributeValueReorderMutation); diff --git a/src/attributes/queries.ts b/src/attributes/queries.ts new file mode 100644 index 000000000..e18d7c8c0 --- /dev/null +++ b/src/attributes/queries.ts @@ -0,0 +1,86 @@ +import gql from "graphql-tag"; + +import { pageInfoFragment, TypedQuery } from "../queries"; +import { + AttributeDetails, + AttributeDetailsVariables +} from "./types/AttributeDetails"; +import { AttributeList, AttributeListVariables } from "./types/AttributeList"; + +export const attributeFragment = gql` + fragment AttributeFragment on Attribute { + id + name + slug + visibleInStorefront + filterableInDashboard + filterableInStorefront + } +`; + +export const attributeDetailsFragment = gql` + ${attributeFragment} + fragment AttributeDetailsFragment on Attribute { + ...AttributeFragment + inputType + storefrontSearchPosition + valueRequired + values { + id + name + slug + type + value + } + } +`; + +const attributeDetails = gql` + ${attributeDetailsFragment} + query AttributeDetails($id: ID!) { + attribute(id: $id) { + ...AttributeDetailsFragment + } + } +`; +export const AttributeDetailsQuery = TypedQuery< + AttributeDetails, + AttributeDetailsVariables +>(attributeDetails); + +const attributeList = gql` + ${attributeFragment} + ${pageInfoFragment} + query AttributeList( + $query: String + $inCategory: ID + $inCollection: ID + $before: String + $after: String + $first: Int + $last: Int + ) { + attributes( + query: $query + inCategory: $inCategory + inCollection: $inCollection + before: $before + after: $after + first: $first + last: $last + ) { + edges { + node { + ...AttributeFragment + } + } + pageInfo { + ...PageInfoFragment + } + } + } +`; +export const AttributeListQuery = TypedQuery< + AttributeList, + AttributeListVariables +>(attributeList); diff --git a/src/attributes/types/AttributeBulkDelete.ts b/src/attributes/types/AttributeBulkDelete.ts new file mode 100644 index 000000000..06ff83fd7 --- /dev/null +++ b/src/attributes/types/AttributeBulkDelete.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL mutation operation: AttributeBulkDelete +// ==================================================== + +export interface AttributeBulkDelete_attributeBulkDelete_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AttributeBulkDelete_attributeBulkDelete { + __typename: "AttributeBulkDelete"; + errors: AttributeBulkDelete_attributeBulkDelete_errors[] | null; +} + +export interface AttributeBulkDelete { + attributeBulkDelete: AttributeBulkDelete_attributeBulkDelete | null; +} + +export interface AttributeBulkDeleteVariables { + ids: string[]; +} diff --git a/src/attributes/types/AttributeCreate.ts b/src/attributes/types/AttributeCreate.ts new file mode 100644 index 000000000..9d8eab0a0 --- /dev/null +++ b/src/attributes/types/AttributeCreate.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: AttributeCreate +// ==================================================== + +export interface AttributeCreate_attributeCreate_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AttributeCreate_attributeCreate_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + type: AttributeValueType | null; + value: string | null; +} + +export interface AttributeCreate_attributeCreate_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; + inputType: AttributeInputTypeEnum | null; + storefrontSearchPosition: number; + valueRequired: boolean; + values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null; +} + +export interface AttributeCreate_attributeCreate { + __typename: "AttributeCreate"; + errors: AttributeCreate_attributeCreate_errors[] | null; + attribute: AttributeCreate_attributeCreate_attribute | null; +} + +export interface AttributeCreate { + attributeCreate: AttributeCreate_attributeCreate | null; +} + +export interface AttributeCreateVariables { + input: AttributeCreateInput; +} diff --git a/src/attributes/types/AttributeDelete.ts b/src/attributes/types/AttributeDelete.ts new file mode 100644 index 000000000..2cd44da90 --- /dev/null +++ b/src/attributes/types/AttributeDelete.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL mutation operation: AttributeDelete +// ==================================================== + +export interface AttributeDelete_attributeDelete_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AttributeDelete_attributeDelete { + __typename: "AttributeDelete"; + errors: AttributeDelete_attributeDelete_errors[] | null; +} + +export interface AttributeDelete { + attributeDelete: AttributeDelete_attributeDelete | null; +} + +export interface AttributeDeleteVariables { + id: string; +} diff --git a/src/attributes/types/AttributeDetails.ts b/src/attributes/types/AttributeDetails.ts new file mode 100644 index 000000000..b1826d42e --- /dev/null +++ b/src/attributes/types/AttributeDetails.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL query operation: AttributeDetails +// ==================================================== + +export interface AttributeDetails_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + type: AttributeValueType | null; + value: string | null; +} + +export interface AttributeDetails_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; + inputType: AttributeInputTypeEnum | null; + storefrontSearchPosition: number; + valueRequired: boolean; + values: (AttributeDetails_attribute_values | null)[] | null; +} + +export interface AttributeDetails { + attribute: AttributeDetails_attribute | null; +} + +export interface AttributeDetailsVariables { + id: string; +} diff --git a/src/attributes/types/AttributeDetailsFragment.ts b/src/attributes/types/AttributeDetailsFragment.ts new file mode 100644 index 000000000..f4c9375f2 --- /dev/null +++ b/src/attributes/types/AttributeDetailsFragment.ts @@ -0,0 +1,32 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: AttributeDetailsFragment +// ==================================================== + +export interface AttributeDetailsFragment_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + type: AttributeValueType | null; + value: string | null; +} + +export interface AttributeDetailsFragment { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; + inputType: AttributeInputTypeEnum | null; + storefrontSearchPosition: number; + valueRequired: boolean; + values: (AttributeDetailsFragment_values | null)[] | null; +} diff --git a/src/attributes/types/AttributeFragment.ts b/src/attributes/types/AttributeFragment.ts new file mode 100644 index 000000000..333736761 --- /dev/null +++ b/src/attributes/types/AttributeFragment.ts @@ -0,0 +1,17 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: AttributeFragment +// ==================================================== + +export interface AttributeFragment { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; +} diff --git a/src/attributes/types/AttributeList.ts b/src/attributes/types/AttributeList.ts new file mode 100644 index 000000000..938c0098d --- /dev/null +++ b/src/attributes/types/AttributeList.ts @@ -0,0 +1,50 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: AttributeList +// ==================================================== + +export interface AttributeList_attributes_edges_node { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; +} + +export interface AttributeList_attributes_edges { + __typename: "AttributeCountableEdge"; + node: AttributeList_attributes_edges_node; +} + +export interface AttributeList_attributes_pageInfo { + __typename: "PageInfo"; + endCursor: string | null; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; +} + +export interface AttributeList_attributes { + __typename: "AttributeCountableConnection"; + edges: AttributeList_attributes_edges[]; + pageInfo: AttributeList_attributes_pageInfo; +} + +export interface AttributeList { + attributes: AttributeList_attributes | null; +} + +export interface AttributeListVariables { + query?: string | null; + inCategory?: string | null; + inCollection?: string | null; + before?: string | null; + after?: string | null; + first?: number | null; + last?: number | null; +} diff --git a/src/attributes/types/AttributeUpdate.ts b/src/attributes/types/AttributeUpdate.ts new file mode 100644 index 000000000..7ec2e5e7c --- /dev/null +++ b/src/attributes/types/AttributeUpdate.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeUpdateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: AttributeUpdate +// ==================================================== + +export interface AttributeUpdate_attributeUpdate_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AttributeUpdate_attributeUpdate_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + type: AttributeValueType | null; + value: string | null; +} + +export interface AttributeUpdate_attributeUpdate_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; + inputType: AttributeInputTypeEnum | null; + storefrontSearchPosition: number; + valueRequired: boolean; + values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null; +} + +export interface AttributeUpdate_attributeUpdate { + __typename: "AttributeUpdate"; + errors: AttributeUpdate_attributeUpdate_errors[] | null; + attribute: AttributeUpdate_attributeUpdate_attribute | null; +} + +export interface AttributeUpdate { + attributeUpdate: AttributeUpdate_attributeUpdate | null; +} + +export interface AttributeUpdateVariables { + id: string; + input: AttributeUpdateInput; +} diff --git a/src/attributes/types/AttributeValueCreate.ts b/src/attributes/types/AttributeValueCreate.ts new file mode 100644 index 000000000..c5450d981 --- /dev/null +++ b/src/attributes/types/AttributeValueCreate.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: AttributeValueCreate +// ==================================================== + +export interface AttributeValueCreate_attributeValueCreate_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AttributeValueCreate_attributeValueCreate_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + type: AttributeValueType | null; + value: string | null; +} + +export interface AttributeValueCreate_attributeValueCreate_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; + inputType: AttributeInputTypeEnum | null; + storefrontSearchPosition: number; + valueRequired: boolean; + values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null; +} + +export interface AttributeValueCreate_attributeValueCreate { + __typename: "AttributeValueCreate"; + errors: AttributeValueCreate_attributeValueCreate_errors[] | null; + attribute: AttributeValueCreate_attributeValueCreate_attribute | null; +} + +export interface AttributeValueCreate { + attributeValueCreate: AttributeValueCreate_attributeValueCreate | null; +} + +export interface AttributeValueCreateVariables { + id: string; + input: AttributeValueCreateInput; +} diff --git a/src/attributes/types/AttributeValueDelete.ts b/src/attributes/types/AttributeValueDelete.ts new file mode 100644 index 000000000..10c5924e0 --- /dev/null +++ b/src/attributes/types/AttributeValueDelete.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: AttributeValueDelete +// ==================================================== + +export interface AttributeValueDelete_attributeValueDelete_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AttributeValueDelete_attributeValueDelete_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + type: AttributeValueType | null; + value: string | null; +} + +export interface AttributeValueDelete_attributeValueDelete_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; + inputType: AttributeInputTypeEnum | null; + storefrontSearchPosition: number; + valueRequired: boolean; + values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null; +} + +export interface AttributeValueDelete_attributeValueDelete { + __typename: "AttributeValueDelete"; + errors: AttributeValueDelete_attributeValueDelete_errors[] | null; + attribute: AttributeValueDelete_attributeValueDelete_attribute | null; +} + +export interface AttributeValueDelete { + attributeValueDelete: AttributeValueDelete_attributeValueDelete | null; +} + +export interface AttributeValueDeleteVariables { + id: string; +} diff --git a/src/attributes/types/AttributeValueReorder.ts b/src/attributes/types/AttributeValueReorder.ts new file mode 100644 index 000000000..492b94e7d --- /dev/null +++ b/src/attributes/types/AttributeValueReorder.ts @@ -0,0 +1,41 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ReorderInput } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: AttributeValueReorder +// ==================================================== + +export interface AttributeValueReorder_attributeReorderValues_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AttributeValueReorder_attributeReorderValues_attribute_values { + __typename: "AttributeValue"; + id: string; +} + +export interface AttributeValueReorder_attributeReorderValues_attribute { + __typename: "Attribute"; + id: string; + values: (AttributeValueReorder_attributeReorderValues_attribute_values | null)[] | null; +} + +export interface AttributeValueReorder_attributeReorderValues { + __typename: "AttributeReorderValues"; + errors: AttributeValueReorder_attributeReorderValues_errors[] | null; + attribute: AttributeValueReorder_attributeReorderValues_attribute | null; +} + +export interface AttributeValueReorder { + attributeReorderValues: AttributeValueReorder_attributeReorderValues | null; +} + +export interface AttributeValueReorderVariables { + id: string; + move: ReorderInput; +} diff --git a/src/attributes/types/AttributeValueUpdate.ts b/src/attributes/types/AttributeValueUpdate.ts new file mode 100644 index 000000000..d1ccbee1f --- /dev/null +++ b/src/attributes/types/AttributeValueUpdate.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: AttributeValueUpdate +// ==================================================== + +export interface AttributeValueUpdate_attributeValueUpdate_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AttributeValueUpdate_attributeValueUpdate_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; + type: AttributeValueType | null; + value: string | null; +} + +export interface AttributeValueUpdate_attributeValueUpdate_attribute { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; + inputType: AttributeInputTypeEnum | null; + storefrontSearchPosition: number; + valueRequired: boolean; + values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null; +} + +export interface AttributeValueUpdate_attributeValueUpdate { + __typename: "AttributeValueUpdate"; + errors: AttributeValueUpdate_attributeValueUpdate_errors[] | null; + attribute: AttributeValueUpdate_attributeValueUpdate_attribute | null; +} + +export interface AttributeValueUpdate { + attributeValueUpdate: AttributeValueUpdate_attributeValueUpdate | null; +} + +export interface AttributeValueUpdateVariables { + id: string; + input: AttributeValueCreateInput; +} diff --git a/src/attributes/urls.ts b/src/attributes/urls.ts new file mode 100644 index 000000000..ccd6c28be --- /dev/null +++ b/src/attributes/urls.ts @@ -0,0 +1,38 @@ +import { stringify as stringifyQs } from "qs"; +import urlJoin from "url-join"; + +import { BulkAction, Dialog, Pagination, SingleAction } from "../types"; + +export const attributeSection = "/attributes/"; + +export type AttributeListUrlDialog = "remove"; +export type AttributeListUrlQueryParams = BulkAction & + Dialog & + Pagination; +export const attributeListPath = attributeSection; +export const attributeListUrl = (params?: AttributeListUrlQueryParams) => + attributeListPath + "?" + stringifyQs(params); + +export type AttributeAddUrlDialog = + | "add-value" + | "edit-value" + | "remove-value" + | "remove-values"; +export type AttributeAddUrlQueryParams = Dialog & + SingleAction; +export const attributeAddPath = urlJoin(attributeSection, "add"); +export const attributeAddUrl = (params?: AttributeAddUrlQueryParams) => + attributeAddPath + "?" + stringifyQs(params); + +export type AttributeUrlDialog = + | "add-value" + | "edit-value" + | "remove" + | "remove-value" + | "remove-values"; +export type AttributeUrlQueryParams = BulkAction & + Dialog & + SingleAction; +export const attributePath = (id: string) => urlJoin(attributeSection, id); +export const attributeUrl = (id: string, params?: AttributeUrlQueryParams) => + attributePath(encodeURIComponent(id)) + "?" + stringifyQs(params); diff --git a/src/attributes/views/AttributeCreate/AttributeCreate.tsx b/src/attributes/views/AttributeCreate/AttributeCreate.tsx new file mode 100644 index 000000000..eac1dad27 --- /dev/null +++ b/src/attributes/views/AttributeCreate/AttributeCreate.tsx @@ -0,0 +1,206 @@ +import React from "react"; +import slugify from "slugify"; + +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ReorderEvent, UserError } from "@saleor/types"; +import { + add, + isSelected, + move, + remove, + updateAtIndex +} from "@saleor/utils/lists"; +import AttributePage from "../../components/AttributePage"; +import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog"; +import AttributeValueEditDialog, { + AttributeValueEditDialogFormData +} from "../../components/AttributeValueEditDialog"; +import { AttributeCreateMutation } from "../../mutations"; +import { AttributeCreate } from "../../types/AttributeCreate"; +import { + attributeAddUrl, + AttributeAddUrlDialog, + AttributeAddUrlQueryParams, + attributeListUrl, + attributeUrl +} from "../../urls"; + +interface AttributeDetailsProps { + params: AttributeAddUrlQueryParams; +} + +function areValuesEqual( + a: AttributeValueEditDialogFormData, + b: AttributeValueEditDialogFormData +) { + return a.name === b.name; +} + +const AttributeDetails: React.FC = ({ params }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + + const [values, setValues] = React.useState< + AttributeValueEditDialogFormData[] + >([]); + const [valueErrors, setValueErrors] = React.useState([]); + + const id = params.id ? parseInt(params.id, 0) : undefined; + + const closeModal = () => + navigate( + attributeAddUrl({ + ...params, + action: undefined, + id: undefined + }), + true + ); + + const openModal = (action: AttributeAddUrlDialog, valueId?: string) => + navigate( + attributeAddUrl({ + ...params, + action, + id: valueId + }) + ); + + const handleValueDelete = () => { + setValues(remove(values[params.id], values, areValuesEqual)); + closeModal(); + }; + const handleCreate = (data: AttributeCreate) => { + if (data.attributeCreate.errors.length === 0) { + notify({ text: i18n.t("Successfully created attribute") }); + navigate(attributeUrl(data.attributeCreate.attribute.id)); + } + }; + const handleValueUpdate = (input: AttributeValueEditDialogFormData) => { + if (isSelected(input, values, areValuesEqual)) { + setValueErrors([ + { + field: "name", + message: i18n.t("A value named {{ name }} already exists", { + context: "value edit error", + name: input.name + }) + } + ]); + } else { + setValues(updateAtIndex(input, values, id)); + closeModal(); + } + }; + const handleValueCreate = (input: AttributeValueEditDialogFormData) => { + if (isSelected(input, values, areValuesEqual)) { + setValueErrors([ + { + field: "name", + message: i18n.t("A value named {{ name }} already exists", { + context: "value edit error", + name: input.name + }) + } + ]); + } else { + setValues(add(input, values)); + closeModal(); + } + }; + const handleValueReorder = ({ newIndex, oldIndex }: ReorderEvent) => + setValues(move(values[oldIndex], values, areValuesEqual, newIndex)); + + return ( + + {(attributeCreate, attributeCreateOpts) => { + const createTransitionState = getMutationState( + attributeCreateOpts.called, + attributeCreateOpts.loading, + maybe(() => attributeCreateOpts.data.attributeCreate.errors) + ); + + return ( + <> + attributeCreateOpts.data.attributeCreate.errors, + [] + )} + onBack={() => navigate(attributeListUrl())} + onDelete={undefined} + onSubmit={input => + attributeCreate({ + variables: { + input: { + ...input, + storefrontSearchPosition: parseInt( + input.storefrontSearchPosition, + 0 + ), + values: values.map(value => ({ + name: value.name + })) + } + } + }) + } + onValueAdd={() => openModal("add-value")} + onValueDelete={id => openModal("remove-value", id)} + onValueReorder={handleValueReorder} + onValueUpdate={id => openModal("edit-value", id)} + saveButtonBarState={createTransitionState} + values={values.map((value, valueIndex) => ({ + __typename: "AttributeValue" as "AttributeValue", + id: valueIndex.toString(), + slug: slugify(value.name).toLowerCase(), + sortOrder: valueIndex, + type: null, + value: null, + ...value + }))} + /> + + {values.length > 0 && ( + <> + values[id].name, "...")} + confirmButtonState="default" + onClose={closeModal} + onConfirm={handleValueDelete} + /> + values[params.id])} + confirmButtonState="default" + disabled={false} + errors={valueErrors} + open={params.action === "edit-value"} + onClose={closeModal} + onSubmit={handleValueUpdate} + /> + + )} + + ); + }} + + ); +}; +AttributeDetails.displayName = "AttributeDetails"; + +export default AttributeDetails; diff --git a/src/attributes/views/AttributeCreate/index.ts b/src/attributes/views/AttributeCreate/index.ts new file mode 100644 index 000000000..d6f344ce6 --- /dev/null +++ b/src/attributes/views/AttributeCreate/index.ts @@ -0,0 +1,2 @@ +export * from "./AttributeCreate"; +export { default } from "./AttributeCreate"; diff --git a/src/attributes/views/AttributeDetails/AttributeDetails.tsx b/src/attributes/views/AttributeDetails/AttributeDetails.tsx new file mode 100644 index 000000000..c5a04e2ff --- /dev/null +++ b/src/attributes/views/AttributeDetails/AttributeDetails.tsx @@ -0,0 +1,360 @@ +import React from "react"; + +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ReorderEvent } from "@saleor/types"; +import { move } from "@saleor/utils/lists"; +import AttributeDeleteDialog from "../../components/AttributeDeleteDialog"; +import AttributePage from "../../components/AttributePage"; +import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog"; +import AttributeValueEditDialog from "../../components/AttributeValueEditDialog"; +import { + AttributeDeleteMutation, + AttributeUpdateMutation, + AttributeValueCreateMutation, + AttributeValueDeleteMutation, + AttributeValueReorderMutation, + AttributeValueUpdateMutation +} from "../../mutations"; +import { AttributeDetailsQuery } from "../../queries"; +import { AttributeDelete } from "../../types/AttributeDelete"; +import { AttributeUpdate } from "../../types/AttributeUpdate"; +import { AttributeValueCreate } from "../../types/AttributeValueCreate"; +import { AttributeValueDelete } from "../../types/AttributeValueDelete"; +import { AttributeValueReorder } from "../../types/AttributeValueReorder"; +import { AttributeValueUpdate } from "../../types/AttributeValueUpdate"; +import { + attributeListUrl, + attributeUrl, + AttributeUrlDialog, + AttributeUrlQueryParams +} from "../../urls"; + +interface AttributeDetailsProps { + id: string; + params: AttributeUrlQueryParams; +} + +const AttributeDetails: React.FC = ({ id, params }) => { + const navigate = useNavigator(); + const notify = useNotifier(); + + const closeModal = () => + navigate( + attributeUrl(id, { + ...params, + action: undefined, + id: undefined, + ids: undefined + }), + true + ); + + const openModal = (action: AttributeUrlDialog, valueId?: string) => + navigate( + attributeUrl(id, { + ...params, + action, + id: valueId + }) + ); + + const handleDelete = (data: AttributeDelete) => { + if (data.attributeDelete.errors.length === 0) { + notify({ text: i18n.t("Attribute removed") }); + navigate(attributeListUrl()); + } + }; + const handleValueDelete = (data: AttributeValueDelete) => { + if (data.attributeValueDelete.errors.length === 0) { + notify({ text: i18n.t("Value removed") }); + closeModal(); + } + }; + const handleUpdate = (data: AttributeUpdate) => { + if (data.attributeUpdate.errors.length === 0) { + notify({ text: i18n.t("Saved changes") }); + } + }; + const handleValueUpdate = (data: AttributeValueUpdate) => { + if (data.attributeValueUpdate.errors.length === 0) { + notify({ text: i18n.t("Saved changes") }); + closeModal(); + } + }; + const handleValueCreate = (data: AttributeValueCreate) => { + if (data.attributeValueCreate.errors.length === 0) { + notify({ text: i18n.t("Added new value") }); + closeModal(); + } + }; + const handleValueReorderMutation = (data: AttributeValueReorder) => { + if (data.attributeReorderValues.errors.length !== 0) { + notify({ + text: i18n.t("Error: {{ errorMessage }}", { + errorMessage: data.attributeReorderValues.errors[0].message + }) + }); + } + }; + + return ( + + {({ data, loading }) => ( + + {(attributeDelete, attributeDeleteOpts) => ( + + {(attributeValueDelete, attributeValueDeleteOpts) => ( + + {(attributeUpdate, attributeUpdateOpts) => ( + + {(attributeValueUpdate, attributeValueUpdateOpts) => ( + + {(attributeValueCreate, attributeValueCreateOpts) => ( + + {attributeValueReorder => { + const deleteTransitionState = getMutationState( + attributeDeleteOpts.called, + attributeDeleteOpts.loading, + maybe( + () => + attributeDeleteOpts.data.attributeDelete + .errors + ) + ); + const deleteValueTransitionState = getMutationState( + attributeValueDeleteOpts.called, + attributeValueDeleteOpts.loading, + maybe( + () => + attributeValueDeleteOpts.data + .attributeValueDelete.errors + ) + ); + const updateTransitionState = getMutationState( + attributeUpdateOpts.called, + attributeUpdateOpts.loading, + maybe( + () => + attributeUpdateOpts.data.attributeUpdate + .errors + ) + ); + const updateValueTransitionState = getMutationState( + attributeValueUpdateOpts.called, + attributeValueUpdateOpts.loading, + maybe( + () => + attributeValueUpdateOpts.data + .attributeValueUpdate.errors + ) + ); + const createValueTransitionState = getMutationState( + attributeValueCreateOpts.called, + attributeValueCreateOpts.loading, + maybe( + () => + attributeValueCreateOpts.data + .attributeValueCreate.errors + ) + ); + + const handleValueReorder = ({ + newIndex, + oldIndex + }: ReorderEvent) => + attributeValueReorder({ + optimisticResponse: { + attributeReorderValues: { + __typename: "AttributeReorderValues", + attribute: { + ...data.attribute, + values: move( + data.attribute.values[oldIndex], + data.attribute.values, + (a, b) => a.id === b.id, + newIndex + ) + }, + errors: [] + } + }, + variables: { + id, + move: { + id: data.attribute.values[oldIndex].id, + sortOrder: newIndex - oldIndex + } + } + }); + + return ( + <> + data.attribute)} + disabled={loading} + errors={maybe( + () => + attributeUpdateOpts.data + .attributeUpdate.errors, + [] + )} + onBack={() => + navigate(attributeListUrl()) + } + onDelete={() => openModal("remove")} + onSubmit={data => { + const input = { + ...data, + inputType: undefined + }; + + attributeUpdate({ + variables: { + id, + input: { + ...input, + storefrontSearchPosition: parseInt( + input.storefrontSearchPosition, + 0 + ) + } + } + }); + }} + onValueAdd={() => openModal("add-value")} + onValueDelete={id => + openModal("remove-value", id) + } + onValueReorder={handleValueReorder} + onValueUpdate={id => + openModal("edit-value", id) + } + saveButtonBarState={updateTransitionState} + values={maybe( + () => data.attribute.values + )} + /> + data.attribute.name, + "..." + )} + confirmButtonState={deleteTransitionState} + onClose={closeModal} + onConfirm={() => + attributeDelete({ + variables: { + id + } + }) + } + /> + data.attribute.name, + "..." + )} + open={params.action === "remove-value"} + name={maybe( + () => + data.attribute.values.find( + value => params.id === value.id + ).name, + "..." + )} + useName={true} + confirmButtonState={ + deleteValueTransitionState + } + onClose={closeModal} + onConfirm={() => + attributeValueDelete({ + variables: { + id: params.id + } + }) + } + /> + + attributeValueCreateOpts.data + .attributeValueCreate.errors, + [] + )} + open={params.action === "add-value"} + onClose={closeModal} + onSubmit={input => + attributeValueCreate({ + variables: { + id, + input + } + }) + } + /> + + data.attribute.values.find( + value => params.id === value.id + ) + )} + confirmButtonState={ + updateValueTransitionState + } + disabled={loading} + errors={maybe( + () => + attributeValueUpdateOpts.data + .attributeValueUpdate.errors, + [] + )} + open={params.action === "edit-value"} + onClose={closeModal} + onSubmit={input => + attributeValueUpdate({ + variables: { + id: data.attribute.values.find( + value => params.id === value.id + ).id, + input + } + }) + } + /> + + ); + }} + + )} + + )} + + )} + + )} + + )} + + )} + + ); +}; +AttributeDetails.displayName = "AttributeDetails"; + +export default AttributeDetails; diff --git a/src/attributes/views/AttributeDetails/index.ts b/src/attributes/views/AttributeDetails/index.ts new file mode 100644 index 000000000..f5f9c85b8 --- /dev/null +++ b/src/attributes/views/AttributeDetails/index.ts @@ -0,0 +1,2 @@ +export * from "./AttributeDetails"; +export { default } from "./AttributeDetails"; diff --git a/src/attributes/views/AttributeList/AttributeList.tsx b/src/attributes/views/AttributeList/AttributeList.tsx new file mode 100644 index 000000000..6e311cfab --- /dev/null +++ b/src/attributes/views/AttributeList/AttributeList.tsx @@ -0,0 +1,137 @@ +import IconButton from "@material-ui/core/IconButton"; +import DeleteIcon from "@material-ui/icons/Delete"; +import React from "react"; + +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import usePaginator, { + createPaginationState +} from "@saleor/hooks/usePaginator"; +import { PAGINATE_BY } from "../../../config"; +import useBulkActions from "../../../hooks/useBulkActions"; +import i18n from "../../../i18n"; +import { getMutationState, maybe } from "../../../misc"; +import AttributeBulkDeleteDialog from "../../components/AttributeBulkDeleteDialog"; +import AttributeListPage from "../../components/AttributeListPage"; +import { AttributeBulkDeleteMutation } from "../../mutations"; +import { AttributeListQuery } from "../../queries"; +import { AttributeBulkDelete } from "../../types/AttributeBulkDelete"; +import { + attributeAddUrl, + attributeListUrl, + AttributeListUrlDialog, + AttributeListUrlQueryParams, + attributeUrl +} from "../../urls"; + +interface AttributeListProps { + params: AttributeListUrlQueryParams; +} + +const AttributeList: React.FC = ({ params }) => { + const navigate = useNavigator(); + const paginate = usePaginator(); + const notify = useNotifier(); + const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( + params.ids + ); + + const closeModal = () => + navigate( + attributeListUrl({ + ...params, + action: undefined, + ids: undefined + }), + true + ); + + const openModal = (action: AttributeListUrlDialog, ids?: string[]) => + navigate( + attributeListUrl({ + ...params, + action, + ids + }) + ); + + const paginationState = createPaginationState(PAGINATE_BY, params); + const queryVariables = React.useMemo(() => paginationState, [params]); + + return ( + + {({ data, loading, refetch }) => { + const { loadNextPage, loadPreviousPage, pageInfo } = paginate( + maybe(() => data.attributes.pageInfo), + paginationState, + params + ); + + const handleBulkDelete = (data: AttributeBulkDelete) => { + if (data.attributeBulkDelete.errors.length === 0) { + closeModal(); + notify({ + text: i18n.t("Attributes removed") + }); + reset(); + refetch(); + } + }; + + return ( + + {(attributeBulkDelete, attributeBulkDeleteOpts) => { + const bulkDeleteMutationState = getMutationState( + attributeBulkDeleteOpts.called, + attributeBulkDeleteOpts.loading, + maybe( + () => attributeBulkDeleteOpts.data.attributeBulkDelete.errors + ) + ); + + return ( + <> + + data.attributes.edges.map(edge => edge.node) + )} + disabled={loading || attributeBulkDeleteOpts.loading} + isChecked={isSelected} + onAdd={() => navigate(attributeAddUrl())} + onNextPage={loadNextPage} + onPreviousPage={loadPreviousPage} + onRowClick={id => () => navigate(attributeUrl(id))} + pageInfo={pageInfo} + selected={listElements.length} + toggle={toggle} + toggleAll={toggleAll} + toolbar={ + openModal("remove", listElements)} + > + + + } + /> + + attributeBulkDelete({ variables: { ids: params.ids } }) + } + onClose={closeModal} + quantity={maybe(() => params.ids.length.toString(), "...")} + /> + + ); + }} + + ); + }} + + ); +}; +AttributeList.displayName = "AttributeList"; + +export default AttributeList; diff --git a/src/attributes/views/AttributeList/index.ts b/src/attributes/views/AttributeList/index.ts new file mode 100644 index 000000000..216f3b4bc --- /dev/null +++ b/src/attributes/views/AttributeList/index.ts @@ -0,0 +1,2 @@ +export * from "./AttributeList"; +export { default } from "./AttributeList"; diff --git a/src/components/ColumnPicker/ColumnPicker.tsx b/src/components/ColumnPicker/ColumnPicker.tsx new file mode 100644 index 000000000..a0d72cb09 --- /dev/null +++ b/src/components/ColumnPicker/ColumnPicker.tsx @@ -0,0 +1,103 @@ +import ClickAwayListener from "@material-ui/core/ClickAwayListener"; +import Grow from "@material-ui/core/Grow"; +import Popper from "@material-ui/core/Popper"; +import { Theme } from "@material-ui/core/styles"; +import { fade } from "@material-ui/core/styles/colorManipulator"; +import makeStyles from "@material-ui/styles/makeStyles"; +import React from "react"; + +import ColumnPickerButton from "./ColumnPickerButton"; +import ColumnPickerContent, { + ColumnPickerContentProps +} from "./ColumnPickerContent"; + +export interface ColumnPickerProps extends ColumnPickerContentProps { + className?: string; + initial?: boolean; +} + +const useStyles = makeStyles( + (theme: Theme) => ({ + popper: { + boxShadow: `0px 5px 10px 0 ${fade(theme.palette.common.black, 0.05)}`, + marginTop: theme.spacing.unit * 2, + zIndex: 1 + } + }), + { + name: "ColumnPicker" + } +); + +const ColumnPicker: React.FC = props => { + const { + className, + columns, + initial = false, + selectedColumns, + onCancel, + onColumnToggle, + onReset, + onSave + } = props; + const classes = useStyles(props); + const anchor = React.useRef(); + const [isExpanded, setExpansionState] = React.useState(false); + + React.useEffect(() => { + setTimeout(() => setExpansionState(initial), 100); + }, []); + + const handleCancel = React.useCallback(() => { + setExpansionState(false); + onCancel(); + }, []); + + const handleSave = () => { + setExpansionState(false); + onSave(); + }; + + return ( +
+ setExpansionState(prevState => !prevState)} + /> + + {({ TransitionProps, placement }) => ( + + setExpansionState(false)} + mouseEvent="onClick" + > + + + + )} + +
+ ); +}; + +export default ColumnPicker; diff --git a/src/components/ColumnPicker/ColumnPickerButton.tsx b/src/components/ColumnPicker/ColumnPickerButton.tsx new file mode 100644 index 000000000..bdd7dde47 --- /dev/null +++ b/src/components/ColumnPicker/ColumnPickerButton.tsx @@ -0,0 +1,67 @@ +import Button from "@material-ui/core/Button"; +import { Theme } from "@material-ui/core/styles"; +import { fade } from "@material-ui/core/styles/colorManipulator"; +import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; +import makeStyles from "@material-ui/styles/makeStyles"; +import classNames from "classnames"; +import React from "react"; + +import i18n from "@saleor/i18n"; + +interface ColumnPickerButtonProps { + active: boolean; + className?: string; + onClick: () => void; +} + +const useStyles = makeStyles( + (theme: Theme) => ({ + icon: { + marginLeft: theme.spacing.unit * 2, + transition: theme.transitions.duration.short + "ms" + }, + root: { + "& span": { + color: theme.palette.primary.main + }, + paddingRight: theme.spacing.unit + }, + rootActive: { + background: fade(theme.palette.primary.main, 0.1) + }, + rotate: { + transform: "rotate(180deg)" + } + }), + { + name: "ColumnPickerButton" + } +); + +const ColumnPickerButton: React.FC = props => { + const { active, className, onClick } = props; + const classes = useStyles(props); + + return ( + + ); +}; + +export default ColumnPickerButton; diff --git a/src/components/ColumnPicker/ColumnPickerContent.tsx b/src/components/ColumnPicker/ColumnPickerContent.tsx new file mode 100644 index 000000000..3629d19d1 --- /dev/null +++ b/src/components/ColumnPicker/ColumnPickerContent.tsx @@ -0,0 +1,128 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import { Theme } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import makeStyles from "@material-ui/styles/makeStyles"; +import classNames from "classnames"; +import React from "react"; + +import useElementScroll from "@saleor/hooks/useElementScroll"; +import i18n from "@saleor/i18n"; +import { isSelected } from "@saleor/utils/lists"; +import ControlledCheckbox from "../ControlledCheckbox"; +import Hr from "../Hr"; + +export interface ColumnPickerChoice { + label: string; + value: string; +} +export interface ColumnPickerContentProps { + columns: ColumnPickerChoice[]; + selectedColumns: string[]; + onCancel: () => void; + onColumnToggle: (column: string) => void; + onReset: () => void; + onSave: () => void; +} + +const useStyles = makeStyles((theme: Theme) => ({ + actionBar: { + display: "flex", + justifyContent: "space-between" + }, + actionBarContainer: { + boxShadow: `0px 0px 0px 0px ${theme.palette.background.paper}`, + transition: theme.transitions.duration.short + "ms" + }, + content: { + display: "grid", + gridColumnGap: theme.spacing.unit * 3, + gridTemplateColumns: "repeat(3, 1fr)", + maxHeight: 256, + overflowX: "visible", + overflowY: "scroll", + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px` + }, + contentContainer: { + padding: 0 + }, + dropShadow: { + boxShadow: `0px -5px 10px 0px ${theme.overrides.MuiCard.root.borderColor}` + } +})); + +const ColumnPickerContent: React.FC = props => { + const { + columns, + selectedColumns, + onCancel, + onColumnToggle, + onReset, + onSave + } = props; + const classes = useStyles(props); + const anchor = React.useRef(); + const scrollPosition = useElementScroll(anchor); + + const dropShadow = anchor.current + ? scrollPosition.y + anchor.current.clientHeight < + anchor.current.scrollHeight + : false; + + return ( + + + + {i18n.t( + "{{ numberOfSelected }} columns selected out of {{ numberOfTotal }}", + { + context: "pick columns to display", + numberOfSelected: selectedColumns.length, + numberOfTotal: columns.length + } + )} + + +
+ +
+ {columns.map(column => ( + a === b + )} + name={column.value} + label={column.label} + onChange={() => onColumnToggle(column.value)} + /> + ))} +
+
+
+ +
+ +
+ + +
+
+
+
+ ); +}; + +export default ColumnPickerContent; diff --git a/src/components/ColumnPicker/index.ts b/src/components/ColumnPicker/index.ts new file mode 100644 index 000000000..301ba74e4 --- /dev/null +++ b/src/components/ColumnPicker/index.ts @@ -0,0 +1,4 @@ +export { default } from "./ColumnPicker"; +export * from "./ColumnPicker"; +export * from "./ColumnPickerButton"; +export * from "./ColumnPickerContent"; diff --git a/src/components/RadioGroupField/RadioGroupField.tsx b/src/components/RadioGroupField/RadioGroupField.tsx new file mode 100644 index 000000000..b22446312 --- /dev/null +++ b/src/components/RadioGroupField/RadioGroupField.tsx @@ -0,0 +1,95 @@ +import FormControl from "@material-ui/core/FormControl"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import FormHelperText from "@material-ui/core/FormHelperText"; +import FormLabel from "@material-ui/core/FormLabel"; +import MenuItem from "@material-ui/core/MenuItem"; +import Radio from "@material-ui/core/Radio"; +import RadioGroup from "@material-ui/core/RadioGroup"; +import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; +import classNames from "classnames"; +import React from "react"; + +import i18n from "../../i18n"; + +const styles = createStyles({ + formControl: { + padding: "0 15px", + width: "100%" + }, + formLabel: { + marginLeft: "-5px", + paddingBottom: "10px" + }, + radioLabel: { + "& > span": { + padding: "6px" + } + } +}); + +interface RadioGroupFieldProps extends WithStyles { + choices: Array<{ + value: string; + label: string | React.ReactNode; + }>; + className?: string; + disabled?: boolean; + error?: boolean; + hint?: string; + label?: string; + name?: string; + value?: string; + onChange: (event: React.ChangeEvent) => void; +} + +export const RadioGroupField = withStyles(styles, { + name: "RadioGroupField" +})( + ({ + className, + classes, + disabled, + error, + label, + choices, + value, + onChange, + name, + hint + }: RadioGroupFieldProps) => { + return ( + + {label ? ( + {label} + ) : null} + + {choices.length > 0 ? ( + choices.map(choice => ( + } + label={choice.label} + key={choice.value} + /> + )) + ) : ( + {i18n.t("No results found")} + )} + + {hint && {hint}} + + ); + } +); +RadioGroupField.displayName = "RadioGroupField"; +export default RadioGroupField; diff --git a/src/components/RadioGroupField/index.ts b/src/components/RadioGroupField/index.ts new file mode 100644 index 000000000..58ebd26ab --- /dev/null +++ b/src/components/RadioGroupField/index.ts @@ -0,0 +1,2 @@ +export { default } from "./RadioGroupField"; +export * from "./RadioGroupField"; diff --git a/src/components/RowNumberSelect/RowNumberSelect.tsx b/src/components/RowNumberSelect/RowNumberSelect.tsx new file mode 100644 index 000000000..2a670e7ae --- /dev/null +++ b/src/components/RowNumberSelect/RowNumberSelect.tsx @@ -0,0 +1,71 @@ +import MenuItem from "@material-ui/core/MenuItem"; +import Select from "@material-ui/core/Select"; +import { Theme } from "@material-ui/core/styles"; +import { createStyles, makeStyles, useTheme } from "@material-ui/styles"; +import React from "react"; + +import i18n from "../../i18n"; +import { ListSettings } from "../../types"; + +const useStyles = makeStyles( + (theme: Theme) => + createStyles({ + label: { + fontSize: 14 + }, + select: { + "& div": { + "&:focus": { + background: "none" + }, + color: theme.palette.primary.main, + padding: "0 10px 0 5px" + }, + "& svg": { + color: theme.palette.primary.main + }, + "&:after, &:before, &:hover": { + border: "none !important" + } + } + }), + { + name: "RowNumberSelect" + } +); + +interface RowNumberSelectProps { + choices: number[]; + className?: string; + settings: ListSettings; + onChange(key: keyof ListSettings, value: any); +} + +const RowNumberSelect: React.FC = ({ + className, + choices, + settings, + onChange +}) => { + const theme = useTheme(); + const classes = useStyles({ theme }); + return ( +
+ {i18n.t("No of Rows:")} + +
+ ); +}; + +export default RowNumberSelect; diff --git a/src/components/RowNumberSelect/index.ts b/src/components/RowNumberSelect/index.ts new file mode 100644 index 000000000..380ec9594 --- /dev/null +++ b/src/components/RowNumberSelect/index.ts @@ -0,0 +1,2 @@ +export { default } from "./RowNumberSelect"; +export * from "./RowNumberSelect"; diff --git a/src/components/SortableTable/SortableHandle.tsx b/src/components/SortableTable/SortableHandle.tsx new file mode 100644 index 000000000..13843db3b --- /dev/null +++ b/src/components/SortableTable/SortableHandle.tsx @@ -0,0 +1,29 @@ +import { Theme } from "@material-ui/core/styles"; +import TableCell from "@material-ui/core/TableCell"; +import makeStyles from "@material-ui/styles/makeStyles"; +import React from "react"; +import { SortableHandle as SortableHandleHoc } from "react-sortable-hoc"; + +import Draggable from "@saleor/icons/Draggable"; + +const useStyles = makeStyles((theme: Theme) => ({ + columnDrag: { + "&:first-child": { + paddingRight: theme.spacing.unit * 2 + }, + cursor: "grab", + width: 48 + theme.spacing.unit * 1.5 + } +})); + +const SortableHandle = SortableHandleHoc(() => { + const classes = useStyles({}); + + return ( + + + + ); +}); + +export default SortableHandle; diff --git a/src/components/SortableTable/SortableTableBody.tsx b/src/components/SortableTable/SortableTableBody.tsx new file mode 100644 index 000000000..5a07bfb83 --- /dev/null +++ b/src/components/SortableTable/SortableTableBody.tsx @@ -0,0 +1,45 @@ +import { Theme } from "@material-ui/core/styles"; +import TableBody, { TableBodyProps } from "@material-ui/core/TableBody"; +import makeStyles from "@material-ui/styles/makeStyles"; +import React from "react"; +import { SortableContainer } from "react-sortable-hoc"; + +import { ReorderAction } from "@saleor/types"; + +const InnerSortableTableBody = SortableContainer( + ({ children, ...props }) => {children} +); + +export interface SortableTableBodyProps { + onSortEnd: ReorderAction; +} + +const useStyles = makeStyles((theme: Theme) => ({ + ghost: { + "& td": { + borderBottom: "none" + }, + background: theme.palette.background.paper, + fontFamily: theme.typography.fontFamily, + fontSize: theme.overrides.MuiTableCell.root.fontSize, + opacity: 0.5 + } +})); + +const SortableTableBody: React.FC< + TableBodyProps & SortableTableBodyProps +> = props => { + const classes = useStyles({}); + + return ( + + ); +}; + +export default SortableTableBody; diff --git a/src/components/SortableTable/SortableTableRow.tsx b/src/components/SortableTable/SortableTableRow.tsx new file mode 100644 index 000000000..f75299921 --- /dev/null +++ b/src/components/SortableTable/SortableTableRow.tsx @@ -0,0 +1,15 @@ +import TableRow, { TableRowProps } from "@material-ui/core/TableRow"; +import React from "react"; +import { SortableElement } from "react-sortable-hoc"; +import SortableHandle from "./SortableHandle"; + +const SortableTableRow = SortableElement( + ({ children, ...props }) => ( + + + {children} + + ) +); + +export default SortableTableRow; diff --git a/src/components/SortableTable/index.ts b/src/components/SortableTable/index.ts new file mode 100644 index 000000000..15d3a941d --- /dev/null +++ b/src/components/SortableTable/index.ts @@ -0,0 +1,5 @@ +export * from "./SortableTableBody"; +export { default as SortableTableBody } from "./SortableTableBody"; + +export * from "./SortableTableRow"; +export { default as SortableTableRow } from "./SortableTableRow"; diff --git a/src/discounts/components/VoucherDates/VoucherDates.tsx b/src/discounts/components/VoucherDates/VoucherDates.tsx new file mode 100644 index 000000000..f15672cf9 --- /dev/null +++ b/src/discounts/components/VoucherDates/VoucherDates.tsx @@ -0,0 +1,103 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; +import Grid from "@saleor/components/Grid"; +import i18n from "../../../i18n"; +import { FormErrors } from "../../../types"; +import { FormData } from "../VoucherDetailsPage"; + +interface VoucherDatesProps { + data: FormData; + defaultCurrency: string; + disabled: boolean; + errors: FormErrors<"endDate" | "startDate">; + onChange: (event: React.ChangeEvent) => void; +} + +const VoucherDates = ({ + data, + disabled, + errors, + onChange +}: VoucherDatesProps) => { + return ( + + + + + + + + + {data.hasEndDate && ( + + + + + )} + + + ); +}; +export default VoucherDates; diff --git a/src/discounts/components/VoucherDates/index.ts b/src/discounts/components/VoucherDates/index.ts new file mode 100644 index 000000000..da24480b6 --- /dev/null +++ b/src/discounts/components/VoucherDates/index.ts @@ -0,0 +1,2 @@ +export { default } from "./VoucherDates"; +export * from "./VoucherDates"; diff --git a/src/discounts/components/VoucherLimits/VoucherLimits.tsx b/src/discounts/components/VoucherLimits/VoucherLimits.tsx new file mode 100644 index 000000000..224c8e360 --- /dev/null +++ b/src/discounts/components/VoucherLimits/VoucherLimits.tsx @@ -0,0 +1,64 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; +import i18n from "../../../i18n"; +import { FormErrors } from "../../../types"; +import { FormData } from "../VoucherDetailsPage"; + +interface VoucherLimitsProps { + data: FormData; + defaultCurrency: string; + disabled: boolean; + errors: FormErrors<"usageLimit">; + onChange: (event: React.ChangeEvent) => void; +} + +const VoucherLimits = ({ + data, + disabled, + errors, + onChange +}: VoucherLimitsProps) => { + return ( + + + + + {data.hasUsageLimit && ( + + )} + + + + ); +}; +export default VoucherLimits; diff --git a/src/discounts/components/VoucherLimits/index.ts b/src/discounts/components/VoucherLimits/index.ts new file mode 100644 index 000000000..5bb995ee4 --- /dev/null +++ b/src/discounts/components/VoucherLimits/index.ts @@ -0,0 +1,2 @@ +export { default } from "./VoucherLimits"; +export * from "./VoucherLimits"; diff --git a/src/discounts/components/VoucherRequirements/VoucherRequirements.tsx b/src/discounts/components/VoucherRequirements/VoucherRequirements.tsx new file mode 100644 index 000000000..4ff9a8aa3 --- /dev/null +++ b/src/discounts/components/VoucherRequirements/VoucherRequirements.tsx @@ -0,0 +1,82 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import { FormSpacer } from "@saleor/components/FormSpacer"; +import RadioGroupField from "@saleor/components/RadioGroupField"; +import { RequirementsPicker } from "@saleor/discounts/types"; +import i18n from "@saleor/i18n"; +import { FormErrors } from "@saleor/types"; +import { FormData } from "../VoucherDetailsPage"; + +interface VoucherRequirementsProps { + data: FormData; + defaultCurrency: string; + disabled: boolean; + errors: FormErrors<"minAmountSpent" | "minCheckoutItemsQuantity">; + onChange: (event: React.ChangeEvent) => void; +} + +const VoucherRequirements = ({ + data, + disabled, + errors, + onChange +}: VoucherRequirementsProps) => { + const requirementsPickerChoices = [ + { + label: i18n.t("None"), + value: RequirementsPicker.NONE + }, + { + label: i18n.t("Minimal order value"), + value: RequirementsPicker.ORDER + }, + { + label: i18n.t("Minimum quantity of items"), + value: RequirementsPicker.ITEM + } + ]; + + return ( + + + + + + {data.requirementsPicker === RequirementsPicker.ORDER ? ( + + ) : data.requirementsPicker === RequirementsPicker.ITEM ? ( + + ) : null} + + + ); +}; +export default VoucherRequirements; diff --git a/src/discounts/components/VoucherRequirements/index.ts b/src/discounts/components/VoucherRequirements/index.ts new file mode 100644 index 000000000..4f8ff6ff3 --- /dev/null +++ b/src/discounts/components/VoucherRequirements/index.ts @@ -0,0 +1,2 @@ +export { default } from "./VoucherRequirements"; +export * from "./VoucherRequirements"; diff --git a/src/discounts/components/VoucherTypes/VoucherTypes.tsx b/src/discounts/components/VoucherTypes/VoucherTypes.tsx new file mode 100644 index 000000000..65074806e --- /dev/null +++ b/src/discounts/components/VoucherTypes/VoucherTypes.tsx @@ -0,0 +1,60 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import Grid from "@saleor/components/Grid"; +import RadioGroupField from "@saleor/components/RadioGroupField"; +import i18n from "../../../i18n"; +import { FormErrors } from "../../../types"; +import { DiscountValueTypeEnum } from "../../../types/globalTypes"; +import { FormData } from "../VoucherDetailsPage"; + +interface VoucherTypesProps { + data: FormData; + errors: FormErrors<"discountType">; + disabled: boolean; + onChange: (event: React.ChangeEvent) => void; +} + +const VoucherTypes = ({ + data, + disabled, + errors, + onChange +}: VoucherTypesProps) => { + const voucherTypeChoices = [ + { + label: i18n.t("Fixed Amount"), + value: DiscountValueTypeEnum.FIXED + }, + { + label: i18n.t("Percentage"), + value: DiscountValueTypeEnum.PERCENTAGE + }, + { + label: i18n.t("Free Shipping"), + value: "SHIPPING" + } + ]; + + return ( + + + + + + + + + ); +}; +export default VoucherTypes; diff --git a/src/discounts/components/VoucherTypes/index.ts b/src/discounts/components/VoucherTypes/index.ts new file mode 100644 index 000000000..89168c9e7 --- /dev/null +++ b/src/discounts/components/VoucherTypes/index.ts @@ -0,0 +1,2 @@ +export { default } from "./VoucherTypes"; +export * from "./VoucherTypes"; diff --git a/src/discounts/components/VoucherValue/VoucherValue.tsx b/src/discounts/components/VoucherValue/VoucherValue.tsx new file mode 100644 index 000000000..8529ef79a --- /dev/null +++ b/src/discounts/components/VoucherValue/VoucherValue.tsx @@ -0,0 +1,112 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Typography from "@material-ui/core/Typography"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import ControlledSwitch from "@saleor/components/ControlledSwitch"; +import { FormSpacer } from "@saleor/components/FormSpacer"; +import Hr from "@saleor/components/Hr"; +import RadioGroupField from "@saleor/components/RadioGroupField"; +import TextFieldWithChoice from "@saleor/components/TextFieldWithChoice"; +import i18n from "../../../i18n"; +import { FormErrors } from "../../../types"; +import { DiscountValueTypeEnum } from "../../../types/globalTypes"; +import { translateVoucherTypes } from "../../translations"; +import { FormData } from "../VoucherDetailsPage"; + +interface VoucherValueProps { + data: FormData; + defaultCurrency: string; + errors: FormErrors<"discountValue" | "type">; + disabled: boolean; + variant: string; + onChange: (event: React.ChangeEvent) => void; +} + +export enum VoucherType { + ENTIRE_ORDER = "ENTIRE_ORDER", + SPECIFIC_PRODUCT = "SPECIFIC_PRODUCT" +} + +const VoucherValue = ({ + data, + defaultCurrency, + disabled, + errors, + variant, + onChange +}: VoucherValueProps) => { + const translatedVoucherTypes = translateVoucherTypes(); + const voucherTypeChoices = Object.values(VoucherType).map(type => ({ + label: translatedVoucherTypes[type], + value: type + })); + + return ( + + + + + + {variant === "update" && ( + <> + + + + )} +
+ + + {i18n.t("Only once per order", { + context: "voucher application" + })} + + {i18n.t( + "If this option is disabled, discount will be counted for every eligible product" + )} + + + } + onChange={onChange} + name={"applyOncePerOrder" as keyof FormData} + disabled={disabled} + /> +
+
+ ); +}; +export default VoucherValue; diff --git a/src/discounts/components/VoucherValue/index.ts b/src/discounts/components/VoucherValue/index.ts new file mode 100644 index 000000000..d8107f9b3 --- /dev/null +++ b/src/discounts/components/VoucherValue/index.ts @@ -0,0 +1,2 @@ +export { default } from "./VoucherValue"; +export * from "./VoucherValue"; diff --git a/src/discounts/types.ts b/src/discounts/types.ts new file mode 100644 index 000000000..9c15c2ed6 --- /dev/null +++ b/src/discounts/types.ts @@ -0,0 +1,5 @@ +export enum RequirementsPicker { + ORDER = "ORDER", + ITEM = "ITEM", + NONE = "NONE" +} diff --git a/src/hooks/useElementScroll.ts b/src/hooks/useElementScroll.ts new file mode 100644 index 000000000..0804d824e --- /dev/null +++ b/src/hooks/useElementScroll.ts @@ -0,0 +1,34 @@ +import throttle from "lodash-es/throttle"; +import { MutableRefObject, useEffect, useState } from "react"; + +function getPosition(anchor?: HTMLElement) { + if (!!anchor) { + return { + x: anchor.scrollLeft, + y: anchor.scrollTop + }; + } + return { + x: 0, + y: 0 + }; +} + +function useElementScroll(anchor: MutableRefObject) { + const [scroll, setScroll] = useState(getPosition(anchor.current)); + + useEffect(() => { + if (!!anchor.current) { + const handleScroll = throttle( + () => setScroll(getPosition(anchor.current)), + 100 + ); + anchor.current.addEventListener("scroll", handleScroll); + + return () => anchor.current.removeEventListener("scroll", handleScroll); + } + }, [anchor.current]); + + return scroll; +} +export default useElementScroll; diff --git a/src/hooks/useForm.ts b/src/hooks/useForm.ts new file mode 100644 index 000000000..e09acd9de --- /dev/null +++ b/src/hooks/useForm.ts @@ -0,0 +1,148 @@ +import isEqual from "lodash-es/isEqual"; +import { useState } from "react"; + +import { UserError } from "@saleor/types"; +import { toggle } from "@saleor/utils/lists"; +import useStateFromProps from "./useStateFromProps"; + +export interface ChangeEvent { + target: { + name: string; + value: TData; + }; +} + +export type FormChange = (event: ChangeEvent, cb?: () => void) => void; + +export interface UseFormResult { + change: FormChange; + data: T; + errors: Record; + hasChanged: boolean; + reset: () => void; + set: (data: T) => void; + submit: () => void; + triggerChange: () => void; + toggleValue: FormChange; +} + +function parseErrors(errors: UserError[]): Record { + return errors + ? errors.reduce( + (acc, curr) => + curr.field + ? { + ...acc, + [curr.field.split(":")[0]]: curr.message + } + : acc, + {} + ) + : {}; +} + +type FormData = Record; + +function merge(prevData: T, prevState: T, data: T): T { + return Object.keys(prevState).reduce( + (acc, key) => { + if (!isEqual(data[key], prevData[key])) { + acc[key as keyof T] = data[key]; + } + + return acc; + }, + { ...prevState } + ); +} + +function handleRefresh( + data: T, + newData: T, + setChanged: (status: boolean) => void +) { + if (isEqual(data, newData)) { + setChanged(false); + } +} + +function useForm( + initial: T, + errors: UserError[], + onSubmit: (data: T) => void +): UseFormResult { + const [hasChanged, setChanged] = useState(false); + const [data, setData] = useStateFromProps(initial, { + mergeFunc: merge, + onRefresh: newData => handleRefresh(data, newData, setChanged) + }); + + function toggleValue(event: ChangeEvent, cb?: () => void) { + const { name, value } = event.target; + const field = data[name as keyof T]; + + if (Array.isArray(field)) { + if (!hasChanged) { + setChanged(true); + } + setData({ + ...data, + [name]: toggle(value, field, isEqual) + }); + } + + if (typeof cb === "function") { + cb(); + } + } + + function change(event: ChangeEvent) { + const { name, value } = event.target; + + if (!(name in data)) { + console.error(`Unknown form field: ${name}`); + return; + } else { + if (data[name] !== value) { + setChanged(true); + } + setData(data => ({ + ...data, + [name]: value + })); + } + } + + function reset() { + setData(initial); + } + + function set(newData: Partial) { + setData(data => ({ + ...data, + ...newData + })); + } + + function submit() { + return onSubmit(data); + } + + function triggerChange() { + setChanged(true); + } + + return { + change, + data, + errors: parseErrors(errors), + hasChanged, + reset, + set, + submit, + toggleValue, + triggerChange + }; +} + +export default useForm; diff --git a/src/hooks/useFormset.ts b/src/hooks/useFormset.ts new file mode 100644 index 000000000..8891d48b5 --- /dev/null +++ b/src/hooks/useFormset.ts @@ -0,0 +1,51 @@ +import useStateFromProps from "./useStateFromProps"; + +export type FormsetChange = (id: string, value: TValue) => void; +export interface FormsetAtomicData { + data: TData; + id: string; + label: string; + value: TValue; +} +export type FormsetData = Array< + FormsetAtomicData +>; +export interface UseFormsetOutput { + change: FormsetChange; + data: FormsetData; + get: (id: string) => FormsetAtomicData; + // Used for some rare situations like dataset change + set: (data: FormsetData) => void; +} +function useFormset( + initial: FormsetData +): UseFormsetOutput { + const [data, setData] = useStateFromProps>( + initial || [] + ); + + function getItem(id: string): FormsetAtomicData { + return data.find(item => item.id === id); + } + + function setItemValue(id: string, value: TValue) { + const itemIndex = data.findIndex(item => item.id === id); + setData([ + ...data.slice(0, itemIndex), + { + ...data[itemIndex], + value + }, + ...data.slice(itemIndex + 1) + ]); + } + + return { + change: setItemValue, + data, + get: getItem, + set: setData + }; +} + +export default useFormset; diff --git a/src/hooks/useListActions.ts b/src/hooks/useListActions.ts new file mode 100644 index 000000000..43f38b88c --- /dev/null +++ b/src/hooks/useListActions.ts @@ -0,0 +1,47 @@ +import { useEffect, useState } from "react"; + +function useListActions( + initial: TData[] = [], + compareFunc: (a: TData, b: TData) => boolean = (a, b) => a === b +) { + const [listElements, setListElements] = useState(initial); + + useEffect(() => setListElements(initial), [JSON.stringify(initial)]); + + function isSelected(data: TData) { + return !!listElements.find(listElement => compareFunc(listElement, data)); + } + + function add(data: TData) { + setListElements([...listElements, data]); + } + + function remove(data: TData) { + setListElements( + listElements.filter(listElement => !compareFunc(listElement, data)) + ); + } + + function reset() { + setListElements([]); + } + + function toggle(data: TData) { + isSelected(data) ? remove(data) : add(data); + } + + function set(data: TData[]) { + setListElements(data); + } + + return { + add, + isSelected, + listElements, + remove, + reset, + set, + toggle + }; +} +export default useListActions; diff --git a/src/hooks/useListSettings.ts b/src/hooks/useListSettings.ts new file mode 100644 index 000000000..75c1afd86 --- /dev/null +++ b/src/hooks/useListSettings.ts @@ -0,0 +1,30 @@ +import useLocalStorage from "@saleor/hooks/useLocalStorage"; +import { AppListViewSettings, defaultListSettings } from "./../config"; +import { ListSettings, ListViews } from "./../types"; + +export interface UseListSettings { + settings: ListSettings; + updateListSettings: (key: keyof ListSettings, value: any) => void; +} +export default function useListSettings( + listName: ListViews +): UseListSettings { + const [settings, setListSettings] = useLocalStorage( + "listConfig", + defaultListSettings + ); + + const updateListSettings = (key: keyof ListSettings, value: any) => + setListSettings(settings => ({ + ...settings, + [listName]: { + ...settings[listName], + [key]: value + } + })); + + return { + settings: settings[listName] as ListSettings, + updateListSettings + }; +} diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts new file mode 100644 index 000000000..13deab7df --- /dev/null +++ b/src/hooks/useLocalStorage.ts @@ -0,0 +1,21 @@ +import { useState } from "react"; + +export type SetLocalStorageValue = T | ((prevValue: T) => T); +export type SetLocalStorage = (value: SetLocalStorageValue) => void; +export default function useLocalStorage( + key: string, + initialValue: T +): [T, SetLocalStorage] { + const [storedValue, setStoredValue] = useState(() => { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : initialValue; + }); + + const setValue = (value: SetLocalStorageValue) => { + const valueToStore = value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + }; + + return [storedValue, setValue]; +} diff --git a/src/hooks/useModalDialogErrors/index.ts b/src/hooks/useModalDialogErrors/index.ts new file mode 100644 index 000000000..0fd055b5f --- /dev/null +++ b/src/hooks/useModalDialogErrors/index.ts @@ -0,0 +1,2 @@ +export { default } from "./useModalDialogErrors"; +export * from "./useModalDialogErrors"; diff --git a/src/hooks/useModalDialogErrors/useModalDialogErrors.test.ts b/src/hooks/useModalDialogErrors/useModalDialogErrors.test.ts new file mode 100644 index 000000000..e61aafc0c --- /dev/null +++ b/src/hooks/useModalDialogErrors/useModalDialogErrors.test.ts @@ -0,0 +1,44 @@ +import { renderHook } from "@testing-library/react-hooks"; + +import useModalDialogErrors from "./useModalDialogErrors"; + +const errors = ["err1", "err2"]; + +test("Does not render errors after close", () => { + const { result, rerender } = renderHook( + ({ errors, open }) => useModalDialogErrors(errors, open), + { + initialProps: { + errors: [] as string[], + open: false + } + } + ); + + // Open modal + rerender({ + errors: [], + open: true + }); + expect(result.current.length).toBe(0); + + // Throw errors + rerender({ + errors, + open: true + }); + expect(result.current.length).toBe(2); + + // Close modal + rerender({ + errors, + open: false + }); + + // Open modal + rerender({ + errors, + open: true + }); + expect(result.current.length).toBe(0); +}); diff --git a/src/hooks/useModalDialogErrors/useModalDialogErrors.ts b/src/hooks/useModalDialogErrors/useModalDialogErrors.ts new file mode 100644 index 000000000..54a393743 --- /dev/null +++ b/src/hooks/useModalDialogErrors/useModalDialogErrors.ts @@ -0,0 +1,17 @@ +import useModalDialogOpen from "../useModalDialogOpen"; +import useStateFromProps from "../useStateFromProps"; + +function useModalDialogErrors( + errors: TError[], + open: boolean +): TError[] { + const [state, setState] = useStateFromProps(errors); + + useModalDialogOpen(open, { + onClose: () => setState([]) + }); + + return state; +} + +export default useModalDialogErrors; diff --git a/src/hooks/useModalDialogOpen/index.ts b/src/hooks/useModalDialogOpen/index.ts new file mode 100644 index 000000000..53579d8b8 --- /dev/null +++ b/src/hooks/useModalDialogOpen/index.ts @@ -0,0 +1,2 @@ +export { default } from "./useModalDialogOpen"; +export * from "./useModalDialogOpen"; diff --git a/src/hooks/useModalDialogOpen/useModalDialogOpen.test.ts b/src/hooks/useModalDialogOpen/useModalDialogOpen.test.ts new file mode 100644 index 000000000..4df311372 --- /dev/null +++ b/src/hooks/useModalDialogOpen/useModalDialogOpen.test.ts @@ -0,0 +1,55 @@ +import { renderHook } from "@testing-library/react-hooks"; + +import useModalDialogOpen from "./useModalDialogOpen"; + +const onClose = jest.fn(); +const onOpen = jest.fn(); + +const cbs = { + onClose, + onOpen +}; + +test("Does not render errors after close", () => { + const { rerender } = renderHook( + ({ open, cbs }) => useModalDialogOpen(open, cbs), + { + initialProps: { + cbs, + open: false + } + } + ); + + // Open modal + rerender({ + cbs, + open: true + }); + expect(onOpen).toBeCalledTimes(1); + expect(onClose).toBeCalledTimes(0); + + // Rerender modal + rerender({ + cbs, + open: true + }); + expect(onOpen).toBeCalledTimes(1); + expect(onClose).toBeCalledTimes(0); + + // Close modal + rerender({ + cbs, + open: false + }); + expect(onOpen).toBeCalledTimes(1); + expect(onClose).toBeCalledTimes(1); + + // Open modal + rerender({ + cbs, + open: true + }); + expect(onOpen).toBeCalledTimes(2); + expect(onClose).toBeCalledTimes(1); +}); diff --git a/src/hooks/useModalDialogOpen/useModalDialogOpen.ts b/src/hooks/useModalDialogOpen/useModalDialogOpen.ts new file mode 100644 index 000000000..8cfb88b2d --- /dev/null +++ b/src/hooks/useModalDialogOpen/useModalDialogOpen.ts @@ -0,0 +1,26 @@ +import { useEffect, useState } from "react"; + +function useModalDialogOpen( + open: boolean, + cbs: { + onClose?: () => void; + onOpen?: () => void; + } +): void { + const [prevOpen, setPrevOpen] = useState(open); + + useEffect(() => { + if (open !== prevOpen) { + setPrevOpen(open); + if (cbs.onOpen && open) { + cbs.onOpen(); + } + + if (cbs.onClose && !open) { + cbs.onClose(); + } + } + }, [open]); +} + +export default useModalDialogOpen; diff --git a/src/hooks/useMultiAutocomplete.ts b/src/hooks/useMultiAutocomplete.ts new file mode 100644 index 000000000..af3a375a2 --- /dev/null +++ b/src/hooks/useMultiAutocomplete.ts @@ -0,0 +1,28 @@ +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { maybe } from "@saleor/misc"; +import useListActions from "./useListActions"; + +function useMultiAutocomplete(initial: MultiAutocompleteChoiceType[] = []) { + const { listElements, toggle } = useListActions( + initial, + (a, b) => a.value === b.value + ); + const handleSelect = ( + event: React.ChangeEvent, + choices: MultiAutocompleteChoiceType[] + ) => { + const value: string = event.target.value; + const match = choices.find(choice => choice.value === value); + toggle({ + label: maybe(() => match.label, value), + value + }); + }; + + return { + change: handleSelect, + data: listElements + }; +} + +export default useMultiAutocomplete; diff --git a/src/hooks/useSearchQuery.ts b/src/hooks/useSearchQuery.ts new file mode 100644 index 000000000..eac204bfc --- /dev/null +++ b/src/hooks/useSearchQuery.ts @@ -0,0 +1,29 @@ +import React from "react"; + +import { ChangeEvent } from "@saleor/hooks/useForm"; + +export type UseSearchQuery = [string, (event: ChangeEvent) => void, () => void]; +function useSearchQuery( + onFetch: (query: string) => void, + initial?: string +): UseSearchQuery { + const [query, setQuery] = React.useState(initial || ""); + const change = (event: ChangeEvent) => { + const value = event.target.value; + + onFetch(value); + setQuery(value); + }; + + const reset = () => + change({ + target: { + name: "", + value: initial || "" + } + }); + + return [query, change, reset]; +} + +export default useSearchQuery; diff --git a/src/hooks/useStateFromProps.ts b/src/hooks/useStateFromProps.ts new file mode 100644 index 000000000..b70bbb2a5 --- /dev/null +++ b/src/hooks/useStateFromProps.ts @@ -0,0 +1,35 @@ +import isEqual from "lodash-es/isEqual"; +import { Dispatch, SetStateAction, useState } from "react"; + +export interface UseStateFromPropsOpts { + mergeFunc?: (prevData: T, state: T, newData: T) => T; + onRefresh?: (data: T) => void; +} + +function useStateFromProps( + data: T, + opts?: UseStateFromPropsOpts +): [T, Dispatch>] { + const [state, setState] = useState(data); + const [prevData, setPrevData] = useState(data); + if (!opts) { + opts = {}; + } + + const { mergeFunc, onRefresh } = opts; + const shouldUpdate = !isEqual(prevData, data); + + if (shouldUpdate) { + const newData = + typeof mergeFunc === "function" ? mergeFunc(prevData, state, data) : data; + setState(newData); + setPrevData(data); + if (typeof onRefresh === "function") { + onRefresh(newData); + } + } + + return [state, setState]; +} + +export default useStateFromProps; diff --git a/src/hooks/useWindowScroll.ts b/src/hooks/useWindowScroll.ts new file mode 100644 index 000000000..5218d58c9 --- /dev/null +++ b/src/hooks/useWindowScroll.ts @@ -0,0 +1,24 @@ +import throttle from "lodash-es/throttle"; +import { useEffect, useState } from "react"; + +function getPosition() { + return { + x: window.pageXOffset, + y: window.pageYOffset + }; +} + +function useWindowScroll() { + const [scroll, setScroll] = useState(getPosition); + + useEffect(() => { + const handleScroll = throttle(() => setScroll(getPosition()), 100); + + window.addEventListener("scroll", handleScroll); + + return () => window.removeEventListener("scroll", handleScroll); + }, []); + + return scroll; +} +export default useWindowScroll; diff --git a/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.tsx b/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.tsx new file mode 100644 index 000000000..add4b20ff --- /dev/null +++ b/src/productTypes/components/AssignAttributeDialog/AssignAttributeDialog.tsx @@ -0,0 +1,192 @@ +import Button from "@material-ui/core/Button"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogContentText from "@material-ui/core/DialogContentText"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import { Theme } from "@material-ui/core/styles"; +import Table from "@material-ui/core/Table"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableRow from "@material-ui/core/TableRow"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import makeStyles from "@material-ui/styles/makeStyles"; +import React from "react"; +import InfiniteScroll from "react-infinite-scroller"; + +import Checkbox from "@saleor/components/Checkbox"; +import ConfirmButton, { + ConfirmButtonTransitionState +} from "@saleor/components/ConfirmButton"; +import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; +import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen"; +import useSearchQuery from "@saleor/hooks/useSearchQuery"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection } from "@saleor/misc"; +import { FetchMoreProps } from "@saleor/types"; +import { SearchAttributes_productType_availableAttributes_edges_node } from "../../containers/SearchAttributes/types/SearchAttributes"; + +const useStyles = makeStyles((theme: Theme) => ({ + checkboxCell: { + paddingLeft: 0 + }, + loadMoreLoaderContainer: { + alignItems: "center", + display: "flex", + height: theme.spacing.unit * 3, + justifyContent: "center" + }, + scrollArea: { + overflowY: "scroll" + }, + wideCell: { + width: "100%" + } +})); + +export interface AssignAttributeDialogProps extends FetchMoreProps { + confirmButtonState: ConfirmButtonTransitionState; + errors: string[]; + open: boolean; + attributes: SearchAttributes_productType_availableAttributes_edges_node[]; + selected: string[]; + onClose: () => void; + onOpen: () => void; + onSubmit: () => void; + onToggle: (id: string) => void; +} + +const AssignAttributeDialog: React.FC = ({ + attributes, + confirmButtonState, + errors: apiErrors, + hasMore, + loading, + open, + selected, + onClose, + onFetch, + onFetchMore, + onOpen, + onSubmit, + onToggle +}: AssignAttributeDialogProps) => { + const classes = useStyles({}); + const [query, onQueryChange, resetQuery] = useSearchQuery(onFetch); + const errors = useModalDialogErrors(apiErrors, open); + + useModalDialogOpen(open, { + onClose: resetQuery, + onOpen + }); + + return ( + + {i18n.t("Assign Attribute")} + + + }} + /> + + + + + + } + threshold={100} + key="infinite-scroll" + > + + + {renderCollection( + attributes, + attribute => { + if (!attribute) { + return null; + } + const isChecked = !!selected.find( + selectedAttribute => selectedAttribute === attribute.id + ); + + return ( + attribute.id)}> + + onToggle(attribute.id)} + /> + + + {attribute.name} + + {attribute.slug} + + + + ); + }, + () => + !loading && ( + + + {i18n.t("No results found")} + + + ) + )} + +
+
+
+ {errors.length > 0 && ( + + {errors.map((error, errorIndex) => ( + + {error} + + ))} + + )} + + + + {i18n.t("Assign attributes", { context: "button" })} + + +
+ ); +}; +AssignAttributeDialog.displayName = "AssignAttributeDialog"; +export default AssignAttributeDialog; diff --git a/src/productTypes/components/AssignAttributeDialog/index.ts b/src/productTypes/components/AssignAttributeDialog/index.ts new file mode 100644 index 000000000..2960aa002 --- /dev/null +++ b/src/productTypes/components/AssignAttributeDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from './AssignAttributeDialog'; +export * from './AssignAttributeDialog'; \ No newline at end of file diff --git a/src/productTypes/components/ProductTypeAttributeUnassignDialog/ProductTypeAttributeUnassignDialog.tsx b/src/productTypes/components/ProductTypeAttributeUnassignDialog/ProductTypeAttributeUnassignDialog.tsx new file mode 100644 index 000000000..e99ce0a0c --- /dev/null +++ b/src/productTypes/components/ProductTypeAttributeUnassignDialog/ProductTypeAttributeUnassignDialog.tsx @@ -0,0 +1,49 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import i18n from "@saleor/i18n"; + +export interface ProductTypeAttributeUnassignDialogProps { + attributeName: string; + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + productTypeName: string; + onClose: () => void; + onConfirm: () => void; +} + +const ProductTypeAttributeUnassignDialog: React.FC< + ProductTypeAttributeUnassignDialogProps +> = ({ + attributeName, + confirmButtonState, + open, + productTypeName, + onClose, + onConfirm +}) => ( + + {{ attributeName }} from {{ productTypeName }}?", + { + attributeName, + productTypeName + } + ) + }} + /> + +); +ProductTypeAttributeUnassignDialog.displayName = + "ProductTypeAttributeUnassignDialog"; +export default ProductTypeAttributeUnassignDialog; diff --git a/src/productTypes/components/ProductTypeAttributeUnassignDialog/index.ts b/src/productTypes/components/ProductTypeAttributeUnassignDialog/index.ts new file mode 100644 index 000000000..dc8bd0a03 --- /dev/null +++ b/src/productTypes/components/ProductTypeAttributeUnassignDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from './ProductTypeAttributeUnassignDialog'; +export * from './ProductTypeAttributeUnassignDialog'; \ No newline at end of file diff --git a/src/productTypes/components/ProductTypeBulkAttributeUnassignDialog/ProductTypeBulkAttributeUnassignDialog.tsx b/src/productTypes/components/ProductTypeBulkAttributeUnassignDialog/ProductTypeBulkAttributeUnassignDialog.tsx new file mode 100644 index 000000000..0f73c08c4 --- /dev/null +++ b/src/productTypes/components/ProductTypeBulkAttributeUnassignDialog/ProductTypeBulkAttributeUnassignDialog.tsx @@ -0,0 +1,49 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import i18n from "@saleor/i18n"; + +export interface ProductTypeBulkAttributeUnassignDialogProps { + attributeQuantity: string; + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + productTypeName: string; + onClose: () => void; + onConfirm: () => void; +} + +const ProductTypeBulkAttributeUnassignDialog: React.FC< + ProductTypeBulkAttributeUnassignDialogProps +> = ({ + attributeQuantity, + confirmButtonState, + open, + productTypeName, + onClose, + onConfirm +}) => ( + + {{ attributeQuantity }} attributes from {{ productTypeName }}?", + { + attributeQuantity, + productTypeName + } + ) + }} + /> + +); +ProductTypeBulkAttributeUnassignDialog.displayName = + "ProductTypeBulkAttributeUnassignDialog"; +export default ProductTypeBulkAttributeUnassignDialog; diff --git a/src/productTypes/components/ProductTypeBulkAttributeUnassignDialog/index.ts b/src/productTypes/components/ProductTypeBulkAttributeUnassignDialog/index.ts new file mode 100644 index 000000000..e6df37e86 --- /dev/null +++ b/src/productTypes/components/ProductTypeBulkAttributeUnassignDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from './ProductTypeBulkAttributeUnassignDialog'; +export * from './ProductTypeBulkAttributeUnassignDialog'; \ No newline at end of file diff --git a/src/productTypes/components/ProductTypeDeleteDialog/ProductTypeDeleteDialog.tsx b/src/productTypes/components/ProductTypeDeleteDialog/ProductTypeDeleteDialog.tsx new file mode 100644 index 000000000..56d42e316 --- /dev/null +++ b/src/productTypes/components/ProductTypeDeleteDialog/ProductTypeDeleteDialog.tsx @@ -0,0 +1,43 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import i18n from "@saleor/i18n"; + +export interface ProductTypeDeleteDialogProps { + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + name: string; + onClose: () => void; + onConfirm: () => void; +} + +const ProductTypeDeleteDialog: React.FC = ({ + confirmButtonState, + open, + name, + onClose, + onConfirm +}) => ( + + {{ name }}?", + { + name + } + ) + }} + /> + +); +ProductTypeDeleteDialog.displayName = "ProductTypeDeleteDialog"; +export default ProductTypeDeleteDialog; diff --git a/src/productTypes/components/ProductTypeDeleteDialog/index.ts b/src/productTypes/components/ProductTypeDeleteDialog/index.ts new file mode 100644 index 000000000..4c9e6f92d --- /dev/null +++ b/src/productTypes/components/ProductTypeDeleteDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from './ProductTypeDeleteDialog'; +export * from './ProductTypeDeleteDialog'; \ No newline at end of file diff --git a/src/productTypes/containers/SearchAttributes/index.tsx b/src/productTypes/containers/SearchAttributes/index.tsx new file mode 100644 index 000000000..6af8faa86 --- /dev/null +++ b/src/productTypes/containers/SearchAttributes/index.tsx @@ -0,0 +1,42 @@ +import gql from "graphql-tag"; + +import { pageInfoFragment } from "@saleor/queries"; +import BaseSearch from "../../../containers/BaseSearch"; +import { + SearchAttributes, + SearchAttributesVariables +} from "./types/SearchAttributes"; + +export const searchAttributes = gql` + ${pageInfoFragment} + query SearchAttributes( + $id: ID! + $after: String + $first: Int! + $query: String! + ) { + productType(id: $id) { + id + availableAttributes( + after: $after + first: $first + filter: { search: $query } + ) { + edges { + node { + id + name + slug + } + } + pageInfo { + ...PageInfoFragment + } + } + } + } +`; + +export default BaseSearch( + searchAttributes +); diff --git a/src/productTypes/containers/SearchAttributes/types/SearchAttributes.ts b/src/productTypes/containers/SearchAttributes/types/SearchAttributes.ts new file mode 100644 index 000000000..b45411fa9 --- /dev/null +++ b/src/productTypes/containers/SearchAttributes/types/SearchAttributes.ts @@ -0,0 +1,50 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: SearchAttributes +// ==================================================== + +export interface SearchAttributes_productType_availableAttributes_edges_node { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; +} + +export interface SearchAttributes_productType_availableAttributes_edges { + __typename: "AttributeCountableEdge"; + node: SearchAttributes_productType_availableAttributes_edges_node; +} + +export interface SearchAttributes_productType_availableAttributes_pageInfo { + __typename: "PageInfo"; + endCursor: string | null; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; +} + +export interface SearchAttributes_productType_availableAttributes { + __typename: "AttributeCountableConnection"; + edges: SearchAttributes_productType_availableAttributes_edges[]; + pageInfo: SearchAttributes_productType_availableAttributes_pageInfo; +} + +export interface SearchAttributes_productType { + __typename: "ProductType"; + id: string; + availableAttributes: SearchAttributes_productType_availableAttributes | null; +} + +export interface SearchAttributes { + productType: SearchAttributes_productType | null; +} + +export interface SearchAttributesVariables { + id: string; + after?: string | null; + first: number; + query: string; +} diff --git a/src/productTypes/types/AssignAttribute.ts b/src/productTypes/types/AssignAttribute.ts new file mode 100644 index 000000000..d6ec1df81 --- /dev/null +++ b/src/productTypes/types/AssignAttribute.ts @@ -0,0 +1,74 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { AttributeAssignInput } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: AssignAttribute +// ==================================================== + +export interface AssignAttribute_attributeAssign_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface AssignAttribute_attributeAssign_productType_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; +} + +export interface AssignAttribute_attributeAssign_productType_productAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; +} + +export interface AssignAttribute_attributeAssign_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; +} + +export interface AssignAttribute_attributeAssign_productType_weight { + __typename: "Weight"; + unit: string; + value: number; +} + +export interface AssignAttribute_attributeAssign_productType { + __typename: "ProductType"; + id: string; + name: string; + hasVariants: boolean; + isShippingRequired: boolean; + taxType: AssignAttribute_attributeAssign_productType_taxType | null; + productAttributes: (AssignAttribute_attributeAssign_productType_productAttributes | null)[] | null; + variantAttributes: (AssignAttribute_attributeAssign_productType_variantAttributes | null)[] | null; + weight: AssignAttribute_attributeAssign_productType_weight | null; +} + +export interface AssignAttribute_attributeAssign { + __typename: "AttributeAssign"; + errors: AssignAttribute_attributeAssign_errors[] | null; + productType: AssignAttribute_attributeAssign_productType | null; +} + +export interface AssignAttribute { + attributeAssign: AssignAttribute_attributeAssign | null; +} + +export interface AssignAttributeVariables { + id: string; + operations: AttributeAssignInput[]; +} diff --git a/src/productTypes/types/ProductTypeAttributeReorder.ts b/src/productTypes/types/ProductTypeAttributeReorder.ts new file mode 100644 index 000000000..32bc41c66 --- /dev/null +++ b/src/productTypes/types/ProductTypeAttributeReorder.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ReorderInput, AttributeTypeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ProductTypeAttributeReorder +// ==================================================== + +export interface ProductTypeAttributeReorder_productTypeReorderAttributes_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; +} + +export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType_productAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; +} + +export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; +} + +export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType_weight { + __typename: "Weight"; + unit: string; + value: number; +} + +export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType { + __typename: "ProductType"; + id: string; + name: string; + hasVariants: boolean; + isShippingRequired: boolean; + taxType: ProductTypeAttributeReorder_productTypeReorderAttributes_productType_taxType | null; + productAttributes: (ProductTypeAttributeReorder_productTypeReorderAttributes_productType_productAttributes | null)[] | null; + variantAttributes: (ProductTypeAttributeReorder_productTypeReorderAttributes_productType_variantAttributes | null)[] | null; + weight: ProductTypeAttributeReorder_productTypeReorderAttributes_productType_weight | null; +} + +export interface ProductTypeAttributeReorder_productTypeReorderAttributes { + __typename: "ProductTypeReorderAttributes"; + errors: ProductTypeAttributeReorder_productTypeReorderAttributes_errors[] | null; + productType: ProductTypeAttributeReorder_productTypeReorderAttributes_productType | null; +} + +export interface ProductTypeAttributeReorder { + productTypeReorderAttributes: ProductTypeAttributeReorder_productTypeReorderAttributes | null; +} + +export interface ProductTypeAttributeReorderVariables { + move: ReorderInput; + productTypeId: string; + type: AttributeTypeEnum; +} diff --git a/src/productTypes/types/UnassignAttribute.ts b/src/productTypes/types/UnassignAttribute.ts new file mode 100644 index 000000000..f47c53826 --- /dev/null +++ b/src/productTypes/types/UnassignAttribute.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL mutation operation: UnassignAttribute +// ==================================================== + +export interface UnassignAttribute_attributeUnassign_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface UnassignAttribute_attributeUnassign_productType_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; +} + +export interface UnassignAttribute_attributeUnassign_productType_productAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; +} + +export interface UnassignAttribute_attributeUnassign_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; +} + +export interface UnassignAttribute_attributeUnassign_productType_weight { + __typename: "Weight"; + unit: string; + value: number; +} + +export interface UnassignAttribute_attributeUnassign_productType { + __typename: "ProductType"; + id: string; + name: string; + hasVariants: boolean; + isShippingRequired: boolean; + taxType: UnassignAttribute_attributeUnassign_productType_taxType | null; + productAttributes: (UnassignAttribute_attributeUnassign_productType_productAttributes | null)[] | null; + variantAttributes: (UnassignAttribute_attributeUnassign_productType_variantAttributes | null)[] | null; + weight: UnassignAttribute_attributeUnassign_productType_weight | null; +} + +export interface UnassignAttribute_attributeUnassign { + __typename: "AttributeUnassign"; + errors: UnassignAttribute_attributeUnassign_errors[] | null; + productType: UnassignAttribute_attributeUnassign_productType | null; +} + +export interface UnassignAttribute { + attributeUnassign: UnassignAttribute_attributeUnassign | null; +} + +export interface UnassignAttributeVariables { + id: string; + ids: (string | null)[]; +} diff --git a/src/products/components/ProductAttributes/ProductAttributes.tsx b/src/products/components/ProductAttributes/ProductAttributes.tsx new file mode 100644 index 000000000..e7c085491 --- /dev/null +++ b/src/products/components/ProductAttributes/ProductAttributes.tsx @@ -0,0 +1,214 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import IconButton from "@material-ui/core/IconButton"; +import { Theme } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; +import makeStyles from "@material-ui/styles/makeStyles"; +import classNames from "classnames"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import Grid from "@saleor/components/Grid"; +import Hr from "@saleor/components/Hr"; +import MultiAutocompleteSelectField, { + MultiAutocompleteChoiceType +} from "@saleor/components/MultiAutocompleteSelectField"; +import SingleAutocompleteSelectField, { + SingleAutocompleteChoiceType +} from "@saleor/components/SingleAutocompleteSelectField"; +import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; +import i18n from "@saleor/i18n"; +import { maybe } from "@saleor/misc"; +import { ProductDetails_product_attributes_attribute_values } from "@saleor/products/types/ProductDetails"; +import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; + +export interface ProductAttributeInputData { + inputType: AttributeInputTypeEnum; + isRequired: boolean; + values: ProductDetails_product_attributes_attribute_values[]; +} +export type ProductAttributeInput = FormsetAtomicData< + ProductAttributeInputData, + string[] +>; +export interface ProductAttributesProps { + attributes: ProductAttributeInput[]; + disabled: boolean; + onChange: FormsetChange; + onMultiChange: FormsetChange; +} + +const useStyles = makeStyles((theme: Theme) => ({ + attributeSection: { + "&:last-of-type": { + paddingBottom: 0 + }, + padding: `${theme.spacing.unit * 2}px 0` + }, + attributeSectionLabel: { + alignItems: "center", + display: "flex" + }, + card: { + overflow: "visible" + }, + cardContent: { + "&:last-child": { + paddingBottom: theme.spacing.unit + }, + paddingTop: theme.spacing.unit + }, + expansionBar: { + display: "flex" + }, + expansionBarButton: { + marginBottom: theme.spacing.unit + }, + expansionBarButtonIcon: { + transition: theme.transitions.duration.short + "ms" + }, + expansionBarLabel: { + color: theme.palette.text.disabled, + fontSize: 14 + }, + expansionBarLabelContainer: { + alignItems: "center", + display: "flex", + flex: 1 + }, + rotate: { + transform: "rotate(180deg)" + } +})); + +function getMultiChoices( + values: ProductDetails_product_attributes_attribute_values[] +): MultiAutocompleteChoiceType[] { + return values.map(value => ({ + label: value.name, + value: value.slug + })); +} + +function getMultiDisplayValue( + attribute: ProductAttributeInput +): MultiAutocompleteChoiceType[] { + return attribute.value.map(attributeValue => { + const definedAttributeValue = attribute.data.values.find( + definedValue => definedValue.slug === attributeValue + ); + if (!!definedAttributeValue) { + return { + label: definedAttributeValue.name, + value: definedAttributeValue.slug + }; + } + + return { + label: attributeValue, + value: attributeValue + }; + }); +} + +function getSingleChoices( + values: ProductDetails_product_attributes_attribute_values[] +): SingleAutocompleteChoiceType[] { + return values.map(value => ({ + label: value.name, + value: value.slug + })); +} + +const ProductAttributes: React.FC = ({ + attributes, + disabled, + onChange, + onMultiChange +}) => { + const classes = useStyles({}); + const [expanded, setExpansionStatus] = React.useState(true); + const toggleExpansion = () => setExpansionStatus(!expanded); + + return ( + + + +
+
+ + {i18n.t("{{ number }} Attributes", { + context: "number of attribute", + number: attributes.length + })} + +
+ + + +
+ {expanded && attributes.length > 0 && ( + <> +
+ {attributes.map((attribute, attributeIndex) => ( + + {attributeIndex > 0 &&
} + +
+ {attribute.label} +
+
+ {attribute.data.inputType === + AttributeInputTypeEnum.DROPDOWN ? ( + + attribute.data.values.find( + value => value.slug === attribute.value[0] + ).name, + attribute.value[0] + )} + emptyOption + name={`attribute:${attribute.label}`} + label={i18n.t("Value")} + value={attribute.value[0]} + onChange={event => + onChange(attribute.id, event.target.value) + } + allowCustomValues={!attribute.data.isRequired} + /> + ) : ( + + onMultiChange(attribute.id, event.target.value) + } + allowCustomValues={!attribute.data.isRequired} + /> + )} +
+
+
+ ))} + + )} +
+
+ ); +}; +ProductAttributes.displayName = "ProductAttributes"; +export default ProductAttributes; diff --git a/src/products/components/ProductAttributes/index.ts b/src/products/components/ProductAttributes/index.ts new file mode 100644 index 000000000..090807d29 --- /dev/null +++ b/src/products/components/ProductAttributes/index.ts @@ -0,0 +1,2 @@ +export { default } from './ProductAttributes'; +export * from './ProductAttributes'; \ No newline at end of file diff --git a/src/products/components/ProductListPage/ProductListPage.tsx b/src/products/components/ProductListPage/ProductListPage.tsx new file mode 100644 index 000000000..0b075177b --- /dev/null +++ b/src/products/components/ProductListPage/ProductListPage.tsx @@ -0,0 +1,133 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import { Theme } from "@material-ui/core/styles"; +import AddIcon from "@material-ui/icons/Add"; +import makeStyles from "@material-ui/styles/makeStyles"; +import React from "react"; + +import { CategoryDetails_category_products_edges_node } from "@saleor/categories/types/CategoryDetails"; +import ColumnPicker, { + ColumnPickerChoice +} from "@saleor/components/ColumnPicker"; +import Container from "@saleor/components/Container"; +import PageHeader from "@saleor/components/PageHeader"; +import ProductList from "@saleor/components/ProductList"; +import { ProductListColumns } from "@saleor/config"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import i18n from "@saleor/i18n"; +import { FilterPageProps, ListActions, PageListProps } from "@saleor/types"; +import { toggle } from "@saleor/utils/lists"; +import { ProductListUrlFilters } from "../../urls"; +import ProductListFilter from "../ProductListFilter"; + +export interface ProductListPageProps + extends PageListProps, + ListActions, + FilterPageProps { + currencySymbol: string; + products: CategoryDetails_category_products_edges_node[]; +} + +const useStyles = makeStyles((theme: Theme) => ({ + columnPicker: { + marginRight: theme.spacing.unit * 3 + } +})); + +export const ProductListPage: React.FC = props => { + const { + currencySymbol, + currentTab, + defaultSettings, + filtersList, + filterTabs, + initialSearch, + settings, + onAdd, + onAll, + onSearchChange, + onFilterAdd, + onFilterSave, + onTabChange, + onFilterDelete, + onUpdateListSettings, + ...listProps + } = props; + const classes = useStyles(props); + const [selectedColumns, setSelectedColumns] = useStateFromProps( + settings.columns + ); + + const handleCancel = React.useCallback( + () => setSelectedColumns(settings.columns), + [settings.columns] + ); + + const handleColumnToggle = (column: ProductListColumns) => + setSelectedColumns(prevSelectedColumns => + toggle(column, prevSelectedColumns, (a, b) => a === b) + ); + + const handleReset = () => setSelectedColumns(defaultSettings.columns); + + const handleSave = () => onUpdateListSettings("columns", selectedColumns); + + const columns: ColumnPickerChoice[] = [ + { + label: i18n.t("Published"), + value: "isPublished" as ProductListColumns + }, + { + label: i18n.t("Price"), + value: "price" as ProductListColumns + }, + { + label: i18n.t("Type"), + value: "productType" as ProductListColumns + } + ]; + + return ( + + + + + + + + + + + ); +}; +ProductListPage.displayName = "ProductListPage"; +export default ProductListPage; diff --git a/src/products/components/ProductListPage/index.ts b/src/products/components/ProductListPage/index.ts new file mode 100644 index 000000000..a5e54aa30 --- /dev/null +++ b/src/products/components/ProductListPage/index.ts @@ -0,0 +1,2 @@ +export { default } from "./ProductListPage"; +export * from "./ProductListPage"; diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts new file mode 100644 index 000000000..05cc7bad1 --- /dev/null +++ b/src/products/utils/data.ts @@ -0,0 +1,224 @@ +import { RawDraftContentState } from "draft-js"; + +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import { maybe } from "@saleor/misc"; +import { + ProductDetails_product, + ProductDetails_product_collections, + ProductDetails_product_variants +} from "@saleor/products/types/ProductDetails"; +import { UserError } from "@saleor/types"; +import { ProductAttributeInput } from "../components/ProductAttributes"; +import { VariantAttributeInput } from "../components/ProductVariantAttributes"; +import { ProductCreateData_productTypes_edges_node_productAttributes } from "../types/ProductCreateData"; +import { + ProductVariant, + ProductVariant_attributes_attribute +} from "../types/ProductVariant"; +import { + ProductVariantCreateData_product, + ProductVariantCreateData_product_productType_variantAttributes +} from "../types/ProductVariantCreateData"; + +export interface Collection { + id: string; + label: string; +} + +interface Node { + id: string; + name: string; +} + +export interface ProductType { + hasVariants: boolean; + id: string; + name: string; + productAttributes: ProductCreateData_productTypes_edges_node_productAttributes[]; +} + +export function getAttributeInputFromProduct( + product: ProductDetails_product +): ProductAttributeInput[] { + return maybe( + (): ProductAttributeInput[] => + product.attributes.map(attribute => ({ + data: { + inputType: attribute.attribute.inputType, + isRequired: attribute.attribute.valueRequired, + values: attribute.attribute.values + }, + id: attribute.attribute.id, + label: attribute.attribute.name, + value: attribute.values.map(value => value.slug) + })), + [] + ); +} + +export interface ProductAttributeValueChoices { + id: string; + values: MultiAutocompleteChoiceType[]; +} +export function getSelectedAttributesFromProduct( + product: ProductDetails_product +): ProductAttributeValueChoices[] { + return maybe( + () => + product.attributes.map(attribute => ({ + id: attribute.attribute.id, + values: attribute.values.map(value => ({ + label: value.name, + value: value.slug + })) + })), + [] + ); +} + +export function getAttributeInputFromProductType( + productType: ProductType +): ProductAttributeInput[] { + return productType.productAttributes.map(attribute => ({ + data: { + inputType: attribute.inputType, + isRequired: attribute.valueRequired, + values: attribute.values + }, + id: attribute.id, + label: attribute.name, + value: [] + })); +} + +export function getAttributeInputFromVariant( + product: ProductVariant +): VariantAttributeInput[] { + return maybe( + (): VariantAttributeInput[] => + product.attributes.map(attribute => ({ + data: { + values: attribute.attribute.values + }, + id: attribute.attribute.id, + label: attribute.attribute.name, + value: attribute.value.slug + })), + [] + ); +} + +export function getVariantAttributeInputFromProduct( + product: ProductVariantCreateData_product +): VariantAttributeInput[] { + return maybe(() => + product.productType.variantAttributes.map(attribute => ({ + data: { + values: attribute.values + }, + id: attribute.id, + label: attribute.name, + value: "" + })) + ); +} + +export function getCollectionInput( + productCollections: ProductDetails_product_collections[] +): Collection[] { + return maybe( + () => + productCollections.map(collection => ({ + id: collection.id, + label: collection.name + })), + [] + ); +} + +export function getChoices(nodes: Node[]): SingleAutocompleteChoiceType[] { + return maybe( + () => + nodes.map(node => ({ + label: node.name, + value: node.id + })), + [] + ); +} + +export interface ProductUpdatePageFormData { + basePrice: number; + category: string | null; + collections: string[]; + chargeTaxes: boolean; + description: RawDraftContentState; + isPublished: boolean; + name: string; + publicationDate: string; + seoDescription: string; + seoTitle: string; + sku: string; + stockQuantity: number; +} + +export function getProductUpdatePageFormData( + product: ProductDetails_product, + variants: ProductDetails_product_variants[] +): ProductUpdatePageFormData { + return { + basePrice: maybe(() => product.basePrice.amount, 0), + category: maybe(() => product.category.id, ""), + chargeTaxes: maybe(() => product.chargeTaxes, false), + collections: maybe( + () => product.collections.map(collection => collection.id), + [] + ), + description: maybe(() => JSON.parse(product.descriptionJson)), + isPublished: maybe(() => product.isPublished, false), + name: maybe(() => product.name, ""), + publicationDate: maybe(() => product.publicationDate, ""), + seoDescription: maybe(() => product.seoDescription, ""), + seoTitle: maybe(() => product.seoTitle, ""), + sku: maybe(() => + product.productType.hasVariants + ? undefined + : variants && variants[0] + ? variants[0].sku + : undefined + ), + stockQuantity: maybe(() => + product.productType.hasVariants + ? undefined + : variants && variants[0] + ? variants[0].quantity + : undefined + ) + }; +} + +export function getVariantAttributeErrors( + errors: UserError[], + variantAttributes: Array< + | ProductVariantCreateData_product_productType_variantAttributes + | ProductVariant_attributes_attribute + > +): Record { + return maybe( + () => + errors.reduce((acc, err) => { + const slug = err.field.split(":")[1]; + const attribute = variantAttributes.find( + attribute => attribute.slug === slug + ); + + if (!!attribute) { + acc[attribute.id] = err.message; + } + + return acc; + }, {}), + {} + ); +} diff --git a/src/products/utils/handlers.ts b/src/products/utils/handlers.ts new file mode 100644 index 000000000..7f0dc5071 --- /dev/null +++ b/src/products/utils/handlers.ts @@ -0,0 +1,117 @@ +import { FormChange } from "@saleor/hooks/useForm"; +import { FormsetChange, FormsetData } from "@saleor/hooks/useFormset"; +import { maybe } from "@saleor/misc"; +import { toggle } from "@saleor/utils/lists"; +import { ProductAttributeInputData } from "../components/ProductAttributes"; +import { + getAttributeInputFromProductType, + ProductAttributeValueChoices, + ProductType +} from "./data"; + +export function createAttributeChangeHandler( + changeAttributeData: FormsetChange, + setSelectedAttributes: (data: ProductAttributeValueChoices[]) => void, + selectedAttributes: ProductAttributeValueChoices[], + attributes: FormsetData, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, value: string) => { + const attributeValue = attributes + .find(attribute => attribute.id === attributeId) + .data.values.find(attributeValue => attributeValue.slug === value); + + const valueChoice = { + label: maybe(() => attributeValue.name, value), + value + }; + + const itemIndex = selectedAttributes.findIndex( + item => item.id === attributeId + ); + const attribute = selectedAttributes[itemIndex]; + + setSelectedAttributes([ + ...selectedAttributes.slice(0, itemIndex), + { + ...attribute, + values: [valueChoice] + }, + ...selectedAttributes.slice(itemIndex + 1) + ]); + + triggerChange(); + changeAttributeData(attributeId, [value]); + }; +} + +export function createAttributeMultiChangeHandler( + changeAttributeData: FormsetChange, + setSelectedAttributes: (data: ProductAttributeValueChoices[]) => void, + selectedAttributes: ProductAttributeValueChoices[], + attributes: FormsetData, + triggerChange: () => void +): FormsetChange { + return (attributeId: string, value: string) => { + const attributeValue = attributes + .find(attribute => attribute.id === attributeId) + .data.values.find(attributeValue => attributeValue.slug === value); + + const valueChoice = { + label: attributeValue ? attributeValue.name : value, + value + }; + + const itemIndex = selectedAttributes.findIndex( + item => item.id === attributeId + ); + const attributeValues = selectedAttributes[itemIndex].values; + + const newAttributeValues = toggle( + valueChoice, + attributeValues, + (a, b) => a.value === b.value + ); + + const newSelectedAttributes = [ + ...selectedAttributes.slice(0, itemIndex), + { + ...selectedAttributes[itemIndex], + values: newAttributeValues + }, + ...selectedAttributes.slice(itemIndex + 1) + ]; + setSelectedAttributes(newSelectedAttributes); + + triggerChange(); + changeAttributeData( + attributeId, + newAttributeValues.map(({ value }) => value) + ); + }; +} + +export function createProductTypeSelectHandler( + change: FormChange, + setAttributes: (data: FormsetData) => void, + setSelectedAttributes: (data: ProductAttributeValueChoices[]) => void, + setProductType: (productType: ProductType) => void, + productTypeChoiceList: ProductType[] +): FormChange { + return (event: React.ChangeEvent) => { + const id = event.target.value; + const selectedProductType = productTypeChoiceList.find( + productType => productType.id === id + ); + setProductType(selectedProductType); + change(event); + + setAttributes(getAttributeInputFromProductType(selectedProductType)); + setSelectedAttributes( + selectedProductType.productAttributes.map(attribute => ({ + id: attribute.id, + values: [] + })) + ); + }; +} diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx new file mode 100644 index 000000000..846a85654 --- /dev/null +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -0,0 +1,333 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import IconButton from "@material-ui/core/IconButton"; +import DeleteIcon from "@material-ui/icons/Delete"; +import React from "react"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import useBulkActions from "@saleor/hooks/useBulkActions"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import placeholderImg from "../../../../images/placeholder255x255.png"; +import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config"; +import SearchCategories from "../../../containers/SearchCategories"; +import SearchCollections from "../../../containers/SearchCollections"; +import i18n from "../../../i18n"; +import { getMutationState, maybe } from "../../../misc"; +import { productTypeUrl } from "../../../productTypes/urls"; +import ProductUpdatePage from "../../components/ProductUpdatePage"; +import ProductUpdateOperations from "../../containers/ProductUpdateOperations"; +import { TypedProductDetailsQuery } from "../../queries"; +import { + ProductImageCreate, + ProductImageCreateVariables +} from "../../types/ProductImageCreate"; +import { ProductUpdate as ProductUpdateMutationResult } from "../../types/ProductUpdate"; +import { ProductVariantBulkDelete } from "../../types/ProductVariantBulkDelete"; +import { + productImageUrl, + productListUrl, + productUrl, + ProductUrlDialog, + ProductUrlQueryParams, + productVariantAddUrl, + productVariantEditUrl +} from "../../urls"; +import { + createImageReorderHandler, + createImageUploadHandler, + createUpdateHandler +} from "./handlers"; + +interface ProductUpdateProps { + id: string; + params: ProductUrlQueryParams; +} + +export const ProductUpdate: React.StatelessComponent = ({ + id, + params +}) => { + const navigate = useNavigator(); + const notify = useNotifier(); + const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( + params.ids + ); + + const openModal = (action: ProductUrlDialog) => + navigate( + productUrl(id, { + action + }) + ); + + return ( + + {({ search: searchCategories, result: searchCategoriesOpts }) => ( + + {({ search: searchCollections, result: searchCollectionsOpts }) => ( + + {({ data, loading, refetch }) => { + const handleDelete = () => { + notify({ text: i18n.t("Product removed") }); + navigate(productListUrl()); + }; + const handleUpdate = (data: ProductUpdateMutationResult) => { + if (data.productUpdate.errors.length === 0) { + notify({ text: i18n.t("Saved changes") }); + } else { + const attributeError = data.productUpdate.errors.find( + err => err.field === "attributes" + ); + if (!!attributeError) { + notify({ text: attributeError.message }); + } + } + }; + + const handleImageCreate = (data: ProductImageCreate) => { + const imageError = data.productImageCreate.errors.find( + error => + error.field === + ("image" as keyof ProductImageCreateVariables) + ); + if (imageError) { + notify({ + text: imageError.message + }); + } + }; + const handleImageDeleteSuccess = () => + notify({ + text: i18n.t("Image successfully deleted") + }); + const handleVariantAdd = () => + navigate(productVariantAddUrl(id)); + + const handleBulkProductVariantDelete = ( + data: ProductVariantBulkDelete + ) => { + if (data.productVariantBulkDelete.errors.length === 0) { + navigate(productUrl(id), true); + reset(); + refetch(); + } + }; + + const product = data ? data.product : undefined; + return ( + + {({ + bulkProductVariantDelete, + createProductImage, + deleteProduct, + deleteProductImage, + reorderProductImages, + updateProduct, + updateSimpleProduct + }) => { + const handleImageDelete = (id: string) => () => + deleteProductImage.mutate({ id }); + const handleImageEdit = (imageId: string) => () => + navigate(productImageUrl(id, imageId)); + const handleSubmit = createUpdateHandler( + product, + updateProduct.mutate, + updateSimpleProduct.mutate + ); + const handleImageUpload = createImageUploadHandler( + id, + createProductImage.mutate + ); + const handleImageReorder = createImageReorderHandler( + product, + reorderProductImages.mutate + ); + + const disableFormSave = + createProductImage.opts.loading || + deleteProduct.opts.loading || + reorderProductImages.opts.loading || + updateProduct.opts.loading || + loading; + const formTransitionState = getMutationState( + updateProduct.opts.called || + updateSimpleProduct.opts.called, + updateProduct.opts.loading || + updateSimpleProduct.opts.loading, + maybe( + () => updateProduct.opts.data.productUpdate.errors + ), + maybe( + () => + updateSimpleProduct.opts.data.productUpdate.errors + ), + maybe( + () => + updateSimpleProduct.opts.data.productVariantUpdate + .errors + ) + ); + const deleteTransitionState = getMutationState( + deleteProduct.opts.called, + deleteProduct.opts.loading, + maybe( + () => deleteProduct.opts.data.productDelete.errors + ) + ); + + const bulkProductVariantDeleteTransitionState = getMutationState( + bulkProductVariantDelete.opts.called, + bulkProductVariantDelete.opts.loading, + maybe( + () => + bulkProductVariantDelete.opts.data + .productVariantBulkDelete.errors + ) + ); + + const categories = maybe( + () => searchCategoriesOpts.data.categories.edges, + [] + ).map(edge => edge.node); + const collections = maybe( + () => searchCollectionsOpts.data.collections.edges, + [] + ).map(edge => edge.node); + const errors = maybe( + () => updateProduct.opts.data.productUpdate.errors, + [] + ); + + return ( + <> + data.product.name)} /> + data.product.images)} + header={maybe(() => product.name)} + placeholderImage={placeholderImg} + product={product} + variants={maybe(() => product.variants)} + onAttributesEdit={() => + navigate( + productTypeUrl(data.product.productType.id) + ) + } + onBack={() => { + navigate(productListUrl()); + }} + onDelete={() => openModal("remove")} + onProductShow={() => { + if (product) { + window.open(product.url); + } + }} + onImageReorder={handleImageReorder} + onSubmit={handleSubmit} + onVariantAdd={handleVariantAdd} + onVariantShow={variantId => () => + navigate( + productVariantEditUrl(product.id, variantId) + )} + onImageUpload={handleImageUpload} + onImageEdit={handleImageEdit} + onImageDelete={handleImageDelete} + toolbar={ + + navigate( + productUrl(id, { + action: "remove-variants", + ids: listElements + }) + ) + } + > + + + } + isChecked={isSelected} + selected={listElements.length} + toggle={toggle} + toggleAll={toggleAll} + /> + navigate(productUrl(id), true)} + confirmButtonState={deleteTransitionState} + onConfirm={() => deleteProduct.mutate({ id })} + variant="delete" + title={i18n.t("Remove product")} + > + {{ name }}?", + { + name: product ? product.name : undefined + } + ) + }} + /> + + navigate(productUrl(id), true)} + confirmButtonState={ + bulkProductVariantDeleteTransitionState + } + onConfirm={() => + bulkProductVariantDelete.mutate({ + ids: params.ids + }) + } + variant="delete" + title={i18n.t("Remove product variants")} + > + {{ number }} variants?", + { + number: maybe( + () => params.ids.length.toString(), + "..." + ) + } + ) + }} + /> + + + ); + }} + + ); + }} + + )} + + )} + + ); +}; +export default ProductUpdate; diff --git a/src/products/views/ProductUpdate/handlers.ts b/src/products/views/ProductUpdate/handlers.ts new file mode 100644 index 000000000..fd356f33a --- /dev/null +++ b/src/products/views/ProductUpdate/handlers.ts @@ -0,0 +1,77 @@ +import { decimal } from "@saleor/misc"; +import { ProductUpdatePageSubmitData } from "@saleor/products/components/ProductUpdatePage"; +import { ProductDetails_product } from "@saleor/products/types/ProductDetails"; +import { ProductImageCreateVariables } from "@saleor/products/types/ProductImageCreate"; +import { ProductImageReorderVariables } from "@saleor/products/types/ProductImageReorder"; +import { ProductUpdateVariables } from "@saleor/products/types/ProductUpdate"; +import { SimpleProductUpdateVariables } from "@saleor/products/types/SimpleProductUpdate"; +import { ReorderEvent } from "@saleor/types"; +import { arrayMove } from "react-sortable-hoc"; + +export function createUpdateHandler( + product: ProductDetails_product, + updateProduct: (variables: ProductUpdateVariables) => void, + updateSimpleProduct: (variables: SimpleProductUpdateVariables) => void +) { + return (data: ProductUpdatePageSubmitData) => { + const productVariables: ProductUpdateVariables = { + attributes: data.attributes.map(attribute => ({ + id: attribute.id, + values: attribute.value[0] === "" ? [] : attribute.value + })), + basePrice: decimal(data.basePrice), + category: data.category, + chargeTaxes: data.chargeTaxes, + collections: data.collections, + descriptionJson: JSON.stringify(data.description), + id: product.id, + isPublished: data.isPublished, + name: data.name, + publicationDate: + data.publicationDate !== "" ? data.publicationDate : null, + seo: { + description: data.seoDescription, + title: data.seoTitle + } + }; + + if (product.productType.hasVariants) { + updateProduct(productVariables); + } else { + updateSimpleProduct({ + ...productVariables, + productVariantId: product.variants[0].id, + productVariantInput: { + quantity: data.stockQuantity, + sku: data.sku + } + }); + } + }; +} + +export function createImageUploadHandler( + id: string, + createProductImage: (variables: ProductImageCreateVariables) => void +) { + return (file: File) => + createProductImage({ + alt: "", + image: file, + product: id + }); +} + +export function createImageReorderHandler( + product: ProductDetails_product, + reorderProductImages: (variables: ProductImageReorderVariables) => void +) { + return ({ newIndex, oldIndex }: ReorderEvent) => { + let ids = product.images.map(image => image.id); + ids = arrayMove(ids, oldIndex, newIndex); + reorderProductImages({ + imagesIds: ids, + productId: product.id + }); + }; +} diff --git a/src/products/views/ProductUpdate/index.ts b/src/products/views/ProductUpdate/index.ts new file mode 100644 index 000000000..7d893197c --- /dev/null +++ b/src/products/views/ProductUpdate/index.ts @@ -0,0 +1 @@ +export { default } from "./ProductUpdate"; diff --git a/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx b/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx new file mode 100644 index 000000000..966078c03 --- /dev/null +++ b/src/siteSettings/components/SiteSettingsAddress/SiteSettingsAddress.tsx @@ -0,0 +1,151 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; +import TextField from "@material-ui/core/TextField"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import FormSpacer from "@saleor/components/FormSpacer"; +import Grid from "@saleor/components/Grid"; +import SingleAutocompleteSelectField, { + SingleAutocompleteChoiceType +} from "@saleor/components/SingleAutocompleteSelectField"; +import { AddressTypeInput } from "@saleor/customers/types"; +import { ChangeEvent } from "@saleor/hooks/useForm"; +import i18n from "@saleor/i18n"; +import { FormErrors } from "@saleor/types"; +import { SiteSettingsPageFormData } from "../SiteSettingsPage"; + +interface SiteSettingsAddressProps extends WithStyles { + countries: SingleAutocompleteChoiceType[]; + data: SiteSettingsPageFormData; + displayCountry: string; + errors: FormErrors; + disabled: boolean; + onChange: (event: ChangeEvent) => void; + onCountryChange: (event: ChangeEvent) => void; +} + +const styles = createStyles({ + root: { + overflow: "visible" + } +}); + +const SiteSettingsAddress = withStyles(styles, { name: "SiteSettingsAddress" })( + ({ + classes, + countries, + data, + disabled, + displayCountry, + errors, + onChange, + onCountryChange + }: SiteSettingsAddressProps) => ( + + + + + + + + + + + + + + + + + + + + + + + ) +); +SiteSettingsAddress.displayName = "SiteSettingsAddress"; +export default SiteSettingsAddress; diff --git a/src/siteSettings/components/SiteSettingsAddress/index.ts b/src/siteSettings/components/SiteSettingsAddress/index.ts new file mode 100644 index 000000000..b15a23918 --- /dev/null +++ b/src/siteSettings/components/SiteSettingsAddress/index.ts @@ -0,0 +1,2 @@ +export { default } from "./SiteSettingsAddress"; +export * from "./SiteSettingsAddress"; diff --git a/src/storybook/stories/attributes/AttributeBulkDeleteDialog.tsx b/src/storybook/stories/attributes/AttributeBulkDeleteDialog.tsx new file mode 100644 index 000000000..0a09020eb --- /dev/null +++ b/src/storybook/stories/attributes/AttributeBulkDeleteDialog.tsx @@ -0,0 +1,19 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import AttributeBulkDeleteDialog, { + AttributeBulkDeleteDialogProps +} from "../../../attributes/components/AttributeBulkDeleteDialog"; +import Decorator from "../../Decorator"; + +const props: AttributeBulkDeleteDialogProps = { + confirmButtonState: "default", + onClose: () => undefined, + onConfirm: () => undefined, + open: true, + quantity: "5" +}; + +storiesOf("Attributes / Delete multiple attributes", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/storybook/stories/attributes/AttributeDeleteDialog.tsx b/src/storybook/stories/attributes/AttributeDeleteDialog.tsx new file mode 100644 index 000000000..23df15a80 --- /dev/null +++ b/src/storybook/stories/attributes/AttributeDeleteDialog.tsx @@ -0,0 +1,19 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import AttributeDeleteDialog, { + AttributeDeleteDialogProps +} from "../../../attributes/components/AttributeDeleteDialog"; +import Decorator from "../../Decorator"; + +const props: AttributeDeleteDialogProps = { + confirmButtonState: "default", + name: "Size", + onClose: () => undefined, + onConfirm: () => undefined, + open: true +}; + +storiesOf("Attributes / Attribute delete", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/storybook/stories/attributes/AttributeListPage.tsx b/src/storybook/stories/attributes/AttributeListPage.tsx new file mode 100644 index 000000000..e757661f3 --- /dev/null +++ b/src/storybook/stories/attributes/AttributeListPage.tsx @@ -0,0 +1,23 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import AttributeListPage, { + AttributeListPageProps +} from "@saleor/attributes/components/AttributeListPage"; +import { attributes } from "@saleor/attributes/fixtures"; +import { listActionsProps, pageListProps } from "@saleor/fixtures"; +import Decorator from "../../Decorator"; + +const props: AttributeListPageProps = { + ...pageListProps.default, + ...listActionsProps, + attributes +}; + +storiesOf("Views / Attributes / Attribute list", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )) + .add("no data", () => ); diff --git a/src/storybook/stories/attributes/AttributePage.tsx b/src/storybook/stories/attributes/AttributePage.tsx new file mode 100644 index 000000000..e2d8f5b68 --- /dev/null +++ b/src/storybook/stories/attributes/AttributePage.tsx @@ -0,0 +1,54 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import AttributePage, { + AttributePageProps +} from "@saleor/attributes/components/AttributePage"; +import { attribute } from "@saleor/attributes/fixtures"; +import { formError } from "@saleor/storybook/misc"; +import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; +import Decorator from "../../Decorator"; + +const props: AttributePageProps = { + attribute, + disabled: false, + errors: [], + onBack: () => undefined, + onDelete: () => undefined, + onSubmit: () => undefined, + onValueAdd: () => undefined, + onValueDelete: () => undefined, + onValueReorder: () => undefined, + onValueUpdate: () => undefined, + saveButtonBarState: "default", + values: attribute.values +}; + +storiesOf("Views / Attributes / Attribute details", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )) + .add("no values", () => ) + .add("form errors", () => ( + + )) + .add("multiple select input", () => ( + + )) + .add("create", () => ); diff --git a/src/storybook/stories/attributes/AttributeValueDeleteDialog.tsx b/src/storybook/stories/attributes/AttributeValueDeleteDialog.tsx new file mode 100644 index 000000000..08f6a5fde --- /dev/null +++ b/src/storybook/stories/attributes/AttributeValueDeleteDialog.tsx @@ -0,0 +1,20 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import AttributeValueDeleteDialog, { + AttributeValueDeleteDialogProps +} from "../../../attributes/components/AttributeValueDeleteDialog"; +import Decorator from "../../Decorator"; + +const props: AttributeValueDeleteDialogProps = { + attributeName: "Size", + confirmButtonState: "default", + name: "XS", + onClose: () => undefined, + onConfirm: () => undefined, + open: true +}; + +storiesOf("Attributes / Attribute value delete", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/storybook/stories/attributes/AttributeValueEditDialog.tsx b/src/storybook/stories/attributes/AttributeValueEditDialog.tsx new file mode 100644 index 000000000..a04afb0cd --- /dev/null +++ b/src/storybook/stories/attributes/AttributeValueEditDialog.tsx @@ -0,0 +1,31 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { attribute } from "@saleor/attributes/fixtures"; +import { formError } from "@saleor/storybook/misc"; +import { AttributeValueType } from "@saleor/types/globalTypes"; +import AttributeValueEditDialog, { + AttributeValueEditDialogProps +} from "../../../attributes/components/AttributeValueEditDialog"; +import Decorator from "../../Decorator"; + +const props: AttributeValueEditDialogProps = { + attributeValue: { + ...attribute.values[0], + type: AttributeValueType.STRING, + value: "" + }, + confirmButtonState: "default", + disabled: false, + errors: [], + onClose: () => undefined, + onSubmit: () => undefined, + open: true +}; + +storiesOf("Attributes / Attribute value edit", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("form errors", () => ( + + )); diff --git a/src/storybook/stories/components/AssignAttributeDialog.tsx b/src/storybook/stories/components/AssignAttributeDialog.tsx new file mode 100644 index 000000000..d438dcc9a --- /dev/null +++ b/src/storybook/stories/components/AssignAttributeDialog.tsx @@ -0,0 +1,33 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { attributes } from "@saleor/attributes/fixtures"; +import { fetchMoreProps } from "@saleor/fixtures"; +import AssignAttributeDialog, { + AssignAttributeDialogProps +} from "@saleor/productTypes/components/AssignAttributeDialog"; +import { formError } from "@saleor/storybook/misc"; +import Decorator from "../../Decorator"; + +const props: AssignAttributeDialogProps = { + ...fetchMoreProps, + attributes: attributes.slice(0, 5), + confirmButtonState: "default", + errors: [], + onClose: () => undefined, + onOpen: () => undefined, + onSubmit: () => undefined, + onToggle: () => undefined, + open: true, + selected: [attributes[0].id, attributes[3].id] +}; + +storiesOf("Generics / Assign attributes dialog", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )) + .add("errors", () => ( + + )); diff --git a/src/storybook/stories/components/AssignProductDialog.tsx b/src/storybook/stories/components/AssignProductDialog.tsx new file mode 100644 index 000000000..cdc91c8f0 --- /dev/null +++ b/src/storybook/stories/components/AssignProductDialog.tsx @@ -0,0 +1,23 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import AssignProductDialog, { + AssignProductDialogProps +} from "@saleor/components/AssignProductDialog"; +import { products } from "@saleor/products/fixtures"; +import placeholderImage from "../../../../images/placeholder60x60.png"; +import Decorator from "../../Decorator"; + +const props: AssignProductDialogProps = { + confirmButtonState: "default", + loading: false, + onClose: () => undefined, + onFetch: () => undefined, + onSubmit: () => undefined, + open: true, + products: products(placeholderImage) +}; + +storiesOf("Generics / Assign product", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/storybook/stories/components/ColumnPicker.tsx b/src/storybook/stories/components/ColumnPicker.tsx new file mode 100644 index 000000000..9da2bc9cc --- /dev/null +++ b/src/storybook/stories/components/ColumnPicker.tsx @@ -0,0 +1,45 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ColumnPicker, { + ColumnPickerProps +} from "@saleor/components/ColumnPicker"; +import { ColumnPickerChoice } from "@saleor/components/ColumnPicker/ColumnPickerContent"; +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "../../Decorator"; + +const columns: ColumnPickerChoice[] = [ + { label: "Name", value: "name" }, + { label: "Value", value: "value" }, + { label: "Type", value: "type" }, + { label: "Size", value: "size" }, + { label: "Status", value: "isPublished" }, + { label: "Price", value: "price" }, + { label: "Digital", value: "isDigital" }, + ...Array(15) + .fill(0) + .map((_, index) => ({ + label: "Attribute " + (index + 1), + value: "attribute_" + index + })) +]; + +const props: ColumnPickerProps = { + columns, + initial: true, + onCancel: () => undefined, + onColumnToggle: () => undefined, + onReset: () => undefined, + onSave: () => undefined, + selectedColumns: [1, 3, 4, 6].map(index => columns[index].value) +}; + +storiesOf("Generics / Column picker", module) + .addDecorator(storyFn => ( +
+ {storyFn()} +
+ )) + .addDecorator(CardDecorator) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/storybook/stories/productTypes/ProductTypeAttributeUnassignDialog.tsx b/src/storybook/stories/productTypes/ProductTypeAttributeUnassignDialog.tsx new file mode 100644 index 000000000..b8e6861db --- /dev/null +++ b/src/storybook/stories/productTypes/ProductTypeAttributeUnassignDialog.tsx @@ -0,0 +1,20 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ProductTypeAttributeUnassignDialog, { + ProductTypeAttributeUnassignDialogProps +} from "../../../productTypes/components/ProductTypeAttributeUnassignDialog"; +import Decorator from "../../Decorator"; + +const props: ProductTypeAttributeUnassignDialogProps = { + attributeName: "Size", + confirmButtonState: "default", + onClose: () => undefined, + onConfirm: () => undefined, + open: true, + productTypeName: "Shoes" +}; + +storiesOf("Views / Product types / Unassign attribute", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/storybook/stories/productTypes/ProductTypeBulkAttributeUnassignDialog.tsx b/src/storybook/stories/productTypes/ProductTypeBulkAttributeUnassignDialog.tsx new file mode 100644 index 000000000..1c3941025 --- /dev/null +++ b/src/storybook/stories/productTypes/ProductTypeBulkAttributeUnassignDialog.tsx @@ -0,0 +1,20 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ProductTypeBulkAttributeUnassignDialog, { + ProductTypeBulkAttributeUnassignDialogProps +} from "../../../productTypes/components/ProductTypeBulkAttributeUnassignDialog"; +import Decorator from "../../Decorator"; + +const props: ProductTypeBulkAttributeUnassignDialogProps = { + attributeQuantity: "4", + confirmButtonState: "default", + onClose: () => undefined, + onConfirm: () => undefined, + open: true, + productTypeName: "Shoes" +}; + +storiesOf("Views / Product types / Unassign multiple attributes", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/storybook/stories/productTypes/ProductTypeDeleteDialog.tsx b/src/storybook/stories/productTypes/ProductTypeDeleteDialog.tsx new file mode 100644 index 000000000..6ffe808fe --- /dev/null +++ b/src/storybook/stories/productTypes/ProductTypeDeleteDialog.tsx @@ -0,0 +1,19 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ProductTypeDeleteDialog, { + ProductTypeDeleteDialogProps +} from "../../../productTypes/components/ProductTypeDeleteDialog"; +import Decorator from "../../Decorator"; + +const props: ProductTypeDeleteDialogProps = { + confirmButtonState: "default", + name: "Shoes", + onClose: () => undefined, + onConfirm: () => undefined, + open: true +}; + +storiesOf("Product types / ProductTypeDeleteDialog", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/storybook/stories/products/ProductListPage.tsx b/src/storybook/stories/products/ProductListPage.tsx new file mode 100644 index 000000000..c5c43dc0a --- /dev/null +++ b/src/storybook/stories/products/ProductListPage.tsx @@ -0,0 +1,50 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { defaultListSettings } from "@saleor/config"; +import { ListViews } from "@saleor/types"; +import placeholderImage from "../../../../images/placeholder255x255.png"; +import { category as categoryFixture } from "../../../categories/fixtures"; +import { + filterPageProps, + filters, + listActionsProps, + pageListProps +} from "../../../fixtures"; +import ProductListPage, { + ProductListPageProps +} from "../../../products/components/ProductListPage"; +import Decorator from "../../Decorator"; + +const products = categoryFixture(placeholderImage).products.edges.map( + edge => edge.node +); + +const props: ProductListPageProps = { + ...listActionsProps, + ...pageListProps.default, + ...filterPageProps, + defaultSettings: defaultListSettings[ListViews.PRODUCT_LIST], + products, + settings: { + ...pageListProps.default.settings, + columns: ["isPublished", "productType", "price"] + } +}; + +storiesOf("Views / Products / Product list", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("with custom filters", () => ( + + )) + .add("loading", () => ( + + )) + .add("no data", () => ); diff --git a/src/utils/errors.ts b/src/utils/errors.ts new file mode 100644 index 000000000..e7920f83b --- /dev/null +++ b/src/utils/errors.ts @@ -0,0 +1,14 @@ +import { maybe } from "@saleor/misc"; +import { UserError } from "@saleor/types"; + +export function getFieldError(errors: UserError[], field: string): string { + const err = errors.find(err => err.field === field); + + return maybe(() => err.message); +} + +export function getErrors(errors: UserError[]): string[] { + return errors + .filter(err => ["", null].includes(err.field)) + .map(err => err.message); +} diff --git a/src/utils/handlers/multiAutocompleteSelectChangeHandler.ts b/src/utils/handlers/multiAutocompleteSelectChangeHandler.ts new file mode 100644 index 000000000..72f792740 --- /dev/null +++ b/src/utils/handlers/multiAutocompleteSelectChangeHandler.ts @@ -0,0 +1,21 @@ +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { ChangeEvent, FormChange } from "@saleor/hooks/useForm"; +import { toggle } from "../lists"; + +function createMultiAutocompleteSelectHandler( + change: FormChange, + setSelected: (choices: MultiAutocompleteChoiceType[]) => void, + selected: MultiAutocompleteChoiceType[], + choices: MultiAutocompleteChoiceType[] +): FormChange { + return (event: ChangeEvent) => { + change(event); + + const id = event.target.value; + const choice = choices.find(choice => choice.value === id); + + setSelected(toggle(choice, selected, (a, b) => a.value === b.value)); + }; +} + +export default createMultiAutocompleteSelectHandler; diff --git a/src/utils/handlers/queryChangeHandler.ts b/src/utils/handlers/queryChangeHandler.ts new file mode 100644 index 000000000..8297c44cb --- /dev/null +++ b/src/utils/handlers/queryChangeHandler.ts @@ -0,0 +1,12 @@ +import { ChangeEvent } from "@saleor/hooks/useForm"; + +export function onQueryChange( + event: ChangeEvent, + onFetch: (data: string) => void, + setQuery: (data: string) => void +) { + const value = event.target.value; + + onFetch(value); + setQuery(value); +} diff --git a/src/utils/handlers/singleAutocompleteSelectChangeHandler.ts b/src/utils/handlers/singleAutocompleteSelectChangeHandler.ts new file mode 100644 index 000000000..ee2265185 --- /dev/null +++ b/src/utils/handlers/singleAutocompleteSelectChangeHandler.ts @@ -0,0 +1,17 @@ +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; +import { FormChange } from "@saleor/hooks/useForm"; + +function createSingleAutocompleteSelectHandler( + change: FormChange, + setSelected: (value: string) => void, + choices: SingleAutocompleteChoiceType[] +): FormChange { + return (event: React.ChangeEvent) => { + change(event); + + const value = event.target.value; + setSelected(choices.find(category => category.value === value).label); + }; +} + +export default createSingleAutocompleteSelectHandler; diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts new file mode 100644 index 000000000..2781c0056 --- /dev/null +++ b/src/utils/i18n.ts @@ -0,0 +1,5 @@ +import i18n from "@saleor/i18n"; + +export function translateBoolean(value: boolean): string { + return value ? i18n.t("Yes") : i18n.t("No"); +} diff --git a/src/utils/lists/__snapshots__/lists.test.ts.snap b/src/utils/lists/__snapshots__/lists.test.ts.snap new file mode 100644 index 000000000..998d4c2e4 --- /dev/null +++ b/src/utils/lists/__snapshots__/lists.test.ts.snap @@ -0,0 +1,65 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Properly calculates output arrays Adds 1`] = ` +Array [ + "lorem", + "ipsum", + "dolor", + "sit", +] +`; + +exports[`Properly calculates output arrays Adds at index 1`] = ` +Array [ + "lorem", + "ipsum", + "sit", + "dolor", +] +`; + +exports[`Properly calculates output arrays Moves 1`] = ` +Array [ + "ipsum", + "lorem", + "dolor", +] +`; + +exports[`Properly calculates output arrays Removes 1`] = ` +Array [ + "lorem", + "dolor", +] +`; + +exports[`Properly calculates output arrays Removes at index 1`] = ` +Array [ + "lorem", + "dolor", +] +`; + +exports[`Properly calculates output arrays Toggles 1`] = ` +Array [ + "ipsum", + "dolor", +] +`; + +exports[`Properly calculates output arrays Toggles 2`] = ` +Array [ + "lorem", + "ipsum", + "dolor", + "sit", +] +`; + +exports[`Properly calculates output arrays Updates at index 1`] = ` +Array [ + "lorem", + "amet", + "dolor", +] +`; diff --git a/src/utils/lists/index.ts b/src/utils/lists/index.ts new file mode 100644 index 000000000..fc00ec863 --- /dev/null +++ b/src/utils/lists/index.ts @@ -0,0 +1 @@ +export * from "./lists"; diff --git a/src/utils/lists/lists.test.ts b/src/utils/lists/lists.test.ts new file mode 100644 index 000000000..dc6976b1c --- /dev/null +++ b/src/utils/lists/lists.test.ts @@ -0,0 +1,48 @@ +import { + add, + addAtIndex, + isSelected, + move, + remove, + removeAtIndex, + toggle, + updateAtIndex +} from "./lists"; + +const initialArray = ["lorem", "ipsum", "dolor"]; + +describe("Properly calculates output arrays", () => { + it("Adds", () => { + expect(add("sit", initialArray)).toMatchSnapshot(); + }); + + it("Adds at index", () => { + expect(addAtIndex("sit", initialArray, 2)).toMatchSnapshot(); + }); + + it("Updates at index", () => { + expect(updateAtIndex("amet", initialArray, 1)).toMatchSnapshot(); + }); + + it("Removes", () => { + expect(remove("ipsum", initialArray, (a, b) => a === b)).toMatchSnapshot(); + }); + + it("Removes at index", () => { + expect(removeAtIndex(initialArray, 1)).toMatchSnapshot(); + }); + + it("Matches", () => { + expect(isSelected("lorem", initialArray, (a, b) => a === b)).toBe(true); + expect(isSelected("sit", initialArray, (a, b) => a === b)).toBe(false); + }); + + it("Toggles", () => { + expect(toggle("lorem", initialArray, (a, b) => a === b)).toMatchSnapshot(); + expect(toggle("sit", initialArray, (a, b) => a === b)).toMatchSnapshot(); + }); + + it("Moves", () => { + expect(move("lorem", initialArray, (a, b) => a === b, 1)).toMatchSnapshot(); + }); +}); diff --git a/src/utils/lists/lists.ts b/src/utils/lists/lists.ts new file mode 100644 index 000000000..21005bacc --- /dev/null +++ b/src/utils/lists/lists.ts @@ -0,0 +1,61 @@ +type List = TData[]; +type Compare = (a: TData, b: TData) => boolean; + +export function isSelected( + data: TData, + list: List, + compare: Compare +) { + return !!list.find(listElement => compare(listElement, data)); +} + +export function add(data: TData, list: List) { + return [...list, data]; +} + +export function addAtIndex( + data: TData, + list: List, + index: number +) { + return [...list.slice(0, index), data, ...list.slice(index)]; +} + +export function move( + data: TData, + list: List, + compare: Compare, + index: number +) { + return addAtIndex(data, remove(data, list, compare), index); +} + +export function updateAtIndex( + data: TData, + list: List, + index: number +) { + return addAtIndex(data, removeAtIndex(list, index), index); +} + +export function remove( + data: TData, + list: List, + compare: Compare +) { + return list.filter(listElement => !compare(listElement, data)); +} + +export function removeAtIndex(list: List, index: number) { + return [...list.slice(0, index), ...list.slice(index + 1)]; +} + +export function toggle( + data: TData, + list: List, + compare: Compare +) { + return isSelected(data, list, compare) + ? remove(data, list, compare) + : add(data, list); +} diff --git a/src/utils/maps.ts b/src/utils/maps.ts new file mode 100644 index 000000000..11b76640e --- /dev/null +++ b/src/utils/maps.ts @@ -0,0 +1,12 @@ +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; +import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; + +export function mapCountriesToChoices( + countries: ShopInfo_shop_countries[] +): Array { + return countries.map(country => ({ + label: country.country, + value: country.code + })); +} From 0c53e9883a5cf5f8c2352ce77389f568c477df34 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Fri, 9 Aug 2019 12:26:22 +0200 Subject: [PATCH 02/12] Do not use asterisk imports --- src/Baseline.tsx | 2 +- src/NotFound.tsx | 2 +- src/auth/AuthProvider.tsx | 2 +- src/auth/components/LoginLoading/LoginLoading.tsx | 2 +- src/auth/components/LoginPage/LoginPage.tsx | 2 +- src/auth/components/SectionRoute.tsx | 2 +- src/auth/index.tsx | 2 +- src/auth/views/Login.tsx | 2 +- .../components/CategoryBackground/CategoryBackground.tsx | 2 +- .../components/CategoryCreatePage/CategoryCreatePage.tsx | 2 +- .../CategoryDeleteDialog/CategoryDeleteDialog.tsx | 2 +- .../components/CategoryDetailsForm/CategoryDetailsForm.tsx | 2 +- src/categories/components/CategoryList/CategoryList.tsx | 2 +- .../components/CategoryListPage/CategoryListPage.tsx | 2 +- .../components/CategoryProducts/CategoryProducts.tsx | 2 +- .../CategoryProductsCard/CategoryProductsCard.tsx | 2 +- .../components/CategoryUpdatePage/CategoryUpdatePage.tsx | 2 +- src/categories/index.tsx | 2 +- src/categories/urls.ts | 2 +- src/categories/views/CategoryCreate.tsx | 2 +- src/categories/views/CategoryDetails.tsx | 2 +- src/categories/views/CategoryList.tsx | 2 +- .../CollectionCreatePage/CollectionCreatePage.tsx | 2 +- .../components/CollectionDetails/CollectionDetails.tsx | 2 +- .../CollectionDetailsPage/CollectionDetailsPage.tsx | 2 +- .../components/CollectionImage/CollectionImage.tsx | 2 +- .../components/CollectionList/CollectionList.tsx | 2 +- .../components/CollectionListPage/CollectionListPage.tsx | 2 +- .../components/CollectionProducts/CollectionProducts.tsx | 2 +- src/collections/containers/CollectionOperations.tsx | 2 +- src/collections/index.tsx | 2 +- src/collections/urls.ts | 2 +- src/collections/views/CollectionCreate.tsx | 2 +- src/collections/views/CollectionDetails.tsx | 2 +- src/collections/views/CollectionList.tsx | 2 +- src/components/ActionDialog/ActionDialog.tsx | 2 +- src/components/AddressEdit/AddressEdit.tsx | 2 +- src/components/AddressFormatter/AddressFormatter.tsx | 2 +- src/components/AppHeader/AppHeader.tsx | 2 +- src/components/AppLayout/AppActionContext.tsx | 2 +- src/components/AppLayout/AppHeaderContext.tsx | 2 +- src/components/AppLayout/AppLayout.tsx | 2 +- src/components/AppLayout/MenuList.tsx | 2 +- src/components/AppLayout/MenuNested.tsx | 2 +- src/components/AppLayout/ResponsiveDrawer.tsx | 2 +- src/components/AppLayout/ThemeSwitch.tsx | 2 +- src/components/AppProgress/index.tsx | 2 +- .../AssignCategoryDialog/AssignCategoryDialog.tsx | 2 +- .../AssignCollectionDialog/AssignCollectionDialog.tsx | 2 +- src/components/AssignProductDialog/AssignProductDialog.tsx | 2 +- .../AutocompleteSelectMenu/AutocompleteSelectMenu.tsx | 2 +- src/components/CardMenu/CardMenu.tsx | 2 +- src/components/CardSpacer.tsx | 2 +- src/components/CardTitle/CardTitle.tsx | 2 +- src/components/Checkbox/Checkbox.tsx | 2 +- src/components/Chip/Chip.tsx | 2 +- src/components/ConfirmButton/ConfirmButton.tsx | 2 +- src/components/ConfirmFormLeaveDialog.tsx | 2 +- src/components/Container.tsx | 2 +- src/components/ControlledCheckbox.tsx | 2 +- src/components/ControlledSwitch.tsx | 2 +- src/components/CountryList/CountryList.tsx | 2 +- src/components/Date/Date.test.tsx | 2 +- src/components/Date/Date.tsx | 2 +- src/components/Date/DateContext.tsx | 2 +- src/components/Date/DateProvider.tsx | 2 +- src/components/Date/DateTime.tsx | 2 +- src/components/Debounce.tsx | 2 +- src/components/DebounceForm.tsx | 2 +- .../DeleteFilterTabDialog/DeleteFilterTabDialog.tsx | 2 +- src/components/DraftRenderer.tsx | 2 +- src/components/EditableTableCell/EditableTableCell.tsx | 2 +- src/components/ErrorMessageCard/ErrorMessageCard.tsx | 2 +- src/components/ErrorPage/ErrorPage.tsx | 2 +- src/components/ExtendedPageHeader/ExtendedPageHeader.tsx | 2 +- src/components/ExternalLink/ExternalLink.tsx | 2 +- src/components/FileUpload/FileUpload.tsx | 2 +- src/components/Filter/Filter.tsx | 2 +- src/components/Filter/FilterContent.tsx | 2 +- src/components/Filter/FilterElement.tsx | 2 +- src/components/FilterBar/FilterBar.tsx | 2 +- src/components/FilterCard/FilterCard.tsx | 2 +- src/components/Form/Form.tsx | 2 +- src/components/Form/FormActions.tsx | 2 +- src/components/Form/FormContext.tsx | 2 +- src/components/FormSpacer.tsx | 2 +- src/components/Grid/Grid.tsx | 2 +- src/components/Hr.tsx | 2 +- src/components/IconButtonTableCell/IconButtonTableCell.tsx | 2 +- src/components/ImageTile/ImageTile.tsx | 2 +- src/components/ImageUpload/ImageUpload.tsx | 2 +- src/components/LanguageSwitch/LanguageSwitch.tsx | 2 +- src/components/Link.tsx | 2 +- src/components/ListField/ListField.tsx | 2 +- src/components/Locale/Locale.tsx | 2 +- src/components/MenuToggle.tsx | 2 +- src/components/Money/Money.tsx | 2 +- src/components/MoneyRange/MoneyRange.tsx | 2 +- .../MultiAutocompleteSelectField.tsx | 2 +- src/components/MultiSelectField/MultiSelectField.tsx | 2 +- src/components/NotFoundPage/NotFoundPage.tsx | 2 +- src/components/PageHeader/PageHeader.tsx | 2 +- src/components/Percent/Percent.tsx | 2 +- src/components/PhoneField/PhoneField.tsx | 2 +- src/components/PriceField/PriceField.tsx | 2 +- src/components/ProductList/ProductList.tsx | 2 +- src/components/RichTextEditor/ImageEntity.tsx | 2 +- src/components/RichTextEditor/ImageSource.tsx | 2 +- src/components/RichTextEditor/LinkEntity.tsx | 2 +- src/components/RichTextEditor/LinkSource.tsx | 2 +- src/components/RichTextEditor/RichTextEditor.tsx | 2 +- src/components/SaveButtonBar/SaveButtonBar.tsx | 2 +- src/components/SaveFilterTabDialog/SaveFilterTabDialog.tsx | 2 +- src/components/SeoForm/SeoForm.tsx | 2 +- src/components/Shop/index.tsx | 2 +- .../SingleAutocompleteSelectField.tsx | 2 +- src/components/SingleSelectField/SingleSelectField.tsx | 2 +- src/components/Skeleton.tsx | 2 +- src/components/StatusLabel/StatusLabel.tsx | 2 +- src/components/Tab/Tab.tsx | 2 +- src/components/Tab/TabContainer.tsx | 2 +- src/components/Tab/Tabs.tsx | 2 +- src/components/TableCellAvatar/TableCellAvatar.tsx | 2 +- src/components/TableFilter/FilterChips.tsx | 2 +- src/components/TableFilter/FilterTab.tsx | 2 +- src/components/TableFilter/FilterTabs.tsx | 2 +- src/components/TableHead/TableHead.tsx | 2 +- src/components/TablePagination/TablePagination.tsx | 2 +- src/components/TablePagination/TablePaginationActions.tsx | 2 +- src/components/TextFieldWithChoice/TextFieldWithChoice.tsx | 2 +- src/components/Theme/ThemeProvider.tsx | 2 +- src/components/Timeline/Timeline.tsx | 2 +- src/components/Timeline/TimelineEvent.tsx | 2 +- src/components/Timeline/TimelineNote.tsx | 2 +- src/components/Timezone/Timezone.tsx | 2 +- src/components/VisibilityCard/VisibilityCard.tsx | 2 +- src/components/Weight/Weight.tsx | 2 +- src/components/WeightRange/WeightRange.tsx | 2 +- src/components/WindowTitle/index.tsx | 2 +- src/components/messages/MessageManager.tsx | 2 +- src/configuration/ConfigurationPage.tsx | 2 +- src/configuration/index.tsx | 2 +- src/containers/BaseSearch.tsx | 2 +- .../components/CustomerAddress/CustomerAddress.tsx | 2 +- .../CustomerAddressDialog/CustomerAddressDialog.tsx | 2 +- .../CustomerAddressListPage/CustomerAddressListPage.tsx | 2 +- .../components/CustomerAddresses/CustomerAddresses.tsx | 2 +- .../CustomerCreateAddress/CustomerCreateAddress.tsx | 2 +- .../CustomerCreateDetails/CustomerCreateDetails.tsx | 2 +- .../components/CustomerCreateNote/CustomerCreateNote.tsx | 2 +- .../components/CustomerCreatePage/CustomerCreatePage.tsx | 2 +- .../components/CustomerDetails/CustomerDetails.tsx | 2 +- .../components/CustomerDetailsPage/CustomerDetailsPage.tsx | 2 +- src/customers/components/CustomerList/CustomerList.tsx | 2 +- .../components/CustomerListPage/CustomerListPage.tsx | 2 +- src/customers/components/CustomerOrders/CustomerOrders.tsx | 2 +- src/customers/components/CustomerStats/CustomerStats.tsx | 2 +- src/customers/index.tsx | 2 +- src/customers/urls.ts | 2 +- src/customers/views/CustomerAddresses.tsx | 2 +- src/customers/views/CustomerCreate.tsx | 2 +- src/customers/views/CustomerDetails.tsx | 2 +- src/customers/views/CustomerList.tsx | 2 +- .../components/DiscountCategories/DiscountCategories.tsx | 2 +- .../components/DiscountCollections/DiscountCollections.tsx | 2 +- .../DiscountCountrySelectDialog.tsx | 2 +- .../components/DiscountProducts/DiscountProducts.tsx | 2 +- src/discounts/components/SaleCreatePage/SaleCreatePage.tsx | 2 +- .../components/SaleDetailsPage/SaleDetailsPage.tsx | 2 +- src/discounts/components/SaleInfo/SaleInfo.tsx | 2 +- src/discounts/components/SaleList/SaleList.tsx | 2 +- src/discounts/components/SaleListPage/SaleListPage.tsx | 2 +- src/discounts/components/SalePricing/SalePricing.tsx | 2 +- src/discounts/components/SaleSummary/SaleSummary.tsx | 2 +- .../components/VoucherCreatePage/VoucherCreatePage.tsx | 2 +- .../components/VoucherDetailsPage/VoucherDetailsPage.tsx | 2 +- src/discounts/components/VoucherInfo/VoucherInfo.tsx | 2 +- src/discounts/components/VoucherList/VoucherList.tsx | 2 +- .../components/VoucherListPage/VoucherListPage.tsx | 2 +- src/discounts/components/VoucherOptions/VoucherOptions.tsx | 2 +- src/discounts/components/VoucherSummary/VoucherSummary.tsx | 2 +- src/discounts/fixtures.ts | 2 +- src/discounts/index.tsx | 2 +- src/discounts/urls.ts | 2 +- src/discounts/views/SaleCreate.tsx | 2 +- src/discounts/views/SaleDetails.tsx | 2 +- src/discounts/views/SaleList.tsx | 2 +- src/discounts/views/VoucherCreate.tsx | 2 +- src/discounts/views/VoucherDetails.tsx | 2 +- src/discounts/views/VoucherList.tsx | 2 +- src/home/components/HomeActivityCard/HomeActivityCard.tsx | 2 +- src/home/components/HomeAnalyticsCard/HomeAnalyticsCard.tsx | 2 +- src/home/components/HomeHeader/HomeHeader.tsx | 2 +- .../HomeNotificationTable/HomeNotificationTable.tsx | 2 +- src/home/components/HomePage/HomePage.tsx | 2 +- .../components/HomeProductListCard/HomeProductListCard.tsx | 2 +- src/home/components/HomeScreen.tsx | 2 +- src/home/index.tsx | 2 +- src/home/views/index.tsx | 2 +- src/hooks/useDateLocalize.ts | 2 +- src/hooks/useScroll.ts | 3 +-- src/i18n.ts | 6 +++--- src/icons/AccountCircle.tsx | 2 +- src/icons/ArrowDropdown.tsx | 2 +- src/icons/Ballot.tsx | 2 +- src/icons/BoldIcon.tsx | 2 +- src/icons/Calendar.tsx | 2 +- src/icons/Draggable.tsx | 2 +- src/icons/Folder.tsx | 2 +- src/icons/HeaderOne.tsx | 2 +- src/icons/HeaderThree.tsx | 2 +- src/icons/HeaderTwo.tsx | 2 +- src/icons/Home.tsx | 2 +- src/icons/Image.tsx | 2 +- src/icons/ItalicIcon.tsx | 2 +- src/icons/LinkIcon.tsx | 2 +- src/icons/LocalShipping.tsx | 2 +- src/icons/Monetization.tsx | 2 +- src/icons/Moon.tsx | 2 +- src/icons/Navigation.tsx | 2 +- src/icons/NoPhoto.tsx | 2 +- src/icons/OrderedListIcon.tsx | 2 +- src/icons/Orders.tsx | 2 +- src/icons/Pages.tsx | 2 +- src/icons/ProductTypes.tsx | 2 +- src/icons/QuotationIcon.tsx | 2 +- src/icons/Sales.tsx | 2 +- src/icons/ShippingMethods.tsx | 2 +- src/icons/Shop.tsx | 2 +- src/icons/SiteSettings.tsx | 2 +- src/icons/StaffMembers.tsx | 2 +- src/icons/StoreMall.tsx | 2 +- src/icons/StrikethroughIcon.tsx | 2 +- src/icons/Sun.tsx | 2 +- src/icons/Taxes.tsx | 2 +- src/icons/Truck.tsx | 2 +- src/icons/UnorderedListIcon.tsx | 2 +- src/icons/Unstyled.tsx | 2 +- src/index.tsx | 2 +- src/misc.ts | 2 +- src/mutations.tsx | 2 +- .../components/MenuCreateDialog/MenuCreateDialog.tsx | 2 +- .../components/MenuDetailsPage/MenuDetailsPage.tsx | 2 +- src/navigation/components/MenuItemDialog/MenuItemDialog.tsx | 2 +- src/navigation/components/MenuItems/MenuItems.tsx | 2 +- src/navigation/components/MenuList/MenuList.tsx | 2 +- src/navigation/components/MenuListPage/MenuListPage.tsx | 2 +- src/navigation/components/MenuProperties/MenuProperties.tsx | 2 +- src/navigation/index.tsx | 2 +- src/navigation/urls.ts | 2 +- src/navigation/views/MenuDetails/index.tsx | 2 +- src/navigation/views/MenuList.tsx | 2 +- .../OrderAddressEditDialog/OrderAddressEditDialog.tsx | 2 +- .../OrderBulkCancelDialog/OrderBulkCancelDialog.tsx | 2 +- .../components/OrderCancelDialog/OrderCancelDialog.tsx | 2 +- src/orders/components/OrderCustomer/OrderCustomer.tsx | 2 +- .../OrderCustomerEditDialog/OrderCustomerEditDialog.tsx | 2 +- .../components/OrderCustomerNote/OrderCustomerNote.tsx | 2 +- src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx | 2 +- .../OrderDraftCancelDialog/OrderDraftCancelDialog.tsx | 2 +- .../components/OrderDraftDetails/OrderDraftDetails.tsx | 2 +- .../OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx | 2 +- .../OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx | 2 +- .../OrderDraftFinalizeDialog/OrderDraftFinalizeDialog.tsx | 2 +- src/orders/components/OrderDraftList/OrderDraftList.tsx | 2 +- .../components/OrderDraftListPage/OrderDraftListPage.tsx | 2 +- src/orders/components/OrderDraftPage/OrderDraftPage.tsx | 2 +- src/orders/components/OrderFulfillment/OrderFulfillment.tsx | 2 +- .../OrderFulfillmentCancelDialog.tsx | 2 +- .../OrderFulfillmentDialog/OrderFulfillmentDialog.tsx | 2 +- .../OrderFulfillmentTrackingDialog.tsx | 2 +- src/orders/components/OrderHistory/OrderHistory.tsx | 2 +- src/orders/components/OrderList/OrderList.tsx | 2 +- src/orders/components/OrderListFilter/OrderListFilter.tsx | 2 +- src/orders/components/OrderListPage/OrderListPage.tsx | 2 +- .../OrderMarkAsPaidDialog/OrderMarkAsPaidDialog.tsx | 2 +- src/orders/components/OrderPayment/OrderPayment.tsx | 2 +- .../components/OrderPaymentDialog/OrderPaymentDialog.tsx | 2 +- .../OrderPaymentVoidDialog/OrderPaymentVoidDialog.tsx | 2 +- .../OrderProductAddDialog/OrderProductAddDialog.tsx | 2 +- .../OrderShippingMethodEditDialog.tsx | 2 +- .../OrderUnfulfilledItems/OrderUnfulfilledItems.tsx | 2 +- src/orders/containers/OrderOperations.tsx | 2 +- src/orders/index.tsx | 2 +- src/orders/urls.ts | 2 +- src/orders/views/OrderDetails/OrderDetailsMessages.tsx | 2 +- src/orders/views/OrderDetails/index.tsx | 2 +- src/orders/views/OrderDraftList.tsx | 2 +- src/orders/views/OrderList/OrderList.tsx | 2 +- src/pages/components/PageDetailsPage/PageDetailsPage.tsx | 2 +- src/pages/components/PageInfo/PageInfo.tsx | 2 +- src/pages/components/PageList/PageList.tsx | 2 +- src/pages/components/PageListPage/PageListPage.tsx | 2 +- src/pages/components/PageSlug/PageSlug.tsx | 2 +- src/pages/index.tsx | 2 +- src/pages/urls.ts | 2 +- src/pages/views/PageCreate.tsx | 2 +- src/pages/views/PageDetails.tsx | 2 +- src/pages/views/PageList.tsx | 2 +- .../ProductTypeAttributeEditDialog.tsx | 2 +- .../ProductTypeAttributes/ProductTypeAttributes.tsx | 2 +- .../ProductTypeCreatePage/ProductTypeCreatePage.tsx | 2 +- .../components/ProductTypeDetails/ProductTypeDetails.tsx | 2 +- .../ProductTypeDetailsPage/ProductTypeDetailsPage.tsx | 2 +- .../components/ProductTypeList/ProductTypeList.tsx | 2 +- .../components/ProductTypeListPage/ProductTypeListPage.tsx | 2 +- .../components/ProductTypeShipping/ProductTypeShipping.tsx | 2 +- .../components/ProductTypeTaxes/ProductTypeTaxes.tsx | 2 +- src/productTypes/containers/ProductTypeOperations.tsx | 2 +- src/productTypes/index.tsx | 2 +- src/productTypes/urls.ts | 2 +- src/productTypes/views/ProductTypeCreate.tsx | 2 +- src/productTypes/views/ProductTypeList.tsx | 2 +- src/productTypes/views/ProductTypeUpdate/errors.tsx | 2 +- src/productTypes/views/ProductTypeUpdate/index.tsx | 2 +- .../ProductCategoryAndCollectionsForm.tsx | 2 +- .../components/ProductCreatePage/ProductCreatePage.tsx | 2 +- .../components/ProductDetailsForm/ProductDetailsForm.tsx | 2 +- .../ProductImageNavigation/ProductImageNavigation.tsx | 2 +- .../components/ProductImagePage/ProductImagePage.tsx | 2 +- src/products/components/ProductImages/ProductImages.tsx | 2 +- src/products/components/ProductListCard/ProductListCard.tsx | 2 +- .../components/ProductListFilter/ProductListFilter.tsx | 2 +- .../components/ProductOrganization/ProductOrganization.tsx | 2 +- src/products/components/ProductPricing/ProductPricing.tsx | 2 +- src/products/components/ProductStock/ProductStock.tsx | 2 +- .../components/ProductUpdatePage/ProductUpdatePage.tsx | 2 +- .../ProductVariantAttributes/ProductVariantAttributes.tsx | 2 +- .../ProductVariantCreatePage/ProductVariantCreatePage.tsx | 2 +- .../ProductVariantDeleteDialog.tsx | 2 +- .../ProductVariantImageSelectDialog.tsx | 2 +- .../ProductVariantImages/ProductVariantImages.tsx | 2 +- .../ProductVariantNavigation/ProductVariantNavigation.tsx | 2 +- .../components/ProductVariantPage/ProductVariantPage.tsx | 2 +- .../components/ProductVariantPrice/ProductVariantPrice.tsx | 2 +- .../components/ProductVariantStock/ProductVariantStock.tsx | 2 +- src/products/components/ProductVariants/ProductVariants.tsx | 2 +- src/products/containers/ProductImagesReorder.tsx | 2 +- src/products/containers/ProductUpdateOperations.tsx | 2 +- src/products/containers/ProductVariantOperations.tsx | 2 +- src/products/index.tsx | 2 +- src/products/urls.ts | 2 +- src/products/views/ProductCreate.tsx | 2 +- src/products/views/ProductImage.tsx | 2 +- src/products/views/ProductList/ProductList.tsx | 2 +- src/products/views/ProductUpdate.tsx | 2 +- src/products/views/ProductVariant.tsx | 2 +- src/products/views/ProductVariantCreate.tsx | 2 +- src/queries.tsx | 2 +- .../ShippingWeightUnitForm/ShippingWeightUnitForm.tsx | 2 +- .../ShippingZoneCountriesAssignDialog.tsx | 2 +- .../ShippingZoneCreatePage/ShippingZoneCreatePage.tsx | 2 +- .../ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx | 2 +- .../components/ShippingZoneInfo/ShippingZoneInfo.tsx | 2 +- .../ShippingZoneRateDialog/ShippingZoneRateDialog.tsx | 2 +- .../components/ShippingZoneRates/ShippingZoneRates.tsx | 2 +- .../components/ShippingZonesList/ShippingZonesList.tsx | 2 +- .../ShippingZonesListPage/ShippingZonesListPage.tsx | 2 +- src/shipping/index.tsx | 2 +- src/shipping/urls.ts | 2 +- src/shipping/views/ShippingZoneCreate.tsx | 2 +- .../ShippingZoneDetails/ShippingZoneDetailsDialogs.tsx | 2 +- .../views/ShippingZoneDetails/ShippingZoneOperations.tsx | 2 +- src/shipping/views/ShippingZoneDetails/index.tsx | 2 +- src/shipping/views/ShippingZonesList.tsx | 2 +- .../components/SiteSettingsDetails/SiteSettingsDetails.tsx | 2 +- .../SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx | 2 +- .../components/SiteSettingsKeys/SiteSettingsKeys.tsx | 2 +- .../components/SiteSettingsPage/SiteSettingsPage.tsx | 2 +- src/siteSettings/index.tsx | 2 +- src/siteSettings/views/index.tsx | 2 +- .../StaffAddMemberDialog/StaffAddMemberDialog.tsx | 2 +- src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx | 2 +- src/staff/components/StaffList/StaffList.tsx | 2 +- src/staff/components/StaffListPage/StaffListPage.tsx | 2 +- src/staff/components/StaffPermissions/StaffPermissions.tsx | 2 +- src/staff/components/StaffProperties/StaffProperties.tsx | 2 +- src/staff/components/StaffStatus/StaffStatus.tsx | 2 +- src/staff/fixtures.ts | 2 +- src/staff/index.tsx | 2 +- src/staff/urls.ts | 2 +- src/staff/views/StaffDetails.tsx | 2 +- src/staff/views/StaffList.tsx | 2 +- src/storybook/CardDecorator.tsx | 2 +- src/storybook/Decorator.tsx | 2 +- src/storybook/Stories.test.ts | 4 ++-- src/storybook/mock.tsx | 2 +- src/storybook/stories/auth/LoginLoading.tsx | 2 +- src/storybook/stories/auth/LoginPage.tsx | 2 +- src/storybook/stories/categories/CategoryCreatePage.tsx | 2 +- src/storybook/stories/categories/CategoryListPage.tsx | 2 +- src/storybook/stories/categories/CategoryProducts.tsx | 2 +- src/storybook/stories/categories/CategoryUpdatePage.tsx | 2 +- src/storybook/stories/collections/CollectionCreatePage.tsx | 2 +- src/storybook/stories/collections/CollectionDetailsPage.tsx | 2 +- src/storybook/stories/collections/CollectionListPage.tsx | 2 +- src/storybook/stories/components/ActionDialog.tsx | 2 +- src/storybook/stories/components/AddressEdit.tsx | 2 +- src/storybook/stories/components/AddressFormatter.tsx | 2 +- src/storybook/stories/components/AutocompleteSelectMenu.tsx | 2 +- src/storybook/stories/components/CardMenu.tsx | 2 +- src/storybook/stories/components/Checkbox.tsx | 2 +- src/storybook/stories/components/Chip.tsx | 2 +- src/storybook/stories/components/Date.tsx | 2 +- src/storybook/stories/components/DateTime.tsx | 2 +- src/storybook/stories/components/DeleteFilterTabDialog.tsx | 2 +- src/storybook/stories/components/EditableTableCell.tsx | 2 +- src/storybook/stories/components/ErrorMessageCard.tsx | 2 +- src/storybook/stories/components/ErrorPage.tsx | 2 +- src/storybook/stories/components/ExternalLink.tsx | 2 +- src/storybook/stories/components/FileUpload.tsx | 2 +- src/storybook/stories/components/Filter.tsx | 2 +- src/storybook/stories/components/Money.tsx | 2 +- src/storybook/stories/components/MoneyRange.tsx | 2 +- .../stories/components/MultiAutocompleteSelectField.tsx | 2 +- src/storybook/stories/components/MultiSelectField.tsx | 2 +- src/storybook/stories/components/NotFoundPage.tsx | 2 +- src/storybook/stories/components/PageHeader.tsx | 2 +- src/storybook/stories/components/Percent.tsx | 2 +- src/storybook/stories/components/PhoneField.tsx | 2 +- src/storybook/stories/components/PriceField.tsx | 2 +- src/storybook/stories/components/RichTextEditor.tsx | 2 +- src/storybook/stories/components/SaveButtonBar.tsx | 2 +- src/storybook/stories/components/SaveFilterTabDialog.tsx | 2 +- .../stories/components/SingleAutocompleteSelectField.tsx | 2 +- src/storybook/stories/components/SingleSelectField.tsx | 2 +- src/storybook/stories/components/Skeleton.tsx | 2 +- src/storybook/stories/components/StatusLabel.tsx | 2 +- src/storybook/stories/components/TablePagination.tsx | 2 +- src/storybook/stories/components/Timeline.tsx | 2 +- src/storybook/stories/components/Weight.tsx | 2 +- src/storybook/stories/components/WeightRange.tsx | 2 +- src/storybook/stories/components/messages.tsx | 2 +- src/storybook/stories/configuration/ConfigurationPage.tsx | 2 +- src/storybook/stories/customers/CustomerAddressDialog.tsx | 2 +- src/storybook/stories/customers/CustomerAddressListPage.tsx | 2 +- src/storybook/stories/customers/CustomerCreatePage.tsx | 2 +- src/storybook/stories/customers/CustomerDetailsPage.tsx | 2 +- src/storybook/stories/customers/CustomerListPage.tsx | 2 +- .../stories/discounts/DiscountCountrySelectDialog.tsx | 2 +- src/storybook/stories/discounts/SaleCreatePage.tsx | 2 +- src/storybook/stories/discounts/SaleDetailsPage.tsx | 2 +- src/storybook/stories/discounts/SaleListPage.tsx | 2 +- src/storybook/stories/discounts/VoucherCreatePage.tsx | 2 +- src/storybook/stories/discounts/VoucherDetailsPage.tsx | 2 +- src/storybook/stories/discounts/VoucherListPage.tsx | 2 +- src/storybook/stories/home/HomePage.tsx | 2 +- src/storybook/stories/navigation/MenuCreateDialog.tsx | 2 +- src/storybook/stories/navigation/MenuDetailsPage.tsx | 2 +- src/storybook/stories/navigation/MenuItemDialog.tsx | 2 +- src/storybook/stories/navigation/MenuListPage.tsx | 2 +- src/storybook/stories/orders/OrderAddressEditDialog.tsx | 2 +- src/storybook/stories/orders/OrderBulkCancelDialog.tsx | 2 +- src/storybook/stories/orders/OrderCancelDialog.tsx | 2 +- src/storybook/stories/orders/OrderCustomer.tsx | 2 +- src/storybook/stories/orders/OrderCustomerEditDialog.tsx | 2 +- src/storybook/stories/orders/OrderDetailsPage.tsx | 2 +- src/storybook/stories/orders/OrderDraftCancelDialog.tsx | 2 +- src/storybook/stories/orders/OrderDraftFinalizeDialog.tsx | 2 +- src/storybook/stories/orders/OrderDraftListPage.tsx | 2 +- src/storybook/stories/orders/OrderDraftPage.tsx | 2 +- .../stories/orders/OrderFulfillmentCancelDialog.tsx | 2 +- src/storybook/stories/orders/OrderFulfillmentDialog.tsx | 2 +- .../stories/orders/OrderFulfillmentTrackingDialog.tsx | 2 +- src/storybook/stories/orders/OrderHistory.tsx | 2 +- src/storybook/stories/orders/OrderListPage.tsx | 2 +- src/storybook/stories/orders/OrderMarkAsPaidDialog.tsx | 2 +- src/storybook/stories/orders/OrderPaymentDialog.tsx | 2 +- src/storybook/stories/orders/OrderPaymentVoidDialog.tsx | 2 +- src/storybook/stories/orders/OrderProductAddDialog.tsx | 2 +- .../stories/orders/OrderShippingMethodEditDialog.tsx | 2 +- src/storybook/stories/pages/PageDetailsPage.tsx | 2 +- src/storybook/stories/pages/PageListPage.tsx | 2 +- .../stories/productTypes/ProductTypeAttributeEditDialog.tsx | 2 +- .../stories/productTypes/ProductTypeCreatePage.tsx | 2 +- .../stories/productTypes/ProductTypeDetailsPage.tsx | 2 +- src/storybook/stories/productTypes/ProductTypeListPage.tsx | 2 +- src/storybook/stories/products/ProductCreatePage.tsx | 2 +- src/storybook/stories/products/ProductImagePage.tsx | 2 +- src/storybook/stories/products/ProductListCard.tsx | 2 +- src/storybook/stories/products/ProductUpdatePage.tsx | 2 +- src/storybook/stories/products/ProductVariantCreatePage.tsx | 2 +- .../stories/products/ProductVariantImageSelectDialog.tsx | 2 +- src/storybook/stories/products/ProductVariantPage.tsx | 2 +- .../stories/shipping/ShippingZoneCountriesAssignDialog.tsx | 2 +- src/storybook/stories/shipping/ShippingZoneCreatePage.tsx | 2 +- src/storybook/stories/shipping/ShippingZoneDetailsPage.tsx | 2 +- src/storybook/stories/shipping/ShippingZoneRateDialog.tsx | 2 +- src/storybook/stories/shipping/ShippingZonesListPage.tsx | 2 +- .../stories/siteSettings/SiteSettingsKeyDialog.tsx | 2 +- src/storybook/stories/siteSettings/SiteSettingsPage.tsx | 2 +- src/storybook/stories/staff/StaffDetailsPage.tsx | 2 +- src/storybook/stories/staff/StaffListPage.tsx | 2 +- src/storybook/stories/taxes/CountryListPage.tsx | 2 +- src/storybook/stories/taxes/CountryTaxesPage.tsx | 2 +- .../stories/translations/TranslationsEntitiesListPage.tsx | 2 +- .../stories/translations/TranslationsLanguageListPage.tsx | 2 +- src/taxes/components/CountryList/CountryList.tsx | 2 +- src/taxes/components/CountryListPage/CountryListPage.tsx | 2 +- src/taxes/components/CountryTaxesPage/CountryTaxesPage.tsx | 2 +- src/taxes/components/TaxConfiguration/TaxConfiguration.tsx | 2 +- src/taxes/index.tsx | 2 +- src/taxes/urls.ts | 2 +- src/taxes/views/CountryList.tsx | 2 +- src/taxes/views/CountryTaxes.tsx | 2 +- .../components/TranslationFields/TranslationFields.tsx | 2 +- .../components/TranslationFields/TranslationFieldsLong.tsx | 2 +- .../components/TranslationFields/TranslationFieldsRich.tsx | 2 +- .../components/TranslationFields/TranslationFieldsSave.tsx | 2 +- .../components/TranslationFields/TranslationFieldsShort.tsx | 2 +- .../TranslationsCategoriesPage.tsx | 2 +- .../TranslationsCollectionsPage.tsx | 2 +- .../TranslationsEntitiesList/TranslationsEntitiesList.tsx | 2 +- .../TranslationsEntitiesListPage.tsx | 2 +- .../TranslationsLanguageList/TranslationsLanguageList.tsx | 2 +- .../TranslationsLanguageListPage.tsx | 2 +- .../TranslationsPagesPage/TranslationsPagesPage.tsx | 2 +- .../TranslationsProductTypesPage.tsx | 2 +- .../TranslationsProductsPage/TranslationsProductsPage.tsx | 2 +- .../TranslationsSalesPage/TranslationsSalesPage.tsx | 2 +- .../TranslationsVouchersPage/TranslationsVouchersPage.tsx | 2 +- src/translations/index.tsx | 2 +- src/translations/urls.ts | 2 +- src/translations/views/TranslationsCategories.tsx | 2 +- src/translations/views/TranslationsCollections.tsx | 2 +- src/translations/views/TranslationsEntities.tsx | 2 +- src/translations/views/TranslationsLanguageList.tsx | 2 +- src/translations/views/TranslationsPages.tsx | 2 +- src/translations/views/TranslationsProductTypes.tsx | 2 +- src/translations/views/TranslationsProducts.tsx | 2 +- src/translations/views/TranslationsSales.tsx | 2 +- src/translations/views/TranslationsVouchers.tsx | 2 +- 532 files changed, 535 insertions(+), 536 deletions(-) diff --git a/src/Baseline.tsx b/src/Baseline.tsx index 5babc7ade..169464771 100644 --- a/src/Baseline.tsx +++ b/src/Baseline.tsx @@ -1,6 +1,6 @@ import CssBaseline from "@material-ui/core/CssBaseline"; import { createStyles, withStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; const styles = createStyles({ "@global": { diff --git a/src/NotFound.tsx b/src/NotFound.tsx index 075e7d4d7..8833b52b7 100644 --- a/src/NotFound.tsx +++ b/src/NotFound.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import NotFoundPage from "./components/NotFoundPage"; import useNavigator from "./hooks/useNavigator"; diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 64da58a5c..147dec6b0 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { getMutationProviderData } from "../misc"; import { PartialMutationProviderOutput } from "../types"; diff --git a/src/auth/components/LoginLoading/LoginLoading.tsx b/src/auth/components/LoginLoading/LoginLoading.tsx index 99ad30b76..f53b921aa 100644 --- a/src/auth/components/LoginLoading/LoginLoading.tsx +++ b/src/auth/components/LoginLoading/LoginLoading.tsx @@ -1,6 +1,6 @@ import CircularProgress from "@material-ui/core/CircularProgress"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; const styles = createStyles({ root: { diff --git a/src/auth/components/LoginPage/LoginPage.tsx b/src/auth/components/LoginPage/LoginPage.tsx index 42860b4ea..148a02d56 100644 --- a/src/auth/components/LoginPage/LoginPage.tsx +++ b/src/auth/components/LoginPage/LoginPage.tsx @@ -7,7 +7,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import SVG from "react-inlinesvg"; import * as backgroundArt from "@assets/images/login-background.svg"; diff --git a/src/auth/components/SectionRoute.tsx b/src/auth/components/SectionRoute.tsx index c5c97d315..a9ec3a493 100644 --- a/src/auth/components/SectionRoute.tsx +++ b/src/auth/components/SectionRoute.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { Route, RouteProps } from "react-router-dom"; import AppLayout from "@saleor/components/AppLayout"; diff --git a/src/auth/index.tsx b/src/auth/index.tsx index a38347d30..5af2ad33e 100644 --- a/src/auth/index.tsx +++ b/src/auth/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { User } from "./types/User"; diff --git a/src/auth/views/Login.tsx b/src/auth/views/Login.tsx index 508f4e8c9..5c61f1269 100644 --- a/src/auth/views/Login.tsx +++ b/src/auth/views/Login.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import LoginPage, { FormData } from "../components/LoginPage"; import { UserContext } from "../index"; diff --git a/src/categories/components/CategoryBackground/CategoryBackground.tsx b/src/categories/components/CategoryBackground/CategoryBackground.tsx index a90674bdc..2dca98b19 100644 --- a/src/categories/components/CategoryBackground/CategoryBackground.tsx +++ b/src/categories/components/CategoryBackground/CategoryBackground.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; diff --git a/src/categories/components/CategoryCreatePage/CategoryCreatePage.tsx b/src/categories/components/CategoryCreatePage/CategoryCreatePage.tsx index 0177f68e2..664d3b2b5 100644 --- a/src/categories/components/CategoryCreatePage/CategoryCreatePage.tsx +++ b/src/categories/components/CategoryCreatePage/CategoryCreatePage.tsx @@ -1,5 +1,5 @@ import { ContentState, convertToRaw, RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import { CardSpacer } from "@saleor/components/CardSpacer"; diff --git a/src/categories/components/CategoryDeleteDialog/CategoryDeleteDialog.tsx b/src/categories/components/CategoryDeleteDialog/CategoryDeleteDialog.tsx index 0dd8a1e29..3fb36f4c8 100644 --- a/src/categories/components/CategoryDeleteDialog/CategoryDeleteDialog.tsx +++ b/src/categories/components/CategoryDeleteDialog/CategoryDeleteDialog.tsx @@ -10,7 +10,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import i18n from "../../../i18n"; diff --git a/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx b/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx index 74e41a6c3..cffbd9579 100644 --- a/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx +++ b/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx @@ -3,7 +3,7 @@ import CardContent from "@material-ui/core/CardContent"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import { RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; diff --git a/src/categories/components/CategoryList/CategoryList.tsx b/src/categories/components/CategoryList/CategoryList.tsx index 232509160..6b8776b8f 100644 --- a/src/categories/components/CategoryList/CategoryList.tsx +++ b/src/categories/components/CategoryList/CategoryList.tsx @@ -11,7 +11,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; diff --git a/src/categories/components/CategoryListPage/CategoryListPage.tsx b/src/categories/components/CategoryListPage/CategoryListPage.tsx index 38acf31eb..e465c255b 100644 --- a/src/categories/components/CategoryListPage/CategoryListPage.tsx +++ b/src/categories/components/CategoryListPage/CategoryListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; diff --git a/src/categories/components/CategoryProducts/CategoryProducts.tsx b/src/categories/components/CategoryProducts/CategoryProducts.tsx index 5f41d6f53..56d7ec5fe 100644 --- a/src/categories/components/CategoryProducts/CategoryProducts.tsx +++ b/src/categories/components/CategoryProducts/CategoryProducts.tsx @@ -12,7 +12,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx b/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx index ca0017123..78f2a2263 100644 --- a/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx +++ b/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import ProductList from "@saleor/components/ProductList"; diff --git a/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx b/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx index 03d242a9f..2b944e305 100644 --- a/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx +++ b/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx @@ -1,5 +1,5 @@ import { RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import { CardSpacer } from "@saleor/components/CardSpacer"; diff --git a/src/categories/index.tsx b/src/categories/index.tsx index 12af20f0a..01178b737 100644 --- a/src/categories/index.tsx +++ b/src/categories/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; import i18n from "../i18n"; diff --git a/src/categories/urls.ts b/src/categories/urls.ts index 09c92f9d2..6d49a8a85 100644 --- a/src/categories/urls.ts +++ b/src/categories/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { ActiveTab, BulkAction, Dialog, Pagination } from "../types"; import { CategoryPageTab } from "./components/CategoryUpdatePage"; diff --git a/src/categories/views/CategoryCreate.tsx b/src/categories/views/CategoryCreate.tsx index 903bd7050..03f42f937 100644 --- a/src/categories/views/CategoryCreate.tsx +++ b/src/categories/views/CategoryCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/categories/views/CategoryDetails.tsx b/src/categories/views/CategoryDetails.tsx index 07e927fca..92d1d1c60 100644 --- a/src/categories/views/CategoryDetails.tsx +++ b/src/categories/views/CategoryDetails.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/categories/views/CategoryList.tsx b/src/categories/views/CategoryList.tsx index 943476702..ee0e15d6e 100644 --- a/src/categories/views/CategoryList.tsx +++ b/src/categories/views/CategoryList.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx b/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx index 46cc82bf0..78e92fa01 100644 --- a/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx +++ b/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import { ContentState, convertToRaw, RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import { CardSpacer } from "@saleor/components/CardSpacer"; diff --git a/src/collections/components/CollectionDetails/CollectionDetails.tsx b/src/collections/components/CollectionDetails/CollectionDetails.tsx index 180342c9f..db44e3ccb 100644 --- a/src/collections/components/CollectionDetails/CollectionDetails.tsx +++ b/src/collections/components/CollectionDetails/CollectionDetails.tsx @@ -3,7 +3,7 @@ import CardContent from "@material-ui/core/CardContent"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import { RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; diff --git a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx index a974ae6ad..9c97df6ea 100644 --- a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx +++ b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx @@ -1,5 +1,5 @@ import { RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import { CardSpacer } from "@saleor/components/CardSpacer"; diff --git a/src/collections/components/CollectionImage/CollectionImage.tsx b/src/collections/components/CollectionImage/CollectionImage.tsx index f2866483f..05758727c 100644 --- a/src/collections/components/CollectionImage/CollectionImage.tsx +++ b/src/collections/components/CollectionImage/CollectionImage.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; diff --git a/src/collections/components/CollectionList/CollectionList.tsx b/src/collections/components/CollectionList/CollectionList.tsx index ce8b61550..6a4b24d9c 100644 --- a/src/collections/components/CollectionList/CollectionList.tsx +++ b/src/collections/components/CollectionList/CollectionList.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/collections/components/CollectionListPage/CollectionListPage.tsx b/src/collections/components/CollectionListPage/CollectionListPage.tsx index 4fd39fe3c..d5580c66e 100644 --- a/src/collections/components/CollectionListPage/CollectionListPage.tsx +++ b/src/collections/components/CollectionListPage/CollectionListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import { Container } from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; diff --git a/src/collections/components/CollectionProducts/CollectionProducts.tsx b/src/collections/components/CollectionProducts/CollectionProducts.tsx index fc8a420df..977c1ed45 100644 --- a/src/collections/components/CollectionProducts/CollectionProducts.tsx +++ b/src/collections/components/CollectionProducts/CollectionProducts.tsx @@ -13,7 +13,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; diff --git a/src/collections/containers/CollectionOperations.tsx b/src/collections/containers/CollectionOperations.tsx index a9f3d8c84..694d85915 100644 --- a/src/collections/containers/CollectionOperations.tsx +++ b/src/collections/containers/CollectionOperations.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { getMutationProviderData } from "../../misc"; import { PartialMutationProviderOutput } from "../../types"; diff --git a/src/collections/index.tsx b/src/collections/index.tsx index 9706de5ce..75a11dd04 100644 --- a/src/collections/index.tsx +++ b/src/collections/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/collections/urls.ts b/src/collections/urls.ts index 305fadbee..e6770071e 100644 --- a/src/collections/urls.ts +++ b/src/collections/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { BulkAction, Dialog, Pagination } from "../types"; diff --git a/src/collections/views/CollectionCreate.tsx b/src/collections/views/CollectionCreate.tsx index 24c4eb73f..29c1de5c6 100644 --- a/src/collections/views/CollectionCreate.tsx +++ b/src/collections/views/CollectionCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/collections/views/CollectionDetails.tsx b/src/collections/views/CollectionDetails.tsx index f9e8c7a3c..26cb4b48e 100644 --- a/src/collections/views/CollectionDetails.tsx +++ b/src/collections/views/CollectionDetails.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import AssignProductDialog from "@saleor/components/AssignProductDialog"; diff --git a/src/collections/views/CollectionList.tsx b/src/collections/views/CollectionList.tsx index 4914dbbaf..008fcfb4d 100644 --- a/src/collections/views/CollectionList.tsx +++ b/src/collections/views/CollectionList.tsx @@ -2,7 +2,7 @@ import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/components/ActionDialog/ActionDialog.tsx b/src/components/ActionDialog/ActionDialog.tsx index ef4e4786f..6387d4ec6 100644 --- a/src/components/ActionDialog/ActionDialog.tsx +++ b/src/components/ActionDialog/ActionDialog.tsx @@ -10,7 +10,7 @@ import { WithStyles } from "@material-ui/core/styles"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import ConfirmButton, { diff --git a/src/components/AddressEdit/AddressEdit.tsx b/src/components/AddressEdit/AddressEdit.tsx index 0e93fd823..428340604 100644 --- a/src/components/AddressEdit/AddressEdit.tsx +++ b/src/components/AddressEdit/AddressEdit.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import { AddressTypeInput } from "../../customers/types"; import i18n from "../../i18n"; diff --git a/src/components/AddressFormatter/AddressFormatter.tsx b/src/components/AddressFormatter/AddressFormatter.tsx index db16de1a4..fa9efcc26 100644 --- a/src/components/AddressFormatter/AddressFormatter.tsx +++ b/src/components/AddressFormatter/AddressFormatter.tsx @@ -1,5 +1,5 @@ import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import { AddressType } from "../../customers/types"; import Skeleton from "../Skeleton"; diff --git a/src/components/AppHeader/AppHeader.tsx b/src/components/AppHeader/AppHeader.tsx index 1cd32ed5a..3c21e7f82 100644 --- a/src/components/AppHeader/AppHeader.tsx +++ b/src/components/AppHeader/AppHeader.tsx @@ -7,7 +7,7 @@ import { } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import ArrowBackIcon from "@material-ui/icons/ArrowBack"; -import * as React from "react"; +import React from "react"; import AppHeaderContext from "../AppLayout/AppHeaderContext"; import Skeleton from "../Skeleton"; diff --git a/src/components/AppLayout/AppActionContext.tsx b/src/components/AppLayout/AppActionContext.tsx index 31e3de50d..6083d883b 100644 --- a/src/components/AppLayout/AppActionContext.tsx +++ b/src/components/AppLayout/AppActionContext.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; const AppActionContext = React.createContext>( undefined diff --git a/src/components/AppLayout/AppHeaderContext.tsx b/src/components/AppLayout/AppHeaderContext.tsx index 73255b665..902655496 100644 --- a/src/components/AppLayout/AppHeaderContext.tsx +++ b/src/components/AppLayout/AppHeaderContext.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; const AppHeaderContext = React.createContext>( undefined diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index ed7759580..35aaf7983 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -13,7 +13,7 @@ import { WithStyles } from "@material-ui/core/styles"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import SVG from "react-inlinesvg"; import { RouteComponentProps, withRouter } from "react-router"; diff --git a/src/components/AppLayout/MenuList.tsx b/src/components/AppLayout/MenuList.tsx index b875a8a9f..994e0225d 100644 --- a/src/components/AppLayout/MenuList.tsx +++ b/src/components/AppLayout/MenuList.tsx @@ -6,7 +6,7 @@ import { } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import { matchPath } from "react-router"; import { User } from "../../auth/types/User"; diff --git a/src/components/AppLayout/MenuNested.tsx b/src/components/AppLayout/MenuNested.tsx index 44f69ed88..afa63fd12 100644 --- a/src/components/AppLayout/MenuNested.tsx +++ b/src/components/AppLayout/MenuNested.tsx @@ -1,6 +1,6 @@ import Typography from "@material-ui/core/Typography"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import { User } from "../../auth/types/User"; import MenuList from "./MenuList"; diff --git a/src/components/AppLayout/ResponsiveDrawer.tsx b/src/components/AppLayout/ResponsiveDrawer.tsx index 224149999..49fac57c1 100644 --- a/src/components/AppLayout/ResponsiveDrawer.tsx +++ b/src/components/AppLayout/ResponsiveDrawer.tsx @@ -6,7 +6,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import { drawerWidth } from "./consts"; const styles = (theme: Theme) => diff --git a/src/components/AppLayout/ThemeSwitch.tsx b/src/components/AppLayout/ThemeSwitch.tsx index 4292f1c73..17112caf2 100644 --- a/src/components/AppLayout/ThemeSwitch.tsx +++ b/src/components/AppLayout/ThemeSwitch.tsx @@ -1,6 +1,6 @@ import { Theme, withStyles } from "@material-ui/core/styles"; import Switch, { SwitchProps } from "@material-ui/core/Switch"; -import * as React from "react"; +import React from "react"; import MoonIcon from "../../icons/Moon"; import SunIcon from "../../icons/Sun"; diff --git a/src/components/AppProgress/index.tsx b/src/components/AppProgress/index.tsx index 7be0c2132..27cc6f67f 100644 --- a/src/components/AppProgress/index.tsx +++ b/src/components/AppProgress/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; interface IAppProgressContext { isProgress: boolean; diff --git a/src/components/AssignCategoryDialog/AssignCategoryDialog.tsx b/src/components/AssignCategoryDialog/AssignCategoryDialog.tsx index 656c63905..a272a7da8 100644 --- a/src/components/AssignCategoryDialog/AssignCategoryDialog.tsx +++ b/src/components/AssignCategoryDialog/AssignCategoryDialog.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/components/AssignCollectionDialog/AssignCollectionDialog.tsx b/src/components/AssignCollectionDialog/AssignCollectionDialog.tsx index bd4cd2dbd..6d0780427 100644 --- a/src/components/AssignCollectionDialog/AssignCollectionDialog.tsx +++ b/src/components/AssignCollectionDialog/AssignCollectionDialog.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import { SearchCollections_collections_edges_node } from "../../containers/SearchCollections/types/SearchCollections"; import i18n from "../../i18n"; diff --git a/src/components/AssignProductDialog/AssignProductDialog.tsx b/src/components/AssignProductDialog/AssignProductDialog.tsx index a675a6d47..9b975d681 100644 --- a/src/components/AssignProductDialog/AssignProductDialog.tsx +++ b/src/components/AssignProductDialog/AssignProductDialog.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/components/AutocompleteSelectMenu/AutocompleteSelectMenu.tsx b/src/components/AutocompleteSelectMenu/AutocompleteSelectMenu.tsx index d00ca186f..20a458e5f 100644 --- a/src/components/AutocompleteSelectMenu/AutocompleteSelectMenu.tsx +++ b/src/components/AutocompleteSelectMenu/AutocompleteSelectMenu.tsx @@ -10,7 +10,7 @@ import { import TextField from "@material-ui/core/TextField"; import ArrowBack from "@material-ui/icons/ArrowBack"; import Downshift from "downshift"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import { diff --git a/src/components/CardMenu/CardMenu.tsx b/src/components/CardMenu/CardMenu.tsx index 4135ce418..1ce0e9c41 100644 --- a/src/components/CardMenu/CardMenu.tsx +++ b/src/components/CardMenu/CardMenu.tsx @@ -8,7 +8,7 @@ import { WithStyles } from "@material-ui/core/styles"; import MoreVertIcon from "@material-ui/icons/MoreVert"; -import * as React from "react"; +import React from "react"; const ITEM_HEIGHT = 48; diff --git a/src/components/CardSpacer.tsx b/src/components/CardSpacer.tsx index 98d70f95a..9f205b573 100644 --- a/src/components/CardSpacer.tsx +++ b/src/components/CardSpacer.tsx @@ -4,7 +4,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/CardTitle/CardTitle.tsx b/src/components/CardTitle/CardTitle.tsx index 92c061f0a..a800a752b 100644 --- a/src/components/CardTitle/CardTitle.tsx +++ b/src/components/CardTitle/CardTitle.tsx @@ -6,7 +6,7 @@ import { } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index a82a63d7c..2a715c8e1 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -8,7 +8,7 @@ import { WithStyles } from "@material-ui/core/styles"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import { stopPropagation } from "../../misc"; export type CheckboxProps = Omit< diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index f003b16b0..535861af7 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -8,7 +8,7 @@ import { fade } from "@material-ui/core/styles/colorManipulator"; import Typography from "@material-ui/core/Typography"; import CloseIcon from "@material-ui/icons/Close"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; export interface ChipProps { className?: string; diff --git a/src/components/ConfirmButton/ConfirmButton.tsx b/src/components/ConfirmButton/ConfirmButton.tsx index b1a1c7b91..07185213a 100644 --- a/src/components/ConfirmButton/ConfirmButton.tsx +++ b/src/components/ConfirmButton/ConfirmButton.tsx @@ -9,7 +9,7 @@ import { } from "@material-ui/core/styles"; import CheckIcon from "@material-ui/icons/Check"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; diff --git a/src/components/ConfirmFormLeaveDialog.tsx b/src/components/ConfirmFormLeaveDialog.tsx index 41bce42b8..4c5334a26 100644 --- a/src/components/ConfirmFormLeaveDialog.tsx +++ b/src/components/ConfirmFormLeaveDialog.tsx @@ -10,7 +10,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import NavigationPrompt from "react-router-navigation-prompt"; import i18n from "../i18n"; diff --git a/src/components/Container.tsx b/src/components/Container.tsx index 57d552d01..3acc3a477 100644 --- a/src/components/Container.tsx +++ b/src/components/Container.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/ControlledCheckbox.tsx b/src/components/ControlledCheckbox.tsx index 6e948e6f9..8ce485c10 100644 --- a/src/components/ControlledCheckbox.tsx +++ b/src/components/ControlledCheckbox.tsx @@ -1,5 +1,5 @@ import FormControlLabel from "@material-ui/core/FormControlLabel"; -import * as React from "react"; +import React from "react"; import Checkbox from "./Checkbox"; diff --git a/src/components/ControlledSwitch.tsx b/src/components/ControlledSwitch.tsx index 6b0b341fd..87b308ba2 100644 --- a/src/components/ControlledSwitch.tsx +++ b/src/components/ControlledSwitch.tsx @@ -1,7 +1,7 @@ import { createStyles, Theme, withStyles, WithStyles } from "@material-ui/core"; import FormControlLabel from "@material-ui/core/FormControlLabel"; import Switch from "@material-ui/core/Switch"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/CountryList/CountryList.tsx b/src/components/CountryList/CountryList.tsx index 014a7db66..ff0893a96 100644 --- a/src/components/CountryList/CountryList.tsx +++ b/src/components/CountryList/CountryList.tsx @@ -15,7 +15,7 @@ import TableRow from "@material-ui/core/TableRow"; import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; import DeleteIcon from "@material-ui/icons/Delete"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/components/Date/Date.test.tsx b/src/components/Date/Date.test.tsx index a1d233515..738b89fbf 100644 --- a/src/components/Date/Date.test.tsx +++ b/src/components/Date/Date.test.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import * as renderer from "react-test-renderer"; import { TimezoneProvider } from "../Timezone"; diff --git a/src/components/Date/Date.tsx b/src/components/Date/Date.tsx index f72d7a3be..a50a4e909 100644 --- a/src/components/Date/Date.tsx +++ b/src/components/Date/Date.tsx @@ -1,6 +1,6 @@ import Tooltip from "@material-ui/core/Tooltip"; import * as moment from "moment-timezone"; -import * as React from "react"; +import React from "react"; import useDateLocalize from "@saleor/hooks/useDateLocalize"; import { LocaleConsumer } from "../Locale"; diff --git a/src/components/Date/DateContext.tsx b/src/components/Date/DateContext.tsx index 449f1bc13..5e404caa0 100644 --- a/src/components/Date/DateContext.tsx +++ b/src/components/Date/DateContext.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; export const DateContext = React.createContext(undefined); const { Provider, Consumer } = DateContext; diff --git a/src/components/Date/DateProvider.tsx b/src/components/Date/DateProvider.tsx index c54f779ce..90226eb0f 100644 --- a/src/components/Date/DateProvider.tsx +++ b/src/components/Date/DateProvider.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { Provider } from "./DateContext"; diff --git a/src/components/Date/DateTime.tsx b/src/components/Date/DateTime.tsx index c3199ce7c..f9c24220b 100644 --- a/src/components/Date/DateTime.tsx +++ b/src/components/Date/DateTime.tsx @@ -1,6 +1,6 @@ import Tooltip from "@material-ui/core/Tooltip"; import * as moment from "moment-timezone"; -import * as React from "react"; +import React from "react"; import ReactMoment from "react-moment"; import { LocaleConsumer } from "../Locale"; diff --git a/src/components/Debounce.tsx b/src/components/Debounce.tsx index 533c52aba..5d5c67571 100644 --- a/src/components/Debounce.tsx +++ b/src/components/Debounce.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; export interface DebounceProps { children: ((props: (...args: T[]) => void) => React.ReactNode); diff --git a/src/components/DebounceForm.tsx b/src/components/DebounceForm.tsx index 0742aa50b..7c8473695 100644 --- a/src/components/DebounceForm.tsx +++ b/src/components/DebounceForm.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import Debounce from "./Debounce"; export interface DebounceFormProps { diff --git a/src/components/DeleteFilterTabDialog/DeleteFilterTabDialog.tsx b/src/components/DeleteFilterTabDialog/DeleteFilterTabDialog.tsx index aa4fb9125..651304cce 100644 --- a/src/components/DeleteFilterTabDialog/DeleteFilterTabDialog.tsx +++ b/src/components/DeleteFilterTabDialog/DeleteFilterTabDialog.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import ActionDialog from "../ActionDialog"; diff --git a/src/components/DraftRenderer.tsx b/src/components/DraftRenderer.tsx index d6fcd508f..052040aa3 100644 --- a/src/components/DraftRenderer.tsx +++ b/src/components/DraftRenderer.tsx @@ -1,6 +1,6 @@ import { RawDraftContentState } from "draft-js"; import * as draftToHtml from "draftjs-to-html"; -import * as React from "react"; +import React from "react"; interface DraftRendererProps { content: RawDraftContentState; diff --git a/src/components/EditableTableCell/EditableTableCell.tsx b/src/components/EditableTableCell/EditableTableCell.tsx index 7438e0762..17db49a77 100644 --- a/src/components/EditableTableCell/EditableTableCell.tsx +++ b/src/components/EditableTableCell/EditableTableCell.tsx @@ -10,7 +10,7 @@ import TableCell from "@material-ui/core/TableCell"; import TextField, { TextFieldProps } from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import Form from "@saleor/components/Form"; diff --git a/src/components/ErrorMessageCard/ErrorMessageCard.tsx b/src/components/ErrorMessageCard/ErrorMessageCard.tsx index ed64be6f2..f3d4ed6ab 100644 --- a/src/components/ErrorMessageCard/ErrorMessageCard.tsx +++ b/src/components/ErrorMessageCard/ErrorMessageCard.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; diff --git a/src/components/ErrorPage/ErrorPage.tsx b/src/components/ErrorPage/ErrorPage.tsx index 299613e9b..f6046e2f0 100644 --- a/src/components/ErrorPage/ErrorPage.tsx +++ b/src/components/ErrorPage/ErrorPage.tsx @@ -6,7 +6,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import SVG from "react-inlinesvg"; import * as notFoundImage from "@assets/images/what.svg"; diff --git a/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx b/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx index 52affe67f..c70ead68d 100644 --- a/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx +++ b/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/ExternalLink/ExternalLink.tsx b/src/components/ExternalLink/ExternalLink.tsx index d3b64db1d..3255f54ea 100644 --- a/src/components/ExternalLink/ExternalLink.tsx +++ b/src/components/ExternalLink/ExternalLink.tsx @@ -1,6 +1,6 @@ import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import Typography, { TypographyProps } from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; const styles = createStyles({ link: { diff --git a/src/components/FileUpload/FileUpload.tsx b/src/components/FileUpload/FileUpload.tsx index ecccddd38..6d07f8fa4 100644 --- a/src/components/FileUpload/FileUpload.tsx +++ b/src/components/FileUpload/FileUpload.tsx @@ -1,7 +1,7 @@ import Button from "@material-ui/core/Button"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; const styles = createStyles({ diff --git a/src/components/Filter/Filter.tsx b/src/components/Filter/Filter.tsx index 8e31b8ea4..fbf862ad3 100644 --- a/src/components/Filter/Filter.tsx +++ b/src/components/Filter/Filter.tsx @@ -12,7 +12,7 @@ import { fade } from "@material-ui/core/styles/colorManipulator"; import Typography from "@material-ui/core/Typography"; import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import { FilterContent } from "."; import i18n from "../../i18n"; diff --git a/src/components/Filter/FilterContent.tsx b/src/components/Filter/FilterContent.tsx index 3822d8cdb..7df9ce86c 100644 --- a/src/components/Filter/FilterContent.tsx +++ b/src/components/Filter/FilterContent.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import { makeStyles } from "@material-ui/styles"; import i18n from "../../i18n"; diff --git a/src/components/Filter/FilterElement.tsx b/src/components/Filter/FilterElement.tsx index 590ebffff..202aab160 100644 --- a/src/components/Filter/FilterElement.tsx +++ b/src/components/Filter/FilterElement.tsx @@ -1,7 +1,7 @@ import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import { makeStyles } from "@material-ui/styles"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import Calendar from "../../icons/Calendar"; diff --git a/src/components/FilterBar/FilterBar.tsx b/src/components/FilterBar/FilterBar.tsx index e8a75a229..db92026cc 100644 --- a/src/components/FilterBar/FilterBar.tsx +++ b/src/components/FilterBar/FilterBar.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import { FilterProps } from "../../types"; diff --git a/src/components/FilterCard/FilterCard.tsx b/src/components/FilterCard/FilterCard.tsx index 2a03d0fa8..30d18c6a8 100644 --- a/src/components/FilterCard/FilterCard.tsx +++ b/src/components/FilterCard/FilterCard.tsx @@ -3,7 +3,7 @@ import CardContent from "@material-ui/core/CardContent"; import CardHeader from "@material-ui/core/CardHeader"; import IconButton from "@material-ui/core/IconButton"; import RefreshIcon from "@material-ui/icons/Refresh"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx index 7b19a244b..306348168 100644 --- a/src/components/Form/Form.tsx +++ b/src/components/Form/Form.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { UserError } from "../../types"; export interface FormProps { diff --git a/src/components/Form/FormActions.tsx b/src/components/Form/FormActions.tsx index 3a1a49a6f..a6a26bf53 100644 --- a/src/components/Form/FormActions.tsx +++ b/src/components/Form/FormActions.tsx @@ -1,7 +1,7 @@ import Button from "@material-ui/core/Button"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import Toolbar from "@material-ui/core/Toolbar"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; diff --git a/src/components/Form/FormContext.tsx b/src/components/Form/FormContext.tsx index b8d73029c..4ef3b8101 100644 --- a/src/components/Form/FormContext.tsx +++ b/src/components/Form/FormContext.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import FormComponent, { FormProps } from "./Form"; diff --git a/src/components/FormSpacer.tsx b/src/components/FormSpacer.tsx index 3401eb6cf..0ab94843d 100644 --- a/src/components/FormSpacer.tsx +++ b/src/components/FormSpacer.tsx @@ -4,7 +4,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/Grid/Grid.tsx b/src/components/Grid/Grid.tsx index b5cc6664c..31a58cd47 100644 --- a/src/components/Grid/Grid.tsx +++ b/src/components/Grid/Grid.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; export type GridVariant = "default" | "inverted" | "uniform"; export interface GridProps extends WithStyles { diff --git a/src/components/Hr.tsx b/src/components/Hr.tsx index 458434005..63694a14b 100644 --- a/src/components/Hr.tsx +++ b/src/components/Hr.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; interface HrProps { className?: string; diff --git a/src/components/IconButtonTableCell/IconButtonTableCell.tsx b/src/components/IconButtonTableCell/IconButtonTableCell.tsx index 558891746..25bf1fed2 100644 --- a/src/components/IconButtonTableCell/IconButtonTableCell.tsx +++ b/src/components/IconButtonTableCell/IconButtonTableCell.tsx @@ -6,7 +6,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TableCell from "@material-ui/core/TableCell"; -import * as React from "react"; +import React from "react"; import { stopPropagation } from "../../misc"; import { ICONBUTTON_SIZE } from "../../theme"; diff --git a/src/components/ImageTile/ImageTile.tsx b/src/components/ImageTile/ImageTile.tsx index deb2c8f93..e1f320b4e 100644 --- a/src/components/ImageTile/ImageTile.tsx +++ b/src/components/ImageTile/ImageTile.tsx @@ -4,7 +4,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; diff --git a/src/components/ImageUpload/ImageUpload.tsx b/src/components/ImageUpload/ImageUpload.tsx index f6d75eb4d..92ae65717 100644 --- a/src/components/ImageUpload/ImageUpload.tsx +++ b/src/components/ImageUpload/ImageUpload.tsx @@ -7,7 +7,7 @@ import { import { fade } from "@material-ui/core/styles/colorManipulator"; import Typography from "@material-ui/core/Typography"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import ImageIcon from "../../icons/Image"; diff --git a/src/components/LanguageSwitch/LanguageSwitch.tsx b/src/components/LanguageSwitch/LanguageSwitch.tsx index 1ec7e5a5b..c0aa61af2 100644 --- a/src/components/LanguageSwitch/LanguageSwitch.tsx +++ b/src/components/LanguageSwitch/LanguageSwitch.tsx @@ -14,7 +14,7 @@ import { import Typography from "@material-ui/core/Typography"; import ArrowDropDown from "@material-ui/icons/ArrowDropDown"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import { LanguageCodeEnum } from "../../types/globalTypes"; diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 6ee63d192..11f5a79e8 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -6,7 +6,7 @@ import { } from "@material-ui/core/styles"; import Typography, { TypographyProps } from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/ListField/ListField.tsx b/src/components/ListField/ListField.tsx index 67ffc58c9..ff05b84b6 100644 --- a/src/components/ListField/ListField.tsx +++ b/src/components/ListField/ListField.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField, { StandardTextFieldProps } from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import Chip from "../Chip"; diff --git a/src/components/Locale/Locale.tsx b/src/components/Locale/Locale.tsx index 05c19a513..d1d4fd023 100644 --- a/src/components/Locale/Locale.tsx +++ b/src/components/Locale/Locale.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; export const LocaleContext = React.createContext("en"); diff --git a/src/components/MenuToggle.tsx b/src/components/MenuToggle.tsx index 54ed5548e..dc27ecccf 100644 --- a/src/components/MenuToggle.tsx +++ b/src/components/MenuToggle.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; interface MenuToggleProps { ariaOwns?: string; diff --git a/src/components/Money/Money.tsx b/src/components/Money/Money.tsx index 62ba7aaaa..164a34af9 100644 --- a/src/components/Money/Money.tsx +++ b/src/components/Money/Money.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { LocaleConsumer } from "../Locale"; diff --git a/src/components/MoneyRange/MoneyRange.tsx b/src/components/MoneyRange/MoneyRange.tsx index c2d1ac103..afcb8a564 100644 --- a/src/components/MoneyRange/MoneyRange.tsx +++ b/src/components/MoneyRange/MoneyRange.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import { LocaleConsumer } from "../Locale"; diff --git a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx index ed8d7452f..0c177fc5e 100644 --- a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx +++ b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx @@ -9,7 +9,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Downshift, { ControllerStateAndHelpers } from "downshift"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import ArrowDropdownIcon from "../../icons/ArrowDropdown"; diff --git a/src/components/MultiSelectField/MultiSelectField.tsx b/src/components/MultiSelectField/MultiSelectField.tsx index 4d11946b7..5fdda787c 100644 --- a/src/components/MultiSelectField/MultiSelectField.tsx +++ b/src/components/MultiSelectField/MultiSelectField.tsx @@ -10,7 +10,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import Checkbox from "../Checkbox"; diff --git a/src/components/NotFoundPage/NotFoundPage.tsx b/src/components/NotFoundPage/NotFoundPage.tsx index 6e51e86a1..73064000a 100644 --- a/src/components/NotFoundPage/NotFoundPage.tsx +++ b/src/components/NotFoundPage/NotFoundPage.tsx @@ -6,7 +6,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import SVG from "react-inlinesvg"; import * as notFoundImage from "@assets/images/not-found-404.svg"; diff --git a/src/components/PageHeader/PageHeader.tsx b/src/components/PageHeader/PageHeader.tsx index 8c20e8186..1fc53ec1c 100644 --- a/src/components/PageHeader/PageHeader.tsx +++ b/src/components/PageHeader/PageHeader.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import ExtendedPageHeader from "../ExtendedPageHeader"; import Skeleton from "../Skeleton"; diff --git a/src/components/Percent/Percent.tsx b/src/components/Percent/Percent.tsx index dc34ff8a9..ee6ed35e3 100644 --- a/src/components/Percent/Percent.tsx +++ b/src/components/Percent/Percent.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { LocaleConsumer } from "../Locale"; diff --git a/src/components/PhoneField/PhoneField.tsx b/src/components/PhoneField/PhoneField.tsx index 800b8affb..0c0267864 100644 --- a/src/components/PhoneField/PhoneField.tsx +++ b/src/components/PhoneField/PhoneField.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import SingleSelectField from "@saleor/components/SingleSelectField"; diff --git a/src/components/PriceField/PriceField.tsx b/src/components/PriceField/PriceField.tsx index 684da90e2..847ac055d 100644 --- a/src/components/PriceField/PriceField.tsx +++ b/src/components/PriceField/PriceField.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/ProductList/ProductList.tsx b/src/components/ProductList/ProductList.tsx index 4b760b0d5..ccbe45ffe 100644 --- a/src/components/ProductList/ProductList.tsx +++ b/src/components/ProductList/ProductList.tsx @@ -9,7 +9,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import TableCellAvatar from "@saleor/components/TableCellAvatar"; import { CategoryDetails_category_products_edges_node } from "../../categories/types/CategoryDetails"; diff --git a/src/components/RichTextEditor/ImageEntity.tsx b/src/components/RichTextEditor/ImageEntity.tsx index b676114a5..4bf9721c0 100644 --- a/src/components/RichTextEditor/ImageEntity.tsx +++ b/src/components/RichTextEditor/ImageEntity.tsx @@ -12,7 +12,7 @@ import { } from "@material-ui/core/styles"; import DeleteIcon from "@material-ui/icons/Delete"; import { ContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; diff --git a/src/components/RichTextEditor/ImageSource.tsx b/src/components/RichTextEditor/ImageSource.tsx index 698c846ab..d238bc09f 100644 --- a/src/components/RichTextEditor/ImageSource.tsx +++ b/src/components/RichTextEditor/ImageSource.tsx @@ -5,7 +5,7 @@ import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; import { AtomicBlockUtils, EditorState, EntityInstance } from "draft-js"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import Form from "../Form"; diff --git a/src/components/RichTextEditor/LinkEntity.tsx b/src/components/RichTextEditor/LinkEntity.tsx index c84c205da..ac8080b43 100644 --- a/src/components/RichTextEditor/LinkEntity.tsx +++ b/src/components/RichTextEditor/LinkEntity.tsx @@ -13,7 +13,7 @@ import { import Typography from "@material-ui/core/Typography"; import DeleteIcon from "@material-ui/icons/Delete"; import { ContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import Link from "../Link"; diff --git a/src/components/RichTextEditor/LinkSource.tsx b/src/components/RichTextEditor/LinkSource.tsx index 3b639fc9a..bbf17eda0 100644 --- a/src/components/RichTextEditor/LinkSource.tsx +++ b/src/components/RichTextEditor/LinkSource.tsx @@ -5,7 +5,7 @@ import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; import { EditorState, EntityInstance, RichUtils } from "draft-js"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import Form from "../Form"; diff --git a/src/components/RichTextEditor/RichTextEditor.tsx b/src/components/RichTextEditor/RichTextEditor.tsx index d9a650191..f5718e513 100644 --- a/src/components/RichTextEditor/RichTextEditor.tsx +++ b/src/components/RichTextEditor/RichTextEditor.tsx @@ -14,7 +14,7 @@ import { ENTITY_TYPE, INLINE_STYLE } from "draftail"; -import * as React from "react"; +import React from "react"; import BoldIcon from "../../icons/BoldIcon"; import HeaderOne from "../../icons/HeaderOne"; diff --git a/src/components/SaveButtonBar/SaveButtonBar.tsx b/src/components/SaveButtonBar/SaveButtonBar.tsx index 4fb7a8bfc..8ed381f20 100644 --- a/src/components/SaveButtonBar/SaveButtonBar.tsx +++ b/src/components/SaveButtonBar/SaveButtonBar.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import useScroll from "@saleor/hooks/useScroll"; import i18n from "../../i18n"; diff --git a/src/components/SaveFilterTabDialog/SaveFilterTabDialog.tsx b/src/components/SaveFilterTabDialog/SaveFilterTabDialog.tsx index 0b257873d..5b26158f3 100644 --- a/src/components/SaveFilterTabDialog/SaveFilterTabDialog.tsx +++ b/src/components/SaveFilterTabDialog/SaveFilterTabDialog.tsx @@ -4,7 +4,7 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import ConfirmButton, { ConfirmButtonTransitionState } from "../ConfirmButton"; diff --git a/src/components/SeoForm/SeoForm.tsx b/src/components/SeoForm/SeoForm.tsx index c4af34cdc..1aceab7c4 100644 --- a/src/components/SeoForm/SeoForm.tsx +++ b/src/components/SeoForm/SeoForm.tsx @@ -10,7 +10,7 @@ import { import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import CardTitle from "../CardTitle"; diff --git a/src/components/Shop/index.tsx b/src/components/Shop/index.tsx index 1c3a017c2..5c9e59799 100644 --- a/src/components/Shop/index.tsx +++ b/src/components/Shop/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { TypedShopInfoQuery } from "./query"; import { ShopInfo_shop } from "./types/ShopInfo"; diff --git a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx index 3d798cf65..74c47c9db 100644 --- a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx +++ b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx @@ -10,7 +10,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Downshift from "downshift"; -import * as React from "react"; +import React from "react"; import { compareTwoStrings } from "string-similarity"; import i18n from "../../i18n"; diff --git a/src/components/SingleSelectField/SingleSelectField.tsx b/src/components/SingleSelectField/SingleSelectField.tsx index 695c29784..22ee50c97 100644 --- a/src/components/SingleSelectField/SingleSelectField.tsx +++ b/src/components/SingleSelectField/SingleSelectField.tsx @@ -6,7 +6,7 @@ import MenuItem from "@material-ui/core/MenuItem"; import Select, { SelectProps } from "@material-ui/core/Select"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; diff --git a/src/components/Skeleton.tsx b/src/components/Skeleton.tsx index ef5d8b7ec..5bcefc194 100644 --- a/src/components/Skeleton.tsx +++ b/src/components/Skeleton.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/StatusLabel/StatusLabel.tsx b/src/components/StatusLabel/StatusLabel.tsx index f3be842da..4d5f979d2 100644 --- a/src/components/StatusLabel/StatusLabel.tsx +++ b/src/components/StatusLabel/StatusLabel.tsx @@ -7,7 +7,7 @@ import { } from "@material-ui/core/styles"; import Typography, { TypographyProps } from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => { const dot = { diff --git a/src/components/Tab/Tab.tsx b/src/components/Tab/Tab.tsx index 1b74444e5..9e7fa1f58 100644 --- a/src/components/Tab/Tab.tsx +++ b/src/components/Tab/Tab.tsx @@ -6,7 +6,7 @@ import { } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/Tab/TabContainer.tsx b/src/components/Tab/TabContainer.tsx index b266e8929..e7562069f 100644 --- a/src/components/Tab/TabContainer.tsx +++ b/src/components/Tab/TabContainer.tsx @@ -4,7 +4,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; export interface TabContainerProps { children: React.ReactNode | React.ReactNodeArray; diff --git a/src/components/Tab/Tabs.tsx b/src/components/Tab/Tabs.tsx index 5eb09a3ff..429553019 100644 --- a/src/components/Tab/Tabs.tsx +++ b/src/components/Tab/Tabs.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; export interface TabsProps { children: ( diff --git a/src/components/TableCellAvatar/TableCellAvatar.tsx b/src/components/TableCellAvatar/TableCellAvatar.tsx index 369694ff9..29e5d7c73 100644 --- a/src/components/TableCellAvatar/TableCellAvatar.tsx +++ b/src/components/TableCellAvatar/TableCellAvatar.tsx @@ -8,7 +8,7 @@ import { import TableCell from "@material-ui/core/TableCell"; import Cached from "@material-ui/icons/Cached"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import Image from "../../icons/Image"; diff --git a/src/components/TableFilter/FilterChips.tsx b/src/components/TableFilter/FilterChips.tsx index a5101de02..f75a77d83 100644 --- a/src/components/TableFilter/FilterChips.tsx +++ b/src/components/TableFilter/FilterChips.tsx @@ -5,7 +5,7 @@ import TextField, { TextFieldProps } from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import ClearIcon from "@material-ui/icons/Clear"; import { createStyles, makeStyles, useTheme } from "@material-ui/styles"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import Filter, { FilterContentSubmitData, IFilter } from "../Filter"; diff --git a/src/components/TableFilter/FilterTab.tsx b/src/components/TableFilter/FilterTab.tsx index e56e77fc5..462baf478 100644 --- a/src/components/TableFilter/FilterTab.tsx +++ b/src/components/TableFilter/FilterTab.tsx @@ -6,7 +6,7 @@ import { } from "@material-ui/core/styles"; import Tab from "@material-ui/core/Tab"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/TableFilter/FilterTabs.tsx b/src/components/TableFilter/FilterTabs.tsx index 380f6ca8f..ca89529ec 100644 --- a/src/components/TableFilter/FilterTabs.tsx +++ b/src/components/TableFilter/FilterTabs.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Tabs from "@material-ui/core/Tabs"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/TableHead/TableHead.tsx b/src/components/TableHead/TableHead.tsx index 5e65c6a5d..92f84e5ef 100644 --- a/src/components/TableHead/TableHead.tsx +++ b/src/components/TableHead/TableHead.tsx @@ -12,7 +12,7 @@ import MuiTableHead, { import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import { Node } from "../../types"; diff --git a/src/components/TablePagination/TablePagination.tsx b/src/components/TablePagination/TablePagination.tsx index 93d149baa..b5e9553f3 100644 --- a/src/components/TablePagination/TablePagination.tsx +++ b/src/components/TablePagination/TablePagination.tsx @@ -9,7 +9,7 @@ import { } from "@material-ui/core/styles"; import TableCell from "@material-ui/core/TableCell"; import Toolbar from "@material-ui/core/Toolbar"; -import * as React from "react"; +import React from "react"; import TablePaginationActions from "./TablePaginationActions"; diff --git a/src/components/TablePagination/TablePaginationActions.tsx b/src/components/TablePagination/TablePaginationActions.tsx index a6ca73715..832f43327 100644 --- a/src/components/TablePagination/TablePaginationActions.tsx +++ b/src/components/TablePagination/TablePaginationActions.tsx @@ -10,7 +10,7 @@ import ArrowLeft from "@material-ui/icons/ArrowLeft"; import ArrowRight from "@material-ui/icons/ArrowRight"; import useTheme from "@saleor/hooks/useTheme"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/TextFieldWithChoice/TextFieldWithChoice.tsx b/src/components/TextFieldWithChoice/TextFieldWithChoice.tsx index 07b311710..cb64f9bc7 100644 --- a/src/components/TextFieldWithChoice/TextFieldWithChoice.tsx +++ b/src/components/TextFieldWithChoice/TextFieldWithChoice.tsx @@ -8,7 +8,7 @@ import { createStyles, WithStyles, withStyles } from "@material-ui/core/styles"; import TextField, { TextFieldProps } from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import DropdownIcon from "@material-ui/icons/ArrowDropDown"; -import * as React from "react"; +import React from "react"; import MenuToggle from "../MenuToggle"; diff --git a/src/components/Theme/ThemeProvider.tsx b/src/components/Theme/ThemeProvider.tsx index 71abbcc57..72757031e 100644 --- a/src/components/Theme/ThemeProvider.tsx +++ b/src/components/Theme/ThemeProvider.tsx @@ -1,7 +1,7 @@ // FIXME: https://github.com/mirumee/saleor/issues/4174 import OldMuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider"; import MuiThemeProvider from "@material-ui/styles/ThemeProvider"; -import * as React from "react"; +import React from "react"; import Baseline from "../../Baseline"; import createTheme, { IThemeColors } from "../../theme"; diff --git a/src/components/Timeline/Timeline.tsx b/src/components/Timeline/Timeline.tsx index 8591932d8..61e5fd72c 100644 --- a/src/components/Timeline/Timeline.tsx +++ b/src/components/Timeline/Timeline.tsx @@ -10,7 +10,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import PersonIcon from "@material-ui/icons/Person"; -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; diff --git a/src/components/Timeline/TimelineEvent.tsx b/src/components/Timeline/TimelineEvent.tsx index 6e2ccd86e..f3dd630ac 100644 --- a/src/components/Timeline/TimelineEvent.tsx +++ b/src/components/Timeline/TimelineEvent.tsx @@ -9,7 +9,7 @@ import { } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; -import * as React from "react"; +import React from "react"; import { DateTime } from "../Date"; diff --git a/src/components/Timeline/TimelineNote.tsx b/src/components/Timeline/TimelineNote.tsx index 4059a7bd6..15856000b 100644 --- a/src/components/Timeline/TimelineNote.tsx +++ b/src/components/Timeline/TimelineNote.tsx @@ -11,7 +11,7 @@ import { import Typography from "@material-ui/core/Typography"; import PersonIcon from "@material-ui/icons/Person"; import * as CRC from "crc-32"; -import * as React from "react"; +import React from "react"; import { DateTime } from "../Date"; import Hr from "../Hr"; diff --git a/src/components/Timezone/Timezone.tsx b/src/components/Timezone/Timezone.tsx index 2690b5d7a..4891be902 100644 --- a/src/components/Timezone/Timezone.tsx +++ b/src/components/Timezone/Timezone.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; export const TimezoneContext = React.createContext(undefined); const { diff --git a/src/components/VisibilityCard/VisibilityCard.tsx b/src/components/VisibilityCard/VisibilityCard.tsx index 5600ff2d3..6cb01c853 100644 --- a/src/components/VisibilityCard/VisibilityCard.tsx +++ b/src/components/VisibilityCard/VisibilityCard.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import ControlledSwitch from "@saleor/components/ControlledSwitch"; diff --git a/src/components/Weight/Weight.tsx b/src/components/Weight/Weight.tsx index 35defbf0b..02988b911 100644 --- a/src/components/Weight/Weight.tsx +++ b/src/components/Weight/Weight.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; export interface Weight { diff --git a/src/components/WeightRange/WeightRange.tsx b/src/components/WeightRange/WeightRange.tsx index 2552fae68..c7a789527 100644 --- a/src/components/WeightRange/WeightRange.tsx +++ b/src/components/WeightRange/WeightRange.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import i18n from "../../i18n"; import { Weight } from "../Weight"; diff --git a/src/components/WindowTitle/index.tsx b/src/components/WindowTitle/index.tsx index 63c99f54b..a06c2cd18 100644 --- a/src/components/WindowTitle/index.tsx +++ b/src/components/WindowTitle/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { Helmet } from "react-helmet"; import useShop from "@saleor/hooks/useShop"; diff --git a/src/components/messages/MessageManager.tsx b/src/components/messages/MessageManager.tsx index b698d0dc3..12bce2e46 100644 --- a/src/components/messages/MessageManager.tsx +++ b/src/components/messages/MessageManager.tsx @@ -2,7 +2,7 @@ import Button from "@material-ui/core/Button"; import IconButton from "@material-ui/core/IconButton"; import Snackbar from "@material-ui/core/Snackbar"; import CloseIcon from "@material-ui/icons/Close"; -import * as React from "react"; +import React from "react"; import { IMessage, MessageContext } from "./"; diff --git a/src/configuration/ConfigurationPage.tsx b/src/configuration/ConfigurationPage.tsx index 8466c11c0..48463ae22 100644 --- a/src/configuration/ConfigurationPage.tsx +++ b/src/configuration/ConfigurationPage.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import { IconProps } from "@material-ui/core/Icon"; import { User } from "../auth/types/User"; diff --git a/src/configuration/index.tsx b/src/configuration/index.tsx index 78a406ee2..9eeeb747e 100644 --- a/src/configuration/index.tsx +++ b/src/configuration/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import useNavigator from "@saleor/hooks/useNavigator"; import useUser from "@saleor/hooks/useUser"; diff --git a/src/containers/BaseSearch.tsx b/src/containers/BaseSearch.tsx index 920d542d7..0742edbc1 100644 --- a/src/containers/BaseSearch.tsx +++ b/src/containers/BaseSearch.tsx @@ -1,5 +1,5 @@ import { DocumentNode } from "graphql"; -import * as React from "react"; +import React from "react"; import Debounce from "../components/Debounce"; import { TypedQuery, TypedQueryResult } from "../queries"; diff --git a/src/customers/components/CustomerAddress/CustomerAddress.tsx b/src/customers/components/CustomerAddress/CustomerAddress.tsx index a54e15575..3cd7fbaec 100644 --- a/src/customers/components/CustomerAddress/CustomerAddress.tsx +++ b/src/customers/components/CustomerAddress/CustomerAddress.tsx @@ -4,7 +4,7 @@ import CardActions from "@material-ui/core/CardActions"; import CardContent from "@material-ui/core/CardContent"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import AddressFormatter from "@saleor/components/AddressFormatter"; import CardMenu from "@saleor/components/CardMenu"; diff --git a/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx b/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx index b7dca9f12..dd2d77d80 100644 --- a/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx +++ b/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx @@ -5,7 +5,7 @@ import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import AddressEdit from "@saleor/components/AddressEdit"; import ConfirmButton, { diff --git a/src/customers/components/CustomerAddressListPage/CustomerAddressListPage.tsx b/src/customers/components/CustomerAddressListPage/CustomerAddressListPage.tsx index e6df42c58..16817416a 100644 --- a/src/customers/components/CustomerAddressListPage/CustomerAddressListPage.tsx +++ b/src/customers/components/CustomerAddressListPage/CustomerAddressListPage.tsx @@ -7,7 +7,7 @@ import { } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import Container from "@saleor/components/Container"; diff --git a/src/customers/components/CustomerAddresses/CustomerAddresses.tsx b/src/customers/components/CustomerAddresses/CustomerAddresses.tsx index ddc0ef85f..54e1a28bf 100644 --- a/src/customers/components/CustomerAddresses/CustomerAddresses.tsx +++ b/src/customers/components/CustomerAddresses/CustomerAddresses.tsx @@ -8,7 +8,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import AddressFormatter from "@saleor/components/AddressFormatter"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx b/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx index c5ce829bd..eb4e13fc7 100644 --- a/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx +++ b/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx @@ -2,7 +2,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import { createStyles, WithStyles, withStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import AddressEdit from "@saleor/components/AddressEdit"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/customers/components/CustomerCreateDetails/CustomerCreateDetails.tsx b/src/customers/components/CustomerCreateDetails/CustomerCreateDetails.tsx index e57113dc5..db1ffec62 100644 --- a/src/customers/components/CustomerCreateDetails/CustomerCreateDetails.tsx +++ b/src/customers/components/CustomerCreateDetails/CustomerCreateDetails.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import i18n from "../../../i18n"; diff --git a/src/customers/components/CustomerCreateNote/CustomerCreateNote.tsx b/src/customers/components/CustomerCreateNote/CustomerCreateNote.tsx index f90e27cb6..e40d3bd48 100644 --- a/src/customers/components/CustomerCreateNote/CustomerCreateNote.tsx +++ b/src/customers/components/CustomerCreateNote/CustomerCreateNote.tsx @@ -2,7 +2,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { FormSpacer } from "@saleor/components/FormSpacer"; diff --git a/src/customers/components/CustomerCreatePage/CustomerCreatePage.tsx b/src/customers/components/CustomerCreatePage/CustomerCreatePage.tsx index 54e316002..1894148a6 100644 --- a/src/customers/components/CustomerCreatePage/CustomerCreatePage.tsx +++ b/src/customers/components/CustomerCreatePage/CustomerCreatePage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import { CardSpacer } from "@saleor/components/CardSpacer"; diff --git a/src/customers/components/CustomerDetails/CustomerDetails.tsx b/src/customers/components/CustomerDetails/CustomerDetails.tsx index d6f7693e0..6ec4ffd0f 100644 --- a/src/customers/components/CustomerDetails/CustomerDetails.tsx +++ b/src/customers/components/CustomerDetails/CustomerDetails.tsx @@ -9,7 +9,7 @@ import { import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import * as moment from "moment-timezone"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; diff --git a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx index 041c0fe50..1e417f960 100644 --- a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx +++ b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import { CardSpacer } from "@saleor/components/CardSpacer"; diff --git a/src/customers/components/CustomerList/CustomerList.tsx b/src/customers/components/CustomerList/CustomerList.tsx index 2dfb35021..8e2b1f17f 100644 --- a/src/customers/components/CustomerList/CustomerList.tsx +++ b/src/customers/components/CustomerList/CustomerList.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/customers/components/CustomerListPage/CustomerListPage.tsx b/src/customers/components/CustomerListPage/CustomerListPage.tsx index d240eec34..74e5aa6f3 100644 --- a/src/customers/components/CustomerListPage/CustomerListPage.tsx +++ b/src/customers/components/CustomerListPage/CustomerListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; diff --git a/src/customers/components/CustomerOrders/CustomerOrders.tsx b/src/customers/components/CustomerOrders/CustomerOrders.tsx index 25c22bbd1..f6883ca34 100644 --- a/src/customers/components/CustomerOrders/CustomerOrders.tsx +++ b/src/customers/components/CustomerOrders/CustomerOrders.tsx @@ -6,7 +6,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { DateTime } from "@saleor/components/Date"; diff --git a/src/customers/components/CustomerStats/CustomerStats.tsx b/src/customers/components/CustomerStats/CustomerStats.tsx index 58ca6d18f..1a2a0b4a6 100644 --- a/src/customers/components/CustomerStats/CustomerStats.tsx +++ b/src/customers/components/CustomerStats/CustomerStats.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { DateTime } from "@saleor/components/Date"; diff --git a/src/customers/index.tsx b/src/customers/index.tsx index b06576887..7ea77efda 100644 --- a/src/customers/index.tsx +++ b/src/customers/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/customers/urls.ts b/src/customers/urls.ts index 53662c0f5..baab66be7 100644 --- a/src/customers/urls.ts +++ b/src/customers/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { BulkAction, Dialog, Pagination, SingleAction } from "../types"; diff --git a/src/customers/views/CustomerAddresses.tsx b/src/customers/views/CustomerAddresses.tsx index 5e968036b..bbb4fb0dc 100644 --- a/src/customers/views/CustomerAddresses.tsx +++ b/src/customers/views/CustomerAddresses.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/customers/views/CustomerCreate.tsx b/src/customers/views/CustomerCreate.tsx index 62d83e07d..f89a867d6 100644 --- a/src/customers/views/CustomerCreate.tsx +++ b/src/customers/views/CustomerCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/customers/views/CustomerDetails.tsx b/src/customers/views/CustomerDetails.tsx index e6e9ab6aa..d1e1b57b5 100644 --- a/src/customers/views/CustomerDetails.tsx +++ b/src/customers/views/CustomerDetails.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/customers/views/CustomerList.tsx b/src/customers/views/CustomerList.tsx index 6961261af..443dc1e9c 100644 --- a/src/customers/views/CustomerList.tsx +++ b/src/customers/views/CustomerList.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/discounts/components/DiscountCategories/DiscountCategories.tsx b/src/discounts/components/DiscountCategories/DiscountCategories.tsx index a608481f5..7c0a1a6ed 100644 --- a/src/discounts/components/DiscountCategories/DiscountCategories.tsx +++ b/src/discounts/components/DiscountCategories/DiscountCategories.tsx @@ -13,7 +13,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; diff --git a/src/discounts/components/DiscountCollections/DiscountCollections.tsx b/src/discounts/components/DiscountCollections/DiscountCollections.tsx index ec1f7900e..e5b0eff8a 100644 --- a/src/discounts/components/DiscountCollections/DiscountCollections.tsx +++ b/src/discounts/components/DiscountCollections/DiscountCollections.tsx @@ -13,7 +13,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; diff --git a/src/discounts/components/DiscountCountrySelectDialog/DiscountCountrySelectDialog.tsx b/src/discounts/components/DiscountCountrySelectDialog/DiscountCountrySelectDialog.tsx index 3a5aadaec..58a650be3 100644 --- a/src/discounts/components/DiscountCountrySelectDialog/DiscountCountrySelectDialog.tsx +++ b/src/discounts/components/DiscountCountrySelectDialog/DiscountCountrySelectDialog.tsx @@ -16,7 +16,7 @@ import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import { filter } from "fuzzaldrin"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import ConfirmButton, { diff --git a/src/discounts/components/DiscountProducts/DiscountProducts.tsx b/src/discounts/components/DiscountProducts/DiscountProducts.tsx index 3df6478f1..5fe393395 100644 --- a/src/discounts/components/DiscountProducts/DiscountProducts.tsx +++ b/src/discounts/components/DiscountProducts/DiscountProducts.tsx @@ -13,7 +13,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; diff --git a/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx b/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx index d5dd1945d..04bff4adb 100644 --- a/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx +++ b/src/discounts/components/SaleCreatePage/SaleCreatePage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx b/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx index d760c43f0..1ac92d025 100644 --- a/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx +++ b/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/discounts/components/SaleInfo/SaleInfo.tsx b/src/discounts/components/SaleInfo/SaleInfo.tsx index 606cf94a6..2b7a646ff 100644 --- a/src/discounts/components/SaleInfo/SaleInfo.tsx +++ b/src/discounts/components/SaleInfo/SaleInfo.tsx @@ -7,7 +7,7 @@ import { withStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import i18n from "../../../i18n"; diff --git a/src/discounts/components/SaleList/SaleList.tsx b/src/discounts/components/SaleList/SaleList.tsx index 80a174b36..42169f3d6 100644 --- a/src/discounts/components/SaleList/SaleList.tsx +++ b/src/discounts/components/SaleList/SaleList.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import Date from "@saleor/components/Date"; diff --git a/src/discounts/components/SaleListPage/SaleListPage.tsx b/src/discounts/components/SaleListPage/SaleListPage.tsx index 0d49e9c22..dcfdc126d 100644 --- a/src/discounts/components/SaleListPage/SaleListPage.tsx +++ b/src/discounts/components/SaleListPage/SaleListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; diff --git a/src/discounts/components/SalePricing/SalePricing.tsx b/src/discounts/components/SalePricing/SalePricing.tsx index 45eb60472..2aed0a2d9 100644 --- a/src/discounts/components/SalePricing/SalePricing.tsx +++ b/src/discounts/components/SalePricing/SalePricing.tsx @@ -8,7 +8,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Hr from "@saleor/components/Hr"; diff --git a/src/discounts/components/SaleSummary/SaleSummary.tsx b/src/discounts/components/SaleSummary/SaleSummary.tsx index 5cfa73db8..939b291c9 100644 --- a/src/discounts/components/SaleSummary/SaleSummary.tsx +++ b/src/discounts/components/SaleSummary/SaleSummary.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardSpacer from "@saleor/components/CardSpacer"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx b/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx index 46147595f..9d46dda9a 100644 --- a/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx +++ b/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx b/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx index fec9980d7..c745b4c3f 100644 --- a/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx +++ b/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx @@ -1,5 +1,5 @@ import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/discounts/components/VoucherInfo/VoucherInfo.tsx b/src/discounts/components/VoucherInfo/VoucherInfo.tsx index 7395fe33f..e6ff5d478 100644 --- a/src/discounts/components/VoucherInfo/VoucherInfo.tsx +++ b/src/discounts/components/VoucherInfo/VoucherInfo.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; diff --git a/src/discounts/components/VoucherList/VoucherList.tsx b/src/discounts/components/VoucherList/VoucherList.tsx index f18df8d5a..a5e53dfeb 100644 --- a/src/discounts/components/VoucherList/VoucherList.tsx +++ b/src/discounts/components/VoucherList/VoucherList.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import Date from "@saleor/components/Date"; diff --git a/src/discounts/components/VoucherListPage/VoucherListPage.tsx b/src/discounts/components/VoucherListPage/VoucherListPage.tsx index cf40fff3f..72cc4f4f2 100644 --- a/src/discounts/components/VoucherListPage/VoucherListPage.tsx +++ b/src/discounts/components/VoucherListPage/VoucherListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; diff --git a/src/discounts/components/VoucherOptions/VoucherOptions.tsx b/src/discounts/components/VoucherOptions/VoucherOptions.tsx index c9bde716b..62731257c 100644 --- a/src/discounts/components/VoucherOptions/VoucherOptions.tsx +++ b/src/discounts/components/VoucherOptions/VoucherOptions.tsx @@ -8,7 +8,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import ControlledSwitch from "@saleor/components/ControlledSwitch"; diff --git a/src/discounts/components/VoucherSummary/VoucherSummary.tsx b/src/discounts/components/VoucherSummary/VoucherSummary.tsx index e3035f126..ec81e3a25 100644 --- a/src/discounts/components/VoucherSummary/VoucherSummary.tsx +++ b/src/discounts/components/VoucherSummary/VoucherSummary.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardSpacer from "@saleor/components/CardSpacer"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/discounts/fixtures.ts b/src/discounts/fixtures.ts index 7f4cef125..8cd6b2951 100644 --- a/src/discounts/fixtures.ts +++ b/src/discounts/fixtures.ts @@ -1,4 +1,4 @@ -import * as placeholderImage from "@assets/images/placeholder60x60.png"; +import placeholderImage from "@assets/images/placeholder60x60.png"; import { SaleType, VoucherDiscountValueType, diff --git a/src/discounts/index.tsx b/src/discounts/index.tsx index 40d4ba0b8..81d336ff4 100644 --- a/src/discounts/index.tsx +++ b/src/discounts/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/discounts/urls.ts b/src/discounts/urls.ts index a33556ed8..0e3833d99 100644 --- a/src/discounts/urls.ts +++ b/src/discounts/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { ActiveTab, BulkAction, Dialog, Pagination } from "../types"; import { SaleDetailsPageTab } from "./components/SaleDetailsPage"; diff --git a/src/discounts/views/SaleCreate.tsx b/src/discounts/views/SaleCreate.tsx index eb5a55f80..445a07bbc 100644 --- a/src/discounts/views/SaleCreate.tsx +++ b/src/discounts/views/SaleCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/discounts/views/SaleDetails.tsx b/src/discounts/views/SaleDetails.tsx index b0a3c473f..cc50292ed 100644 --- a/src/discounts/views/SaleDetails.tsx +++ b/src/discounts/views/SaleDetails.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog"; diff --git a/src/discounts/views/SaleList.tsx b/src/discounts/views/SaleList.tsx index 97e1a302e..b51ba16f9 100644 --- a/src/discounts/views/SaleList.tsx +++ b/src/discounts/views/SaleList.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/discounts/views/VoucherCreate.tsx b/src/discounts/views/VoucherCreate.tsx index 00efa1210..331a6a6db 100644 --- a/src/discounts/views/VoucherCreate.tsx +++ b/src/discounts/views/VoucherCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/discounts/views/VoucherDetails.tsx b/src/discounts/views/VoucherDetails.tsx index b3ef7ec5e..ff8012c98 100644 --- a/src/discounts/views/VoucherDetails.tsx +++ b/src/discounts/views/VoucherDetails.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog"; diff --git a/src/discounts/views/VoucherList.tsx b/src/discounts/views/VoucherList.tsx index e8ca0d404..8d5949a30 100644 --- a/src/discounts/views/VoucherList.tsx +++ b/src/discounts/views/VoucherList.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/home/components/HomeActivityCard/HomeActivityCard.tsx b/src/home/components/HomeActivityCard/HomeActivityCard.tsx index f28f72ded..6062878ed 100644 --- a/src/home/components/HomeActivityCard/HomeActivityCard.tsx +++ b/src/home/components/HomeActivityCard/HomeActivityCard.tsx @@ -4,7 +4,7 @@ import ListItem from "@material-ui/core/ListItem"; import ListItemText from "@material-ui/core/ListItemText"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { DateTime } from "@saleor/components/Date"; diff --git a/src/home/components/HomeAnalyticsCard/HomeAnalyticsCard.tsx b/src/home/components/HomeAnalyticsCard/HomeAnalyticsCard.tsx index faafc0a2e..3d509b5a3 100644 --- a/src/home/components/HomeAnalyticsCard/HomeAnalyticsCard.tsx +++ b/src/home/components/HomeAnalyticsCard/HomeAnalyticsCard.tsx @@ -8,7 +8,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import i18n from "../../../i18n"; diff --git a/src/home/components/HomeHeader/HomeHeader.tsx b/src/home/components/HomeHeader/HomeHeader.tsx index ff692eb2a..30c746f1b 100644 --- a/src/home/components/HomeHeader/HomeHeader.tsx +++ b/src/home/components/HomeHeader/HomeHeader.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import Skeleton from "@saleor/components/Skeleton"; import i18n from "../../../i18n"; diff --git a/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx b/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx index 752ca3032..cca7c0886 100644 --- a/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx +++ b/src/home/components/HomeNotificationTable/HomeNotificationTable.tsx @@ -11,7 +11,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"; -import * as React from "react"; +import React from "react"; import Skeleton from "@saleor/components/Skeleton"; import i18n from "../../../i18n"; diff --git a/src/home/components/HomePage/HomePage.tsx b/src/home/components/HomePage/HomePage.tsx index af26fe46f..1607ee20f 100644 --- a/src/home/components/HomePage/HomePage.tsx +++ b/src/home/components/HomePage/HomePage.tsx @@ -4,7 +4,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import CardSpacer from "@saleor/components/CardSpacer"; import Container from "@saleor/components/Container"; diff --git a/src/home/components/HomeProductListCard/HomeProductListCard.tsx b/src/home/components/HomeProductListCard/HomeProductListCard.tsx index d6783210a..550ef52a7 100644 --- a/src/home/components/HomeProductListCard/HomeProductListCard.tsx +++ b/src/home/components/HomeProductListCard/HomeProductListCard.tsx @@ -11,7 +11,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Money from "@saleor/components/Money"; diff --git a/src/home/components/HomeScreen.tsx b/src/home/components/HomeScreen.tsx index 9c30edce0..06a801b2c 100644 --- a/src/home/components/HomeScreen.tsx +++ b/src/home/components/HomeScreen.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Container from "@saleor/components/Container"; diff --git a/src/home/index.tsx b/src/home/index.tsx index e7520f23f..df012335c 100644 --- a/src/home/index.tsx +++ b/src/home/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import HomePage from "./views"; diff --git a/src/home/views/index.tsx b/src/home/views/index.tsx index b0a06a884..116175d7c 100644 --- a/src/home/views/index.tsx +++ b/src/home/views/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import useNavigator from "@saleor/hooks/useNavigator"; import useUser from "@saleor/hooks/useUser"; diff --git a/src/hooks/useDateLocalize.ts b/src/hooks/useDateLocalize.ts index 88426f520..3ef7cb9ef 100644 --- a/src/hooks/useDateLocalize.ts +++ b/src/hooks/useDateLocalize.ts @@ -1,4 +1,4 @@ -import * as moment from "moment-timezone"; +import moment from "moment-timezone"; import { useContext } from "react"; import { LocaleContext } from "@saleor/components/Locale"; diff --git a/src/hooks/useScroll.ts b/src/hooks/useScroll.ts index 355ae5e5e..6e871b8c2 100644 --- a/src/hooks/useScroll.ts +++ b/src/hooks/useScroll.ts @@ -1,5 +1,4 @@ -// tslint:disable:no-submodule-imports -import * as throttle from "lodash/throttle"; +import throttle from "lodash-es/throttle"; import { useEffect, useState } from "react"; function getPosition() { diff --git a/src/i18n.ts b/src/i18n.ts index d15b0883b..91d59c28c 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -1,6 +1,6 @@ -import * as i18n from "i18next"; -import * as LanguageDetector from "i18next-browser-languagedetector"; -import * as XHR from "i18next-xhr-backend"; +import i18n from "i18next"; +import LanguageDetector from "i18next-browser-languagedetector"; +import XHR from "i18next-xhr-backend"; i18n.use(XHR); i18n.use(LanguageDetector); diff --git a/src/icons/AccountCircle.tsx b/src/icons/AccountCircle.tsx index 49eae5c1e..efe95777e 100644 --- a/src/icons/AccountCircle.tsx +++ b/src/icons/AccountCircle.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const AccountCircle = createSvgIcon( <> diff --git a/src/icons/ArrowDropdown.tsx b/src/icons/ArrowDropdown.tsx index e8f324da3..a072df495 100644 --- a/src/icons/ArrowDropdown.tsx +++ b/src/icons/ArrowDropdown.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const NoPhoto = createSvgIcon( diff --git a/src/icons/Ballot.tsx b/src/icons/Ballot.tsx index d5dfb8afe..587c3379c 100644 --- a/src/icons/Ballot.tsx +++ b/src/icons/Ballot.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Ballot = createSvgIcon( <> diff --git a/src/icons/BoldIcon.tsx b/src/icons/BoldIcon.tsx index ae22592b2..56a480e56 100644 --- a/src/icons/BoldIcon.tsx +++ b/src/icons/BoldIcon.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const BoldIcon = createSvgIcon( <> diff --git a/src/icons/Calendar.tsx b/src/icons/Calendar.tsx index a67c5e60e..c7250b42d 100644 --- a/src/icons/Calendar.tsx +++ b/src/icons/Calendar.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Calendar = createSvgIcon( <> diff --git a/src/icons/Draggable.tsx b/src/icons/Draggable.tsx index 948ef738e..cdaad7d5e 100644 --- a/src/icons/Draggable.tsx +++ b/src/icons/Draggable.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Draggable = createSvgIcon( <> diff --git a/src/icons/Folder.tsx b/src/icons/Folder.tsx index f9e6bdb5d..74a16ed95 100644 --- a/src/icons/Folder.tsx +++ b/src/icons/Folder.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Folder = createSvgIcon( <> diff --git a/src/icons/HeaderOne.tsx b/src/icons/HeaderOne.tsx index 31409752f..0d0d9eb07 100644 --- a/src/icons/HeaderOne.tsx +++ b/src/icons/HeaderOne.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const HeaderOne = createSvgIcon( <> diff --git a/src/icons/HeaderThree.tsx b/src/icons/HeaderThree.tsx index 8544d68ec..f33495274 100644 --- a/src/icons/HeaderThree.tsx +++ b/src/icons/HeaderThree.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const HeaderThree = createSvgIcon( <> diff --git a/src/icons/HeaderTwo.tsx b/src/icons/HeaderTwo.tsx index 8a9b4d289..f2775da8f 100644 --- a/src/icons/HeaderTwo.tsx +++ b/src/icons/HeaderTwo.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const HeaderTwo = createSvgIcon( <> diff --git a/src/icons/Home.tsx b/src/icons/Home.tsx index 1e574e556..91b1fd8a7 100644 --- a/src/icons/Home.tsx +++ b/src/icons/Home.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Home = createSvgIcon( <> diff --git a/src/icons/Image.tsx b/src/icons/Image.tsx index 3ff31d962..458cbc3c9 100644 --- a/src/icons/Image.tsx +++ b/src/icons/Image.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Image = createSvgIcon( <> diff --git a/src/icons/ItalicIcon.tsx b/src/icons/ItalicIcon.tsx index ced567f02..eb750528a 100644 --- a/src/icons/ItalicIcon.tsx +++ b/src/icons/ItalicIcon.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const ItalicIcon = createSvgIcon( <> diff --git a/src/icons/LinkIcon.tsx b/src/icons/LinkIcon.tsx index adfd37b6f..0865ae2a0 100644 --- a/src/icons/LinkIcon.tsx +++ b/src/icons/LinkIcon.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const LinkIcon = createSvgIcon( <> diff --git a/src/icons/LocalShipping.tsx b/src/icons/LocalShipping.tsx index b0440c68e..6eab76ef3 100644 --- a/src/icons/LocalShipping.tsx +++ b/src/icons/LocalShipping.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const LocalShipping = createSvgIcon( <> diff --git a/src/icons/Monetization.tsx b/src/icons/Monetization.tsx index 1dd34af67..b315a839d 100644 --- a/src/icons/Monetization.tsx +++ b/src/icons/Monetization.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Monetization = createSvgIcon( <> diff --git a/src/icons/Moon.tsx b/src/icons/Moon.tsx index 88df92b0f..acbd52ee2 100644 --- a/src/icons/Moon.tsx +++ b/src/icons/Moon.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Moon = createSvgIcon( <> diff --git a/src/icons/Navigation.tsx b/src/icons/Navigation.tsx index 57b83ec11..c7c7d29a9 100644 --- a/src/icons/Navigation.tsx +++ b/src/icons/Navigation.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Navigation = createSvgIcon( <> diff --git a/src/icons/NoPhoto.tsx b/src/icons/NoPhoto.tsx index 60f61c2ab..c83d77a73 100644 --- a/src/icons/NoPhoto.tsx +++ b/src/icons/NoPhoto.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const NoPhoto = createSvgIcon( diff --git a/src/icons/OrderedListIcon.tsx b/src/icons/OrderedListIcon.tsx index e203dc794..95a76af22 100644 --- a/src/icons/OrderedListIcon.tsx +++ b/src/icons/OrderedListIcon.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const OrderedListIcon = createSvgIcon( <> diff --git a/src/icons/Orders.tsx b/src/icons/Orders.tsx index 264a652f3..7026b704a 100644 --- a/src/icons/Orders.tsx +++ b/src/icons/Orders.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Orders = createSvgIcon( <> diff --git a/src/icons/Pages.tsx b/src/icons/Pages.tsx index cf4be3c29..6aa43bc32 100644 --- a/src/icons/Pages.tsx +++ b/src/icons/Pages.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Pages = createSvgIcon( <> diff --git a/src/icons/ProductTypes.tsx b/src/icons/ProductTypes.tsx index 57d3b88f7..c86899204 100644 --- a/src/icons/ProductTypes.tsx +++ b/src/icons/ProductTypes.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const ProductTypes = createSvgIcon( <> diff --git a/src/icons/QuotationIcon.tsx b/src/icons/QuotationIcon.tsx index 16e855053..0d49f21b4 100644 --- a/src/icons/QuotationIcon.tsx +++ b/src/icons/QuotationIcon.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const QuotationIcon = createSvgIcon( <> diff --git a/src/icons/Sales.tsx b/src/icons/Sales.tsx index dc85268a7..dae31094f 100644 --- a/src/icons/Sales.tsx +++ b/src/icons/Sales.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Sales = createSvgIcon( <> diff --git a/src/icons/ShippingMethods.tsx b/src/icons/ShippingMethods.tsx index b695a80d6..0c4c90d03 100644 --- a/src/icons/ShippingMethods.tsx +++ b/src/icons/ShippingMethods.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const ShippingMethods = createSvgIcon( <> diff --git a/src/icons/Shop.tsx b/src/icons/Shop.tsx index 397a69410..1cc474bfb 100644 --- a/src/icons/Shop.tsx +++ b/src/icons/Shop.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Shop = createSvgIcon( <> diff --git a/src/icons/SiteSettings.tsx b/src/icons/SiteSettings.tsx index fc7d50bad..38829145d 100644 --- a/src/icons/SiteSettings.tsx +++ b/src/icons/SiteSettings.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const SiteSettings = createSvgIcon( <> diff --git a/src/icons/StaffMembers.tsx b/src/icons/StaffMembers.tsx index 1a3072e0d..bf1c45162 100644 --- a/src/icons/StaffMembers.tsx +++ b/src/icons/StaffMembers.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const StaffMembers = createSvgIcon( <> diff --git a/src/icons/StoreMall.tsx b/src/icons/StoreMall.tsx index bec5a200c..753ba2d15 100644 --- a/src/icons/StoreMall.tsx +++ b/src/icons/StoreMall.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const StoreMall = createSvgIcon( <> diff --git a/src/icons/StrikethroughIcon.tsx b/src/icons/StrikethroughIcon.tsx index 93f8286fc..accad0a5f 100644 --- a/src/icons/StrikethroughIcon.tsx +++ b/src/icons/StrikethroughIcon.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const HeaderOne = createSvgIcon( <> diff --git a/src/icons/Sun.tsx b/src/icons/Sun.tsx index ff870b2f9..310ee1907 100644 --- a/src/icons/Sun.tsx +++ b/src/icons/Sun.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Sun = createSvgIcon( <> diff --git a/src/icons/Taxes.tsx b/src/icons/Taxes.tsx index bfc3ce334..ae62d4206 100644 --- a/src/icons/Taxes.tsx +++ b/src/icons/Taxes.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Taxes = createSvgIcon( <> diff --git a/src/icons/Truck.tsx b/src/icons/Truck.tsx index edfe088ed..f90abaa5b 100644 --- a/src/icons/Truck.tsx +++ b/src/icons/Truck.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Truck = createSvgIcon( <> diff --git a/src/icons/UnorderedListIcon.tsx b/src/icons/UnorderedListIcon.tsx index f62a439d3..d92de9a2b 100644 --- a/src/icons/UnorderedListIcon.tsx +++ b/src/icons/UnorderedListIcon.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const UnorderedListIcon = createSvgIcon( <> diff --git a/src/icons/Unstyled.tsx b/src/icons/Unstyled.tsx index c5993628a..0fc19f44a 100644 --- a/src/icons/Unstyled.tsx +++ b/src/icons/Unstyled.tsx @@ -1,5 +1,5 @@ import createSvgIcon from "@material-ui/icons/utils/createSvgIcon"; -import * as React from "react"; +import React from "react"; export const Unstyled = createSvgIcon( <> diff --git a/src/index.tsx b/src/index.tsx index 229fbe5c8..61c8d3654 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -5,7 +5,7 @@ import { BatchHttpLink } from "apollo-link-batch-http"; import { setContext } from "apollo-link-context"; import { ErrorResponse, onError } from "apollo-link-error"; import { createUploadLink } from "apollo-upload-client"; -import * as React from "react"; +import React from "react"; import { ApolloProvider } from "react-apollo"; import { render } from "react-dom"; import { BrowserRouter, Route, Switch } from "react-router-dom"; diff --git a/src/misc.ts b/src/misc.ts index c41a3b0e7..83f9e9aec 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -1,4 +1,4 @@ -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { MutationFn, MutationResult } from "react-apollo"; import { ConfirmButtonTransitionState } from "./components/ConfirmButton/ConfirmButton"; diff --git a/src/mutations.tsx b/src/mutations.tsx index b4689bfea..f7f27665b 100644 --- a/src/mutations.tsx +++ b/src/mutations.tsx @@ -1,6 +1,6 @@ import { ApolloError } from "apollo-client"; import { DocumentNode } from "graphql"; -import * as React from "react"; +import React from "react"; import { Mutation, MutationFn, diff --git a/src/navigation/components/MenuCreateDialog/MenuCreateDialog.tsx b/src/navigation/components/MenuCreateDialog/MenuCreateDialog.tsx index 1620b5b19..fd06ad788 100644 --- a/src/navigation/components/MenuCreateDialog/MenuCreateDialog.tsx +++ b/src/navigation/components/MenuCreateDialog/MenuCreateDialog.tsx @@ -4,7 +4,7 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/navigation/components/MenuDetailsPage/MenuDetailsPage.tsx b/src/navigation/components/MenuDetailsPage/MenuDetailsPage.tsx index 4ce143bb4..6c22f0d0d 100644 --- a/src/navigation/components/MenuDetailsPage/MenuDetailsPage.tsx +++ b/src/navigation/components/MenuDetailsPage/MenuDetailsPage.tsx @@ -1,5 +1,5 @@ import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx b/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx index 6cb3f59ec..c607ab133 100644 --- a/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx +++ b/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx @@ -5,7 +5,7 @@ import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; import * as isUrl from "is-url"; -import * as React from "react"; +import React from "react"; import AutocompleteSelectMenu from "@saleor/components/AutocompleteSelectMenu"; import ConfirmButton, { diff --git a/src/navigation/components/MenuItems/MenuItems.tsx b/src/navigation/components/MenuItems/MenuItems.tsx index 715e29e65..ef17ab78a 100644 --- a/src/navigation/components/MenuItems/MenuItems.tsx +++ b/src/navigation/components/MenuItems/MenuItems.tsx @@ -13,7 +13,7 @@ import Typography from "@material-ui/core/Typography"; import DeleteIcon from "@material-ui/icons/Delete"; import EditIcon from "@material-ui/icons/Edit"; import classNames from "classnames"; -import * as React from "react"; +import React from "react"; import SortableTree, { NodeRendererProps, TreeItem } from "react-sortable-tree"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/navigation/components/MenuList/MenuList.tsx b/src/navigation/components/MenuList/MenuList.tsx index 2fb39843d..12d802106 100644 --- a/src/navigation/components/MenuList/MenuList.tsx +++ b/src/navigation/components/MenuList/MenuList.tsx @@ -11,7 +11,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import IconButtonTableCell from "@saleor/components/IconButtonTableCell"; diff --git a/src/navigation/components/MenuListPage/MenuListPage.tsx b/src/navigation/components/MenuListPage/MenuListPage.tsx index 49c3df930..8e6b98d56 100644 --- a/src/navigation/components/MenuListPage/MenuListPage.tsx +++ b/src/navigation/components/MenuListPage/MenuListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import Container from "@saleor/components/Container"; diff --git a/src/navigation/components/MenuProperties/MenuProperties.tsx b/src/navigation/components/MenuProperties/MenuProperties.tsx index 15436c793..a0379b149 100644 --- a/src/navigation/components/MenuProperties/MenuProperties.tsx +++ b/src/navigation/components/MenuProperties/MenuProperties.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import i18n from "../../../i18n"; diff --git a/src/navigation/index.tsx b/src/navigation/index.tsx index 46ea8411c..70e3663fc 100644 --- a/src/navigation/index.tsx +++ b/src/navigation/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { menuListPath, menuPath } from "./urls"; diff --git a/src/navigation/urls.ts b/src/navigation/urls.ts index 2481f46d4..f0f791e61 100644 --- a/src/navigation/urls.ts +++ b/src/navigation/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { BulkAction, Dialog, Pagination, SingleAction } from "../types"; export const navigationSection = "/navigation"; diff --git a/src/navigation/views/MenuDetails/index.tsx b/src/navigation/views/MenuDetails/index.tsx index c5ad9d9d1..c26d39343 100644 --- a/src/navigation/views/MenuDetails/index.tsx +++ b/src/navigation/views/MenuDetails/index.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/navigation/views/MenuList.tsx b/src/navigation/views/MenuList.tsx index 7e9f0a561..35f05d79c 100644 --- a/src/navigation/views/MenuList.tsx +++ b/src/navigation/views/MenuList.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx b/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx index 81687a3e4..fa5eb177f 100644 --- a/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx +++ b/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx @@ -4,7 +4,7 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import AddressEdit from "@saleor/components/AddressEdit"; import ConfirmButton, { diff --git a/src/orders/components/OrderBulkCancelDialog/OrderBulkCancelDialog.tsx b/src/orders/components/OrderBulkCancelDialog/OrderBulkCancelDialog.tsx index f9029dd82..0c25ddeb0 100644 --- a/src/orders/components/OrderBulkCancelDialog/OrderBulkCancelDialog.tsx +++ b/src/orders/components/OrderBulkCancelDialog/OrderBulkCancelDialog.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; diff --git a/src/orders/components/OrderCancelDialog/OrderCancelDialog.tsx b/src/orders/components/OrderCancelDialog/OrderCancelDialog.tsx index ee8b15758..51eb429aa 100644 --- a/src/orders/components/OrderCancelDialog/OrderCancelDialog.tsx +++ b/src/orders/components/OrderCancelDialog/OrderCancelDialog.tsx @@ -10,7 +10,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/orders/components/OrderCustomer/OrderCustomer.tsx b/src/orders/components/OrderCustomer/OrderCustomer.tsx index 92970c79c..ea42bf8f7 100644 --- a/src/orders/components/OrderCustomer/OrderCustomer.tsx +++ b/src/orders/components/OrderCustomer/OrderCustomer.tsx @@ -8,7 +8,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import ExternalLink from "@saleor/components/ExternalLink"; diff --git a/src/orders/components/OrderCustomerEditDialog/OrderCustomerEditDialog.tsx b/src/orders/components/OrderCustomerEditDialog/OrderCustomerEditDialog.tsx index 2c7b1f271..3cd9804bb 100644 --- a/src/orders/components/OrderCustomerEditDialog/OrderCustomerEditDialog.tsx +++ b/src/orders/components/OrderCustomerEditDialog/OrderCustomerEditDialog.tsx @@ -9,7 +9,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/orders/components/OrderCustomerNote/OrderCustomerNote.tsx b/src/orders/components/OrderCustomerNote/OrderCustomerNote.tsx index 331473340..4a3b075ff 100644 --- a/src/orders/components/OrderCustomerNote/OrderCustomerNote.tsx +++ b/src/orders/components/OrderCustomerNote/OrderCustomerNote.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx index a1d3d7f3a..b20a70e10 100644 --- a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx +++ b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardMenu from "@saleor/components/CardMenu"; diff --git a/src/orders/components/OrderDraftCancelDialog/OrderDraftCancelDialog.tsx b/src/orders/components/OrderDraftCancelDialog/OrderDraftCancelDialog.tsx index 61704b193..95866ae35 100644 --- a/src/orders/components/OrderDraftCancelDialog/OrderDraftCancelDialog.tsx +++ b/src/orders/components/OrderDraftCancelDialog/OrderDraftCancelDialog.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; diff --git a/src/orders/components/OrderDraftDetails/OrderDraftDetails.tsx b/src/orders/components/OrderDraftDetails/OrderDraftDetails.tsx index 17f1e478c..3eeb932e7 100644 --- a/src/orders/components/OrderDraftDetails/OrderDraftDetails.tsx +++ b/src/orders/components/OrderDraftDetails/OrderDraftDetails.tsx @@ -1,7 +1,7 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import i18n from "../../../i18n"; diff --git a/src/orders/components/OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx b/src/orders/components/OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx index 1ba483fe3..09b0cd8cc 100644 --- a/src/orders/components/OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx +++ b/src/orders/components/OrderDraftDetailsProducts/OrderDraftDetailsProducts.tsx @@ -13,7 +13,7 @@ import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import { DebounceForm } from "@saleor/components/DebounceForm"; import Form from "@saleor/components/Form"; diff --git a/src/orders/components/OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx b/src/orders/components/OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx index 70eebafd1..1ca1898c6 100644 --- a/src/orders/components/OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx +++ b/src/orders/components/OrderDraftDetailsSummary/OrderDraftDetailsSummary.tsx @@ -4,7 +4,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import Link from "@saleor/components/Link"; import Money from "@saleor/components/Money"; diff --git a/src/orders/components/OrderDraftFinalizeDialog/OrderDraftFinalizeDialog.tsx b/src/orders/components/OrderDraftFinalizeDialog/OrderDraftFinalizeDialog.tsx index 4238d299e..f8b72cb60 100644 --- a/src/orders/components/OrderDraftFinalizeDialog/OrderDraftFinalizeDialog.tsx +++ b/src/orders/components/OrderDraftFinalizeDialog/OrderDraftFinalizeDialog.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; diff --git a/src/orders/components/OrderDraftList/OrderDraftList.tsx b/src/orders/components/OrderDraftList/OrderDraftList.tsx index b0c25797d..e6ab22d0b 100644 --- a/src/orders/components/OrderDraftList/OrderDraftList.tsx +++ b/src/orders/components/OrderDraftList/OrderDraftList.tsx @@ -9,7 +9,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import { DateTime } from "@saleor/components/Date"; diff --git a/src/orders/components/OrderDraftListPage/OrderDraftListPage.tsx b/src/orders/components/OrderDraftListPage/OrderDraftListPage.tsx index 9e9d2235c..09b7063c2 100644 --- a/src/orders/components/OrderDraftListPage/OrderDraftListPage.tsx +++ b/src/orders/components/OrderDraftListPage/OrderDraftListPage.tsx @@ -1,7 +1,7 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; diff --git a/src/orders/components/OrderDraftPage/OrderDraftPage.tsx b/src/orders/components/OrderDraftPage/OrderDraftPage.tsx index 074d3a65d..7e357f547 100644 --- a/src/orders/components/OrderDraftPage/OrderDraftPage.tsx +++ b/src/orders/components/OrderDraftPage/OrderDraftPage.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardMenu from "@saleor/components/CardMenu"; diff --git a/src/orders/components/OrderFulfillment/OrderFulfillment.tsx b/src/orders/components/OrderFulfillment/OrderFulfillment.tsx index 2672675e2..ee4d4241d 100644 --- a/src/orders/components/OrderFulfillment/OrderFulfillment.tsx +++ b/src/orders/components/OrderFulfillment/OrderFulfillment.tsx @@ -13,7 +13,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardMenu from "@saleor/components/CardMenu"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/orders/components/OrderFulfillmentCancelDialog/OrderFulfillmentCancelDialog.tsx b/src/orders/components/OrderFulfillmentCancelDialog/OrderFulfillmentCancelDialog.tsx index 992db9000..8db361464 100644 --- a/src/orders/components/OrderFulfillmentCancelDialog/OrderFulfillmentCancelDialog.tsx +++ b/src/orders/components/OrderFulfillmentCancelDialog/OrderFulfillmentCancelDialog.tsx @@ -10,7 +10,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx b/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx index 23370da00..92657cdb8 100644 --- a/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx +++ b/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx @@ -15,7 +15,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/orders/components/OrderFulfillmentTrackingDialog/OrderFulfillmentTrackingDialog.tsx b/src/orders/components/OrderFulfillmentTrackingDialog/OrderFulfillmentTrackingDialog.tsx index 0f6c9e9c3..df3df08fe 100644 --- a/src/orders/components/OrderFulfillmentTrackingDialog/OrderFulfillmentTrackingDialog.tsx +++ b/src/orders/components/OrderFulfillmentTrackingDialog/OrderFulfillmentTrackingDialog.tsx @@ -4,7 +4,7 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/orders/components/OrderHistory/OrderHistory.tsx b/src/orders/components/OrderHistory/OrderHistory.tsx index 93dda34e4..e04b7e204 100644 --- a/src/orders/components/OrderHistory/OrderHistory.tsx +++ b/src/orders/components/OrderHistory/OrderHistory.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import Form from "@saleor/components/Form"; import Hr from "@saleor/components/Hr"; diff --git a/src/orders/components/OrderList/OrderList.tsx b/src/orders/components/OrderList/OrderList.tsx index 67b0e01b5..b64b8c872 100644 --- a/src/orders/components/OrderList/OrderList.tsx +++ b/src/orders/components/OrderList/OrderList.tsx @@ -9,7 +9,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import { DateTime } from "@saleor/components/Date"; diff --git a/src/orders/components/OrderListFilter/OrderListFilter.tsx b/src/orders/components/OrderListFilter/OrderListFilter.tsx index 6fe3b547d..8651b6b0a 100644 --- a/src/orders/components/OrderListFilter/OrderListFilter.tsx +++ b/src/orders/components/OrderListFilter/OrderListFilter.tsx @@ -1,5 +1,5 @@ import * as moment from "moment-timezone"; -import * as React from "react"; +import React from "react"; import { DateContext } from "@saleor/components/Date/DateContext"; import { FieldType, IFilter } from "@saleor/components/Filter"; diff --git a/src/orders/components/OrderListPage/OrderListPage.tsx b/src/orders/components/OrderListPage/OrderListPage.tsx index 9b20884cf..96d25ba14 100644 --- a/src/orders/components/OrderListPage/OrderListPage.tsx +++ b/src/orders/components/OrderListPage/OrderListPage.tsx @@ -1,7 +1,7 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; diff --git a/src/orders/components/OrderMarkAsPaidDialog/OrderMarkAsPaidDialog.tsx b/src/orders/components/OrderMarkAsPaidDialog/OrderMarkAsPaidDialog.tsx index dc32a3be7..19e83e459 100644 --- a/src/orders/components/OrderMarkAsPaidDialog/OrderMarkAsPaidDialog.tsx +++ b/src/orders/components/OrderMarkAsPaidDialog/OrderMarkAsPaidDialog.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; diff --git a/src/orders/components/OrderPayment/OrderPayment.tsx b/src/orders/components/OrderPayment/OrderPayment.tsx index 31638a022..2701e0c81 100644 --- a/src/orders/components/OrderPayment/OrderPayment.tsx +++ b/src/orders/components/OrderPayment/OrderPayment.tsx @@ -8,7 +8,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { Hr } from "@saleor/components/Hr"; diff --git a/src/orders/components/OrderPaymentDialog/OrderPaymentDialog.tsx b/src/orders/components/OrderPaymentDialog/OrderPaymentDialog.tsx index 89a87c0fe..bdb77880d 100644 --- a/src/orders/components/OrderPaymentDialog/OrderPaymentDialog.tsx +++ b/src/orders/components/OrderPaymentDialog/OrderPaymentDialog.tsx @@ -4,7 +4,7 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/orders/components/OrderPaymentVoidDialog/OrderPaymentVoidDialog.tsx b/src/orders/components/OrderPaymentVoidDialog/OrderPaymentVoidDialog.tsx index 06a36d9b3..28ae4a093 100644 --- a/src/orders/components/OrderPaymentVoidDialog/OrderPaymentVoidDialog.tsx +++ b/src/orders/components/OrderPaymentVoidDialog/OrderPaymentVoidDialog.tsx @@ -4,7 +4,7 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogContentText from "@material-ui/core/DialogContentText"; import DialogTitle from "@material-ui/core/DialogTitle"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx b/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx index 38df75cbe..1acc3292a 100644 --- a/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx +++ b/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx @@ -15,7 +15,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import * as InfiniteScroll from "react-infinite-scroller"; import Checkbox from "@saleor/components/Checkbox"; diff --git a/src/orders/components/OrderShippingMethodEditDialog/OrderShippingMethodEditDialog.tsx b/src/orders/components/OrderShippingMethodEditDialog/OrderShippingMethodEditDialog.tsx index 66803c788..d290f5f03 100644 --- a/src/orders/components/OrderShippingMethodEditDialog/OrderShippingMethodEditDialog.tsx +++ b/src/orders/components/OrderShippingMethodEditDialog/OrderShippingMethodEditDialog.tsx @@ -9,7 +9,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx b/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx index 19949f23f..7a20e34b0 100644 --- a/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx +++ b/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx @@ -7,7 +7,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Money from "@saleor/components/Money"; diff --git a/src/orders/containers/OrderOperations.tsx b/src/orders/containers/OrderOperations.tsx index 2a9297fcd..f41eeb3c2 100644 --- a/src/orders/containers/OrderOperations.tsx +++ b/src/orders/containers/OrderOperations.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { getMutationProviderData } from "../../misc"; import { PartialMutationProviderOutput } from "../../types"; diff --git a/src/orders/index.tsx b/src/orders/index.tsx index 09490bb35..7dad19c21 100644 --- a/src/orders/index.tsx +++ b/src/orders/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/orders/urls.ts b/src/orders/urls.ts index 5edbbd758..c54540c6c 100644 --- a/src/orders/urls.ts +++ b/src/orders/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { ActiveTab, diff --git a/src/orders/views/OrderDetails/OrderDetailsMessages.tsx b/src/orders/views/OrderDetails/OrderDetailsMessages.tsx index e4383c107..d24d7f741 100644 --- a/src/orders/views/OrderDetails/OrderDetailsMessages.tsx +++ b/src/orders/views/OrderDetails/OrderDetailsMessages.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; diff --git a/src/orders/views/OrderDetails/index.tsx b/src/orders/views/OrderDetails/index.tsx index 1afec5dcd..a1cb70178 100644 --- a/src/orders/views/OrderDetails/index.tsx +++ b/src/orders/views/OrderDetails/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/orders/views/OrderDraftList.tsx b/src/orders/views/OrderDraftList.tsx index e73c966b8..17e9ebf2a 100644 --- a/src/orders/views/OrderDraftList.tsx +++ b/src/orders/views/OrderDraftList.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/orders/views/OrderList/OrderList.tsx b/src/orders/views/OrderList/OrderList.tsx index f0b19205e..7c40ef420 100644 --- a/src/orders/views/OrderList/OrderList.tsx +++ b/src/orders/views/OrderList/OrderList.tsx @@ -1,5 +1,5 @@ import Button from "@material-ui/core/Button"; -import * as React from "react"; +import React from "react"; import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; import SaveFilterTabDialog, { diff --git a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx index 18cbd54b9..d55ad79e8 100644 --- a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx +++ b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx @@ -4,7 +4,7 @@ import { convertToRaw, RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/pages/components/PageInfo/PageInfo.tsx b/src/pages/components/PageInfo/PageInfo.tsx index 3d7d5de9e..db53dcb7e 100644 --- a/src/pages/components/PageInfo/PageInfo.tsx +++ b/src/pages/components/PageInfo/PageInfo.tsx @@ -2,7 +2,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; diff --git a/src/pages/components/PageList/PageList.tsx b/src/pages/components/PageList/PageList.tsx index ad77e8d68..a02195c84 100644 --- a/src/pages/components/PageList/PageList.tsx +++ b/src/pages/components/PageList/PageList.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/pages/components/PageListPage/PageListPage.tsx b/src/pages/components/PageListPage/PageListPage.tsx index 1e6a72bc9..d5666cb8c 100644 --- a/src/pages/components/PageListPage/PageListPage.tsx +++ b/src/pages/components/PageListPage/PageListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import Container from "@saleor/components/Container"; diff --git a/src/pages/components/PageSlug/PageSlug.tsx b/src/pages/components/PageSlug/PageSlug.tsx index b251e4af2..0cfa9c7ba 100644 --- a/src/pages/components/PageSlug/PageSlug.tsx +++ b/src/pages/components/PageSlug/PageSlug.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import slugify from "slugify"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index d156d0941..e45c1b49f 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/pages/urls.ts b/src/pages/urls.ts index 6761dd81f..dd4c6c88f 100644 --- a/src/pages/urls.ts +++ b/src/pages/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { BulkAction, Dialog, Pagination } from "../types"; diff --git a/src/pages/views/PageCreate.tsx b/src/pages/views/PageCreate.tsx index 367d4b929..615deab40 100644 --- a/src/pages/views/PageCreate.tsx +++ b/src/pages/views/PageCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/pages/views/PageDetails.tsx b/src/pages/views/PageDetails.tsx index e8b64a283..90d0e1b00 100644 --- a/src/pages/views/PageDetails.tsx +++ b/src/pages/views/PageDetails.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/pages/views/PageList.tsx b/src/pages/views/PageList.tsx index bdfea8fbb..c22b5e6d6 100644 --- a/src/pages/views/PageList.tsx +++ b/src/pages/views/PageList.tsx @@ -2,7 +2,7 @@ import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/productTypes/components/ProductTypeAttributeEditDialog/ProductTypeAttributeEditDialog.tsx b/src/productTypes/components/ProductTypeAttributeEditDialog/ProductTypeAttributeEditDialog.tsx index 7e431962c..e275ed5c8 100644 --- a/src/productTypes/components/ProductTypeAttributeEditDialog/ProductTypeAttributeEditDialog.tsx +++ b/src/productTypes/components/ProductTypeAttributeEditDialog/ProductTypeAttributeEditDialog.tsx @@ -4,7 +4,7 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import Form from "@saleor/components/Form"; import { FormSpacer } from "@saleor/components/FormSpacer"; diff --git a/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx b/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx index e75f41ab4..68bec7865 100644 --- a/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx +++ b/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx @@ -13,7 +13,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/productTypes/components/ProductTypeCreatePage/ProductTypeCreatePage.tsx b/src/productTypes/components/ProductTypeCreatePage/ProductTypeCreatePage.tsx index 76dfcda74..d59b23daf 100644 --- a/src/productTypes/components/ProductTypeCreatePage/ProductTypeCreatePage.tsx +++ b/src/productTypes/components/ProductTypeCreatePage/ProductTypeCreatePage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/productTypes/components/ProductTypeDetails/ProductTypeDetails.tsx b/src/productTypes/components/ProductTypeDetails/ProductTypeDetails.tsx index 984b1f03d..e25977bd0 100644 --- a/src/productTypes/components/ProductTypeDetails/ProductTypeDetails.tsx +++ b/src/productTypes/components/ProductTypeDetails/ProductTypeDetails.tsx @@ -2,7 +2,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import i18n from "../../../i18n"; diff --git a/src/productTypes/components/ProductTypeDetailsPage/ProductTypeDetailsPage.tsx b/src/productTypes/components/ProductTypeDetailsPage/ProductTypeDetailsPage.tsx index bffc67b0d..051cf9b4c 100644 --- a/src/productTypes/components/ProductTypeDetailsPage/ProductTypeDetailsPage.tsx +++ b/src/productTypes/components/ProductTypeDetailsPage/ProductTypeDetailsPage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/productTypes/components/ProductTypeList/ProductTypeList.tsx b/src/productTypes/components/ProductTypeList/ProductTypeList.tsx index 6ad65504f..b95404c7c 100644 --- a/src/productTypes/components/ProductTypeList/ProductTypeList.tsx +++ b/src/productTypes/components/ProductTypeList/ProductTypeList.tsx @@ -11,7 +11,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx b/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx index 52c754557..67d130f2e 100644 --- a/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx +++ b/src/productTypes/components/ProductTypeListPage/ProductTypeListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import Container from "@saleor/components/Container"; diff --git a/src/productTypes/components/ProductTypeShipping/ProductTypeShipping.tsx b/src/productTypes/components/ProductTypeShipping/ProductTypeShipping.tsx index 5ad7b53e8..be0b5daa2 100644 --- a/src/productTypes/components/ProductTypeShipping/ProductTypeShipping.tsx +++ b/src/productTypes/components/ProductTypeShipping/ProductTypeShipping.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; diff --git a/src/productTypes/components/ProductTypeTaxes/ProductTypeTaxes.tsx b/src/productTypes/components/ProductTypeTaxes/ProductTypeTaxes.tsx index c824355b4..77dff8964 100644 --- a/src/productTypes/components/ProductTypeTaxes/ProductTypeTaxes.tsx +++ b/src/productTypes/components/ProductTypeTaxes/ProductTypeTaxes.tsx @@ -1,6 +1,6 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import SingleSelectField from "@saleor/components/SingleSelectField"; diff --git a/src/productTypes/containers/ProductTypeOperations.tsx b/src/productTypes/containers/ProductTypeOperations.tsx index f99152083..ce34e2660 100644 --- a/src/productTypes/containers/ProductTypeOperations.tsx +++ b/src/productTypes/containers/ProductTypeOperations.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { getMutationProviderData } from "../../misc"; import { PartialMutationProviderOutput } from "../../types"; diff --git a/src/productTypes/index.tsx b/src/productTypes/index.tsx index c5232cfe0..b80cb3dcf 100644 --- a/src/productTypes/index.tsx +++ b/src/productTypes/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/productTypes/urls.ts b/src/productTypes/urls.ts index 509f22d47..2ba27d635 100644 --- a/src/productTypes/urls.ts +++ b/src/productTypes/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { BulkAction, Dialog, Pagination, SingleAction } from "../types"; import { AttributeTypeEnum } from "../types/globalTypes"; diff --git a/src/productTypes/views/ProductTypeCreate.tsx b/src/productTypes/views/ProductTypeCreate.tsx index 1b21b96f2..51bc8cab0 100644 --- a/src/productTypes/views/ProductTypeCreate.tsx +++ b/src/productTypes/views/ProductTypeCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/productTypes/views/ProductTypeList.tsx b/src/productTypes/views/ProductTypeList.tsx index 40d66ef2f..101b9fc26 100644 --- a/src/productTypes/views/ProductTypeList.tsx +++ b/src/productTypes/views/ProductTypeList.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/productTypes/views/ProductTypeUpdate/errors.tsx b/src/productTypes/views/ProductTypeUpdate/errors.tsx index 851bf6c30..d61ce121b 100644 --- a/src/productTypes/views/ProductTypeUpdate/errors.tsx +++ b/src/productTypes/views/ProductTypeUpdate/errors.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { UserError } from "../../../types"; diff --git a/src/productTypes/views/ProductTypeUpdate/index.tsx b/src/productTypes/views/ProductTypeUpdate/index.tsx index d7099f31b..25348b6ec 100644 --- a/src/productTypes/views/ProductTypeUpdate/index.tsx +++ b/src/productTypes/views/ProductTypeUpdate/index.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/products/components/ProductCategoryAndCollectionsForm/ProductCategoryAndCollectionsForm.tsx b/src/products/components/ProductCategoryAndCollectionsForm/ProductCategoryAndCollectionsForm.tsx index ae890a1a2..0e109b9a6 100644 --- a/src/products/components/ProductCategoryAndCollectionsForm/ProductCategoryAndCollectionsForm.tsx +++ b/src/products/components/ProductCategoryAndCollectionsForm/ProductCategoryAndCollectionsForm.tsx @@ -1,6 +1,6 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; -import * as React from "react"; +import React from "react"; import FormSpacer from "@saleor/components/FormSpacer"; import MultiSelectField from "@saleor/components/MultiSelectField"; diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index ce65f194e..216e575a4 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -1,5 +1,5 @@ import { RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx b/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx index 2c3c47074..66fef810b 100644 --- a/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx +++ b/src/products/components/ProductDetailsForm/ProductDetailsForm.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; diff --git a/src/products/components/ProductImageNavigation/ProductImageNavigation.tsx b/src/products/components/ProductImageNavigation/ProductImageNavigation.tsx index ca3112569..ad9d8937c 100644 --- a/src/products/components/ProductImageNavigation/ProductImageNavigation.tsx +++ b/src/products/components/ProductImageNavigation/ProductImageNavigation.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/products/components/ProductImagePage/ProductImagePage.tsx b/src/products/components/ProductImagePage/ProductImagePage.tsx index 4363e2546..f5b4ebe6e 100644 --- a/src/products/components/ProductImagePage/ProductImagePage.tsx +++ b/src/products/components/ProductImagePage/ProductImagePage.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/products/components/ProductImages/ProductImages.tsx b/src/products/components/ProductImages/ProductImages.tsx index 1cef09dcd..a4727eca5 100644 --- a/src/products/components/ProductImages/ProductImages.tsx +++ b/src/products/components/ProductImages/ProductImages.tsx @@ -12,7 +12,7 @@ import { import CardTitle from "@saleor/components/CardTitle"; import ImageTile from "@saleor/components/ImageTile"; import ImageUpload from "@saleor/components/ImageUpload"; -import * as React from "react"; +import React from "react"; import { SortableContainer, SortableElement } from "react-sortable-hoc"; import i18n from "../../../i18n"; import { ProductDetails_product_images } from "../../types/ProductDetails"; diff --git a/src/products/components/ProductListCard/ProductListCard.tsx b/src/products/components/ProductListCard/ProductListCard.tsx index c14617933..618c7a480 100644 --- a/src/products/components/ProductListCard/ProductListCard.tsx +++ b/src/products/components/ProductListCard/ProductListCard.tsx @@ -1,7 +1,7 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; diff --git a/src/products/components/ProductListFilter/ProductListFilter.tsx b/src/products/components/ProductListFilter/ProductListFilter.tsx index a9db1f5cf..4381325cb 100644 --- a/src/products/components/ProductListFilter/ProductListFilter.tsx +++ b/src/products/components/ProductListFilter/ProductListFilter.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { FieldType, IFilter } from "@saleor/components/Filter"; import FilterBar from "@saleor/components/FilterBar"; diff --git a/src/products/components/ProductOrganization/ProductOrganization.tsx b/src/products/components/ProductOrganization/ProductOrganization.tsx index 1081878db..2c757074f 100644 --- a/src/products/components/ProductOrganization/ProductOrganization.tsx +++ b/src/products/components/ProductOrganization/ProductOrganization.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardSpacer from "@saleor/components/CardSpacer"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/products/components/ProductPricing/ProductPricing.tsx b/src/products/components/ProductPricing/ProductPricing.tsx index 41cb7b50a..fc22a32f0 100644 --- a/src/products/components/ProductPricing/ProductPricing.tsx +++ b/src/products/components/ProductPricing/ProductPricing.tsx @@ -6,7 +6,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; diff --git a/src/products/components/ProductStock/ProductStock.tsx b/src/products/components/ProductStock/ProductStock.tsx index f861b817c..3407fcb84 100644 --- a/src/products/components/ProductStock/ProductStock.tsx +++ b/src/products/components/ProductStock/ProductStock.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import i18n from "../../../i18n"; diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 6f1d6f0aa..93db6711c 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -1,5 +1,5 @@ import { convertFromRaw, RawDraftContentState } from "draft-js"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx b/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx index 06a49a06c..1d45faf12 100644 --- a/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx +++ b/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx @@ -6,7 +6,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField"; diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index b865a7b3d..5ea1ef7fb 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/products/components/ProductVariantDeleteDialog/ProductVariantDeleteDialog.tsx b/src/products/components/ProductVariantDeleteDialog/ProductVariantDeleteDialog.tsx index 73569e58e..b900e973b 100644 --- a/src/products/components/ProductVariantDeleteDialog/ProductVariantDeleteDialog.tsx +++ b/src/products/components/ProductVariantDeleteDialog/ProductVariantDeleteDialog.tsx @@ -10,7 +10,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/products/components/ProductVariantImageSelectDialog/ProductVariantImageSelectDialog.tsx b/src/products/components/ProductVariantImageSelectDialog/ProductVariantImageSelectDialog.tsx index 19c5bde55..1708e288e 100644 --- a/src/products/components/ProductVariantImageSelectDialog/ProductVariantImageSelectDialog.tsx +++ b/src/products/components/ProductVariantImageSelectDialog/ProductVariantImageSelectDialog.tsx @@ -10,7 +10,7 @@ import { WithStyles } from "@material-ui/core/styles"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import i18n from "../../../i18n"; import { ProductImage } from "../../types/ProductImage"; diff --git a/src/products/components/ProductVariantImages/ProductVariantImages.tsx b/src/products/components/ProductVariantImages/ProductVariantImages.tsx index b111281cb..6dda4936c 100644 --- a/src/products/components/ProductVariantImages/ProductVariantImages.tsx +++ b/src/products/components/ProductVariantImages/ProductVariantImages.tsx @@ -8,7 +8,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx index 34efe0235..45252d754 100644 --- a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx +++ b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx @@ -11,7 +11,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 90caa1290..1dd8be01e 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx index 7166242bf..077a4b49d 100644 --- a/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx +++ b/src/products/components/ProductVariantPrice/ProductVariantPrice.tsx @@ -6,7 +6,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import PriceField from "@saleor/components/PriceField"; diff --git a/src/products/components/ProductVariantStock/ProductVariantStock.tsx b/src/products/components/ProductVariantStock/ProductVariantStock.tsx index 9e07de8b6..748f85362 100644 --- a/src/products/components/ProductVariantStock/ProductVariantStock.tsx +++ b/src/products/components/ProductVariantStock/ProductVariantStock.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import i18n from "../../../i18n"; diff --git a/src/products/components/ProductVariants/ProductVariants.tsx b/src/products/components/ProductVariants/ProductVariants.tsx index 389834ac2..5e7acbe77 100644 --- a/src/products/components/ProductVariants/ProductVariants.tsx +++ b/src/products/components/ProductVariants/ProductVariants.tsx @@ -13,7 +13,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; diff --git a/src/products/containers/ProductImagesReorder.tsx b/src/products/containers/ProductImagesReorder.tsx index 3a9cd2d7b..bd49b8a4a 100644 --- a/src/products/containers/ProductImagesReorder.tsx +++ b/src/products/containers/ProductImagesReorder.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { TypedMutationInnerProps } from "../../mutations"; import { TypedProductImagesReorder } from "../mutations"; diff --git a/src/products/containers/ProductUpdateOperations.tsx b/src/products/containers/ProductUpdateOperations.tsx index e00558c41..3ed040bbc 100644 --- a/src/products/containers/ProductUpdateOperations.tsx +++ b/src/products/containers/ProductUpdateOperations.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { getMutationProviderData, maybe } from "../../misc"; import { PartialMutationProviderOutput } from "../../types"; diff --git a/src/products/containers/ProductVariantOperations.tsx b/src/products/containers/ProductVariantOperations.tsx index a636ac989..d93bf6bde 100644 --- a/src/products/containers/ProductVariantOperations.tsx +++ b/src/products/containers/ProductVariantOperations.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { getMutationProviderData } from "../../misc"; import { PartialMutationProviderOutput } from "../../types"; diff --git a/src/products/index.tsx b/src/products/index.tsx index d5023e58f..c82475310 100644 --- a/src/products/index.tsx +++ b/src/products/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/products/urls.ts b/src/products/urls.ts index 7f9fa09ce..2a62be3bd 100644 --- a/src/products/urls.ts +++ b/src/products/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { ActiveTab, BulkAction, Dialog, Filters, Pagination } from "../types"; diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 7a996113a..78db87e84 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/products/views/ProductImage.tsx b/src/products/views/ProductImage.tsx index e2c6601bc..bb7e4a193 100644 --- a/src/products/views/ProductImage.tsx +++ b/src/products/views/ProductImage.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index ad411bf24..7494e6053 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -2,7 +2,7 @@ import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; diff --git a/src/products/views/ProductUpdate.tsx b/src/products/views/ProductUpdate.tsx index c97645a79..c67671c79 100644 --- a/src/products/views/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import { arrayMove } from "react-sortable-hoc"; import * as placeholderImg from "@assets/images/placeholder255x255.png"; diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index be42c88f7..6af9aa183 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import * as placeholderImg from "@assets/images/placeholder255x255.png"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index f2dfa4f09..848f1480e 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/queries.tsx b/src/queries.tsx index 2364ee6b7..43a066183 100644 --- a/src/queries.tsx +++ b/src/queries.tsx @@ -1,6 +1,6 @@ import { DocumentNode } from "graphql"; import gql from "graphql-tag"; -import * as React from "react"; +import React from "react"; import { Query, QueryResult } from "react-apollo"; import { ApolloQueryResult } from "apollo-client"; diff --git a/src/shipping/components/ShippingWeightUnitForm/ShippingWeightUnitForm.tsx b/src/shipping/components/ShippingWeightUnitForm/ShippingWeightUnitForm.tsx index b1f72dc6b..ef3f1065e 100644 --- a/src/shipping/components/ShippingWeightUnitForm/ShippingWeightUnitForm.tsx +++ b/src/shipping/components/ShippingWeightUnitForm/ShippingWeightUnitForm.tsx @@ -2,7 +2,7 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; import CardActions from "@material-ui/core/CardActions"; import CardContent from "@material-ui/core/CardContent"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Form from "@saleor/components/Form"; diff --git a/src/shipping/components/ShippingZoneCountriesAssignDialog/ShippingZoneCountriesAssignDialog.tsx b/src/shipping/components/ShippingZoneCountriesAssignDialog/ShippingZoneCountriesAssignDialog.tsx index 2f7ce5238..9f59522bd 100644 --- a/src/shipping/components/ShippingZoneCountriesAssignDialog/ShippingZoneCountriesAssignDialog.tsx +++ b/src/shipping/components/ShippingZoneCountriesAssignDialog/ShippingZoneCountriesAssignDialog.tsx @@ -16,7 +16,7 @@ import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import { filter } from "fuzzaldrin"; -import * as React from "react"; +import React from "react"; import Checkbox from "@saleor/components/Checkbox"; import ConfirmButton, { diff --git a/src/shipping/components/ShippingZoneCreatePage/ShippingZoneCreatePage.tsx b/src/shipping/components/ShippingZoneCreatePage/ShippingZoneCreatePage.tsx index ee805fb05..63f27bf09 100644 --- a/src/shipping/components/ShippingZoneCreatePage/ShippingZoneCreatePage.tsx +++ b/src/shipping/components/ShippingZoneCreatePage/ShippingZoneCreatePage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx index 96040ad4e..a6d573f68 100644 --- a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx +++ b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx b/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx index ff17908e5..58abf77a9 100644 --- a/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx +++ b/src/shipping/components/ShippingZoneInfo/ShippingZoneInfo.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import i18n from "../../../i18n"; diff --git a/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.tsx b/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.tsx index 75c0b05dc..e08f6e5dd 100644 --- a/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.tsx +++ b/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.tsx @@ -11,7 +11,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx b/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx index 589edd5b7..2d3c41e7e 100644 --- a/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx +++ b/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx @@ -13,7 +13,7 @@ import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; import EditIcon from "@material-ui/icons/Edit"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import IconButtonTableCell from "@saleor/components/IconButtonTableCell"; diff --git a/src/shipping/components/ShippingZonesList/ShippingZonesList.tsx b/src/shipping/components/ShippingZonesList/ShippingZonesList.tsx index 29974968e..838fe4f14 100644 --- a/src/shipping/components/ShippingZonesList/ShippingZonesList.tsx +++ b/src/shipping/components/ShippingZonesList/ShippingZonesList.tsx @@ -13,7 +13,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; diff --git a/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx b/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx index 45042f23c..f63fddfe1 100644 --- a/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx +++ b/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import Container from "@saleor/components/Container"; diff --git a/src/shipping/index.tsx b/src/shipping/index.tsx index ca38149ef..6ba390e46 100644 --- a/src/shipping/index.tsx +++ b/src/shipping/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/shipping/urls.ts b/src/shipping/urls.ts index c622da0a7..be4d3d316 100644 --- a/src/shipping/urls.ts +++ b/src/shipping/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { BulkAction, Dialog, Pagination, SingleAction } from "../types"; import { ShippingMethodTypeEnum } from "../types/globalTypes"; diff --git a/src/shipping/views/ShippingZoneCreate.tsx b/src/shipping/views/ShippingZoneCreate.tsx index f185d026b..b474dfdea 100644 --- a/src/shipping/views/ShippingZoneCreate.tsx +++ b/src/shipping/views/ShippingZoneCreate.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; diff --git a/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.tsx b/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.tsx index 228ff0063..48173f196 100644 --- a/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.tsx +++ b/src/shipping/views/ShippingZoneDetails/ShippingZoneDetailsDialogs.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; diff --git a/src/shipping/views/ShippingZoneDetails/ShippingZoneOperations.tsx b/src/shipping/views/ShippingZoneDetails/ShippingZoneOperations.tsx index 59d3909ad..bf7e0ca1a 100644 --- a/src/shipping/views/ShippingZoneDetails/ShippingZoneOperations.tsx +++ b/src/shipping/views/ShippingZoneDetails/ShippingZoneOperations.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { getMutationProviderData } from "../../../misc"; import { PartialMutationProviderOutput } from "../../../types"; diff --git a/src/shipping/views/ShippingZoneDetails/index.tsx b/src/shipping/views/ShippingZoneDetails/index.tsx index e898c0113..46b72770e 100644 --- a/src/shipping/views/ShippingZoneDetails/index.tsx +++ b/src/shipping/views/ShippingZoneDetails/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; diff --git a/src/shipping/views/ShippingZonesList.tsx b/src/shipping/views/ShippingZonesList.tsx index 1debefce2..6559a4fa9 100644 --- a/src/shipping/views/ShippingZonesList.tsx +++ b/src/shipping/views/ShippingZonesList.tsx @@ -1,7 +1,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/siteSettings/components/SiteSettingsDetails/SiteSettingsDetails.tsx b/src/siteSettings/components/SiteSettingsDetails/SiteSettingsDetails.tsx index 136f9c1be..f3cf77d2b 100644 --- a/src/siteSettings/components/SiteSettingsDetails/SiteSettingsDetails.tsx +++ b/src/siteSettings/components/SiteSettingsDetails/SiteSettingsDetails.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; diff --git a/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx b/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx index 37c0bdab0..8c4a60285 100644 --- a/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx +++ b/src/siteSettings/components/SiteSettingsKeyDialog/SiteSettingsKeyDialog.tsx @@ -4,7 +4,7 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; -import * as React from "react"; +import React from "react"; import Form, { FormProps } from "@saleor/components/Form"; import { FormSpacer } from "@saleor/components/FormSpacer"; diff --git a/src/siteSettings/components/SiteSettingsKeys/SiteSettingsKeys.tsx b/src/siteSettings/components/SiteSettingsKeys/SiteSettingsKeys.tsx index 8dcbd39c0..0a22686fd 100644 --- a/src/siteSettings/components/SiteSettingsKeys/SiteSettingsKeys.tsx +++ b/src/siteSettings/components/SiteSettingsKeys/SiteSettingsKeys.tsx @@ -13,7 +13,7 @@ import TableCell from "@material-ui/core/TableCell"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; diff --git a/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx b/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx index 9bc9a9694..5cfa4c6f3 100644 --- a/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx +++ b/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx @@ -1,5 +1,5 @@ import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; diff --git a/src/siteSettings/index.tsx b/src/siteSettings/index.tsx index 14598e14c..3d83171f7 100644 --- a/src/siteSettings/index.tsx +++ b/src/siteSettings/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps } from "react-router-dom"; import { siteSettingsPath, SiteSettingsUrlQueryParams } from "./urls"; diff --git a/src/siteSettings/views/index.tsx b/src/siteSettings/views/index.tsx index b0db5c7ea..a1032f38e 100644 --- a/src/siteSettings/views/index.tsx +++ b/src/siteSettings/views/index.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; diff --git a/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.tsx b/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.tsx index 0d97ee9b0..f4d218a5b 100644 --- a/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.tsx +++ b/src/staff/components/StaffAddMemberDialog/StaffAddMemberDialog.tsx @@ -11,7 +11,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState diff --git a/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx b/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx index 9083b22a0..7156935e3 100644 --- a/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx +++ b/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import CardSpacer from "@saleor/components/CardSpacer"; diff --git a/src/staff/components/StaffList/StaffList.tsx b/src/staff/components/StaffList/StaffList.tsx index 0216dd234..f15a69e6a 100644 --- a/src/staff/components/StaffList/StaffList.tsx +++ b/src/staff/components/StaffList/StaffList.tsx @@ -13,7 +13,7 @@ import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; import * as classNames from "classnames"; -import * as React from "react"; +import React from "react"; import Skeleton from "@saleor/components/Skeleton"; import TablePagination from "@saleor/components/TablePagination"; diff --git a/src/staff/components/StaffListPage/StaffListPage.tsx b/src/staff/components/StaffListPage/StaffListPage.tsx index b5a5f0fd1..9aabdf949 100644 --- a/src/staff/components/StaffListPage/StaffListPage.tsx +++ b/src/staff/components/StaffListPage/StaffListPage.tsx @@ -1,6 +1,6 @@ import Button from "@material-ui/core/Button"; import AddIcon from "@material-ui/icons/Add"; -import * as React from "react"; +import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import { Container } from "@saleor/components/Container"; diff --git a/src/staff/components/StaffPermissions/StaffPermissions.tsx b/src/staff/components/StaffPermissions/StaffPermissions.tsx index f992b6051..d67db42a2 100644 --- a/src/staff/components/StaffPermissions/StaffPermissions.tsx +++ b/src/staff/components/StaffPermissions/StaffPermissions.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; diff --git a/src/staff/components/StaffProperties/StaffProperties.tsx b/src/staff/components/StaffProperties/StaffProperties.tsx index b58a5cc18..e4312e044 100644 --- a/src/staff/components/StaffProperties/StaffProperties.tsx +++ b/src/staff/components/StaffProperties/StaffProperties.tsx @@ -8,7 +8,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import SVG from "react-inlinesvg"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/staff/components/StaffStatus/StaffStatus.tsx b/src/staff/components/StaffStatus/StaffStatus.tsx index 20fab8d68..a988d888a 100644 --- a/src/staff/components/StaffStatus/StaffStatus.tsx +++ b/src/staff/components/StaffStatus/StaffStatus.tsx @@ -1,7 +1,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Typography from "@material-ui/core/Typography"; -import * as React from "react"; +import React from "react"; import CardTitle from "@saleor/components/CardTitle"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; diff --git a/src/staff/fixtures.ts b/src/staff/fixtures.ts index d403007d2..465ddc169 100644 --- a/src/staff/fixtures.ts +++ b/src/staff/fixtures.ts @@ -1,4 +1,4 @@ -import * as avatarImage from "@assets/images/avatars/avatar1.png"; +import avatarImage from "@assets/images/avatars/avatar1.png"; import { PermissionEnum } from "../types/globalTypes"; import { StaffList_staffUsers_edges_node } from "./types/StaffList"; import { StaffMemberDetails_user } from "./types/StaffMemberDetails"; diff --git a/src/staff/index.tsx b/src/staff/index.tsx index 5413b5995..66dea74b0 100644 --- a/src/staff/index.tsx +++ b/src/staff/index.tsx @@ -1,5 +1,5 @@ import { parse as parseQs } from "qs"; -import * as React from "react"; +import React from "react"; import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; diff --git a/src/staff/urls.ts b/src/staff/urls.ts index 56c2211a6..4508afdfd 100644 --- a/src/staff/urls.ts +++ b/src/staff/urls.ts @@ -1,5 +1,5 @@ import { stringify as stringifyQs } from "qs"; -import * as urlJoin from "url-join"; +import urlJoin from "url-join"; import { BulkAction, Dialog, Pagination } from "../types"; diff --git a/src/staff/views/StaffDetails.tsx b/src/staff/views/StaffDetails.tsx index 9dfb77312..e71e67f9a 100644 --- a/src/staff/views/StaffDetails.tsx +++ b/src/staff/views/StaffDetails.tsx @@ -1,5 +1,5 @@ import DialogContentText from "@material-ui/core/DialogContentText"; -import * as React from "react"; +import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; diff --git a/src/staff/views/StaffList.tsx b/src/staff/views/StaffList.tsx index 4a33bfbc4..eb3d94f50 100644 --- a/src/staff/views/StaffList.tsx +++ b/src/staff/views/StaffList.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; diff --git a/src/storybook/CardDecorator.tsx b/src/storybook/CardDecorator.tsx index e00a37481..f94b6ea87 100644 --- a/src/storybook/CardDecorator.tsx +++ b/src/storybook/CardDecorator.tsx @@ -1,6 +1,6 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; -import * as React from "react"; +import React from "react"; const CardDecorator = storyFn => ( Date: Fri, 9 Aug 2019 12:26:27 +0200 Subject: [PATCH 03/12] Update configs --- tsconfig.json | 1 + tslint.json | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 851eb5552..b6fe58c9d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "baseUrl": ".", + "esModuleInterop": true, "jsx": "react", "lib": ["es2017", "dom", "esnext"], "sourceMap": true, diff --git a/tslint.json b/tslint.json index f96db258e..4b2ac2eb4 100644 --- a/tslint.json +++ b/tslint.json @@ -7,6 +7,12 @@ "no-console": [true, "log"], "no-implicit-dependencies": false, "no-shadowed-variable": false, - "no-submodule-imports": [true, "@material-ui", "@assets", "@saleor"] + "no-submodule-imports": [ + true, + "@material-ui", + "lodash-es", + "@assets", + "@saleor" + ] } } From bd211a6720b86990610110c51f848b8b30d63a30 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Fri, 9 Aug 2019 13:14:35 +0200 Subject: [PATCH 04/12] Update from saleor/master --- src/auth/components/LoginPage/LoginPage.tsx | 8 +- src/auth/mutations.ts | 3 + src/auth/types/TokenAuth.ts | 6 + src/auth/types/User.ts | 6 + src/auth/types/VerifyToken.ts | 6 + .../components/CategoryList/CategoryList.tsx | 18 +- .../CategoryListPage/CategoryListPage.tsx | 8 +- .../CategoryProductsCard.tsx | 4 + src/categories/views/CategoryList.tsx | 15 +- .../CollectionList/CollectionList.tsx | 18 +- .../CollectionListPage/CollectionListPage.tsx | 4 +- .../CollectionProducts/CollectionProducts.tsx | 58 +- src/collections/fixtures.ts | 40 + src/collections/views/CollectionDetails.tsx | 8 +- src/collections/views/CollectionList.tsx | 14 +- src/components/ActionDialog/ActionDialog.tsx | 2 +- src/components/AddressEdit/AddressEdit.tsx | 30 +- src/components/AppLayout/AppLayout.tsx | 168 +- src/components/AppLayout/MenuList.tsx | 350 +- src/components/AppLayout/MenuNested.tsx | 230 +- src/components/AppLayout/ResponsiveDrawer.tsx | 19 +- src/components/AppLayout/consts.ts | 3 +- src/components/AppLayout/menuStructure.ts | 14 + .../AssignCategoryDialog.tsx | 210 +- .../AssignCollectionDialog.tsx | 213 +- .../AssignProductDialog.tsx | 226 +- src/components/CardTitle/CardTitle.tsx | 4 +- src/components/Checkbox/Checkbox.tsx | 19 +- src/components/Container.tsx | 2 +- src/components/ControlledCheckbox.tsx | 2 +- src/components/ControlledSwitch.tsx | 17 +- src/components/Date/Date.test.tsx | 2 +- src/components/Date/Date.tsx | 2 +- src/components/Date/DateTime.tsx | 2 +- src/components/DraftRenderer.tsx | 2 +- src/components/Dropzone.tsx | 2 +- .../EditableTableCell/EditableTableCell.tsx | 63 +- src/components/ErrorPage/ErrorPage.tsx | 2 +- .../ExtendedPageHeader/ExtendedPageHeader.tsx | 2 +- src/components/Filter/Filter.tsx | 2 +- src/components/Filter/FilterContent.tsx | 2 +- src/components/Filter/FilterElement.tsx | 2 +- src/components/FilterBar/FilterBar.tsx | 1 + src/components/Form/Form.tsx | 170 +- src/components/Form/index.ts | 3 +- src/components/Link.tsx | 2 +- .../MultiAutocompleteSelectField.tsx | 360 +- src/components/NotFoundPage/NotFoundPage.tsx | 4 +- src/components/PageHeader/PageHeader.tsx | 5 +- src/components/ProductList/ProductList.tsx | 285 +- .../RichTextEditor/RichTextEditor.tsx | 107 +- .../SaveButtonBar/SaveButtonBar.tsx | 4 +- src/components/SeoForm/SeoForm.tsx | 7 +- .../SingleAutocompleteSelectField.tsx | 144 +- src/components/Skeleton.tsx | 15 +- src/components/StatusLabel/StatusLabel.tsx | 2 +- src/components/Tab/Tab.tsx | 2 +- .../TableCellAvatar/TableCellAvatar.tsx | 53 +- src/components/TableFilter/FilterChips.tsx | 2 +- src/components/TableHead/TableHead.tsx | 44 +- .../TablePagination/TablePagination.tsx | 18 +- .../TablePaginationActions.tsx | 2 +- .../TextFieldWithChoice.tsx | 2 +- src/components/Theme/ThemeProvider.tsx | 22 +- src/components/Timeline/TimelineNote.tsx | 4 +- src/config.ts | 56 + src/configuration/index.tsx | 44 +- .../CustomerAddressDialog.tsx | 100 +- .../CustomerCreateAddress.tsx | 17 +- .../CustomerCreatePage/CustomerCreatePage.tsx | 109 +- .../CustomerDetails/CustomerDetails.tsx | 2 +- .../CustomerDetailsPage.tsx | 8 +- .../components/CustomerList/CustomerList.tsx | 18 +- .../CustomerListPage/CustomerListPage.tsx | 4 +- src/customers/types.ts | 5 +- src/customers/views/CustomerAddresses.tsx | 35 +- src/customers/views/CustomerCreate.tsx | 10 +- src/customers/views/CustomerList.tsx | 14 +- .../DiscountCategories/DiscountCategories.tsx | 14 +- .../DiscountCollections.tsx | 14 +- .../DiscountProducts/DiscountProducts.tsx | 62 +- .../components/SaleList/SaleList.tsx | 22 +- .../components/SaleListPage/SaleListPage.tsx | 4 +- .../components/SalePricing/SalePricing.tsx | 6 + .../VoucherCreatePage/VoucherCreatePage.tsx | 79 +- .../VoucherDetailsPage/VoucherDetailsPage.tsx | 117 +- .../components/VoucherInfo/VoucherInfo.tsx | 128 +- .../components/VoucherList/VoucherList.tsx | 28 +- .../VoucherListPage/VoucherListPage.tsx | 8 +- .../VoucherSummary/VoucherSummary.tsx | 8 +- src/discounts/fixtures.ts | 21 +- src/discounts/queries.ts | 4 +- src/discounts/translations.ts | 10 +- src/discounts/types/VoucherCataloguesAdd.ts | 11 +- .../types/VoucherCataloguesRemove.ts | 11 +- src/discounts/types/VoucherCreate.ts | 7 +- src/discounts/types/VoucherDetails.ts | 11 +- src/discounts/types/VoucherDetailsFragment.ts | 11 +- src/discounts/types/VoucherFragment.ts | 7 +- src/discounts/types/VoucherList.ts | 7 +- src/discounts/types/VoucherUpdate.ts | 7 +- src/discounts/views/SaleDetails.tsx | 12 +- src/discounts/views/SaleList.tsx | 14 +- src/discounts/views/VoucherCreate.tsx | 51 +- src/discounts/views/VoucherDetails.tsx | 87 +- src/discounts/views/VoucherList.tsx | 14 +- src/fixtures.ts | 20 +- src/home/components/HomeHeader/HomeHeader.tsx | 38 +- .../HomeProductListCard.tsx | 8 +- src/home/queries.ts | 1 - src/home/types/Home.ts | 3 +- src/hooks/useBulkActions.ts | 34 +- src/icons/Calendar.tsx | 2 +- src/icons/Draggable.tsx | 2 +- src/icons/Image.tsx | 2 +- src/icons/Navigation.tsx | 4 +- src/icons/Orders.tsx | 2 +- src/icons/Pages.tsx | 2 +- src/icons/ProductTypes.tsx | 2 +- src/icons/Sales.tsx | 2 +- src/icons/ShippingMethods.tsx | 2 +- src/icons/SiteSettings.tsx | 2 +- src/icons/StaffMembers.tsx | 2 +- src/icons/Taxes.tsx | 2 +- src/index.tsx | 257 +- src/misc.ts | 47 +- .../MenuItemDialog/MenuItemDialog.tsx | 176 +- .../components/MenuList/MenuList.tsx | 21 +- .../components/MenuListPage/MenuListPage.tsx | 4 +- src/navigation/urls.ts | 1 + src/navigation/views/MenuDetails/index.tsx | 21 +- src/navigation/views/MenuDetails/utils.ts | 40 +- src/navigation/views/MenuList.tsx | 16 +- .../OrderAddressEditDialog.tsx | 103 +- .../OrderCustomer/OrderCustomer.tsx | 36 +- .../OrderCustomerEditDialog.tsx | 10 +- .../OrderDraftDetailsProducts.tsx | 60 +- .../OrderDraftList/OrderDraftList.tsx | 20 +- .../OrderDraftListPage/OrderDraftListPage.tsx | 4 +- .../OrderFulfillment/OrderFulfillment.tsx | 63 +- .../OrderFulfillmentDialog.tsx | 96 +- src/orders/components/OrderList/OrderList.tsx | 20 +- .../OrderListFilter/OrderListFilter.tsx | 2 +- .../OrderPaymentDialog/OrderPaymentDialog.tsx | 4 +- .../OrderProductAddDialog.tsx | 447 +- .../OrderUnfulfilledItems.tsx | 57 +- src/orders/views/OrderDetails/index.tsx | 26 +- src/orders/views/OrderDraftList.tsx | 14 +- src/orders/views/OrderList/OrderList.tsx | 12 +- src/pages/components/PageList/PageList.tsx | 21 +- .../components/PageListPage/PageListPage.tsx | 8 +- src/pages/views/PageList.tsx | 17 +- .../ProductTypeAttributes.tsx | 162 +- .../ProductTypeCreatePage.tsx | 70 +- .../ProductTypeDetails/ProductTypeDetails.tsx | 8 +- .../ProductTypeDetailsPage.tsx | 105 +- .../ProductTypeList/ProductTypeList.tsx | 14 +- .../ProductTypeTaxes/ProductTypeTaxes.tsx | 76 +- .../containers/ProductTypeOperations.tsx | 198 +- src/productTypes/fixtures.ts | 128 +- src/productTypes/mutations.ts | 120 +- src/productTypes/queries.ts | 34 +- src/productTypes/types/ProductTypeCreate.ts | 28 +- .../types/ProductTypeCreateData.ts | 7 + src/productTypes/types/ProductTypeDetails.ts | 35 +- .../types/ProductTypeDetailsFragment.ts | 28 +- src/productTypes/types/ProductTypeFragment.ts | 10 +- src/productTypes/types/ProductTypeList.ts | 12 +- src/productTypes/types/ProductTypeUpdate.ts | 28 +- src/productTypes/urls.ts | 11 +- src/productTypes/views/ProductTypeCreate.tsx | 12 +- .../views/ProductTypeUpdate/index.tsx | 475 +- .../ProductCreatePage/ProductCreatePage.tsx | 209 +- .../ProductDetailsForm/ProductDetailsForm.tsx | 19 +- .../ProductImageNavigation.tsx | 2 +- .../ProductImages/ProductImages.tsx | 28 +- .../ProductOrganization.tsx | 356 +- .../ProductUpdatePage/ProductUpdatePage.tsx | 436 +- .../ProductVariantAttributes.tsx | 212 +- .../ProductVariantCreatePage.tsx | 187 +- .../ProductVariantImageSelectDialog.tsx | 2 +- .../ProductVariantNavigation.tsx | 52 +- .../ProductVariantPage/ProductVariantPage.tsx | 248 +- .../ProductVariants/ProductVariants.tsx | 6 +- src/products/fixtures.ts | 147 +- src/products/mutations.ts | 6 + src/products/queries.ts | 11 +- src/products/types/Product.ts | 9 +- src/products/types/ProductCreate.ts | 10 +- src/products/types/ProductCreateData.ts | 5 +- src/products/types/ProductDetails.ts | 9 +- src/products/types/ProductImageCreate.ts | 9 +- src/products/types/ProductImageUpdate.ts | 9 +- src/products/types/ProductUpdate.ts | 10 +- src/products/types/ProductVariant.ts | 3 +- .../types/ProductVariantCreateData.ts | 2 +- src/products/types/ProductVariantDetails.ts | 3 +- src/products/types/SimpleProductUpdate.ts | 13 +- src/products/types/VariantCreate.ts | 3 +- src/products/types/VariantImageAssign.ts | 3 +- src/products/types/VariantImageUnassign.ts | 3 +- src/products/types/VariantUpdate.ts | 3 +- src/products/views/ProductCreate.tsx | 32 +- .../views/ProductList/ProductList.tsx | 29 +- src/products/views/ProductUpdate.tsx | 2 +- .../views/ProductUpdate/ProductUpdate.tsx | 2 +- src/products/views/ProductVariant.tsx | 25 +- src/products/views/ProductVariantCreate.tsx | 26 +- .../ShippingZonesList/ShippingZonesList.tsx | 20 +- .../ShippingZonesListPage.tsx | 6 +- src/shipping/views/ShippingZonesList.tsx | 16 +- .../SiteSettingsPage/SiteSettingsPage.tsx | 126 +- src/siteSettings/fixtures.ts | 26 + src/siteSettings/mutations.ts | 14 + src/siteSettings/queries.ts | 9 + src/siteSettings/types/AuthorizationKeyAdd.ts | 30 + .../types/AuthorizationKeyDelete.ts | 30 + src/siteSettings/types/ShopFragment.ts | 30 + src/siteSettings/types/ShopSettingsUpdate.ts | 73 +- src/siteSettings/types/SiteSettings.ts | 30 + src/siteSettings/views/index.tsx | 19 +- .../StaffDetailsPage/StaffDetailsPage.tsx | 6 +- src/staff/components/StaffList/StaffList.tsx | 12 +- .../StaffListPage/StaffListPage.tsx | 4 +- .../StaffPermissions/StaffPermissions.tsx | 2 +- .../StaffProperties/StaffProperties.tsx | 2 +- src/staff/views/StaffList.tsx | 17 +- src/storybook/Decorator.tsx | 33 +- src/storybook/Stories.test.ts | 10 +- .../__snapshots__/Stories.test.ts.snap | 37874 ++++++++++++---- src/storybook/config.js | 18 +- .../stories/categories/CategoryProducts.tsx | 2 +- .../stories/categories/CategoryUpdatePage.tsx | 8 +- .../collections/CollectionDetailsPage.tsx | 4 +- .../stories/components/AddressEdit.tsx | 7 +- .../components/AssignProductDialog.tsx | 2 +- .../MultiAutocompleteSelectField.tsx | 117 +- .../SingleAutocompleteSelectField.tsx | 108 +- .../configuration/ConfigurationPage.tsx | 4 + src/storybook/stories/home/HomePage.tsx | 2 +- .../stories/navigation/MenuItemDialog.tsx | 22 +- .../orders/OrderCustomerEditDialog.tsx | 6 +- .../stories/orders/OrderDetailsPage.tsx | 2 +- .../stories/orders/OrderDraftPage.tsx | 2 +- .../stories/orders/OrderFulfillmentDialog.tsx | 2 +- .../stories/orders/OrderProductAddDialog.tsx | 2 +- .../productTypes/ProductTypeCreatePage.tsx | 13 +- .../productTypes/ProductTypeDetailsPage.tsx | 30 +- .../stories/products/ProductCreatePage.tsx | 6 +- .../stories/products/ProductImagePage.tsx | 2 +- .../stories/products/ProductListCard.tsx | 2 +- .../stories/products/ProductListPage.tsx | 2 +- .../stories/products/ProductUpdatePage.tsx | 139 +- .../products/ProductVariantCreatePage.tsx | 39 +- .../ProductVariantImageSelectDialog.tsx | 2 +- .../stories/products/ProductVariantPage.tsx | 17 +- src/storybook/webpack.config.js | 2 +- .../components/CountryList/CountryList.tsx | 2 +- src/theme.ts | 145 +- .../TranslationsEntitiesList.tsx | 2 +- src/types.ts | 46 +- src/types/globalTypes.ts | 72 +- 262 files changed, 35477 insertions(+), 13429 deletions(-) diff --git a/src/auth/components/LoginPage/LoginPage.tsx b/src/auth/components/LoginPage/LoginPage.tsx index 148a02d56..b13d25090 100644 --- a/src/auth/components/LoginPage/LoginPage.tsx +++ b/src/auth/components/LoginPage/LoginPage.tsx @@ -10,14 +10,14 @@ import Typography from "@material-ui/core/Typography"; import React from "react"; import SVG from "react-inlinesvg"; -import * as backgroundArt from "@assets/images/login-background.svg"; -import * as saleorDarkLogo from "@assets/images/logo-dark.svg"; -import * as saleorLightLogo from "@assets/images/logo-light.svg"; +import backgroundArt from "@assets/images/login-background.svg"; +import saleorDarkLogo from "@assets/images/logo-dark.svg"; +import saleorLightLogo from "@assets/images/logo-light.svg"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import Form from "@saleor/components/Form"; import { FormSpacer } from "@saleor/components/FormSpacer"; import useTheme from "@saleor/hooks/useTheme"; -import i18n from "../../../i18n"; +import i18n from "@saleor/i18n"; export interface FormData { email: string; diff --git a/src/auth/mutations.ts b/src/auth/mutations.ts index 8d4007ba7..6566fee3f 100644 --- a/src/auth/mutations.ts +++ b/src/auth/mutations.ts @@ -16,6 +16,9 @@ export const fragmentUser = gql` code name } + avatar { + url + } } `; diff --git a/src/auth/types/TokenAuth.ts b/src/auth/types/TokenAuth.ts index a282edc47..3cae7fada 100644 --- a/src/auth/types/TokenAuth.ts +++ b/src/auth/types/TokenAuth.ts @@ -20,6 +20,11 @@ export interface TokenAuth_tokenCreate_user_permissions { name: string; } +export interface TokenAuth_tokenCreate_user_avatar { + __typename: "Image"; + url: string; +} + export interface TokenAuth_tokenCreate_user { __typename: "User"; id: string; @@ -29,6 +34,7 @@ export interface TokenAuth_tokenCreate_user { isStaff: boolean; note: string | null; permissions: (TokenAuth_tokenCreate_user_permissions | null)[] | null; + avatar: TokenAuth_tokenCreate_user_avatar | null; } export interface TokenAuth_tokenCreate { diff --git a/src/auth/types/User.ts b/src/auth/types/User.ts index 6027f7f82..6609b9897 100644 --- a/src/auth/types/User.ts +++ b/src/auth/types/User.ts @@ -14,6 +14,11 @@ export interface User_permissions { name: string; } +export interface User_avatar { + __typename: "Image"; + url: string; +} + export interface User { __typename: "User"; id: string; @@ -23,4 +28,5 @@ export interface User { isStaff: boolean; note: string | null; permissions: (User_permissions | null)[] | null; + avatar: User_avatar | null; } diff --git a/src/auth/types/VerifyToken.ts b/src/auth/types/VerifyToken.ts index 52dc496a4..10e83496e 100644 --- a/src/auth/types/VerifyToken.ts +++ b/src/auth/types/VerifyToken.ts @@ -14,6 +14,11 @@ export interface VerifyToken_tokenVerify_user_permissions { name: string; } +export interface VerifyToken_tokenVerify_user_avatar { + __typename: "Image"; + url: string; +} + export interface VerifyToken_tokenVerify_user { __typename: "User"; id: string; @@ -23,6 +28,7 @@ export interface VerifyToken_tokenVerify_user { isStaff: boolean; note: string | null; permissions: (VerifyToken_tokenVerify_user_permissions | null)[] | null; + avatar: VerifyToken_tokenVerify_user_avatar | null; } export interface VerifyToken_tokenVerify { diff --git a/src/categories/components/CategoryList/CategoryList.tsx b/src/categories/components/CategoryList/CategoryList.tsx index 6b8776b8f..293e04830 100644 --- a/src/categories/components/CategoryList/CategoryList.tsx +++ b/src/categories/components/CategoryList/CategoryList.tsx @@ -18,9 +18,9 @@ import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; -import { renderCollection } from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; const styles = (theme: Theme) => createStyles({ @@ -67,11 +67,14 @@ interface CategoryListProps onAdd?(); } +const numberOfColumns = 4; + const CategoryList = withStyles(styles, { name: "CategoryList" })( ({ categories, classes, disabled, + settings, isRoot, pageInfo, isChecked, @@ -82,6 +85,7 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })( onAdd, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick }: CategoryListProps) => ( @@ -97,6 +101,7 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })( )} toggle(category.id)} /> @@ -173,7 +181,7 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })( }, () => ( - + {isRoot ? i18n.t("No categories found") : i18n.t("No subcategories found")} diff --git a/src/categories/components/CategoryListPage/CategoryListPage.tsx b/src/categories/components/CategoryListPage/CategoryListPage.tsx index e465c255b..c3829c4bd 100644 --- a/src/categories/components/CategoryListPage/CategoryListPage.tsx +++ b/src/categories/components/CategoryListPage/CategoryListPage.tsx @@ -4,8 +4,8 @@ import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; import CategoryList from "../CategoryList"; export interface CategoryTableProps extends PageListProps, ListActions { @@ -24,9 +24,11 @@ export interface CategoryTableProps extends PageListProps, ListActions { export const CategoryListPage: React.StatelessComponent = ({ categories, disabled, + settings, onAdd, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick, pageInfo, isChecked, @@ -46,9 +48,11 @@ export const CategoryListPage: React.StatelessComponent = ({ onAdd={onAdd} onRowClick={onRowClick} disabled={disabled} + settings={settings} isRoot={true} onNextPage={onNextPage} onPreviousPage={onPreviousPage} + onUpdateListSettings={onUpdateListSettings} pageInfo={pageInfo} isChecked={isChecked} selected={selected} diff --git a/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx b/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx index 78f2a2263..e862eb68b 100644 --- a/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx +++ b/src/categories/components/CategoryProductsCard/CategoryProductsCard.tsx @@ -40,6 +40,10 @@ export const CategoryProductsCard: React.StatelessComponent< } /> = ({ const { isSelected, listElements, toggle, toggleAll, reset } = useBulkActions( params.ids ); - - const paginationState = createPaginationState(PAGINATE_BY, params); + const { updateListSettings, settings } = useListSettings( + ListViews.CATEGORY_LIST + ); + const paginationState = createPaginationState(settings.rowNumber, params); return ( {({ data, loading, refetch }) => { @@ -73,11 +76,13 @@ export const CategoryList: React.StatelessComponent = ({ () => data.categories.edges.map(edge => edge.node), [] )} + settings={settings} onAdd={() => navigate(categoryAddUrl())} onRowClick={id => () => navigate(categoryUrl(id))} disabled={loading} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} pageInfo={pageInfo} isChecked={isSelected} selected={listElements.length} diff --git a/src/collections/components/CollectionList/CollectionList.tsx b/src/collections/components/CollectionList/CollectionList.tsx index 6a4b24d9c..fd63f69a3 100644 --- a/src/collections/components/CollectionList/CollectionList.tsx +++ b/src/collections/components/CollectionList/CollectionList.tsx @@ -17,9 +17,9 @@ import Skeleton from "@saleor/components/Skeleton"; import StatusLabel from "@saleor/components/StatusLabel"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; -import { maybe, renderCollection } from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; import { CollectionList_collections_edges_node } from "../../types/CollectionList"; const styles = (theme: Theme) => @@ -50,13 +50,17 @@ interface CollectionListProps collections: CollectionList_collections_edges_node[]; } +const numberOfColumns = 5; + const CollectionList = withStyles(styles, { name: "CollectionList" })( ({ classes, collections, disabled, + settings, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick, pageInfo, isChecked, @@ -68,6 +72,7 @@ const CollectionList = withStyles(styles, { name: "CollectionList" })(
toggle(collection.id)} /> @@ -151,7 +159,7 @@ const CollectionList = withStyles(styles, { name: "CollectionList" })( }, () => ( - + {i18n.t("No collections found")} diff --git a/src/collections/components/CollectionListPage/CollectionListPage.tsx b/src/collections/components/CollectionListPage/CollectionListPage.tsx index d5580c66e..b115569fb 100644 --- a/src/collections/components/CollectionListPage/CollectionListPage.tsx +++ b/src/collections/components/CollectionListPage/CollectionListPage.tsx @@ -4,8 +4,8 @@ import React from "react"; import { Container } from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; import { CollectionList_collections_edges_node } from "../../types/CollectionList"; import CollectionList from "../CollectionList/CollectionList"; diff --git a/src/collections/components/CollectionProducts/CollectionProducts.tsx b/src/collections/components/CollectionProducts/CollectionProducts.tsx index 977c1ed45..b957a489a 100644 --- a/src/collections/components/CollectionProducts/CollectionProducts.tsx +++ b/src/collections/components/CollectionProducts/CollectionProducts.tsx @@ -19,7 +19,9 @@ import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; 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 TablePagination from "@saleor/components/TablePagination"; import i18n from "../../../i18n"; @@ -29,12 +31,27 @@ import { CollectionDetails_collection } from "../../types/CollectionDetails"; const styles = (theme: Theme) => createStyles({ - iconCell: { + colActions: { "&:last-child": { paddingRight: 0 }, width: 48 + theme.spacing.unit / 2 }, + colName: { + width: "auto" + }, + colNameLabel: { + marginLeft: AVATAR_MARGIN + }, + colPublished: { + width: 200 + }, + colType: { + width: 200 + }, + table: { + tableLayout: "fixed" + }, tableRow: { cursor: "pointer" } @@ -48,6 +65,8 @@ export interface CollectionProductsProps onProductUnassign: (id: string, event: React.MouseEvent) => void; } +const numberOfColumns = 5; + const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })( ({ classes, @@ -89,24 +108,32 @@ const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })( } /> -
+
collection.products.edges.map(edge => edge.node))} toggleAll={toggleAll} toolbar={toolbar} > - {i18n.t("Name", { context: "table header" })} - {i18n.t("Type", { context: "table header" })} - + + + {i18n.t("Name", { context: "table header" })} + + + + {i18n.t("Type", { context: "table header" })} + + {i18n.t("Published", { context: "table header" })} + pageInfo.hasNextPage)} onNextPage={onNextPage} hasPreviousPage={maybe(() => pageInfo.hasPreviousPage)} @@ -132,22 +159,23 @@ const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })( toggle(product.id)} /> product.thumbnail.url)} - /> - + > {maybe(() => product.name, )} - - + + {maybe( () => product.productType.name, )} - + {maybe( () => ( )} - + onProductUnassign(product.id, event)} @@ -176,7 +204,9 @@ const CollectionProducts = withStyles(styles, { name: "CollectionProducts" })( () => ( - {i18n.t("No products found")} + + {i18n.t("No products found")} + ) )} diff --git a/src/collections/fixtures.ts b/src/collections/fixtures.ts index 423cf82fc..8fc75b09b 100644 --- a/src/collections/fixtures.ts +++ b/src/collections/fixtures.ts @@ -22,6 +22,46 @@ export const collections: CollectionList_collections_edges_node[] = [ __typename: "ProductCountableConnection", 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: ( diff --git a/src/collections/views/CollectionDetails.tsx b/src/collections/views/CollectionDetails.tsx index 26cb4b48e..bdabd01c1 100644 --- a/src/collections/views/CollectionDetails.tsx +++ b/src/collections/views/CollectionDetails.tsx @@ -287,14 +287,12 @@ export const CollectionDetails: React.StatelessComponent< open={params.action === "assign"} onFetch={search} loading={result.loading} - onClose={() => navigate(collectionUrl(id), true, true)} - onSubmit={formData => + onClose={closeModal} + onSubmit={products => assignProduct.mutate({ ...paginationState, collectionId: id, - productIds: formData.products.map( - product => product.id - ) + productIds: products.map(product => product.id) }) } products={maybe(() => diff --git a/src/collections/views/CollectionList.tsx b/src/collections/views/CollectionList.tsx index 008fcfb4d..90a3f6686 100644 --- a/src/collections/views/CollectionList.tsx +++ b/src/collections/views/CollectionList.tsx @@ -6,14 +6,15 @@ import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import { PAGINATE_BY } from "../../config"; -import i18n from "../../i18n"; -import { getMutationState, maybe } from "../../misc"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import CollectionListPage from "../components/CollectionListPage/CollectionListPage"; import { TypedCollectionBulkDelete, @@ -43,6 +44,9 @@ export const CollectionList: React.StatelessComponent = ({ const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); + const { updateListSettings, settings } = useListSettings( + ListViews.COLLECTION_LIST + ); const closeModal = () => navigate( @@ -62,7 +66,7 @@ export const CollectionList: React.StatelessComponent = ({ }) ); - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); return ( {({ data, loading, refetch }) => { @@ -129,8 +133,10 @@ export const CollectionList: React.StatelessComponent = ({ collections={maybe(() => data.collections.edges.map(edge => edge.node) )} + settings={settings} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} pageInfo={pageInfo} onRowClick={id => () => navigate(collectionUrl(id))} toolbar={ diff --git a/src/components/ActionDialog/ActionDialog.tsx b/src/components/ActionDialog/ActionDialog.tsx index 6387d4ec6..3a5df3a46 100644 --- a/src/components/ActionDialog/ActionDialog.tsx +++ b/src/components/ActionDialog/ActionDialog.tsx @@ -9,7 +9,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import i18n from "../../i18n"; diff --git a/src/components/AddressEdit/AddressEdit.tsx b/src/components/AddressEdit/AddressEdit.tsx index 428340604..7960d3180 100644 --- a/src/components/AddressEdit/AddressEdit.tsx +++ b/src/components/AddressEdit/AddressEdit.tsx @@ -7,12 +7,13 @@ import { import TextField from "@material-ui/core/TextField"; import React from "react"; -import { AddressTypeInput } from "../../customers/types"; -import i18n from "../../i18n"; -import { maybe } from "../../misc"; -import { FormErrors } from "../../types"; +import { AddressTypeInput } from "@saleor/customers/types"; +import i18n from "@saleor/i18n"; +import { FormErrors } from "@saleor/types"; import FormSpacer from "../FormSpacer"; -import SingleAutocompleteSelectField from "../SingleAutocompleteSelectField"; +import SingleAutocompleteSelectField, { + SingleAutocompleteChoiceType +} from "../SingleAutocompleteSelectField"; const styles = (theme: Theme) => createStyles({ @@ -24,24 +25,25 @@ const styles = (theme: Theme) => }); interface AddressEditProps extends WithStyles { - countries?: Array<{ - code: string; - label: string; - }>; + countries: SingleAutocompleteChoiceType[]; + countryDisplayValue: string; data: AddressTypeInput; disabled?: boolean; errors: FormErrors; onChange(event: React.ChangeEvent); + onCountryChange(event: React.ChangeEvent); } const AddressEdit = withStyles(styles, { name: "AddressEdit" })( ({ classes, countries, + countryDisplayValue, data, disabled, errors, - onChange + onChange, + onCountryChange }: AddressEditProps) => ( <>
@@ -152,16 +154,14 @@ const AddressEdit = withStyles(styles, { name: "AddressEdit" })(
countries.map(c => ({ ...c, value: c.code })), - [] - )} + choices={countries} InputProps={{ autoComplete: "off" }} diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index 35aaf7983..350b12752 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -1,6 +1,8 @@ +import Avatar from "@material-ui/core/Avatar"; import Chip from "@material-ui/core/Chip"; import ClickAwayListener from "@material-ui/core/ClickAwayListener"; import Grow from "@material-ui/core/Grow"; +import Hidden from "@material-ui/core/Hidden"; import LinearProgress from "@material-ui/core/LinearProgress"; import MenuItem from "@material-ui/core/MenuItem"; import Menu from "@material-ui/core/MenuList"; @@ -12,23 +14,25 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import SVG from "react-inlinesvg"; import { RouteComponentProps, withRouter } from "react-router"; -import * as saleorDarkLogo from "@assets/images/logo-dark.svg"; -import * as saleorLightLogo from "@assets/images/logo-light.svg"; +import saleorDarkLogoSmall from "@assets/logo-dark-small.svg"; +import saleorDarkLogo from "@assets/logo-dark.svg"; +import menuArrowIcon from "@assets/menu-arrow-icon.svg"; import AppProgressProvider from "@saleor/components/AppProgress"; +import useLocalStorage from "@saleor/hooks/useLocalStorage"; import useNavigator from "@saleor/hooks/useNavigator"; import useTheme from "@saleor/hooks/useTheme"; import useUser from "@saleor/hooks/useUser"; -import i18n from "../../i18n"; -import ArrowDropdown from "../../icons/ArrowDropdown"; +import i18n from "@saleor/i18n"; +import ArrowDropdown from "@saleor/icons/ArrowDropdown"; import Container from "../Container"; import AppActionContext from "./AppActionContext"; import AppHeaderContext from "./AppHeaderContext"; -import { appLoaderHeight, drawerWidth } from "./consts"; +import { appLoaderHeight, drawerWidth, drawerWidthExpanded } from "./consts"; import MenuList from "./MenuList"; import menuStructure from "./menuStructure"; import ResponsiveDrawer from "./ResponsiveDrawer"; @@ -51,9 +55,18 @@ const styles = (theme: Theme) => transition: theme.transitions.duration.standard + "ms" }, content: { - display: "flex", - flexDirection: "column", - minHeight: `calc(100vh - ${appLoaderHeight}px)` + [theme.breakpoints.down("sm")]: { + paddingLeft: 0 + }, + paddingLeft: drawerWidthExpanded, + transition: "padding-left 0.5s ease", + width: "100%" + }, + contentToggle: { + [theme.breakpoints.down("sm")]: { + paddingLeft: 0 + }, + paddingLeft: drawerWidth }, darkThemeSwitch: { marginRight: theme.spacing.unit * 2 @@ -67,20 +80,72 @@ const styles = (theme: Theme) => hide: { 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: { "& svg": { - height: "100%" + height: "100%", + margin: "20px 50px" }, + background: theme.palette.secondary.main, display: "block", - height: 28 + height: 80 + }, + logoDark: { + "& path": { + fill: theme.palette.common.white + }, + background: theme.palette.primary.main + }, + logoSmall: { + "& svg": { + margin: "0px 25px" + } }, menu: { - marginTop: theme.spacing.unit * 4 + background: theme.palette.background.paper, + height: "100vh", + padding: 25 }, menuIcon: { - [theme.breakpoints.up("md")]: { - display: "none" - }, "& span": { "&:nth-child(1)": { top: 15 @@ -101,6 +166,9 @@ const styles = (theme: Theme) => transition: ".25s ease-in-out", width: "60%" }, + [theme.breakpoints.up("md")]: { + display: "none" + }, background: theme.palette.background.paper, borderRadius: "50%", cursor: "pointer", @@ -109,7 +177,7 @@ const styles = (theme: Theme) => marginRight: theme.spacing.unit * 2, position: "relative", transform: "rotate(0deg)", - transition: ".2s ease-in-out", + transition: `${theme.transitions.duration.shorter}ms ease-in-out`, width: 42 }, menuIconDark: { @@ -135,15 +203,16 @@ const styles = (theme: Theme) => position: "absolute", zIndex: 1999 }, + menuSmall: { + background: theme.palette.background.paper, + height: "100vh", + padding: 25 + }, popover: { zIndex: 1 }, root: { - [theme.breakpoints.down("sm")]: { - gridTemplateColumns: "1fr" - }, - display: "grid", - gridTemplateColumns: `${drawerWidth}px 1fr` + width: `100%` }, rotate: { transform: "rotate(180deg)" @@ -153,7 +222,7 @@ const styles = (theme: Theme) => padding: 0 }, background: theme.palette.background.paper, - padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 4}px` + padding: `0 ${theme.spacing.unit * 4}px` }, spacer: { flex: 1 @@ -163,8 +232,8 @@ const styles = (theme: Theme) => display: "flex" }, userChip: { - backgroundColor: theme.palette.common.white, - border: `1px solid ${theme.palette.grey[200]}` + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary }, userMenuContainer: { position: "relative" @@ -191,7 +260,7 @@ interface AppLayoutProps { const AppLayout = withStyles(styles, { name: "AppLayout" })( - withRouter>( + withRouter, any>( ({ classes, children, @@ -200,6 +269,7 @@ const AppLayout = withStyles(styles, { WithStyles & RouteComponentProps) => { const { isDark, toggleTheme } = useTheme(); + const [isMenuSmall, setMenuSmall] = useLocalStorage("isMenuSmall", false); const [isDrawerOpened, setDrawerState] = React.useState(false); const [isMenuOpened, setMenuState] = React.useState(false); const appActionAnchor = React.useRef(); @@ -223,6 +293,10 @@ const AppLayout = withStyles(styles, { navigate(url); }; + const handleIsMenuSmall = () => { + setMenuSmall(!isMenuSmall); + }; + return ( {({ isProgress }) => ( @@ -239,14 +313,37 @@ const AppLayout = withStyles(styles, { setDrawerState(false)} open={isDrawerOpened} + small={!isMenuSmall} > - +
+ +
+ +
+ +
+
-
+
@@ -283,6 +384,11 @@ const AppLayout = withStyles(styles, { ref={anchor} > + ) + } className={classes.userChip} label={ <> diff --git a/src/components/AppLayout/MenuList.tsx b/src/components/AppLayout/MenuList.tsx index 994e0225d..96eb2999c 100644 --- a/src/components/AppLayout/MenuList.tsx +++ b/src/components/AppLayout/MenuList.tsx @@ -7,8 +7,11 @@ import { import Typography from "@material-ui/core/Typography"; import classNames from "classnames"; import React from "react"; +import SVG from "react-inlinesvg"; 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 { configurationMenu, configurationMenuUrl } from "../../configuration"; import i18n from "../../i18n"; @@ -19,6 +22,44 @@ import { IMenuItem } from "./menuStructure"; const styles = (theme: Theme) => 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: { display: "flex", flexDirection: "column", @@ -28,24 +69,33 @@ const styles = (theme: Theme) => paddingBottom: theme.spacing.unit * 3 }, menuListItem: { - "&:hover": { - color: theme.palette.primary.main - }, alignItems: "center", display: "block", - marginTop: theme.spacing.unit * 2, + marginBottom: theme.spacing.unit * 5, paddingLeft: 0, textDecoration: "none", transition: theme.transitions.duration.standard + "ms" }, menuListItemActive: { - "&:before": { - background: theme.palette.primary.main, + "& $menuListItemText": { + 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: "''", - height: "100%", - left: -32, + height: 0, position: "absolute", - width: 5 + right: -35, + top: 15, + width: 0 }, position: "relative" }, @@ -54,123 +104,231 @@ const styles = (theme: Theme) => color: theme.palette.primary.main }, cursor: "pointer", + display: "inline-block", fontSize: "1rem", fontWeight: 500, + opacity: 1, + paddingLeft: 16, textTransform: "uppercase", - transition: theme.transitions.duration.standard + "ms" + transition: `opacity ${theme.transitions.duration.shorter}ms ease 0.1s` }, - menuListNested: { - "& $menuListItemActive": { - "& $menuListItemText": { - color: theme.palette.primary.main - }, - "&:before": { - borderRadius: "100%", - height: 8, - marginLeft: 9, - marginTop: 7, - width: 8 - } - }, - "& $menuListItemText": { - textTransform: "none" - }, - marginLeft: theme.spacing.unit * 3 + menuListItemTextHide: { + opacity: 0, + position: "absolute", + transition: `opacity ${theme.transitions.duration.shorter}ms ease` + }, + subMenu: { + padding: "0 15px" + }, + subMenuDrawer: { + background: "#000", + cursor: "pointer", + height: "100vh", + left: 0, + opacity: 0.2, + position: "absolute", + top: 0, + width: 0, + zIndex: -2 + }, + subMenuDrawerOpen: { + width: `100vw` } }); interface MenuListProps { className?: string; menuItems: IMenuItem[]; + isMenuSmall: boolean; location: string; user: User; renderConfigure: boolean; onMenuItemClick: (url: string, event: React.MouseEvent) => void; } + +export interface IActiveSubMenu { + isActive: boolean; + label: string | null; +} + const MenuList = withStyles(styles, { name: "MenuList" })( ({ classes, className, menuItems, + isMenuSmall, location, user, renderConfigure, onMenuItemClick - }: MenuListProps & WithStyles) => ( -
- {/* 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] - }); + }: MenuListProps & WithStyles) => { + const { isDark } = useTheme(); + const [activeSubMenu, setActiveSubMenu] = React.useState({ + isActive: false, + label: null + }); - if ( - menuItem.permission && - !user.permissions.map(perm => perm.code).includes(menuItem.permission) - ) { - return null; - } + const handleSubMenu = itemLabel => { + setActiveSubMenu({ + isActive: + itemLabel === activeSubMenu.label ? !activeSubMenu.isActive : true, + label: itemLabel + }); + }; - if (!menuItem.url) { - const isAnyChildActive = menuItem.children.reduce( - (acc, child) => acc || isActive(child), - false - ); + const closeSubMenu = (menuItemUrl, event) => { + setActiveSubMenu({ + isActive: false, + label: null + }); + if (menuItemUrl && event) { + onMenuItemClick(menuItemUrl, event); + event.stopPropagation(); + event.preventDefault(); + } + }; + + return ( +
+ {/* 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 ( +
+
handleSubMenu(menuItem.ariaLabel)} + > + + + {menuItem.label} + +
+ +
closeSubMenu(null, event)} + className={classNames(classes.subMenuDrawer, { + [classes.subMenuDrawerOpen]: activeSubMenu.isActive + })} + /> +
+ ); + } return ( - closeSubMenu(menuItem.url, event)} key={menuItem.label} - /> + > +
+ + + {menuItem.label} + +
+ ); - } - - return ( - onMenuItemClick(menuItem.url, event)} - key={menuItem.label} - > - + user.permissions + .map(perm => perm.code) + .includes(menuItem.permission) + ).length > 0 && ( + onMenuItemClick(configurationMenuUrl, event)} > - {menuItem.label} - - - ); - })} - {renderConfigure && - configurationMenu.filter(menuItem => - user.permissions.map(perm => perm.code).includes(menuItem.permission) - ).length > 0 && ( - onMenuItemClick(configurationMenuUrl, event)} - > - - {i18n.t("Configure")} - - - )} -
- ) +
+ + + {i18n.t("Configure")} + +
+ + )} +
+ ); + } ); export default MenuList; diff --git a/src/components/AppLayout/MenuNested.tsx b/src/components/AppLayout/MenuNested.tsx index afa63fd12..795a759d1 100644 --- a/src/components/AppLayout/MenuNested.tsx +++ b/src/components/AppLayout/MenuNested.tsx @@ -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 classNames from "classnames"; import React from "react"; +import SVG from "react-inlinesvg"; -import { User } from "../../auth/types/User"; -import MenuList from "./MenuList"; +import menuArrowIcon from "@assets/images/menu-arrow-icon.svg"; +import useTheme from "@saleor/hooks/useTheme"; +import { createHref } from "@saleor/misc"; +import { drawerWidthExpanded } from "./consts"; +import { IActiveSubMenu } from "./MenuList"; 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 { - classes: Record< - | "menuListItem" - | "menuListItemActive" - | "menuListItemText" - | "menuListNested", - string - >; - isAnyChildActive: boolean; + activeItem: IActiveSubMenu; + ariaLabel: string; + closeSubMenu: ({ isActive, label }: IActiveSubMenu) => void; + icon: string; menuItem: IMenuItem; - location: string; - user: User; + title: string; + handleSubMenu: (itemLabel: string) => void; onMenuItemClick: (url: string, event: React.MouseEvent) => void; } -const MenuNested: React.FC = ({ - classes, - isAnyChildActive, - location, - menuItem, - onMenuItemClick, - user -}) => { - const [isOpened, setOpenStatus] = React.useState(false); - - return ( -
setOpenStatus(!isOpened)} - className={classNames(classes.menuListItem, { - [classes.menuListItemActive]: isAnyChildActive - })} - > - - {menuItem.label} - - {isOpened && ( -
- +const MenuNested = withStyles(styles, { name: "MenuNested" })( + ({ + activeItem, + ariaLabel, + classes, + closeSubMenu, + icon, + menuItem, + onMenuItemClick, + title + }: MenuNestedProps & WithStyles) => { + const menuItems = menuItem.children; + const { isDark } = useTheme(); + const closeMenu = (menuItemUrl, event) => { + onMenuItemClick(menuItemUrl, event); + closeSubMenu({ + isActive: false, + label: null + }); + event.stopPropagation(); + event.preventDefault(); + }; + return ( + <> +
+ + + + +
{title}
+ +
+ closeSubMenu({ + isActive: false, + label: null + }) + } + > + +
+
+
+ {menuItems.map(item => { + return ( + closeMenu(item.url, event)} + key={item.label} + > + + {item.label} + + + ); + })}
- )} -
- ); -}; + + ); + } +); export default MenuNested; diff --git a/src/components/AppLayout/ResponsiveDrawer.tsx b/src/components/AppLayout/ResponsiveDrawer.tsx index 49fac57c1..cc8e026c0 100644 --- a/src/components/AppLayout/ResponsiveDrawer.tsx +++ b/src/components/AppLayout/ResponsiveDrawer.tsx @@ -7,7 +7,7 @@ import { WithStyles } from "@material-ui/core/styles"; import React from "react"; -import { drawerWidth } from "./consts"; +import { drawerWidth, drawerWidthExpanded } from "./consts"; const styles = (theme: Theme) => createStyles({ @@ -15,31 +15,38 @@ const styles = (theme: Theme) => backgroundColor: theme.palette.background.paper, border: "none", height: "100vh", - padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 4}px`, + overflow: "visible", + padding: 0, position: "fixed" as "fixed", + transition: "width 0.2s ease", + width: drawerWidthExpanded + }, + drawerDesktopSmall: { + overflow: "visible", + transition: "width 0.2s ease", width: drawerWidth }, drawerMobile: { - padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 4}px`, - width: drawerWidth, + width: drawerWidthExpanded } }); interface ResponsiveDrawerProps extends WithStyles { children?: React.ReactNode; open: boolean; + small: boolean; onClose?(); } const ResponsiveDrawer = withStyles(styles, { name: "ResponsiveDrawer" })( - ({ children, classes, onClose, open }: ResponsiveDrawerProps) => ( + ({ children, classes, onClose, open, small }: ResponsiveDrawerProps) => ( <> {children} diff --git a/src/components/AppLayout/consts.ts b/src/components/AppLayout/consts.ts index eb1ecfeef..a6d98fbba 100644 --- a/src/components/AppLayout/consts.ts +++ b/src/components/AppLayout/consts.ts @@ -1,3 +1,4 @@ -export const drawerWidth = 256; +export const drawerWidthExpanded = 256; +export const drawerWidth = 80; export const navigationBarHeight = 64; export const appLoaderHeight = 4; diff --git a/src/components/AppLayout/menuStructure.ts b/src/components/AppLayout/menuStructure.ts index 1ff572241..1c13c5dfa 100644 --- a/src/components/AppLayout/menuStructure.ts +++ b/src/components/AppLayout/menuStructure.ts @@ -8,9 +8,17 @@ import { productListUrl } from "../../products/urls"; import { languageListUrl } from "../../translations/urls"; 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 { ariaLabel: string; children?: IMenuItem[]; + icon?: any; label: string; permission?: PermissionEnum; url?: string; @@ -19,6 +27,7 @@ export interface IMenuItem { const menuStructure: IMenuItem[] = [ { ariaLabel: "home", + icon: homeIcon, label: i18n.t("Home", { context: "Menu label" }), url: "/" }, @@ -41,6 +50,7 @@ const menuStructure: IMenuItem[] = [ url: collectionListUrl() } ], + icon: catalogIcon, label: i18n.t("Catalog", { context: "Menu label" }), permission: PermissionEnum.MANAGE_PRODUCTS }, @@ -60,11 +70,13 @@ const menuStructure: IMenuItem[] = [ url: orderDraftListUrl() } ], + icon: ordersIcon, label: i18n.t("Orders", { context: "Menu label" }), permission: PermissionEnum.MANAGE_ORDERS }, { ariaLabel: "customers", + icon: customerIcon, label: i18n.t("Customers", { context: "Menu label" }), permission: PermissionEnum.MANAGE_USERS, url: customerListUrl() @@ -84,11 +96,13 @@ const menuStructure: IMenuItem[] = [ url: voucherListUrl() } ], + icon: discountsIcon, label: i18n.t("Discounts", { context: "Menu label" }), permission: PermissionEnum.MANAGE_DISCOUNTS }, { ariaLabel: "translations", + icon: translationIcon, label: i18n.t("Translations", { context: "Menu label" }), permission: PermissionEnum.MANAGE_TRANSLATIONS, url: languageListUrl diff --git a/src/components/AssignCategoryDialog/AssignCategoryDialog.tsx b/src/components/AssignCategoryDialog/AssignCategoryDialog.tsx index a272a7da8..b8504b729 100644 --- a/src/components/AssignCategoryDialog/AssignCategoryDialog.tsx +++ b/src/components/AssignCategoryDialog/AssignCategoryDialog.tsx @@ -15,8 +15,8 @@ import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; -import Form from "@saleor/components/Form"; import FormSpacer from "@saleor/components/FormSpacer"; +import useSearchQuery from "@saleor/hooks/useSearchQuery"; import { SearchCategories_categories_edges_node } from "../../containers/SearchCategories/types/SearchCategories"; import i18n from "../../i18n"; import Checkbox from "../Checkbox"; @@ -50,13 +50,28 @@ interface AssignCategoriesDialogProps extends WithStyles { loading: boolean; onClose: () => 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, { name: "AssignCategoriesDialog" })( @@ -69,104 +84,93 @@ const AssignCategoriesDialog = withStyles(styles, { onClose, onFetch, onSubmit - }: AssignCategoriesDialogProps) => ( - -
- {({ data, change }) => ( - <> - {i18n.t("Assign Categories")} - - 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 && - }} - /> - -
- - {categories && - categories.map(category => { - const isChecked = !!data.categories.find( - selectedCategories => - selectedCategories.id === category.id - ); + }: AssignCategoriesDialogProps) => { + const [query, onQueryChange] = useSearchQuery(onFetch); + const [selectedCategories, setSelectedCategories] = React.useState< + SearchCategories_categories_edges_node[] + >([]); - return ( - - - - isChecked - ? change({ - target: { - name: "categories", - value: data.categories.filter( - selectedCategories => - selectedCategories.id !== - category.id - ) - } - } as any) - : change({ - target: { - name: "categories", - value: [...data.categories, category] - } - } as any) - } - /> - - - {category.name} - - - ); - })} - -
- - - - - {i18n.t("Assign categories", { context: "button" })} - - - - )} - - - ) + const handleSubmit = () => onSubmit(selectedCategories); + + return ( + + {i18n.t("Assign Categories")} + + + }} + /> + + + + {categories && + categories.map(category => { + const isSelected = !!selectedCategories.find( + selectedCategories => selectedCategories.id === category.id + ); + + return ( + + + + handleCategoryAssign( + category, + isSelected, + selectedCategories, + setSelectedCategories + ) + } + /> + + + {category.name} + + + ); + })} + +
+
+ + + + {i18n.t("Assign categories", { context: "button" })} + + +
+ ); + } ); AssignCategoriesDialog.displayName = "AssignCategoriesDialog"; export default AssignCategoriesDialog; diff --git a/src/components/AssignCollectionDialog/AssignCollectionDialog.tsx b/src/components/AssignCollectionDialog/AssignCollectionDialog.tsx index 6d0780427..a8e316a4a 100644 --- a/src/components/AssignCollectionDialog/AssignCollectionDialog.tsx +++ b/src/components/AssignCollectionDialog/AssignCollectionDialog.tsx @@ -12,13 +12,13 @@ import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; 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 i18n from "../../i18n"; import Checkbox from "../Checkbox"; import ConfirmButton, { ConfirmButtonTransitionState } from "../ConfirmButton/ConfirmButton"; -import Form from "../Form"; import FormSpacer from "../FormSpacer"; export interface FormData { @@ -50,13 +50,28 @@ interface AssignCollectionDialogProps extends WithStyles { loading: boolean; onClose: () => 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, { name: "AssignCollectionDialog" })( @@ -69,104 +84,94 @@ const AssignCollectionDialog = withStyles(styles, { onClose, onFetch, onSubmit - }: AssignCollectionDialogProps) => ( - -
- {({ data, change }) => ( - <> - {i18n.t("Assign Collection")} - - 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 && - }} - /> - - - - {collections && - collections.map(category => { - const isChecked = !!data.collections.find( - selectedCollection => - selectedCollection.id === category.id - ); + }: AssignCollectionDialogProps) => { + const [query, onQueryChange] = useSearchQuery(onFetch); + const [selectedCollections, setSelectedCollections] = React.useState< + SearchCollections_collections_edges_node[] + >([]); - return ( - - - - isChecked - ? change({ - target: { - name: "collections", - value: data.collections.filter( - selectedCollection => - selectedCollection.id !== - category.id - ) - } - } as any) - : change({ - target: { - name: "collections", - value: [...data.collections, category] - } - } as any) - } - /> - - - {category.name} - - - ); - })} - -
-
- - - - {i18n.t("Assign collections", { context: "button" })} - - - - )} -
-
- ) + const handleSubmit = () => onSubmit(selectedCollections); + + return ( + + {i18n.t("Assign Collection")} + + + }} + /> + + + + {collections && + collections.map(collection => { + const isSelected = !!selectedCollections.find( + selectedCollection => + selectedCollection.id === collection.id + ); + + return ( + + + + handleCollectionAssign( + collection, + isSelected, + selectedCollections, + setSelectedCollections + ) + } + /> + + + {collection.name} + + + ); + })} + +
+
+ + + + {i18n.t("Assign collections", { context: "button" })} + + +
+ ); + } ); AssignCollectionDialog.displayName = "AssignCollectionDialog"; export default AssignCollectionDialog; diff --git a/src/components/AssignProductDialog/AssignProductDialog.tsx b/src/components/AssignProductDialog/AssignProductDialog.tsx index 9b975d681..8e3bdfddf 100644 --- a/src/components/AssignProductDialog/AssignProductDialog.tsx +++ b/src/components/AssignProductDialog/AssignProductDialog.tsx @@ -15,12 +15,12 @@ import React from "react"; import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; -import Form from "@saleor/components/Form"; import FormSpacer from "@saleor/components/FormSpacer"; 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 i18n from "../../i18n"; -import { maybe } from "../../misc"; import Checkbox from "../Checkbox"; export interface FormData { @@ -40,25 +40,41 @@ const styles = createStyles({ overflow: { overflowY: "visible" }, + scrollArea: { + overflowY: "scroll" + }, wideCell: { width: "100%" } }); -interface AssignProductDialogProps extends WithStyles { +export interface AssignProductDialogProps { confirmButtonState: ConfirmButtonTransitionState; open: boolean; products: SearchProducts_products_edges_node[]; loading: boolean; onClose: () => 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, { name: "AssignProductDialog" })( @@ -71,106 +87,102 @@ const AssignProductDialog = withStyles(styles, { onClose, onFetch, onSubmit - }: AssignProductDialogProps) => ( - -
- {({ data, change }) => ( - <> - {i18n.t("Assign Product")} - - 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 && - }} - /> - - - - {products && - products.map(product => { - const isChecked = !!data.products.find( - selectedProduct => selectedProduct.id === product.id - ); + }: AssignProductDialogProps & WithStyles) => { + const [query, onQueryChange] = useSearchQuery(onFetch); + const [selectedProducts, setSelectedProducts] = React.useState< + SearchProducts_products_edges_node[] + >([]); - return ( - - product.thumbnail.url)} + const handleSubmit = () => onSubmit(selectedProducts); + + return ( + + {i18n.t("Assign Product")} + + + }} + /> + +
+
+ + {products && + products.map(product => { + const isSelected = selectedProducts.some( + selectedProduct => selectedProduct.id === product.id + ); + + return ( + + product.thumbnail.url)} + /> + + {product.name} + + + + handleProductAssign( + product, + isSelected, + selectedProducts, + setSelectedProducts + ) + } /> - - {product.name} - - - - isChecked - ? change({ - target: { - name: "products", - value: data.products.filter( - selectedProduct => - selectedProduct.id !== product.id - ) - } - } as any) - : change({ - target: { - name: "products", - value: [...data.products, product] - } - } as any) - } - /> - - - ); - })} - -
-
- - - - {i18n.t("Assign products", { context: "button" })} - - - - )} -
-
- ) + + + ); + })} + + + + + + + + {i18n.t("Assign products", { context: "button" })} + + + + ); + } ); AssignProductDialog.displayName = "AssignProductDialog"; export default AssignProductDialog; diff --git a/src/components/CardTitle/CardTitle.tsx b/src/components/CardTitle/CardTitle.tsx index a800a752b..55b92178d 100644 --- a/src/components/CardTitle/CardTitle.tsx +++ b/src/components/CardTitle/CardTitle.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; const styles = (theme: Theme) => @@ -32,7 +32,7 @@ const styles = (theme: Theme) => lineHeight: 1 }, toolbar: { - marginRight: -theme.spacing.unit * 2 + marginRight: -theme.spacing.unit } }); diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 2a715c8e1..31c23d9ce 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -9,7 +9,6 @@ import { } from "@material-ui/core/styles"; import classNames from "classnames"; import React from "react"; -import { stopPropagation } from "../../misc"; export type CheckboxProps = Omit< MuiCheckboxProps, @@ -19,7 +18,9 @@ export type CheckboxProps = Omit< | "indeterminateIcon" | "classes" | "onChange" + | "onClick" > & { + disableClickPropagation?: boolean; onChange?: (event: React.ChangeEvent) => void; }; @@ -45,7 +46,6 @@ const styles = (theme: Theme) => }, "&:before": { background: "rgba(0, 0, 0, 0)", - borderRadius: 2, content: '""', height: 8, left: 2, @@ -56,7 +56,6 @@ const styles = (theme: Theme) => }, WebkitAppearance: "none", border: `1px solid ${theme.palette.grey[500]}`, - borderRadius: 4, boxSizing: "border-box", cursor: "pointer", height: 14, @@ -84,21 +83,31 @@ const Checkbox = withStyles(styles, { name: "Checkbox" })( className, classes, disabled, + disableClickPropagation, indeterminate, onChange, - onClick, value, name, ...props }: CheckboxProps & WithStyles) => { const inputRef = React.useRef(null); + const handleClick = React.useCallback( + disableClickPropagation + ? event => { + event.stopPropagation(); + inputRef.current.click(); + } + : () => inputRef.current.click(), + [] + ); + return ( inputRef.current.click())} + onClick={handleClick} > diff --git a/src/components/ControlledCheckbox.tsx b/src/components/ControlledCheckbox.tsx index 8ce485c10..aef9e52e2 100644 --- a/src/components/ControlledCheckbox.tsx +++ b/src/components/ControlledCheckbox.tsx @@ -20,7 +20,7 @@ export const ControlledCheckbox: React.StatelessComponent< onChange({ target: { name, value: !checked } })} + disableClickPropagation onChange={() => onChange({ target: { name, value: !checked } })} /> } diff --git a/src/components/ControlledSwitch.tsx b/src/components/ControlledSwitch.tsx index 87b308ba2..07a244006 100644 --- a/src/components/ControlledSwitch.tsx +++ b/src/components/ControlledSwitch.tsx @@ -7,6 +7,9 @@ const styles = (theme: Theme) => createStyles({ label: { marginLeft: theme.spacing.unit * 2 + }, + labelText: { + fontSize: 14 } }); @@ -15,7 +18,7 @@ interface ControlledSwitchProps extends WithStyles { disabled?: boolean; label: string | React.ReactNode; name: string; - secondLabel?: string; + secondLabel?: string | React.ReactNode; uncheckedLabel?: string | React.ReactNode; onChange?(event: React.ChangeEvent); } @@ -46,7 +49,17 @@ export const ControlledSwitch = withStyles(styles, { } label={
- {uncheckedLabel ? (checked ? label : uncheckedLabel) : label} + {uncheckedLabel ? ( + checked ? ( + label + ) : ( + uncheckedLabel + ) + ) : typeof label === "string" ? ( + {label} + ) : ( + label + )}
{secondLabel ? secondLabel : null}
} diff --git a/src/components/Date/Date.test.tsx b/src/components/Date/Date.test.tsx index 738b89fbf..77f2d4126 100644 --- a/src/components/Date/Date.test.tsx +++ b/src/components/Date/Date.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import * as renderer from "react-test-renderer"; +import renderer from "react-test-renderer"; import { TimezoneProvider } from "../Timezone"; import Date from "./Date"; diff --git a/src/components/Date/Date.tsx b/src/components/Date/Date.tsx index a50a4e909..6a13ae3bb 100644 --- a/src/components/Date/Date.tsx +++ b/src/components/Date/Date.tsx @@ -1,5 +1,5 @@ import Tooltip from "@material-ui/core/Tooltip"; -import * as moment from "moment-timezone"; +import moment from "moment-timezone"; import React from "react"; import useDateLocalize from "@saleor/hooks/useDateLocalize"; diff --git a/src/components/Date/DateTime.tsx b/src/components/Date/DateTime.tsx index f9c24220b..31af9987e 100644 --- a/src/components/Date/DateTime.tsx +++ b/src/components/Date/DateTime.tsx @@ -1,5 +1,5 @@ import Tooltip from "@material-ui/core/Tooltip"; -import * as moment from "moment-timezone"; +import moment from "moment-timezone"; import React from "react"; import ReactMoment from "react-moment"; diff --git a/src/components/DraftRenderer.tsx b/src/components/DraftRenderer.tsx index 052040aa3..67affe033 100644 --- a/src/components/DraftRenderer.tsx +++ b/src/components/DraftRenderer.tsx @@ -1,5 +1,5 @@ import { RawDraftContentState } from "draft-js"; -import * as draftToHtml from "draftjs-to-html"; +import draftToHtml from "draftjs-to-html"; import React from "react"; interface DraftRendererProps { diff --git a/src/components/Dropzone.tsx b/src/components/Dropzone.tsx index 1a8c62114..804532431 100644 --- a/src/components/Dropzone.tsx +++ b/src/components/Dropzone.tsx @@ -1,3 +1,3 @@ /* tslint:disable:no-submodule-imports */ -import * as Dropzone from "react-dropzone/dist/index"; +import Dropzone from "react-dropzone/dist/index"; export default Dropzone; diff --git a/src/components/EditableTableCell/EditableTableCell.tsx b/src/components/EditableTableCell/EditableTableCell.tsx index 17db49a77..594c244ca 100644 --- a/src/components/EditableTableCell/EditableTableCell.tsx +++ b/src/components/EditableTableCell/EditableTableCell.tsx @@ -9,10 +9,10 @@ import { import TableCell from "@material-ui/core/TableCell"; import TextField, { TextFieldProps } from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; -import Form from "@saleor/components/Form"; +import useForm from "@saleor/hooks/useForm"; const styles = (theme: Theme) => createStyles({ @@ -66,48 +66,39 @@ export const EditableTableCell = withStyles(styles, { value, onConfirm }: EditableTableCellProps) => { - const [opened, setOpenStatus] = React.useState(focused); - const enable = () => setOpenStatus(true); - const disable = () => setOpenStatus(false); - const handleConfirm = (data: { value: string }) => { disable(); onConfirm(data.value); }; + const [opened, setOpenStatus] = React.useState(focused); + const { change, data } = useForm({ value }, [], handleConfirm); + const enable = () => setOpenStatus(true); + const disable = () => setOpenStatus(false); + return ( {opened &&
} -
- {({ change, data }) => ( - <> - - {value || defaultValue} - - {opened && ( -
- - - - - -
- )} - - )} -
+ + {value || defaultValue} + + {opened && ( +
+ + + + + +
+ )} ); } diff --git a/src/components/ErrorPage/ErrorPage.tsx b/src/components/ErrorPage/ErrorPage.tsx index f6046e2f0..485cc22d9 100644 --- a/src/components/ErrorPage/ErrorPage.tsx +++ b/src/components/ErrorPage/ErrorPage.tsx @@ -9,7 +9,7 @@ import Typography from "@material-ui/core/Typography"; import React from "react"; import SVG from "react-inlinesvg"; -import * as notFoundImage from "@assets/images/what.svg"; +import notFoundImage from "@assets/images/what.svg"; import i18n from "../../i18n"; export interface ErrorPageProps extends WithStyles { diff --git a/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx b/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx index c70ead68d..7a71d6375 100644 --- a/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx +++ b/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx @@ -4,7 +4,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; const styles = (theme: Theme) => diff --git a/src/components/Filter/Filter.tsx b/src/components/Filter/Filter.tsx index fbf862ad3..fa32d49ae 100644 --- a/src/components/Filter/Filter.tsx +++ b/src/components/Filter/Filter.tsx @@ -11,7 +11,7 @@ import { import { fade } from "@material-ui/core/styles/colorManipulator"; import Typography from "@material-ui/core/Typography"; import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import { FilterContent } from "."; diff --git a/src/components/Filter/FilterContent.tsx b/src/components/Filter/FilterContent.tsx index 7df9ce86c..40b798327 100644 --- a/src/components/Filter/FilterContent.tsx +++ b/src/components/Filter/FilterContent.tsx @@ -47,7 +47,7 @@ const FilterContent: React.FC = ({ }) => { const [menuValue, setMenuValue] = React.useState(""); const [filterValue, setFilterValue] = React.useState(""); - const classes = useStyles(); + const classes = useStyles({}); const activeMenu = menuValue ? getMenuItemByValue(filters, menuValue) diff --git a/src/components/Filter/FilterElement.tsx b/src/components/Filter/FilterElement.tsx index 202aab160..e9735d5a0 100644 --- a/src/components/Filter/FilterElement.tsx +++ b/src/components/Filter/FilterElement.tsx @@ -41,7 +41,7 @@ const FilterElement: React.FC = ({ onChange, value }) => { - const classes = useStyles(); + const classes = useStyles({}); if (filter.data.type === FieldType.date) { return ( diff --git a/src/components/FilterBar/FilterBar.tsx b/src/components/FilterBar/FilterBar.tsx index db92026cc..f5adc696d 100644 --- a/src/components/FilterBar/FilterBar.tsx +++ b/src/components/FilterBar/FilterBar.tsx @@ -41,6 +41,7 @@ const FilterBar: React.FC = ({ onTabChange(tabIndex + 1)} label={tab.name} + key={tabIndex} /> ))} {isCustom && ( diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx index 306348168..25512cf57 100644 --- a/src/components/Form/Form.tsx +++ b/src/components/Form/Form.tsx @@ -1,170 +1,42 @@ import React from "react"; -import { UserError } from "../../types"; -export interface FormProps { - children: (props: { - data: T; - hasChanged: boolean; - errors: { [key: string]: string }; - change(event: React.ChangeEvent, cb?: () => void); - reset(); - submit(event?: React.FormEvent); - }) => React.ReactElement; +import useForm, { UseFormResult } from "@saleor/hooks/useForm"; +import { UserError } from "@saleor/types"; + +export interface FormProps { + children: (props: UseFormResult) => React.ReactNode; + confirmLeave?: boolean; errors?: UserError[]; initial?: T; - confirmLeave?: boolean; - useForm?: boolean; resetOnSubmit?: boolean; - onSubmit?(data: T); + onSubmit?: (data: T) => void; } -interface FormComponentProps extends FormProps { - hasChanged: boolean; - toggleFormChangeState: () => void; -} +function Form(props: FormProps) { + const { children, errors, initial, resetOnSubmit, onSubmit } = props; + const renderProps = useForm(initial, errors, onSubmit); -interface FormState { - initial: T; - fields: T; - hasChanged: boolean; -} + function handleSubmit(event?: React.FormEvent, cb?: () => void) { + const { reset, submit } = renderProps; -class FormComponent extends React.Component< - FormComponentProps, - FormState -> { - static getDerivedStateFromProps( - nextProps: FormComponentProps, - prevState: FormState - ): FormState { - 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 = { - 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, 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) => { - switch (event.keyCode) { - // Enter - case 13: - this.props.onSubmit(this.state.fields); - break; - } - }; - - handleSubmit = (event?: React.FormEvent, cb?: () => void) => { - const { resetOnSubmit, onSubmit } = this.props; if (event) { event.stopPropagation(); event.preventDefault(); } - if (onSubmit !== undefined) { - onSubmit(this.state.fields); - } + if (cb) { cb(); } + if (resetOnSubmit) { - this.setState({ - fields: this.state.initial - }); + reset(); } - }; - render() { - 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 ? ( -
{contents}
- ) : ( -
{contents}
- ); + submit(); } + + return
{children(renderProps)}
; } -export default FormComponent; +Form.displayName = "Form"; + +export default Form; diff --git a/src/components/Form/index.ts b/src/components/Form/index.ts index 1f0b7dda4..98ebc8cc6 100644 --- a/src/components/Form/index.ts +++ b/src/components/Form/index.ts @@ -1,5 +1,4 @@ export * from "./Form"; +export { default } from "./Form"; export { default as FormActions } from "./FormActions"; export * from "./FormActions"; -export * from "./FormContext"; -export { default } from "./FormContext"; diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 11f5a79e8..762cd74a8 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography, { TypographyProps } from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; const styles = (theme: Theme) => diff --git a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx index 0c177fc5e..7c6ac5d97 100644 --- a/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx +++ b/src/components/MultiAutocompleteSelectField/MultiAutocompleteSelectField.tsx @@ -1,4 +1,5 @@ import CircularProgress from "@material-ui/core/CircularProgress"; +import IconButton from "@material-ui/core/IconButton"; import MenuItem from "@material-ui/core/MenuItem"; import Paper from "@material-ui/core/Paper"; import { @@ -8,27 +9,77 @@ import { WithStyles } from "@material-ui/core/styles"; 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 React from "react"; +import { compareTwoStrings } from "string-similarity"; -import i18n from "../../i18n"; -import ArrowDropdownIcon from "../../icons/ArrowDropdown"; -import Debounce, { DebounceProps } from "../Debounce"; +import { fade } from "@material-ui/core/styles/colorManipulator"; +import Checkbox from "@saleor/components/Checkbox"; +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; value: string; } const styles = (theme: Theme) => createStyles({ + checkbox: { + height: 24, + width: 20 + }, 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: { flexGrow: 1, 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: { left: 0, marginTop: theme.spacing.unit, @@ -39,39 +90,32 @@ const styles = (theme: Theme) => } }); -export interface MultiAutocompleteSelectFieldChildrenFunc { - deleteItem: (item: ChoiceType) => void; - items: ChoiceType[]; -} -export type MultiAutocompleteSelectFieldChildren = ( - props: MultiAutocompleteSelectFieldChildrenFunc -) => React.ReactNode; - -export interface MultiAutocompleteSelectFieldProps - extends WithStyles { +export interface MultiAutocompleteSelectFieldProps { + allowCustomValues?: boolean; + displayValues: MultiAutocompleteChoiceType[]; name: string; - children: MultiAutocompleteSelectFieldChildren; - choices: ChoiceType[]; - value?: ChoiceType[]; + choices: MultiAutocompleteChoiceType[]; + value: string[]; loading?: boolean; placeholder?: string; helperText?: string; label?: string; - fetchChoices(value: string); - onChange(event); + fetchChoices?: (value: string) => void; + onChange: (event: React.ChangeEvent) => void; } const DebounceAutocomplete: React.ComponentType< DebounceProps > = Debounce; -export const MultiAutocompleteSelectField = withStyles(styles, { +export const MultiAutocompleteSelectFieldComponent = withStyles(styles, { name: "MultiAutocompleteSelectField" })( ({ - children, + allowCustomValues, choices, classes, + displayValues, helperText, label, loading, @@ -80,102 +124,200 @@ export const MultiAutocompleteSelectField = withStyles(styles, { value, fetchChoices, onChange - }: MultiAutocompleteSelectFieldProps) => { + }: MultiAutocompleteSelectFieldProps & WithStyles) => { const handleSelect = ( - item: ChoiceType, - { reset }: ControllerStateAndHelpers + item: string, + downshiftOpts?: ControllerStateAndHelpers ) => { - reset({ inputValue: "" }); - onChange({ target: { name, value: [...value, item] } }); + if (downshiftOpts) { + downshiftOpts.reset({ inputValue: "" }); + } + onChange({ + target: { name, value: item } + } as any); }; - const handleDelete = (item: ChoiceType) => { - 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 - ); + const suggestions = choices.filter(choice => !value.includes(choice.value)); return ( - - {debounce => ( - (item ? item.label : "")} - onSelect={handleSelect} - onInputValueChange={value => debounce(value)} - > - {({ - getInputProps, - getItemProps, - isOpen, - selectedItem, - toggleMenu, - closeMenu, - openMenu, - highlightedIndex - }) => { - return ( -
- - {loading ? ( - - ) : ( - - )} -
- ), - id: undefined, - onBlur: closeMenu, - onFocus: openMenu - }} - helperText={helperText} - label={label} - fullWidth={true} - /> - {isOpen && ( - - {!loading && filteredChoices.length > 0 - ? filteredChoices.map((suggestion, index) => ( - - {suggestion.label} - - )) - : !loading && ( - - {i18n.t("No results found")} - - )} - + <> + ""} + > + {({ + getInputProps, + getItemProps, + isOpen, + toggleMenu, + highlightedIndex, + inputValue + }) => ( +
+ + {loading ? ( + + ) : ( + + )} +
+ ), + id: undefined, + onClick: toggleMenu + }} + helperText={helperText} + label={label} + fullWidth={true} + /> + {isOpen && (!!inputValue || !!choices.length) && ( + + {choices.length > 0 || + displayValues.length > 0 || + allowCustomValues ? ( + <> + {displayValues.map(value => ( + + + + {value.label} + + + ))} + {displayValues.length > 0 && suggestions.length > 0 && ( +
+ )} + {suggestions.map((suggestion, index) => ( + + + + {suggestion.label} + + + ))} + {allowCustomValues && + inputValue && + !choices.find( + choice => + choice.label.toLowerCase() === + inputValue.toLowerCase() + ) && ( + + + {i18n.t("Add new value: {{ value }}", { + context: "add custom option", + value: inputValue + })} + + + )} + + ) : ( + !loading && ( + + {i18n.t("No results found")} + + ) )} - {children({ - deleteItem: handleDelete, - items: selectedItem - })} -
- ); - }} - + + )} + + )} + +
+ {displayValues.map(value => ( +
+
+ + {value.label} + + handleSelect(value.value)} + > + + +
+
+ ))} +
+ + ); + } +); +const MultiAutocompleteSelectField: React.FC< + MultiAutocompleteSelectFieldProps +> = ({ choices, fetchChoices, ...props }) => { + const [query, setQuery] = React.useState(""); + if (fetchChoices) { + return ( + + {debounceFn => ( + )} ); } -); + + 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 ( + setQuery(q || "")} + choices={sortedChoices} + {...props} + /> + ); +}; MultiAutocompleteSelectField.displayName = "MultiAutocompleteSelectField"; export default MultiAutocompleteSelectField; diff --git a/src/components/NotFoundPage/NotFoundPage.tsx b/src/components/NotFoundPage/NotFoundPage.tsx index 73064000a..236e905a0 100644 --- a/src/components/NotFoundPage/NotFoundPage.tsx +++ b/src/components/NotFoundPage/NotFoundPage.tsx @@ -9,8 +9,8 @@ import Typography from "@material-ui/core/Typography"; import React from "react"; import SVG from "react-inlinesvg"; -import * as notFoundImage from "@assets/images/not-found-404.svg"; -import i18n from "../../i18n"; +import notFoundImage from "@assets/images/not-found-404.svg"; +import i18n from "@saleor/i18n"; const styles = (theme: Theme) => createStyles({ diff --git a/src/components/PageHeader/PageHeader.tsx b/src/components/PageHeader/PageHeader.tsx index 1fc53ec1c..843be5df3 100644 --- a/src/components/PageHeader/PageHeader.tsx +++ b/src/components/PageHeader/PageHeader.tsx @@ -12,6 +12,9 @@ import Skeleton from "../Skeleton"; const styles = (theme: Theme) => createStyles({ + root: { + display: "flex" + }, title: { flex: 1, fontSize: 24, @@ -35,7 +38,7 @@ const PageHeader = withStyles(styles)( } > - {children} +
{children}
) ); diff --git a/src/components/ProductList/ProductList.tsx b/src/components/ProductList/ProductList.tsx index ccbe45ffe..4aa0b39da 100644 --- a/src/components/ProductList/ProductList.tsx +++ b/src/components/ProductList/ProductList.tsx @@ -11,11 +11,15 @@ import TableFooter from "@material-ui/core/TableFooter"; import TableRow from "@material-ui/core/TableRow"; 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 i18n from "../../i18n"; -import { maybe, renderCollection } from "../../misc"; -import { ListActions, ListProps } from "../../types"; import Checkbox from "../Checkbox"; import Money from "../Money"; import Skeleton from "../Skeleton"; @@ -27,7 +31,7 @@ const styles = (theme: Theme) => createStyles({ [theme.breakpoints.up("lg")]: { colName: { - width: 430 + width: "auto" }, colPrice: { width: 200 @@ -39,12 +43,14 @@ const styles = (theme: Theme) => width: 200 } }, - avatarCell: { - paddingLeft: theme.spacing.unit * 2, - paddingRight: 0, - width: theme.spacing.unit * 5 + colFill: { + padding: 0, + width: "100%" }, colName: {}, + colNameHeader: { + marginLeft: AVATAR_MARGIN + }, colPrice: { textAlign: "right" }, @@ -53,6 +59,12 @@ const styles = (theme: Theme) => link: { cursor: "pointer" }, + table: { + tableLayout: "fixed" + }, + tableContainer: { + overflowX: "scroll" + }, textLeft: { textAlign: "left" }, @@ -62,7 +74,7 @@ const styles = (theme: Theme) => }); interface ProductListProps - extends ListProps, + extends ListProps, ListActions, WithStyles { products: CategoryDetails_category_products_edges_node[]; @@ -71,6 +83,7 @@ interface ProductListProps export const ProductList = withStyles(styles, { name: "ProductList" })( ({ classes, + settings, disabled, isChecked, pageInfo, @@ -81,114 +94,158 @@ export const ProductList = withStyles(styles, { name: "ProductList" })( toolbar, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick - }: ProductListProps) => ( - - - - - {i18n.t("Name", { context: "object" })} - - - {i18n.t("Type", { context: "object" })} - - - {i18n.t("Published", { context: "object" })} - - - {i18n.t("Price", { context: "object" })} - - - - - - - - - {renderCollection( - products, - product => { - const isSelected = product ? isChecked(product.id) : false; + }: ProductListProps) => { + const displayColumn = React.useCallback( + (column: ProductListColumns) => + isSelected(column, settings.columns, (a, b) => a === b), + [settings.columns] + ); + const numberOfColumns = 2 + settings.columns.length; - return ( - - - toggle(product.id)} - /> - - product.thumbnail.url)} - /> - - {product ? product.name : } - - - {product && product.productType ? ( - product.productType.name - ) : ( - - )} - - - {product && maybe(() => product.isAvailable !== undefined) ? ( - - ) : ( - - )} - - - {maybe(() => product.basePrice) && - maybe(() => product.basePrice.amount) !== undefined && - maybe(() => product.basePrice.currency) !== undefined ? ( - - ) : ( - - )} - - - ); - }, - () => ( + return ( +
+
+ + + {displayColumn("productType") && } + {displayColumn("isPublished") && ( + + )} + {displayColumn("price") && } + + + + {i18n.t("Name", { context: "object" })} + + + {displayColumn("productType") && ( + + {i18n.t("Type", { context: "object" })} + + )} + {displayColumn("isPublished") && ( + + {i18n.t("Published", { context: "object" })} + + )} + {displayColumn("price") && ( + + {i18n.t("Price", { context: "object" })} + + )} + + - {i18n.t("No products found")} + - ) - )} - -
- ) + + + {renderCollection( + products, + product => { + const isSelected = product ? isChecked(product.id) : false; + + return ( + + + toggle(product.id)} + /> + + product.thumbnail.url)} + > + {product ? product.name : } + + {displayColumn("productType") && ( + + {product && product.productType ? ( + product.productType.name + ) : ( + + )} + + )} + {displayColumn("isPublished") && ( + + {product && + maybe(() => product.isAvailable !== undefined) ? ( + + ) : ( + + )} + + )} + {displayColumn("price") && ( + + {maybe(() => product.basePrice) && + maybe(() => product.basePrice.amount) !== undefined && + maybe(() => product.basePrice.currency) !== + undefined ? ( + + ) : ( + + )} + + )} + + ); + }, + () => ( + + + {i18n.t("No products found")} + + + ) + )} + + + + ); + } ); ProductList.displayName = "ProductList"; export default ProductList; diff --git a/src/components/RichTextEditor/RichTextEditor.tsx b/src/components/RichTextEditor/RichTextEditor.tsx index f5718e513..6c4a7e01c 100644 --- a/src/components/RichTextEditor/RichTextEditor.tsx +++ b/src/components/RichTextEditor/RichTextEditor.tsx @@ -6,7 +6,7 @@ import { } from "@material-ui/core/styles"; import { fade } from "@material-ui/core/styles/colorManipulator"; import Typography from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import { RawDraftContentState } from "draft-js"; import { BLOCK_TYPE, @@ -14,6 +14,7 @@ import { ENTITY_TYPE, INLINE_STYLE } from "draftail"; +import isEqual from "lodash-es/isEqual"; import React from "react"; import BoldIcon from "../../icons/BoldIcon"; @@ -29,6 +30,7 @@ import UnorderedListIcon from "../../icons/UnorderedListIcon"; // import ImageEntity from "./ImageEntity"; // import ImageSource from "./ImageSource"; +import { ChangeEvent } from "@saleor/hooks/useForm"; import LinkEntity from "./LinkEntity"; import LinkSource from "./LinkSource"; @@ -45,22 +47,6 @@ export interface RichTextEditorProps { const styles = (theme: Theme) => 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: { color: theme.palette.error.main }, @@ -68,21 +54,13 @@ const styles = (theme: Theme) => marginTop: theme.spacing.unit * 0.75 }, input: { - "&:hover": { - 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" + position: "relative" }, label: { fontSize: theme.typography.caption.fontSize, - marginBottom: theme.spacing.unit * 2, - marginTop: -21 + left: 12, + position: "absolute", + top: 9 }, linkIcon: { marginTop: 2 @@ -97,8 +75,6 @@ const styles = (theme: Theme) => color: theme.palette.primary.light }, "&:after": { - animationDuration: theme.transitions.duration.shortest + "ms", - animationFillMode: "both", background: theme.palette.getContrastText( theme.palette.background.default ), @@ -120,16 +96,19 @@ const styles = (theme: Theme) => "& .Draftail": { "&-Editor": { "&--focus": { - "& .DraftEditor": { - "&-editorContainer": { - "&:after": { - animationName: "focus !important", - background: theme.palette.primary.main, - transform: "scaleX(0) scaleY(1)" - } - } - } - } + boxShadow: `inset 0px 0px 0px 2px ${theme.palette.primary.main}` + }, + "&:hover": { + borderColor: theme.palette.primary.main + }, + 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": { "&Button": { @@ -190,6 +169,7 @@ const styles = (theme: Theme) => display: "inline-flex", flexWrap: "wrap", marginBottom: theme.spacing.unit, + marginTop: 10, [theme.breakpoints.down(460)]: { width: "min-content" } @@ -205,24 +185,7 @@ const styles = (theme: Theme) => "&$error": { "& .Draftail": { "&-Editor": { - "& .DraftEditor": { - "&-editorContainer": { - "&:after": { - animationName: "none", - background: theme.palette.error.main, - transform: "scaleX(1) scaleY(1)" - } - } - }, - "&--focus": { - "& .DraftEditor": { - "&-editorContainer": { - "&:after": { - animationName: "none !important" - } - } - } - } + borderColor: theme.palette.error.main } } } @@ -240,6 +203,23 @@ const styles = (theme: Theme) => 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" })( ({ classes, @@ -267,14 +247,7 @@ const RichTextEditor = withStyles(styles, { name: "RichTextEditor" })( rawContentState={ initial && Object.keys(initial).length > 0 ? initial : null } - onSave={value => - onChange({ - target: { - name, - value - } - } as any) - } + onSave={value => handleSave(value, initial, name, onChange)} blockTypes={[ { icon: , diff --git a/src/components/SaveButtonBar/SaveButtonBar.tsx b/src/components/SaveButtonBar/SaveButtonBar.tsx index 8ed381f20..7c1877546 100644 --- a/src/components/SaveButtonBar/SaveButtonBar.tsx +++ b/src/components/SaveButtonBar/SaveButtonBar.tsx @@ -9,7 +9,7 @@ import { import classNames from "classnames"; import React from "react"; -import useScroll from "@saleor/hooks/useScroll"; +import useWindowScroll from "@saleor/hooks/useWindowScroll"; import i18n from "../../i18n"; import { maybe } from "../../misc"; import AppActionContext from "../AppLayout/AppActionContext"; @@ -82,7 +82,7 @@ export const SaveButtonBar = withStyles(styles, { name: "SaveButtonBar" })( onSave, ...props }: SaveButtonBarProps) => { - const scrollPosition = useScroll(); + const scrollPosition = useWindowScroll(); const scrolledToBottom = scrollPosition.y + window.innerHeight >= document.body.scrollHeight; diff --git a/src/components/SeoForm/SeoForm.tsx b/src/components/SeoForm/SeoForm.tsx index 1aceab7c4..fecca3cc1 100644 --- a/src/components/SeoForm/SeoForm.tsx +++ b/src/components/SeoForm/SeoForm.tsx @@ -9,7 +9,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import i18n from "../../i18n"; @@ -43,6 +43,9 @@ const styles = (theme: Theme) => flex: 1 }, labelContainer: { + "& span": { + paddingRight: 30 + }, display: "flex" }, preview: { @@ -128,7 +131,6 @@ const SeoForm = withStyles(styles, { name: "SeoForm" })( )} value={title.slice(0, 69)} disabled={loading || disabled} - InputLabelProps={{ shrink: true }} placeholder={titlePlaceholder} onChange={onChange} fullWidth @@ -158,7 +160,6 @@ const SeoForm = withStyles(styles, { name: "SeoForm" })( fullWidth multiline placeholder={descriptionPlaceholder} - InputLabelProps={{ shrink: true }} rows={10} /> diff --git a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx index 74c47c9db..35bab83f6 100644 --- a/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx +++ b/src/components/SingleAutocompleteSelectField/SingleAutocompleteSelectField.tsx @@ -1,4 +1,5 @@ import { Omit } from "@material-ui/core"; +import CircularProgress from "@material-ui/core/CircularProgress"; import { InputProps } from "@material-ui/core/Input"; import MenuItem from "@material-ui/core/MenuItem"; import Paper from "@material-ui/core/Paper"; @@ -9,10 +10,12 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; import Downshift from "downshift"; import React from "react"; import { compareTwoStrings } from "string-similarity"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; import i18n from "../../i18n"; import ArrowDropdownIcon from "../../icons/ArrowDropdown"; import Debounce, { DebounceProps } from "../Debounce"; @@ -23,6 +26,10 @@ const styles = (theme: Theme) => flexGrow: 1, position: "relative" }, + menuItem: { + height: "auto", + whiteSpace: "normal" + }, paper: { borderRadius: 4, left: 0, @@ -34,26 +41,26 @@ const styles = (theme: Theme) => } }); +export interface SingleAutocompleteChoiceType { + label: string; + value: any; +} export interface SingleAutocompleteSelectFieldProps { error?: boolean; name: string; - choices: Array<{ - label: string; - value: any; - }>; - value?: { - label: string; - value: any; - }; + displayValue: string; + emptyOption?: boolean; + choices: SingleAutocompleteChoiceType[]; + value?: string; disabled?: boolean; loading?: boolean; placeholder?: string; - custom?: boolean; + allowCustomValues?: boolean; helperText?: string; label?: string; InputProps?: InputProps; - fetchChoices?(value: string); - onChange(event); + fetchChoices?: (value: string) => void; + onChange: (event: React.ChangeEvent) => void; } interface SingleAutocompleteSelectFieldState { @@ -73,8 +80,10 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, { ({ choices, classes, - custom, + allowCustomValues, disabled, + displayValue, + emptyOption, error, helperText, label, @@ -86,16 +95,24 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, { fetchChoices, onChange }: SingleAutocompleteSelectFieldProps & WithStyles) => { - const handleChange = item => onChange({ target: { name, value: item } }); + const [prevDisplayValue] = useStateFromProps(displayValue); + const handleChange = item => + onChange({ + target: { + name, + value: item + } + } as any); return ( {debounceFn => ( (item ? item.label : "")} - onSelect={handleChange} + defaultInputValue={displayValue} + itemToString={() => displayValue} onInputValueChange={value => debounceFn(value)} + onSelect={handleChange} + selectedItem={value} > {({ getInputProps, @@ -106,13 +123,18 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, { toggleMenu, openMenu, closeMenu, - highlightedIndex + highlightedIndex, + reset }) => { - const isCustom = + const isCustomValueSelected = choices && selectedItem - ? choices.filter(c => c.value === selectedItem.value) - .length === 0 + ? choices.filter(c => c.value === selectedItem).length === 0 : false; + + if (prevDisplayValue !== displayValue) { + reset({ inputValue: displayValue }); + } + return (
+
+ {loading ? ( + + ) : ( + + )} +
), error, id: undefined, @@ -136,36 +162,66 @@ const SingleAutocompleteSelectFieldComponent = withStyles(styles, { label={label} fullWidth={true} /> - {isOpen && ( + {isOpen && (!!inputValue || !!choices.length) && ( - {loading ? ( - - {i18n.t("Loading...")} - - ) : choices.length > 0 || custom ? ( + {choices.length > 0 || allowCustomValues ? ( <> - {choices.map((suggestion, index) => ( + {emptyOption && ( - {suggestion.label} - - ))} - {custom && ( - - {i18n.t("Add custom value")} + + {i18n.t("None")} + )} + {choices.map((suggestion, index) => { + const choiceIndex = index + (emptyOption ? 1 : 0); + + return ( + + {suggestion.label} + + ); + })} + {allowCustomValues && + !!inputValue && + !choices.find( + choice => + choice.label.toLowerCase() === + inputValue.toLowerCase() + ) && ( + + {i18n.t("Add new value: {{ value }}", { + context: "add custom option", + value: inputValue + })} + + )} ) : ( diff --git a/src/components/Skeleton.tsx b/src/components/Skeleton.tsx index 5bcefc194..7327f3418 100644 --- a/src/components/Skeleton.tsx +++ b/src/components/Skeleton.tsx @@ -17,6 +17,11 @@ const styles = (theme: Theme) => opacity: 1 } }, + primary: { + "&$skeleton": { + background: theme.palette.primary.main + } + }, skeleton: { animation: "skeleton-animation .75s linear infinite forwards alternate", background: theme.palette.background.default, @@ -29,12 +34,18 @@ const styles = (theme: Theme) => interface SkeletonProps extends WithStyles { className?: string; + primary?: boolean; style?: React.CSSProperties; } const Skeleton = withStyles(styles, { name: "Skeleton" })( - ({ className, classes, style }: SkeletonProps) => ( - + ({ className, classes, primary, style }: SkeletonProps) => ( + ) diff --git a/src/components/StatusLabel/StatusLabel.tsx b/src/components/StatusLabel/StatusLabel.tsx index 4d5f979d2..267dcb89b 100644 --- a/src/components/StatusLabel/StatusLabel.tsx +++ b/src/components/StatusLabel/StatusLabel.tsx @@ -6,7 +6,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography, { TypographyProps } from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; const styles = (theme: Theme) => { diff --git a/src/components/Tab/Tab.tsx b/src/components/Tab/Tab.tsx index 9e7fa1f58..595845b1c 100644 --- a/src/components/Tab/Tab.tsx +++ b/src/components/Tab/Tab.tsx @@ -5,7 +5,7 @@ import { WithStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; const styles = (theme: Theme) => diff --git a/src/components/TableCellAvatar/TableCellAvatar.tsx b/src/components/TableCellAvatar/TableCellAvatar.tsx index 29e5d7c73..19ce47f88 100644 --- a/src/components/TableCellAvatar/TableCellAvatar.tsx +++ b/src/components/TableCellAvatar/TableCellAvatar.tsx @@ -7,11 +7,13 @@ import { } from "@material-ui/core/styles"; import TableCell from "@material-ui/core/TableCell"; import Cached from "@material-ui/icons/Cached"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import Image from "../../icons/Image"; +export const AVATAR_MARGIN = 56; + const styles = (theme: Theme) => createStyles({ avatar: { @@ -19,8 +21,17 @@ const styles = (theme: Theme) => border: `1px solid ${theme.overrides.MuiCard.root.borderColor}`, borderRadius: 2, color: "#bdbdbd", + display: "inline-flex", padding: theme.spacing.unit / 2 }, + children: { + alignSelf: "center", + marginLeft: theme.spacing.unit * 2 + }, + content: { + alignItems: "center", + display: "flex" + }, root: { paddingRight: theme.spacing.unit * 3, width: "1%" @@ -31,25 +42,35 @@ interface TableCellAvatarProps extends WithStyles { className?: string; thumbnail?: string; avatarProps?: string; + children?: React.ReactNode | React.ReactNodeArray; } const TableCellAvatar = withStyles(styles, { name: "TableCellAvatar" })( - ({ classes, className, thumbnail, avatarProps }: TableCellAvatarProps) => ( + ({ + classes, + children, + className, + thumbnail, + avatarProps + }: TableCellAvatarProps) => ( - {thumbnail === undefined ? ( - - - - ) : thumbnail === null ? ( - - - - ) : ( - - )} +
+ {thumbnail === undefined ? ( + + + + ) : thumbnail === null ? ( + + + + ) : ( + + )} + {children} +
) ); diff --git a/src/components/TableFilter/FilterChips.tsx b/src/components/TableFilter/FilterChips.tsx index f75a77d83..273829c19 100644 --- a/src/components/TableFilter/FilterChips.tsx +++ b/src/components/TableFilter/FilterChips.tsx @@ -27,7 +27,7 @@ const useInputStyles = makeStyles({ }); const Search: React.FC = props => { - const classes = useInputStyles(); + const classes = useInputStyles({}); return ( }, checkboxPartialSelect: { "&:after": { - background: "#fff", + background: theme.palette.common.white, content: "''", height: 2, position: "absolute", @@ -50,6 +52,10 @@ const styles = (theme: Theme) => height: 47, marginRight: -theme.spacing.unit * 2 }, + dragRows: { + padding: 0, + width: 52 + }, padding: { "&:last-child": { padding: 0 @@ -57,7 +63,6 @@ const styles = (theme: Theme) => }, root: { backgroundColor: fade(theme.palette.primary.main, 0.05), - borderBottom: "1px solid rgba(224, 224, 224, 1)", paddingLeft: 0, paddingRight: 24 }, @@ -77,7 +82,9 @@ const TableHead = withStyles(styles, { ({ classes, children, + colSpan, disabled, + dragRows, items, selected, toggleAll, @@ -87,13 +94,21 @@ const TableHead = withStyles(styles, { return ( - - {items && items.length > 0 ? ( + {dragRows && (items === undefined || items.length > 0) && ( + + )} + {(items === undefined || items.length > 0) && ( + toggleAll(items, selected)} /> - ) : null} - + + )} {selected ? ( <> - +
{selected && ( diff --git a/src/components/TablePagination/TablePagination.tsx b/src/components/TablePagination/TablePagination.tsx index b5e9553f3..e36d93f21 100644 --- a/src/components/TablePagination/TablePagination.tsx +++ b/src/components/TablePagination/TablePagination.tsx @@ -11,6 +11,9 @@ import TableCell from "@material-ui/core/TableCell"; import Toolbar from "@material-ui/core/Toolbar"; import React from "react"; +import RowNumberSelect from "@saleor/components/RowNumberSelect"; +import { maybe } from "@saleor/misc"; +import { ListSettings } from "../../types"; import TablePaginationActions from "./TablePaginationActions"; const styles = (theme: Theme) => @@ -50,6 +53,7 @@ const styles = (theme: Theme) => toolbar: { height: 56, minHeight: 56, + paddingLeft: 2, paddingRight: 2 } }); @@ -59,11 +63,13 @@ interface TablePaginationProps extends WithStyles { backIconButtonProps?: Partial; colSpan: number; component?: string | typeof TableCell; + settings?: ListSettings; hasNextPage: boolean; hasPreviousPage: boolean; nextIconButtonProps?: Partial; onNextPage(event); onPreviousPage(event); + onUpdateListSettings?(key: keyof ListSettings, value: any): void; } const TablePagination = withStyles(styles, { name: "TablePagination" })( @@ -73,11 +79,13 @@ const TablePagination = withStyles(styles, { name: "TablePagination" })( classes, colSpan: colSpanProp, component: Component, + settings, hasNextPage, hasPreviousPage, nextIconButtonProps, onNextPage, onPreviousPage, + onUpdateListSettings, ...other }: TablePaginationProps) => { let colSpan; @@ -89,7 +97,15 @@ const TablePagination = withStyles(styles, { name: "TablePagination" })( return ( -
+
+ {maybe(() => settings.rowNumber) && ( + + )} +
diff --git a/src/components/TextFieldWithChoice/TextFieldWithChoice.tsx b/src/components/TextFieldWithChoice/TextFieldWithChoice.tsx index cb64f9bc7..2bf8c6408 100644 --- a/src/components/TextFieldWithChoice/TextFieldWithChoice.tsx +++ b/src/components/TextFieldWithChoice/TextFieldWithChoice.tsx @@ -78,7 +78,7 @@ const TextFieldWithChoice = withStyles(styles, { {ChoiceProps.label} - + {ChoiceProps.values ? : null}
; + [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 + } +}; diff --git a/src/configuration/index.tsx b/src/configuration/index.tsx index 9eeeb747e..5cc3beab8 100644 --- a/src/configuration/index.tsx +++ b/src/configuration/index.tsx @@ -1,28 +1,36 @@ import React from "react"; +import { attributeListUrl } from "@saleor/attributes/urls"; +import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useUser from "@saleor/hooks/useUser"; -import { WindowTitle } from "../components/WindowTitle"; -import i18n from "../i18n"; -import Navigation from "../icons/Navigation"; -import Pages from "../icons/Pages"; -import ProductTypes from "../icons/ProductTypes"; -import ShippingMethods from "../icons/ShippingMethods"; -import SiteSettings from "../icons/SiteSettings"; -import StaffMembers from "../icons/StaffMembers"; -import Taxes from "../icons/Taxes"; -import { maybe } from "../misc"; -import { menuListUrl } from "../navigation/urls"; -import { pageListUrl } from "../pages/urls"; -import { productTypeListUrl } from "../productTypes/urls"; -import { shippingZonesListUrl } from "../shipping/urls"; -import { siteSettingsUrl } from "../siteSettings/urls"; -import { staffListUrl } from "../staff/urls"; -import { taxSection } from "../taxes/urls"; -import { PermissionEnum } from "../types/globalTypes"; +import i18n from "@saleor/i18n"; +import Navigation from "@saleor/icons/Navigation"; +import Pages from "@saleor/icons/Pages"; +import ProductTypes from "@saleor/icons/ProductTypes"; +import ShippingMethods from "@saleor/icons/ShippingMethods"; +import SiteSettings from "@saleor/icons/SiteSettings"; +import StaffMembers from "@saleor/icons/StaffMembers"; +import Taxes from "@saleor/icons/Taxes"; +import { maybe } from "@saleor/misc"; +import { menuListUrl } from "@saleor/navigation/urls"; +import { pageListUrl } from "@saleor/pages/urls"; +import { productTypeListUrl } from "@saleor/productTypes/urls"; +import { shippingZonesListUrl } from "@saleor/shipping/urls"; +import { siteSettingsUrl } from "@saleor/siteSettings/urls"; +import { staffListUrl } from "@saleor/staff/urls"; +import { taxSection } from "@saleor/taxes/urls"; +import { PermissionEnum } from "@saleor/types/globalTypes"; import ConfigurationPage, { MenuItem } from "./ConfigurationPage"; export const configurationMenu: MenuItem[] = [ + { + description: i18n.t("Determine attributes used to create product types"), + icon: , + permission: PermissionEnum.MANAGE_PRODUCTS, + title: i18n.t("Attributes"), + url: attributeListUrl() + }, { description: i18n.t("Define types of products you sell"), icon: , diff --git a/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx b/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx index dd2d77d80..1925f7d8c 100644 --- a/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx +++ b/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx @@ -12,9 +12,11 @@ import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Form from "@saleor/components/Form"; -import i18n from "../../../i18n"; -import { maybe } from "../../../misc"; -import { UserError } from "../../../types"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import i18n from "@saleor/i18n"; +import { maybe } from "@saleor/misc"; +import { UserError } from "@saleor/types"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import { AddressTypeInput } from "../../types"; import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses"; @@ -50,14 +52,14 @@ const CustomerAddressDialog = withStyles(styles, {})( onClose, onConfirm }: CustomerAddressDialogProps & WithStyles) => { + const [countryDisplayName, setCountryDisplayName] = useStateFromProps( + maybe(() => address.country.country, "") + ); const initialForm: AddressTypeInput = { city: maybe(() => address.city, ""), cityArea: maybe(() => address.cityArea, ""), companyName: maybe(() => address.companyName, ""), - country: { - label: maybe(() => address.country.country, ""), - value: maybe(() => address.country.code, "") - }, + country: maybe(() => address.country.code, ""), countryArea: maybe(() => address.countryArea, ""), firstName: maybe(() => address.firstName, ""), lastName: maybe(() => address.lastName, ""), @@ -66,6 +68,16 @@ const CustomerAddressDialog = withStyles(styles, {})( streetAddress1: maybe(() => address.streetAddress1, ""), streetAddress2: maybe(() => address.streetAddress2, "") }; + + const countryChoices = maybe( + () => + countries.map(country => ({ + label: country.label, + value: country.code + })), + [] + ); + return (
- {({ change, data, errors, submit }) => ( - <> - - {variant === "create" - ? i18n.t("Add Address") - : i18n.t("Edit Address")} - - - - - - - - {i18n.t("Save Address", { context: "button" })} - - - - - )} + {({ change, data, errors, submit }) => { + const handleCountrySelect = createSingleAutocompleteSelectHandler( + change, + setCountryDisplayName, + countryChoices + ); + + return ( + <> + + {variant === "create" + ? i18n.t("Add Address") + : i18n.t("Edit Address")} + + + + + + + + {i18n.t("Save Address", { context: "button" })} + + + + + ); + }}
); diff --git a/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx b/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx index eb4e13fc7..9ba4fa0c2 100644 --- a/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx +++ b/src/customers/components/CustomerCreateAddress/CustomerCreateAddress.tsx @@ -7,10 +7,10 @@ import React from "react"; import AddressEdit from "@saleor/components/AddressEdit"; import CardTitle from "@saleor/components/CardTitle"; import { FormSpacer } from "@saleor/components/FormSpacer"; +import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import i18n from "../../../i18n"; import { FormErrors } from "../../../types"; import { AddressTypeInput } from "../../types"; -import { CustomerCreateData_shop_countries } from "../../types/CustomerCreateData"; const styles = createStyles({ overflow: { @@ -19,11 +19,13 @@ const styles = createStyles({ }); export interface CustomerCreateAddressProps extends WithStyles { - countries: CustomerCreateData_shop_countries[]; + countries: SingleAutocompleteChoiceType[]; + countryDisplayName: string; data: AddressTypeInput; disabled: boolean; errors: FormErrors; onChange(event: React.ChangeEvent); + onCountryChange(event: React.ChangeEvent); } const CustomerCreateAddress = withStyles(styles, { @@ -32,10 +34,12 @@ const CustomerCreateAddress = withStyles(styles, { ({ classes, countries, + countryDisplayName, data, disabled, errors, - onChange + onChange, + onCountryChange }: CustomerCreateAddressProps) => ( @@ -45,14 +49,13 @@ const CustomerCreateAddress = withStyles(styles, {
({ - code: country.code, - label: country.country - }))} + countries={countries} data={data} disabled={disabled} + countryDisplayValue={countryDisplayName} errors={errors} onChange={onChange} + onCountryChange={onCountryChange} /> diff --git a/src/customers/components/CustomerCreatePage/CustomerCreatePage.tsx b/src/customers/components/CustomerCreatePage/CustomerCreatePage.tsx index 1894148a6..a38cc3cbb 100644 --- a/src/customers/components/CustomerCreatePage/CustomerCreatePage.tsx +++ b/src/customers/components/CustomerCreatePage/CustomerCreatePage.tsx @@ -8,6 +8,7 @@ import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import i18n from "../../../i18n"; import { UserError } from "../../../types"; import { AddressTypeInput } from "../../types"; @@ -27,10 +28,7 @@ const initialForm: CustomerCreatePageFormData = { city: "", cityArea: "", companyName: "", - country: { - label: "", - value: "" - }, + country: "", countryArea: "", customerFirstName: "", customerLastName: "", @@ -60,46 +58,69 @@ const CustomerCreatePage: React.StatelessComponent = ({ saveButtonBar, onBack, onSubmit -}: CustomerCreatePageProps) => ( -
- {({ change, data, errors: formErrors, hasChanged, submit }) => ( - - {i18n.t("Customers")} - - -
- { + const [countryDisplayName, setCountryDisplayName] = React.useState(""); + const countryChoices = countries.map(country => ({ + label: country.country, + value: country.code + })); + + return ( + + {({ change, data, errors: formErrors, hasChanged, submit }) => { + const handleCountrySelect = createSingleAutocompleteSelectHandler( + change, + setCountryDisplayName, + countryChoices + ); + + return ( + + {i18n.t("Customers")} + + +
+ + + + + +
+
+ - - - - -
-
- -
- )} -
-); + + ); + }} + + ); +}; CustomerCreatePage.displayName = "CustomerCreatePage"; export default CustomerCreatePage; diff --git a/src/customers/components/CustomerDetails/CustomerDetails.tsx b/src/customers/components/CustomerDetails/CustomerDetails.tsx index 6ec4ffd0f..c564a56fe 100644 --- a/src/customers/components/CustomerDetails/CustomerDetails.tsx +++ b/src/customers/components/CustomerDetails/CustomerDetails.tsx @@ -8,7 +8,7 @@ import { } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; -import * as moment from "moment-timezone"; +import moment from "moment-timezone"; import React from "react"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx index 1e417f960..ca41daeeb 100644 --- a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx +++ b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx @@ -55,11 +55,11 @@ const CustomerDetailsPage: React.StatelessComponent<
customer.email), - firstName: maybe(() => customer.firstName), + email: maybe(() => customer.email, ""), + firstName: maybe(() => customer.firstName, ""), isActive: maybe(() => customer.isActive, false), - lastName: maybe(() => customer.lastName), - note: maybe(() => customer.note) + lastName: maybe(() => customer.lastName, ""), + note: maybe(() => customer.note, "") }} onSubmit={onSubmit} confirmLeave diff --git a/src/customers/components/CustomerList/CustomerList.tsx b/src/customers/components/CustomerList/CustomerList.tsx index 8e2b1f17f..5381f23f6 100644 --- a/src/customers/components/CustomerList/CustomerList.tsx +++ b/src/customers/components/CustomerList/CustomerList.tsx @@ -16,9 +16,9 @@ import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; -import { getUserName, maybe, renderCollection } from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { getUserName, maybe, renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; import { ListCustomers_customers_edges_node } from "../../types/ListCustomers"; const styles = (theme: Theme) => @@ -47,14 +47,18 @@ export interface CustomerListProps customers: ListCustomers_customers_edges_node[]; } +const numberOfColumns = 4; + const CustomerList = withStyles(styles, { name: "CustomerList" })( ({ classes, + settings, disabled, customers, pageInfo, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick, toolbar, toggle, @@ -65,6 +69,7 @@ const CustomerList = withStyles(styles, { name: "CustomerList" })( toggle(customer.id)} /> @@ -132,7 +140,7 @@ const CustomerList = withStyles(styles, { name: "CustomerList" })( }, () => ( - + {i18n.t("No customers found")} diff --git a/src/customers/components/CustomerListPage/CustomerListPage.tsx b/src/customers/components/CustomerListPage/CustomerListPage.tsx index 74e5aa6f3..e39dad20c 100644 --- a/src/customers/components/CustomerListPage/CustomerListPage.tsx +++ b/src/customers/components/CustomerListPage/CustomerListPage.tsx @@ -4,8 +4,8 @@ import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; import { ListCustomers_customers_edges_node } from "../../types/ListCustomers"; import CustomerList from "../CustomerList/CustomerList"; diff --git a/src/customers/types.ts b/src/customers/types.ts index 1eee8acac..fa3a76f4e 100644 --- a/src/customers/types.ts +++ b/src/customers/types.ts @@ -2,10 +2,7 @@ export interface AddressTypeInput { city: string; cityArea?: string; companyName?: string; - country: { - label: string; - value: string; - }; + country: string; countryArea?: string; firstName: string; lastName: string; diff --git a/src/customers/views/CustomerAddresses.tsx b/src/customers/views/CustomerAddresses.tsx index bbb4fb0dc..9f72bce6a 100644 --- a/src/customers/views/CustomerAddresses.tsx +++ b/src/customers/views/CustomerAddresses.tsx @@ -133,6 +133,16 @@ const CustomerAddresses: React.FC = ({ [] ) ); + + const countryChoices = maybe( + () => + shop.countries.map(country => ({ + code: country.code, + label: country.country + })), + [] + ); + return ( <> = ({ - shop.countries.map(country => ({ - code: country.code, - label: country.country - })), - [] - )} + countries={countryChoices} errors={maybe( () => createCustomerAddressOpts.data.addressCreate @@ -173,14 +176,11 @@ const CustomerAddresses: React.FC = ({ open={params.action === "add"} variant="create" onClose={closeModal} - onConfirm={formData => + onConfirm={input => createCustomerAddress({ variables: { id, - input: { - ...formData, - country: formData.country.value - } + input } }) } @@ -192,7 +192,7 @@ const CustomerAddresses: React.FC = ({ ) )} confirmButtonState={updateAddressTransitionState} - countries={[]} + countries={countryChoices} errors={maybe( () => updateCustomerAddressOpts.data.addressUpdate @@ -202,14 +202,11 @@ const CustomerAddresses: React.FC = ({ open={params.action === "edit"} variant="edit" onClose={closeModal} - onConfirm={formData => + onConfirm={input => updateCustomerAddress({ variables: { id: params.id, - input: { - ...formData, - country: formData.country.value - } + input } }) } diff --git a/src/customers/views/CustomerCreate.tsx b/src/customers/views/CustomerCreate.tsx index f89a867d6..10b5719d0 100644 --- a/src/customers/views/CustomerCreate.tsx +++ b/src/customers/views/CustomerCreate.tsx @@ -67,14 +67,8 @@ export const CustomerCreate: React.StatelessComponent<{}> = () => { createCustomer({ variables: { input: { - defaultBillingAddress: { - ...address, - country: address.country.value - }, - defaultShippingAddress: { - ...address, - country: address.country.value - }, + defaultBillingAddress: address, + defaultShippingAddress: address, email: formData.email, firstName: formData.customerFirstName, lastName: formData.customerLastName, diff --git a/src/customers/views/CustomerList.tsx b/src/customers/views/CustomerList.tsx index 443dc1e9c..b36ed9e2a 100644 --- a/src/customers/views/CustomerList.tsx +++ b/src/customers/views/CustomerList.tsx @@ -5,14 +5,15 @@ import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import { PAGINATE_BY } from "../../config"; -import i18n from "../../i18n"; -import { getMutationState, maybe } from "../../misc"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import CustomerListPage from "../components/CustomerListPage"; import { TypedBulkRemoveCustomers } from "../mutations"; import { TypedCustomerListQuery } from "../queries"; @@ -37,6 +38,9 @@ export const CustomerList: React.StatelessComponent = ({ const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); + const { updateListSettings, settings } = useListSettings( + ListViews.CUSTOMER_LIST + ); const closeModal = () => navigate( @@ -48,7 +52,7 @@ export const CustomerList: React.StatelessComponent = ({ true ); - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); return ( @@ -87,11 +91,13 @@ export const CustomerList: React.StatelessComponent = ({ customers={maybe(() => data.customers.edges.map(edge => edge.node) )} + settings={settings} disabled={loading} pageInfo={pageInfo} onAdd={() => navigate(customerAddUrl)} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} onRowClick={id => () => navigate(customerUrl(id))} toolbar={ width: "60%" } }); + +const numberOfColumns = 4; + const DiscountCategories = withStyles(styles, { name: "DiscountCategories" })( @@ -71,9 +74,7 @@ const DiscountCategories = withStyles(styles, { }: DiscountCategoriesProps & WithStyles) => ( sale.name) - })} + title={i18n.t("Eligible Categories")} toolbar={
sale.categories.edges.map(edge => edge.node))} @@ -89,7 +91,6 @@ const DiscountCategories = withStyles(styles, { toolbar={toolbar} > <> - {i18n.t("Category name")} @@ -102,7 +103,7 @@ const DiscountCategories = withStyles(styles, { toggle(category.id)} /> @@ -158,7 +160,7 @@ const DiscountCategories = withStyles(styles, { }, () => ( - + {i18n.t("No categories found")} diff --git a/src/discounts/components/DiscountCollections/DiscountCollections.tsx b/src/discounts/components/DiscountCollections/DiscountCollections.tsx index e5b0eff8a..f863204db 100644 --- a/src/discounts/components/DiscountCollections/DiscountCollections.tsx +++ b/src/discounts/components/DiscountCollections/DiscountCollections.tsx @@ -50,6 +50,9 @@ const styles = (theme: Theme) => width: "60%" } }); + +const numberOfColumns = 4; + const DiscountCollections = withStyles(styles, { name: "DiscountCollections" })( @@ -71,9 +74,7 @@ const DiscountCollections = withStyles(styles, { }: DiscountCollectionsProps & WithStyles) => ( sale.name) - })} + title={i18n.t("Eligible Collections")} toolbar={
sale.collections.edges.map(edge => edge.node))} @@ -94,11 +96,12 @@ const DiscountCollections = withStyles(styles, { {i18n.t("Products")} + toggle(collection.id)} /> @@ -156,7 +160,7 @@ const DiscountCollections = withStyles(styles, { }, () => ( - + {i18n.t("No collections found")} diff --git a/src/discounts/components/DiscountProducts/DiscountProducts.tsx b/src/discounts/components/DiscountProducts/DiscountProducts.tsx index 5fe393395..3910cfad9 100644 --- a/src/discounts/components/DiscountProducts/DiscountProducts.tsx +++ b/src/discounts/components/DiscountProducts/DiscountProducts.tsx @@ -19,7 +19,9 @@ import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; 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 TablePagination from "@saleor/components/TablePagination"; import i18n from "../../../i18n"; @@ -36,28 +38,34 @@ export interface SaleProductsProps extends ListProps, ListActions { const styles = (theme: Theme) => createStyles({ - [theme.breakpoints.up("lg")]: { - colName: {}, - colPublished: { - width: 150 - }, - colType: { - width: 200 - } - }, - colName: {}, - colPublished: {}, - colType: {}, - iconCell: { + colActions: { "&:last-child": { paddingRight: 0 }, width: 48 + theme.spacing.unit / 2 }, + colName: { + width: "auto" + }, + colNameLabel: { + marginLeft: AVATAR_MARGIN + }, + colPublished: { + width: 150 + }, + colType: { + width: 200 + }, + table: { + tableLayout: "fixed" + }, tableRow: { cursor: "pointer" } }); + +const numberOfColumns = 5; + const DiscountProducts = withStyles(styles, { name: "DiscountProducts" })( @@ -79,9 +87,7 @@ const DiscountProducts = withStyles(styles, { }: SaleProductsProps & WithStyles) => ( sale.name) - })} + title={i18n.t("Eligible Products")} toolbar={
sale.products.edges.map(edge => edge.node))} toggleAll={toggleAll} toolbar={toolbar} > - - - {i18n.t("Product name")} + + {i18n.t("Product name")} + {i18n.t("Product Type")} @@ -112,7 +119,7 @@ const DiscountProducts = withStyles(styles, { toggle(product.id)} /> product.thumbnail.url)} - /> - + > {maybe(() => product.name, )} - + {maybe( () => product.productType.name, @@ -170,7 +178,7 @@ const DiscountProducts = withStyles(styles, { )} - + { @@ -186,7 +194,9 @@ const DiscountProducts = withStyles(styles, { }, () => ( - {i18n.t("No products found")} + + {i18n.t("No products found")} + ) )} diff --git a/src/discounts/components/SaleList/SaleList.tsx b/src/discounts/components/SaleList/SaleList.tsx index 42169f3d6..54669a3f1 100644 --- a/src/discounts/components/SaleList/SaleList.tsx +++ b/src/discounts/components/SaleList/SaleList.tsx @@ -19,10 +19,10 @@ import Percent from "@saleor/components/Percent"; import Skeleton from "@saleor/components/Skeleton"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; -import { maybe, renderCollection } from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; -import { SaleType } from "../../../types/globalTypes"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; +import { SaleType } from "@saleor/types/globalTypes"; import { SaleList_sales_edges_node } from "../../types/SaleList"; export interface SaleListProps extends ListProps, ListActions { @@ -59,15 +59,19 @@ const styles = (theme: Theme) => } }); +const numberOfColumns = 5; + const SaleList = withStyles(styles, { name: "SaleList" })( ({ classes, + settings, defaultCurrency, disabled, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick, pageInfo, sales, @@ -80,6 +84,7 @@ const SaleList = withStyles(styles, {
toggle(sale.id)} /> @@ -184,7 +192,9 @@ const SaleList = withStyles(styles, { }, () => ( - {i18n.t("No sales found")} + + {i18n.t("No sales found")} + ) )} diff --git a/src/discounts/components/SaleListPage/SaleListPage.tsx b/src/discounts/components/SaleListPage/SaleListPage.tsx index dcfdc126d..bf0eca395 100644 --- a/src/discounts/components/SaleListPage/SaleListPage.tsx +++ b/src/discounts/components/SaleListPage/SaleListPage.tsx @@ -4,8 +4,8 @@ import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; import { SaleList_sales_edges_node } from "../../types/SaleList"; import SaleList from "../SaleList"; diff --git a/src/discounts/components/SalePricing/SalePricing.tsx b/src/discounts/components/SalePricing/SalePricing.tsx index 2aed0a2d9..4f5fb3790 100644 --- a/src/discounts/components/SalePricing/SalePricing.tsx +++ b/src/discounts/components/SalePricing/SalePricing.tsx @@ -96,6 +96,9 @@ const SalePricing = withStyles(styles, { label={i18n.t("Start Date")} value={data.startDate} type="date" + InputLabelProps={{ + shrink: true + }} fullWidth /> diff --git a/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx b/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx index 9d46dda9a..c04902833 100644 --- a/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx +++ b/src/discounts/components/VoucherCreatePage/VoucherCreatePage.tsx @@ -11,22 +11,33 @@ import SaveButtonBar from "@saleor/components/SaveButtonBar"; import i18n from "../../../i18n"; import { UserError } from "../../../types"; import { - VoucherDiscountValueType, - VoucherType + DiscountValueTypeEnum, + VoucherTypeEnum } from "../../../types/globalTypes"; +import { RequirementsPicker } from "../../types"; +import VoucherDates from "../VoucherDates"; 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 { + applyOncePerCustomer: boolean; applyOncePerOrder: boolean; code: string; - discountType: VoucherDiscountValueType; + discountType: DiscountValueTypeEnum; endDate: string; - minAmountSpent: number; - name: string; + endTime: string; + hasEndDate: boolean; + hasUsageLimit: boolean; + minAmountSpent: string; + minCheckoutItemsQuantity: string; + requirementsPicker: RequirementsPicker; startDate: string; - type: VoucherType; - usageLimit: number; + startTime: string; + type: VoucherTypeEnum; + usageLimit: string; value: number; } @@ -48,15 +59,21 @@ const VoucherCreatePage: React.StatelessComponent = ({ onSubmit }) => { const initialForm: FormData = { + applyOncePerCustomer: false, applyOncePerOrder: false, code: "", - discountType: VoucherDiscountValueType.FIXED, + discountType: DiscountValueTypeEnum.FIXED, endDate: "", - minAmountSpent: 0, - name: "", + endTime: "", + hasEndDate: false, + hasUsageLimit: false, + minAmountSpent: "0", + minCheckoutItemsQuantity: "0", + requirementsPicker: RequirementsPicker.NONE, startDate: "", - type: VoucherType.VALUE, - usageLimit: 0, + startTime: "", + type: VoucherTypeEnum.ENTIRE_ORDER, + usageLimit: "0", value: 0 }; @@ -72,11 +89,28 @@ const VoucherCreatePage: React.StatelessComponent = ({ data={data} errors={formErrors} disabled={disabled} - variant="create" onChange={change} + variant="create" /> - + {data.discountType.toString() !== "SHIPPING" ? ( + + ) : null} + + = ({ onChange={change} /> + + + = ({ collectionListToolbar, 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 = { + applyOncePerCustomer: maybe(() => voucher.applyOncePerCustomer, false), applyOncePerOrder: maybe(() => voucher.applyOncePerOrder, false), code: maybe(() => voucher.code, ""), discountType: maybe( () => voucher.discountValueType, - VoucherDiscountValueType.FIXED + DiscountValueTypeEnum.FIXED ), - endDate: maybe(() => voucher.endDate, ""), - minAmountSpent: maybe(() => voucher.minAmountSpent.amount, 0), - name: maybe(() => voucher.name, ""), - startDate: maybe(() => voucher.startDate, ""), - type: maybe(() => voucher.type, VoucherType.VALUE), - usageLimit: maybe(() => voucher.usageLimit || 0, 0), + endDate: splitDateTime(maybe(() => voucher.endDate, "")).date, + endTime: splitDateTime(maybe(() => voucher.endDate, "")).time, + hasEndDate: maybe(() => !!voucher.endDate), + hasUsageLimit: maybe(() => !!voucher.usageLimit), + minAmountSpent: maybe(() => voucher.minAmountSpent.amount.toString(), "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) }; @@ -137,28 +167,37 @@ const VoucherDetailsPage: React.StatelessComponent = ({ {({ change, data, errors: formErrors, hasChanged, submit }) => ( {i18n.t("Vouchers")} - voucher.name)} /> + voucher.code)} />
- - {data.type === VoucherType.CATEGORY || - data.type === VoucherType.COLLECTION || - data.type === VoucherType.PRODUCT ? ( + {data.discountType.toString() !== "SHIPPING" ? ( + + ) : null} + + {data.type === VoucherTypeEnum.SPECIFIC_PRODUCT && + data.discountType.toString() !== "SHIPPING" ? ( <> = ({ /> )} - ) : data.type === VoucherType.SHIPPING ? ( + ) : null} + + {data.discountType.toString() === "SHIPPING" ? ( voucher.countries)} disabled={disabled} emptyText={i18n.t("Voucher applies to all countries")} title={ <> - {i18n.t("Countries assigned to {{ voucherName }}", { - voucherName: maybe(() => voucher.name) - })} + {i18n.t("Countries")} {i18n.t("Vouchers limited to these countries")} @@ -265,6 +304,30 @@ const VoucherDetailsPage: React.StatelessComponent = ({ onCountryUnassign={onCountryUnassign} /> ) : null} + + + + + +
; + errors: FormErrors<"code">; disabled: boolean; variant: "create" | "update"; - onChange: (event: React.ChangeEvent) => void; + onChange: (event: any) => void; } -const styles = (theme: Theme) => - createStyles({ - nameInput: { - gridColumnEnd: "span 2" - }, - root: { - display: "grid", - gridColumnGap: theme.spacing.unit * 2 + "px", - gridTemplateColumns: "1fr 1fr" - } - }); +const VoucherInfo = ({ + data, + disabled, + errors, + variant, + onChange +}: VoucherInfoProps) => { + const onGenerateCode = () => + onChange({ + target: { + name: "code", + value: generateCode(10) + } + }); -const VoucherInfo = withStyles(styles, { - name: "VoucherInfo" -})( - ({ - classes, - data, - disabled, - errors, - variant, - onChange - }: VoucherInfoProps & WithStyles) => { - const translatedVoucherTypes = translateVoucherTypes(); - const voucherTypeChoices = Object.values(VoucherType).map(type => ({ - label: translatedVoucherTypes[type], - value: type - })); - - return ( - - - - - -
- - -
-
-
- ); - } -); + return ( + + + {i18n.t("Generate Code")} + + ) + } + /> + + + + + ); +}; export default VoucherInfo; diff --git a/src/discounts/components/VoucherList/VoucherList.tsx b/src/discounts/components/VoucherList/VoucherList.tsx index a5e53dfeb..0c8dda994 100644 --- a/src/discounts/components/VoucherList/VoucherList.tsx +++ b/src/discounts/components/VoucherList/VoucherList.tsx @@ -19,10 +19,10 @@ import Percent from "@saleor/components/Percent"; import Skeleton from "@saleor/components/Skeleton"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; -import { maybe, renderCollection } from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; -import { VoucherDiscountValueType } from "../../../types/globalTypes"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; +import { DiscountValueTypeEnum } from "@saleor/types/globalTypes"; import { VoucherList_vouchers_edges_node } from "../../types/VoucherList"; export interface VoucherListProps extends ListProps, ListActions { @@ -74,15 +74,19 @@ const styles = (theme: Theme) => } }); +const numberOfColumns = 7; + const VoucherList = withStyles(styles, { name: "VoucherList" })( ({ classes, + settings, defaultCurrency, disabled, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick, pageInfo, vouchers, @@ -95,6 +99,7 @@ const VoucherList = withStyles(styles, {
- {i18n.t("Name", { + {i18n.t("Code", { context: "voucher list table header" })} @@ -135,9 +140,11 @@ const VoucherList = withStyles(styles, { toggle(voucher.id)} /> - {maybe(() => voucher.name, )} + {maybe(() => voucher.code, )} {voucher && voucher.minAmountSpent ? ( @@ -202,7 +210,7 @@ const VoucherList = withStyles(styles, { voucher.discountValueType && voucher.discountValue ? ( voucher.discountValueType === - VoucherDiscountValueType.FIXED ? ( + DiscountValueTypeEnum.FIXED ? ( ( - {i18n.t("No vouchers found")} + + {i18n.t("No vouchers found")} + ) )} diff --git a/src/discounts/components/VoucherListPage/VoucherListPage.tsx b/src/discounts/components/VoucherListPage/VoucherListPage.tsx index 72cc4f4f2..f8b75c59a 100644 --- a/src/discounts/components/VoucherListPage/VoucherListPage.tsx +++ b/src/discounts/components/VoucherListPage/VoucherListPage.tsx @@ -4,8 +4,8 @@ import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; import { VoucherList_vouchers_edges_node } from "../../types/VoucherList"; import VoucherList from "../VoucherList"; @@ -17,9 +17,11 @@ export interface VoucherListPageProps extends PageListProps, ListActions { const VoucherListPage: React.StatelessComponent = ({ defaultCurrency, disabled, + settings, onAdd, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick, pageInfo, vouchers, @@ -38,9 +40,11 @@ const VoucherListPage: React.StatelessComponent = ({ = ({ - {i18n.t("Name")} + {i18n.t("Code")} - {maybe(() => voucher.name, )} + {maybe(() => voucher.code, )} @@ -51,7 +51,7 @@ const VoucherSummary: React.StatelessComponent = ({ {maybe( () => - voucher.discountValueType === VoucherDiscountValueType.FIXED ? ( + voucher.discountValueType === DiscountValueTypeEnum.FIXED ? ( ({ - [VoucherType.CATEGORY]: i18n.t("Selected Categories"), - [VoucherType.COLLECTION]: i18n.t("Selected Collections"), - [VoucherType.PRODUCT]: i18n.t("Selected Products"), - [VoucherType.SHIPPING]: i18n.t("Shipment"), - [VoucherType.VALUE]: i18n.t("All Products") + [VoucherTypeEnum.SHIPPING]: i18n.t("Shipment"), + [VoucherTypeEnum.ENTIRE_ORDER]: i18n.t("Entire order"), + [VoucherTypeEnum.SPECIFIC_PRODUCT]: i18n.t("Specific Products") }); diff --git a/src/discounts/types/VoucherCataloguesAdd.ts b/src/discounts/types/VoucherCataloguesAdd.ts index f005d2e19..51f997373 100644 --- a/src/discounts/types/VoucherCataloguesAdd.ts +++ b/src/discounts/types/VoucherCataloguesAdd.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // 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 @@ -133,18 +133,19 @@ export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_categories { export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher { __typename: "Voucher"; id: string; - name: string | null; + code: string; startDate: any; endDate: any | null; usageLimit: number | null; - discountValueType: VoucherDiscountValueType; + discountValueType: DiscountValueTypeEnum; discountValue: number; countries: (VoucherCataloguesAdd_voucherCataloguesAdd_voucher_countries | null)[] | null; minAmountSpent: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_minAmountSpent | null; - type: VoucherType; - code: string; + minCheckoutItemsQuantity: number | null; + type: VoucherTypeEnum; used: number; applyOncePerOrder: boolean; + applyOncePerCustomer: boolean; products: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_products | null; collections: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_collections | null; categories: VoucherCataloguesAdd_voucherCataloguesAdd_voucher_categories | null; diff --git a/src/discounts/types/VoucherCataloguesRemove.ts b/src/discounts/types/VoucherCataloguesRemove.ts index 417934c69..12cbb13da 100644 --- a/src/discounts/types/VoucherCataloguesRemove.ts +++ b/src/discounts/types/VoucherCataloguesRemove.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // 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 @@ -133,18 +133,19 @@ export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_categor export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher { __typename: "Voucher"; id: string; - name: string | null; + code: string; startDate: any; endDate: any | null; usageLimit: number | null; - discountValueType: VoucherDiscountValueType; + discountValueType: DiscountValueTypeEnum; discountValue: number; countries: (VoucherCataloguesRemove_voucherCataloguesRemove_voucher_countries | null)[] | null; minAmountSpent: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_minAmountSpent | null; - type: VoucherType; - code: string; + minCheckoutItemsQuantity: number | null; + type: VoucherTypeEnum; used: number; applyOncePerOrder: boolean; + applyOncePerCustomer: boolean; products: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_products | null; collections: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_collections | null; categories: VoucherCataloguesRemove_voucherCataloguesRemove_voucher_categories | null; diff --git a/src/discounts/types/VoucherCreate.ts b/src/discounts/types/VoucherCreate.ts index eca02664f..e45cb1d5b 100644 --- a/src/discounts/types/VoucherCreate.ts +++ b/src/discounts/types/VoucherCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // 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 @@ -29,14 +29,15 @@ export interface VoucherCreate_voucherCreate_voucher_minAmountSpent { export interface VoucherCreate_voucherCreate_voucher { __typename: "Voucher"; id: string; - name: string | null; + code: string; startDate: any; endDate: any | null; usageLimit: number | null; - discountValueType: VoucherDiscountValueType; + discountValueType: DiscountValueTypeEnum; discountValue: number; countries: (VoucherCreate_voucherCreate_voucher_countries | null)[] | null; minAmountSpent: VoucherCreate_voucherCreate_voucher_minAmountSpent | null; + minCheckoutItemsQuantity: number | null; } export interface VoucherCreate_voucherCreate { diff --git a/src/discounts/types/VoucherDetails.ts b/src/discounts/types/VoucherDetails.ts index dae5f8b1a..e6efc06b3 100644 --- a/src/discounts/types/VoucherDetails.ts +++ b/src/discounts/types/VoucherDetails.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // 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 @@ -127,18 +127,19 @@ export interface VoucherDetails_voucher_categories { export interface VoucherDetails_voucher { __typename: "Voucher"; id: string; - name: string | null; + code: string; startDate: any; endDate: any | null; usageLimit: number | null; - discountValueType: VoucherDiscountValueType; + discountValueType: DiscountValueTypeEnum; discountValue: number; countries: (VoucherDetails_voucher_countries | null)[] | null; minAmountSpent: VoucherDetails_voucher_minAmountSpent | null; - type: VoucherType; - code: string; + minCheckoutItemsQuantity: number | null; + type: VoucherTypeEnum; used: number; applyOncePerOrder: boolean; + applyOncePerCustomer: boolean; products: VoucherDetails_voucher_products | null; collections: VoucherDetails_voucher_collections | null; categories: VoucherDetails_voucher_categories | null; diff --git a/src/discounts/types/VoucherDetailsFragment.ts b/src/discounts/types/VoucherDetailsFragment.ts index b34b7d793..f8481e68d 100644 --- a/src/discounts/types/VoucherDetailsFragment.ts +++ b/src/discounts/types/VoucherDetailsFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // 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 @@ -127,18 +127,19 @@ export interface VoucherDetailsFragment_categories { export interface VoucherDetailsFragment { __typename: "Voucher"; id: string; - name: string | null; + code: string; startDate: any; endDate: any | null; usageLimit: number | null; - discountValueType: VoucherDiscountValueType; + discountValueType: DiscountValueTypeEnum; discountValue: number; countries: (VoucherDetailsFragment_countries | null)[] | null; minAmountSpent: VoucherDetailsFragment_minAmountSpent | null; - type: VoucherType; - code: string; + minCheckoutItemsQuantity: number | null; + type: VoucherTypeEnum; used: number; applyOncePerOrder: boolean; + applyOncePerCustomer: boolean; products: VoucherDetailsFragment_products | null; collections: VoucherDetailsFragment_collections | null; categories: VoucherDetailsFragment_categories | null; diff --git a/src/discounts/types/VoucherFragment.ts b/src/discounts/types/VoucherFragment.ts index c8d8aab47..62a8483a4 100644 --- a/src/discounts/types/VoucherFragment.ts +++ b/src/discounts/types/VoucherFragment.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { VoucherDiscountValueType } from "./../../types/globalTypes"; +import { DiscountValueTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: VoucherFragment @@ -23,12 +23,13 @@ export interface VoucherFragment_minAmountSpent { export interface VoucherFragment { __typename: "Voucher"; id: string; - name: string | null; + code: string; startDate: any; endDate: any | null; usageLimit: number | null; - discountValueType: VoucherDiscountValueType; + discountValueType: DiscountValueTypeEnum; discountValue: number; countries: (VoucherFragment_countries | null)[] | null; minAmountSpent: VoucherFragment_minAmountSpent | null; + minCheckoutItemsQuantity: number | null; } diff --git a/src/discounts/types/VoucherList.ts b/src/discounts/types/VoucherList.ts index cce0df721..9b421ef41 100644 --- a/src/discounts/types/VoucherList.ts +++ b/src/discounts/types/VoucherList.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { VoucherDiscountValueType } from "./../../types/globalTypes"; +import { DiscountValueTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: VoucherList @@ -23,14 +23,15 @@ export interface VoucherList_vouchers_edges_node_minAmountSpent { export interface VoucherList_vouchers_edges_node { __typename: "Voucher"; id: string; - name: string | null; + code: string; startDate: any; endDate: any | null; usageLimit: number | null; - discountValueType: VoucherDiscountValueType; + discountValueType: DiscountValueTypeEnum; discountValue: number; countries: (VoucherList_vouchers_edges_node_countries | null)[] | null; minAmountSpent: VoucherList_vouchers_edges_node_minAmountSpent | null; + minCheckoutItemsQuantity: number | null; } export interface VoucherList_vouchers_edges { diff --git a/src/discounts/types/VoucherUpdate.ts b/src/discounts/types/VoucherUpdate.ts index 2dd2521b2..1dd2e53c6 100644 --- a/src/discounts/types/VoucherUpdate.ts +++ b/src/discounts/types/VoucherUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // 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: VoucherUpdate @@ -29,14 +29,15 @@ export interface VoucherUpdate_voucherUpdate_voucher_minAmountSpent { export interface VoucherUpdate_voucherUpdate_voucher { __typename: "Voucher"; id: string; - name: string | null; + code: string; startDate: any; endDate: any | null; usageLimit: number | null; - discountValueType: VoucherDiscountValueType; + discountValueType: DiscountValueTypeEnum; discountValue: number; countries: (VoucherUpdate_voucherUpdate_voucher_countries | null)[] | null; minAmountSpent: VoucherUpdate_voucherUpdate_voucher_minAmountSpent | null; + minCheckoutItemsQuantity: number | null; } export interface VoucherUpdate_voucherUpdate { diff --git a/src/discounts/views/SaleDetails.tsx b/src/discounts/views/SaleDetails.tsx index cc50292ed..d74c74531 100644 --- a/src/discounts/views/SaleDetails.tsx +++ b/src/discounts/views/SaleDetails.tsx @@ -339,13 +339,13 @@ export const SaleDetails: React.StatelessComponent = ({ onFetch={searchProducts} loading={searchProductsOpts.loading} onClose={closeModal} - onSubmit={formData => + onSubmit={products => saleCataloguesAdd({ variables: { ...paginationState, id, input: { - products: formData.products.map( + products: products.map( product => product.id ) } @@ -383,13 +383,13 @@ export const SaleDetails: React.StatelessComponent = ({ onFetch={searchCategories} loading={searchCategoriesOpts.loading} onClose={closeModal} - onSubmit={formData => + onSubmit={categories => saleCataloguesAdd({ variables: { ...paginationState, id, input: { - categories: formData.categories.map( + categories: categories.map( product => product.id ) } @@ -420,13 +420,13 @@ export const SaleDetails: React.StatelessComponent = ({ onFetch={searchCollections} loading={searchCollectionsOpts.loading} onClose={closeModal} - onSubmit={formData => + onSubmit={collections => saleCataloguesAdd({ variables: { ...paginationState, id, input: { - collections: formData.collections.map( + collections: collections.map( product => product.id ) } diff --git a/src/discounts/views/SaleList.tsx b/src/discounts/views/SaleList.tsx index b51ba16f9..c159bf502 100644 --- a/src/discounts/views/SaleList.tsx +++ b/src/discounts/views/SaleList.tsx @@ -6,15 +6,16 @@ import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; import useShop from "@saleor/hooks/useShop"; -import { PAGINATE_BY } from "../../config"; -import i18n from "../../i18n"; -import { getMutationState, maybe } from "../../misc"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import SaleListPage from "../components/SaleListPage"; import { TypedSaleBulkDelete } from "../mutations"; import { TypedSaleList } from "../queries"; @@ -40,10 +41,13 @@ export const SaleList: React.StatelessComponent = ({ const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); + const { updateListSettings, settings } = useListSettings( + ListViews.SALES_LIST + ); const closeModal = () => navigate(saleListUrl(), true); - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); return ( @@ -86,11 +90,13 @@ export const SaleList: React.StatelessComponent = ({ shop.defaultCurrency)} sales={maybe(() => data.sales.edges.map(edge => edge.node))} + settings={settings} disabled={loading} pageInfo={pageInfo} onAdd={() => navigate(saleAddUrl)} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} onRowClick={id => () => navigate(saleUrl(id))} isChecked={isSelected} selected={listElements.length} diff --git a/src/discounts/views/VoucherCreate.tsx b/src/discounts/views/VoucherCreate.tsx index 331a6a6db..cc4a7bc33 100644 --- a/src/discounts/views/VoucherCreate.tsx +++ b/src/discounts/views/VoucherCreate.tsx @@ -5,25 +5,17 @@ import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import i18n from "../../i18n"; -import { decimal, getMutationState, maybe } from "../../misc"; +import { decimal, getMutationState, joinDateTime, maybe } from "../../misc"; import { DiscountValueTypeEnum, - VoucherDiscountValueType, VoucherTypeEnum } from "../../types/globalTypes"; import VoucherCreatePage from "../components/VoucherCreatePage"; import { TypedVoucherCreate } from "../mutations"; +import { RequirementsPicker } from "../types"; import { VoucherCreate } from "../types/VoucherCreate"; import { voucherListUrl, voucherUrl } from "../urls"; -function discountValueTypeEnum( - type: VoucherDiscountValueType -): DiscountValueTypeEnum { - return type.toString() === DiscountValueTypeEnum.FIXED - ? DiscountValueTypeEnum.FIXED - : DiscountValueTypeEnum.PERCENTAGE; -} - export const VoucherDetails: React.StatelessComponent = () => { const navigate = useNavigator(); const notify = useNotifier(); @@ -61,18 +53,37 @@ export const VoucherDetails: React.StatelessComponent = () => { voucherCreate({ variables: { input: { + applyOncePerCustomer: formData.applyOncePerCustomer, + applyOncePerOrder: formData.applyOncePerOrder, code: formData.code, - discountValue: decimal(formData.value), - discountValueType: discountValueTypeEnum( - formData.discountType + discountValue: + formData.discountType.toString() === "SHIPPING" + ? 100 + : decimal(formData.value), + discountValueType: + formData.discountType.toString() === "SHIPPING" + ? DiscountValueTypeEnum.PERCENTAGE + : formData.discountType, + endDate: formData.hasEndDate + ? joinDateTime(formData.endDate, formData.endTime) + : null, + minAmountSpent: + formData.requirementsPicker !== RequirementsPicker.ORDER + ? 0 + : parseFloat(formData.minAmountSpent), + minCheckoutItemsQuantity: + formData.requirementsPicker !== RequirementsPicker.ITEM + ? 0 + : parseFloat(formData.minCheckoutItemsQuantity), + startDate: joinDateTime( + formData.startDate, + formData.startTime ), - endDate: - formData.endDate === "" ? null : formData.endDate, - minAmountSpent: formData.minAmountSpent, - name: formData.name, - startDate: - formData.startDate === "" ? null : formData.startDate, - type: VoucherTypeEnum[formData.type] + type: + formData.discountType.toString() === "SHIPPING" + ? VoucherTypeEnum.ENTIRE_ORDER + : formData.type, + usageLimit: parseInt(formData.usageLimit, 10) } } }) diff --git a/src/discounts/views/VoucherDetails.tsx b/src/discounts/views/VoucherDetails.tsx index ff8012c98..c7f90799e 100644 --- a/src/discounts/views/VoucherDetails.tsx +++ b/src/discounts/views/VoucherDetails.tsx @@ -21,11 +21,11 @@ import SearchCategories from "../../containers/SearchCategories"; import SearchCollections from "../../containers/SearchCollections"; import SearchProducts from "../../containers/SearchProducts"; import i18n from "../../i18n"; -import { decimal, getMutationState, maybe } from "../../misc"; +import { decimal, getMutationState, joinDateTime, maybe } from "../../misc"; import { productUrl } from "../../products/urls"; import { DiscountValueTypeEnum, - VoucherDiscountValueType + VoucherTypeEnum } from "../../types/globalTypes"; import DiscountCountrySelectDialog from "../components/DiscountCountrySelectDialog"; import VoucherDetailsPage, { @@ -38,6 +38,7 @@ import { TypedVoucherUpdate } from "../mutations"; import { TypedVoucherDetails } from "../queries"; +import { RequirementsPicker } from "../types"; import { VoucherCataloguesAdd } from "../types/VoucherCataloguesAdd"; import { VoucherCataloguesRemove } from "../types/VoucherCataloguesRemove"; import { VoucherDelete } from "../types/VoucherDelete"; @@ -54,14 +55,6 @@ interface VoucherDetailsProps { params: VoucherUrlQueryParams; } -function discountValueTypeEnum( - type: VoucherDiscountValueType -): DiscountValueTypeEnum { - return type.toString() === DiscountValueTypeEnum.FIXED - ? DiscountValueTypeEnum.FIXED - : DiscountValueTypeEnum.PERCENTAGE; -} - export const VoucherDetails: React.StatelessComponent = ({ id, params @@ -323,19 +316,51 @@ export const VoucherDetails: React.StatelessComponent = ({ variables: { id, input: { - discountValue: decimal(formData.value), - discountValueType: discountValueTypeEnum( - formData.discountType + applyOncePerCustomer: + formData.applyOncePerCustomer, + applyOncePerOrder: + formData.applyOncePerOrder, + discountValue: + formData.discountType.toString() === + "SHIPPING" + ? 100 + : decimal(formData.value), + discountValueType: + formData.discountType.toString() === + "SHIPPING" + ? DiscountValueTypeEnum.PERCENTAGE + : formData.discountType, + endDate: formData.hasEndDate + ? joinDateTime( + formData.endDate, + formData.endTime + ) + : null, + minAmountSpent: + formData.requirementsPicker !== + RequirementsPicker.ORDER + ? 0 + : parseFloat(formData.minAmountSpent), + minCheckoutItemsQuantity: + formData.requirementsPicker !== + RequirementsPicker.ITEM + ? 0 + : parseFloat( + formData.minCheckoutItemsQuantity + ), + startDate: joinDateTime( + formData.startDate, + formData.startTime ), - endDate: - formData.endDate === "" - ? null - : formData.endDate, - name: formData.name, - startDate: - formData.startDate === "" - ? null - : formData.startDate + type: + formData.discountType.toString() === + "SHIPPING" + ? VoucherTypeEnum.SHIPPING + : formData.type, + usageLimit: parseInt( + formData.usageLimit, + 10 + ) } } }) @@ -401,13 +426,13 @@ export const VoucherDetails: React.StatelessComponent = ({ onFetch={searchCategories} loading={searchCategoriesOpts.loading} onClose={closeModal} - onSubmit={formData => + onSubmit={categories => voucherCataloguesAdd({ variables: { ...paginationState, id, input: { - categories: formData.categories.map( + categories: categories.map( product => product.id ) } @@ -438,13 +463,13 @@ export const VoucherDetails: React.StatelessComponent = ({ onFetch={searchCollections} loading={searchCollectionsOpts.loading} onClose={closeModal} - onSubmit={formData => + onSubmit={collections => voucherCataloguesAdd({ variables: { ...paginationState, id, input: { - collections: formData.collections.map( + collections: collections.map( product => product.id ) } @@ -490,13 +515,13 @@ export const VoucherDetails: React.StatelessComponent = ({ onFetch={searchProducts} loading={searchProductsOpts.loading} onClose={closeModal} - onSubmit={formData => + onSubmit={products => voucherCataloguesAdd({ variables: { ...paginationState, id, input: { - products: formData.products.map( + products: products.map( product => product.id ) } @@ -597,10 +622,10 @@ export const VoucherDetails: React.StatelessComponent = ({ {{ voucherName }}?", + "Are you sure you want to remove {{ voucherCode }}?", { - voucherName: maybe( - () => data.voucher.name, + voucherCode: maybe( + () => data.voucher.code, "..." ) } diff --git a/src/discounts/views/VoucherList.tsx b/src/discounts/views/VoucherList.tsx index 8d5949a30..fe47592a2 100644 --- a/src/discounts/views/VoucherList.tsx +++ b/src/discounts/views/VoucherList.tsx @@ -6,15 +6,16 @@ import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; import useShop from "@saleor/hooks/useShop"; -import { PAGINATE_BY } from "../../config"; -import i18n from "../../i18n"; -import { getMutationState, maybe } from "../../misc"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import VoucherListPage from "../components/VoucherListPage"; import { TypedVoucherBulkDelete } from "../mutations"; import { TypedVoucherList } from "../queries"; @@ -40,10 +41,13 @@ export const VoucherList: React.StatelessComponent = ({ const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); + const { updateListSettings, settings } = useListSettings( + ListViews.VOUCHER_LIST + ); const closeModal = () => navigate(voucherListUrl(), true); - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); return ( @@ -84,6 +88,7 @@ export const VoucherList: React.StatelessComponent = ({ shop.defaultCurrency)} + settings={settings} vouchers={maybe(() => data.vouchers.edges.map(edge => edge.node) )} @@ -92,6 +97,7 @@ export const VoucherList: React.StatelessComponent = ({ onAdd={() => navigate(voucherAddUrl)} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} onRowClick={id => () => navigate(voucherUrl(id))} isChecked={isSelected} selected={listElements.length} diff --git a/src/fixtures.ts b/src/fixtures.ts index ef1d1aeec..10f3345cd 100644 --- a/src/fixtures.ts +++ b/src/fixtures.ts @@ -1,5 +1,10 @@ import { Filter } from "./components/TableFilter"; -import { FilterPageProps, ListActions, PageListProps } from "./types"; +import { + FetchMoreProps, + FilterPageProps, + ListActions, + PageListProps +} from "./types"; const pageInfo = { hasNextPage: true, @@ -12,7 +17,8 @@ export const pageListProps: { [key: string]: PageListProps } = { onNextPage: undefined, onPreviousPage: undefined, onRowClick: () => undefined, - pageInfo + pageInfo, + settings: { rowNumber: 20 } }, loading: { disabled: true, @@ -20,7 +26,8 @@ export const pageListProps: { [key: string]: PageListProps } = { onNextPage: undefined, onPreviousPage: undefined, onRowClick: () => undefined, - pageInfo + pageInfo, + settings: undefined } }; export const listActionsProps: ListActions = { @@ -123,3 +130,10 @@ export const filters: Filter[] = [ ...filter, label: filter.label + filterIndex })); + +export const fetchMoreProps: FetchMoreProps = { + hasMore: true, + loading: false, + onFetch: () => undefined, + onFetchMore: () => undefined +}; diff --git a/src/home/components/HomeHeader/HomeHeader.tsx b/src/home/components/HomeHeader/HomeHeader.tsx index 30c746f1b..a8076669c 100644 --- a/src/home/components/HomeHeader/HomeHeader.tsx +++ b/src/home/components/HomeHeader/HomeHeader.tsx @@ -28,26 +28,24 @@ interface HomeOrdersCardProps extends WithStyles { } const HomeOrdersCard = withStyles(styles, { name: "HomeOrdersCard" })( - ({ classes, userName }: HomeOrdersCardProps) => { - return ( -
- - {userName ? ( - i18n.t("Hello there, {{userName}}", { userName }) - ) : ( - - )} - - - {userName ? ( - i18n.t("Here is some information we gathered about your store") - ) : ( - - )} - -
- ); - } + ({ classes, userName }: HomeOrdersCardProps) => ( +
+ + {userName ? ( + i18n.t("Hello there, {{userName}}", { userName }) + ) : ( + + )} + + + {userName ? ( + i18n.t("Here is some information we gathered about your store") + ) : ( + + )} + +
+ ) ); HomeOrdersCard.displayName = "HomeOrdersCard"; export default HomeOrdersCard; diff --git a/src/home/components/HomeProductListCard/HomeProductListCard.tsx b/src/home/components/HomeProductListCard/HomeProductListCard.tsx index 550ef52a7..a7aaa297b 100644 --- a/src/home/components/HomeProductListCard/HomeProductListCard.tsx +++ b/src/home/components/HomeProductListCard/HomeProductListCard.tsx @@ -10,7 +10,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import CardTitle from "@saleor/components/CardTitle"; @@ -81,11 +81,7 @@ export const HomeProductList = withStyles(styles, { name: "HomeProductList" })( {maybe(() => variant.attributes - .map(attribute => attribute.value) - .sort((a, b) => - a.sortOrder > b.sortOrder ? 1 : -1 - ) - .map(attribute => attribute.name) + .map(attribute => attribute.value.name) .join(" / ") )} diff --git a/src/home/queries.ts b/src/home/queries.ts index 8e5011fdf..46aee8590 100644 --- a/src/home/queries.ts +++ b/src/home/queries.ts @@ -37,7 +37,6 @@ const home = gql` value { id name - sortOrder } } product { diff --git a/src/home/types/Home.ts b/src/home/types/Home.ts index 9400d0adc..b79c2e0c6 100644 --- a/src/home/types/Home.ts +++ b/src/home/types/Home.ts @@ -54,12 +54,11 @@ export interface Home_productTopToday_edges_node_attributes_value { __typename: "AttributeValue"; id: string; name: string | null; - sortOrder: number | null; } export interface Home_productTopToday_edges_node_attributes { __typename: "SelectedAttribute"; - value: Home_productTopToday_edges_node_attributes_value; + value: Home_productTopToday_edges_node_attributes_value | null; } export interface Home_productTopToday_edges_node_product_thumbnail { diff --git a/src/hooks/useBulkActions.ts b/src/hooks/useBulkActions.ts index 986308782..b4c86fe61 100644 --- a/src/hooks/useBulkActions.ts +++ b/src/hooks/useBulkActions.ts @@ -1,34 +1,22 @@ -import { useState } from "react"; import { Node } from "../types"; +import useListActions from "./useListActions"; function useBulkActions(initial: string[] = []) { - const [listElements, setListElements] = useState(initial); - - function isSelected(id: string) { - return !!listElements.find(listElement => listElement === id); - } - - function add(id: string) { - setListElements([...listElements, id]); - } - - function remove(id: string) { - setListElements(listElements.filter(listElement => listElement !== id)); - } - - function reset() { - setListElements([]); - } - - function toggle(id: string) { - isSelected(id) ? remove(id) : add(id); - } + const { + add, + isSelected, + listElements, + remove, + reset, + set, + toggle + } = useListActions(initial); function toggleAll(items: Node[], selected: number) { const allItems = items.map(item => item.id); reset(); if (selected !== allItems.length) { - setListElements(allItems); + set(allItems); } } diff --git a/src/icons/Calendar.tsx b/src/icons/Calendar.tsx index c7250b42d..3df4eb749 100644 --- a/src/icons/Calendar.tsx +++ b/src/icons/Calendar.tsx @@ -18,7 +18,7 @@ export const Calendar = createSvgIcon( y2="25.7327" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/Draggable.tsx b/src/icons/Draggable.tsx index cdaad7d5e..245d84a6f 100644 --- a/src/icons/Draggable.tsx +++ b/src/icons/Draggable.tsx @@ -21,7 +21,7 @@ export const Draggable = createSvgIcon( y2="10.4478" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/Image.tsx b/src/icons/Image.tsx index 458cbc3c9..00e4e9238 100644 --- a/src/icons/Image.tsx +++ b/src/icons/Image.tsx @@ -16,7 +16,7 @@ export const Image = createSvgIcon( y2="23.5714" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/Navigation.tsx b/src/icons/Navigation.tsx index c7c7d29a9..5e48f814d 100644 --- a/src/icons/Navigation.tsx +++ b/src/icons/Navigation.tsx @@ -24,7 +24,7 @@ export const Navigation = createSvgIcon( y2="46.8339" gradientUnits="userSpaceOnUse" > - + - + diff --git a/src/icons/Orders.tsx b/src/icons/Orders.tsx index 7026b704a..14e6a0246 100644 --- a/src/icons/Orders.tsx +++ b/src/icons/Orders.tsx @@ -18,7 +18,7 @@ export const Orders = createSvgIcon( y2="40.6161" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/Pages.tsx b/src/icons/Pages.tsx index 6aa43bc32..8a74b6472 100644 --- a/src/icons/Pages.tsx +++ b/src/icons/Pages.tsx @@ -18,7 +18,7 @@ export const Pages = createSvgIcon( y2="46.7403" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/ProductTypes.tsx b/src/icons/ProductTypes.tsx index c86899204..a35417ac8 100644 --- a/src/icons/ProductTypes.tsx +++ b/src/icons/ProductTypes.tsx @@ -18,7 +18,7 @@ export const ProductTypes = createSvgIcon( y2="46.78" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/Sales.tsx b/src/icons/Sales.tsx index dae31094f..4aba8d63c 100644 --- a/src/icons/Sales.tsx +++ b/src/icons/Sales.tsx @@ -18,7 +18,7 @@ export const Sales = createSvgIcon( y2="46.3533" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/ShippingMethods.tsx b/src/icons/ShippingMethods.tsx index 0c4c90d03..145bd25dd 100644 --- a/src/icons/ShippingMethods.tsx +++ b/src/icons/ShippingMethods.tsx @@ -18,7 +18,7 @@ export const ShippingMethods = createSvgIcon( y2="47.1429" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/SiteSettings.tsx b/src/icons/SiteSettings.tsx index 38829145d..a0fdac5f8 100644 --- a/src/icons/SiteSettings.tsx +++ b/src/icons/SiteSettings.tsx @@ -18,7 +18,7 @@ export const SiteSettings = createSvgIcon( y2="47.2508" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/StaffMembers.tsx b/src/icons/StaffMembers.tsx index bf1c45162..f583d01a3 100644 --- a/src/icons/StaffMembers.tsx +++ b/src/icons/StaffMembers.tsx @@ -18,7 +18,7 @@ export const StaffMembers = createSvgIcon( y2="46.2508" gradientUnits="userSpaceOnUse" > - + diff --git a/src/icons/Taxes.tsx b/src/icons/Taxes.tsx index ae62d4206..0f910dced 100644 --- a/src/icons/Taxes.tsx +++ b/src/icons/Taxes.tsx @@ -18,7 +18,7 @@ export const Taxes = createSvgIcon( y2="43.2798" gradientUnits="userSpaceOnUse" > - + diff --git a/src/index.tsx b/src/index.tsx index 61c8d3654..63110ff2e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -10,6 +10,8 @@ import { ApolloProvider } from "react-apollo"; import { render } from "react-dom"; import { BrowserRouter, Route, Switch } from "react-router-dom"; +import AttributeSection from "./attributes"; +import { attributeSection } from "./attributes/urls"; import { getAuthToken, removeAuthToken } from "./auth"; import AuthProvider from "./auth/AuthProvider"; import LoginLoading from "./auth/components/LoginLoading/LoginLoading"; @@ -19,9 +21,7 @@ import Login from "./auth/views/Login"; import CategorySection from "./categories"; import CollectionSection from "./collections"; import { AppProgressProvider } from "./components/AppProgress"; -// import { ConfirmFormLeaveDialog } from "./components/ConfirmFormLeaveDialog"; import { DateProvider } from "./components/Date"; -import { FormProvider } from "./components/Form"; import { LocaleProvider } from "./components/Locale"; import { MessageManager } from "./components/messages"; import { ShopProvider } from "./components/Shop"; @@ -71,12 +71,18 @@ const authLink = setContext((_, context) => { }; }); +// DON'T TOUCH THIS +// These are separate clients and do not share configs between themselves +// so we need to explicitly set them const linkOptions = { credentials: "same-origin", uri: API_URI }; const uploadLink = createUploadLink(linkOptions); -const batchLink = new BatchHttpLink(linkOptions); +const batchLink = new BatchHttpLink({ + batchInterval: 100, + ...linkOptions +}); const link = ApolloLink.split( operation => operation.getContext().useBatching, @@ -102,134 +108,129 @@ const App: React.FC = () => { const isDark = localStorage.getItem("theme") === "true"; return ( - - - - - - - - - - - {/* FIXME: #3424 */} - {/* */} - - {({ - hasToken, - isAuthenticated, - tokenAuthLoading, - tokenVerifyLoading, - user - }) => { - return isAuthenticated && - !tokenAuthLoading && - !tokenVerifyLoading ? ( - + + + + + + + + + + + {({ + hasToken, + isAuthenticated, + tokenAuthLoading, + tokenVerifyLoading, + user + }) => { + return isAuthenticated && + !tokenAuthLoading && + !tokenVerifyLoading ? ( + + + + + + + + + + + + + + + + + + {configurationMenu.filter(menuItem => + hasPermission(menuItem.permission, user) + ).length > 0 && ( - - - - - - - - - - - - - - - {configurationMenu.filter(menuItem => - hasPermission(menuItem.permission, user) - ).length > 0 && ( - - )} - - - ) : hasToken && tokenVerifyLoading ? ( - - ) : ( - - ); - }} - - - - - - - - - - + )} + + + ) : hasToken && tokenVerifyLoading ? ( + + ) : ( + + ); + }} + + + + + + + + + ); }; diff --git a/src/misc.ts b/src/misc.ts index 83f9e9aec..d27eeded2 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -1,6 +1,7 @@ +import moment from "moment-timezone"; +import { MutationFn, MutationResult } from "react-apollo"; import urlJoin from "url-join"; -import { MutationFn, MutationResult } from "react-apollo"; import { ConfirmButtonTransitionState } from "./components/ConfirmButton/ConfirmButton"; import { APP_MOUNT_URI } from "./config"; import { AddressType } from "./customers/types"; @@ -94,10 +95,7 @@ export const transformAddressToForm = (data: AddressType) => ({ city: maybe(() => data.city, ""), cityArea: maybe(() => data.cityArea, ""), companyName: maybe(() => data.companyName, ""), - country: { - label: maybe(() => data.country.country, ""), - value: maybe(() => data.country.code, "") - }, + country: maybe(() => data.country.code, ""), countryArea: maybe(() => data.countryArea, ""), firstName: maybe(() => data.firstName, ""), lastName: maybe(() => data.lastName, ""), @@ -145,7 +143,9 @@ export const translatedAuthorizationKeyTypes = () => ({ [AuthorizationKeyType.GOOGLE_OAUTH2]: i18n.t("Google OAuth2") }); -export function maybe(exp: () => T, d?: T) { +export function maybe(exp: () => T): T | undefined; +export function maybe(exp: () => T, d: T): T; +export function maybe(exp: any, d?: any) { try { const result = exp(); return result === undefined ? d : result; @@ -236,3 +236,38 @@ export function stopPropagation(cb: () => void) { cb(); }; } + +export function joinDateTime(date: string, time?: string) { + if (!date) { + return null; + } + const setTime = time || "00:00"; + const dateTime = moment(date + " " + setTime).format(); + return dateTime; +} + +export function splitDateTime(dateTime: string) { + if (!dateTime) { + return { + date: "", + time: "" + }; + } + // Default html input format YYYY-MM-DD HH:mm + const splitDateTime = moment(dateTime) + .format("YYYY-MM-DD HH:mm") + .split(" "); + return { + date: splitDateTime[0], + time: splitDateTime[1] + }; +} + +export function generateCode(charNum: number) { + let result = ""; + const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + for (let i = 0; i < charNum; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return result; +} diff --git a/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx b/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx index c607ab133..87c9a0c56 100644 --- a/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx +++ b/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx @@ -4,20 +4,24 @@ import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; import TextField from "@material-ui/core/TextField"; -import * as isUrl from "is-url"; +import Typography from "@material-ui/core/Typography"; +import isUrl from "is-url"; import React from "react"; import AutocompleteSelectMenu from "@saleor/components/AutocompleteSelectMenu"; import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; -import Form from "@saleor/components/Form"; import FormSpacer from "@saleor/components/FormSpacer"; -import { SearchCategories_categories_edges_node } from "../../../containers/SearchCategories/types/SearchCategories"; -import { SearchCollections_collections_edges_node } from "../../../containers/SearchCollections/types/SearchCollections"; -import { SearchPages_pages_edges_node } from "../../../containers/SearchPages/types/SearchPages"; -import i18n from "../../../i18n"; -import { getMenuItemByValue, IMenu } from "../../../utils/menu"; +import { SearchCategories_categories_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories"; +import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections"; +import { SearchPages_pages_edges_node } from "@saleor/containers/SearchPages/types/SearchPages"; +import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import i18n from "@saleor/i18n"; +import { UserError } from "@saleor/types"; +import { getErrors, getFieldError } from "@saleor/utils/errors"; +import { getMenuItemByValue, IMenu } from "@saleor/utils/menu"; export type MenuItemType = "category" | "collection" | "link" | "page"; export interface MenuItemData { @@ -32,6 +36,7 @@ export interface MenuItemDialogFormData extends MenuItemData { export interface MenuItemDialogProps { confirmButtonState: ConfirmButtonTransitionState; disabled: boolean; + errors: UserError[]; initial?: MenuItemDialogFormData; initialDisplayValue?: string; loading: boolean; @@ -69,6 +74,7 @@ function getDisplayValue(menu: IMenu, value: string): string { const MenuItemDialog: React.StatelessComponent = ({ confirmButtonState, disabled, + errors: apiErrors, initial, initialDisplayValue, loading, @@ -80,9 +86,13 @@ const MenuItemDialog: React.StatelessComponent = ({ collections, pages }) => { + const errors = useModalDialogErrors(apiErrors, open); const [displayValue, setDisplayValue] = React.useState( initialDisplayValue || "" ); + const [data, setData] = useStateFromProps( + initial || defaultInitial + ); const [url, setUrl] = React.useState(undefined); // Refresh initial display value if changed @@ -96,6 +106,8 @@ const MenuItemDialog: React.StatelessComponent = ({ setUrl(undefined); }, [open]); + const mutationErrors = getErrors(errors); + let options: IMenu = []; if (categories.length > 0) { @@ -177,6 +189,23 @@ const MenuItemDialog: React.StatelessComponent = ({ onQueryChange(query); }; + const handleSelectChange = (event: React.ChangeEvent) => { + const value = event.target.value; + const menuItemData = getMenuItemData(value); + + setData(value => ({ + ...value, + ...menuItemData + })); + setDisplayValue(getDisplayValue(options, value)); + }; + + const handleSubmit = () => onSubmit(data); + + const idError = ["category", "collection", "page", "url"] + .map(field => getFieldError(errors, field)) + .reduce((acc, err) => acc || err); + return ( = ({ }} > - {i18n.t("Add Item", { - context: "create new menu item" - })} + {!!initial + ? i18n.t("Edit Item", { + context: "edit menu item" + }) + : i18n.t("Add Item", { + context: "create new menu item" + })} - - {({ change, data, submit }) => { - const handleSelectChange = (event: React.ChangeEvent) => { - const value = event.target.value; - const menuItemData = getMenuItemData(value); - change( - { - target: { - name: "id", - value: menuItemData.id - } - } as any, - () => - change( - { - target: { - name: "type", - value: menuItemData.type - } - } as any, - () => setDisplayValue(getDisplayValue(options, value)) - ) - ); - }; - - return ( - <> - - - - - - - - - {i18n.t("Submit", { context: "button" })} - - - - ); - }} - + + + setData(value => ({ + ...value, + name: event.target.value + })) + } + name="name" + error={!!getFieldError(errors, "name")} + helperText={getFieldError(errors, "name")} + /> + + + {mutationErrors.length > 0 && ( + <> + + {mutationErrors.map(err => ( + + {err} + + ))} + + )} + + + + + {i18n.t("Submit", { context: "button" })} + + ); }; diff --git a/src/navigation/components/MenuList/MenuList.tsx b/src/navigation/components/MenuList/MenuList.tsx index 12d802106..75c89ef23 100644 --- a/src/navigation/components/MenuList/MenuList.tsx +++ b/src/navigation/components/MenuList/MenuList.tsx @@ -18,9 +18,9 @@ import IconButtonTableCell from "@saleor/components/IconButtonTableCell"; import Skeleton from "@saleor/components/Skeleton"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; -import { maybe, renderCollection } from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; import { MenuList_menus_edges_node } from "../../types/MenuList"; export interface MenuListProps extends ListProps, ListActions { @@ -44,15 +44,20 @@ const styles = (theme: Theme) => cursor: "pointer" } }); + +const numberOfColumns = 4; + const MenuList = withStyles(styles, { name: "MenuList" })( ({ classes, + settings, disabled, isChecked, menus, onDelete, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick, pageInfo, selected, @@ -63,6 +68,7 @@ const MenuList = withStyles(styles, { name: "MenuList" })(
toggle(menu.id)} /> @@ -130,7 +139,9 @@ const MenuList = withStyles(styles, { name: "MenuList" })( }, () => ( - {i18n.t("No menus found")} + + {i18n.t("No menus found")} + ) )} diff --git a/src/navigation/components/MenuListPage/MenuListPage.tsx b/src/navigation/components/MenuListPage/MenuListPage.tsx index 8e6b98d56..04f0f5926 100644 --- a/src/navigation/components/MenuListPage/MenuListPage.tsx +++ b/src/navigation/components/MenuListPage/MenuListPage.tsx @@ -5,8 +5,8 @@ import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; import { MenuList_menus_edges_node } from "../../types/MenuList"; import MenuList from "../MenuList"; diff --git a/src/navigation/urls.ts b/src/navigation/urls.ts index f0f791e61..0d70cf491 100644 --- a/src/navigation/urls.ts +++ b/src/navigation/urls.ts @@ -1,5 +1,6 @@ import { stringify as stringifyQs } from "qs"; import urlJoin from "url-join"; + import { BulkAction, Dialog, Pagination, SingleAction } from "../types"; export const navigationSection = "/navigation"; diff --git a/src/navigation/views/MenuDetails/index.tsx b/src/navigation/views/MenuDetails/index.tsx index c26d39343..1bc722399 100644 --- a/src/navigation/views/MenuDetails/index.tsx +++ b/src/navigation/views/MenuDetails/index.tsx @@ -44,6 +44,7 @@ import { } from "./successHandlers"; import { getInitialDisplayValue, + getMenuItemCreateInputData, getMenuItemInputData, getMoves, getRemoveIds @@ -262,10 +263,10 @@ const MenuDetails: React.FC = ({ id, params }) => { data: MenuItemDialogFormData ) => { const variables: MenuItemCreateVariables = { - input: { - menu: id, - ...getMenuItemInputData(data) - } + input: getMenuItemCreateInputData( + id, + data + ) }; menuItemCreate({ variables }); @@ -286,6 +287,12 @@ const MenuDetails: React.FC = ({ id, params }) => { open={params.action === "add-item"} categories={categories} collections={collections} + errors={maybe( + () => + menuItemCreateOpts.data + .menuItemCreate.errors, + [] + )} pages={pages} loading={ categorySearch.result.loading || @@ -355,6 +362,12 @@ const MenuDetails: React.FC = ({ id, params }) => { open={params.action === "edit-item"} categories={categories} collections={collections} + errors={maybe( + () => + menuItemUpdateOpts.data + .menuItemUpdate.errors, + [] + )} pages={pages} initial={initialFormData} initialDisplayValue={getInitialDisplayValue( diff --git a/src/navigation/views/MenuDetails/utils.ts b/src/navigation/views/MenuDetails/utils.ts index 188bd357b..1ad873304 100644 --- a/src/navigation/views/MenuDetails/utils.ts +++ b/src/navigation/views/MenuDetails/utils.ts @@ -1,4 +1,8 @@ -import { MenuItemInput, MenuItemMoveInput } from "../../../types/globalTypes"; +import { + MenuItemCreateInput, + MenuItemInput, + MenuItemMoveInput +} from "../../../types/globalTypes"; import { MenuDetailsSubmitData } from "../../components/MenuDetailsPage"; import { MenuItemDialogFormData } from "../../components/MenuItemDialog"; import { unknownTypeError } from "../../components/MenuItems"; @@ -34,6 +38,38 @@ export function getMenuItemInputData( return variables; } +export function getMenuItemCreateInputData( + menu: string, + data: MenuItemDialogFormData +): MenuItemCreateInput { + const variables: MenuItemCreateInput = { + menu, + name: data.name + }; + switch (data.type) { + case "category": + variables.category = data.id; + break; + + case "collection": + variables.collection = data.id; + break; + + case "page": + variables.page = data.id; + break; + + case "link": + variables.url = data.id; + break; + + default: + throw unknownTypeError; + } + + return variables; +} + export function getInitialDisplayValue(item: MenuDetails_menu_items): string { if (!item) { return "..."; @@ -47,7 +83,7 @@ export function getInitialDisplayValue(item: MenuDetails_menu_items): string { } else if (item.url) { return item.url; } else { - throw unknownTypeError; + return ""; } } diff --git a/src/navigation/views/MenuList.tsx b/src/navigation/views/MenuList.tsx index 35f05d79c..fbde8d36f 100644 --- a/src/navigation/views/MenuList.tsx +++ b/src/navigation/views/MenuList.tsx @@ -3,16 +3,17 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; +import { configurationMenuUrl } from "@saleor/configuration"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import { PAGINATE_BY } from "../../config"; -import { configurationMenuUrl } from "../../configuration"; -import i18n from "../../i18n"; -import { getMutationState, maybe } from "../../misc"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import MenuCreateDialog from "../components/MenuCreateDialog"; import MenuListPage from "../components/MenuListPage"; import { @@ -36,6 +37,9 @@ const MenuList: React.FC = ({ params }) => { const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); + const { updateListSettings, settings } = useListSettings( + ListViews.NAVIGATION_LIST + ); const closeModal = () => navigate( @@ -48,7 +52,7 @@ const MenuList: React.FC = ({ params }) => { true ); - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); return ( @@ -129,6 +133,7 @@ const MenuList: React.FC = ({ params }) => { menus={maybe(() => data.menus.edges.map(edge => edge.node) )} + settings={settings} onAdd={() => navigate( menuListUrl({ @@ -147,6 +152,7 @@ const MenuList: React.FC = ({ params }) => { } onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} onRowClick={id => () => navigate(menuUrl(id))} pageInfo={pageInfo} isChecked={isSelected} diff --git a/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx b/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx index fa5eb177f..c351178cb 100644 --- a/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx +++ b/src/orders/components/OrderAddressEditDialog/OrderAddressEditDialog.tsx @@ -11,6 +11,9 @@ import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Form from "@saleor/components/Form"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { maybe } from "@saleor/misc"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import { AddressTypeInput } from "../../../customers/types"; import i18n from "../../../i18n"; import { UserError } from "../../../types"; @@ -48,43 +51,69 @@ const OrderAddressEditDialog = withStyles(styles, { countries, onClose, onConfirm - }: OrderAddressEditDialogProps) => ( - -
- {({ change, data, errors, submit }) => ( - <> - - {variant === "billing" - ? i18n.t("Edit billing address", { context: "title" }) - : i18n.t("Edit shipping address", { context: "title" })} - - - - - - - - {i18n.t("Confirm", { context: "button" })} - - - - )} - -
- ) + }: OrderAddressEditDialogProps) => { + const [countryDisplayName, setCountryDisplayName] = useStateFromProps( + maybe( + () => countries.find(country => address.country === country.code).label + ) + ); + const countryChoices = countries.map(country => ({ + label: country.label, + value: country.code + })); + + return ( + +
+ {({ change, data, errors, submit }) => { + const handleCountrySelect = createSingleAutocompleteSelectHandler( + change, + setCountryDisplayName, + countryChoices + ); + + return ( + <> + + {variant === "billing" + ? i18n.t("Edit billing address", { context: "title" }) + : i18n.t("Edit shipping address", { context: "title" })} + + + + + + + + {i18n.t("Confirm", { context: "button" })} + + + + ); + }} + +
+ ); + } ); OrderAddressEditDialog.displayName = "OrderAddressEditDialog"; export default OrderAddressEditDialog; diff --git a/src/orders/components/OrderCustomer/OrderCustomer.tsx b/src/orders/components/OrderCustomer/OrderCustomer.tsx index ea42bf8f7..c2319a911 100644 --- a/src/orders/components/OrderCustomer/OrderCustomer.tsx +++ b/src/orders/components/OrderCustomer/OrderCustomer.tsx @@ -17,6 +17,8 @@ import Hr from "@saleor/components/Hr"; import Link from "@saleor/components/Link"; import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField"; import Skeleton from "@saleor/components/Skeleton"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import { SearchCustomers_customers_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers"; import { customerUrl } from "../../../customers/urls"; import i18n from "../../../i18n"; @@ -72,12 +74,17 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })( onProfileView, onShippingAddressEdit }: OrderCustomerProps) => { + const user = maybe(() => order.user); + + const [userDisplayName, setUserDisplayName] = useStateFromProps( + maybe(() => user.email, "") + ); const [isInEditMode, setEditModeStatus] = React.useState(false); const toggleEditMode = () => setEditModeStatus(!isInEditMode); const billingAddress = maybe(() => order.billingAddress); const shippingAddress = maybe(() => order.shippingAddress); - const user = maybe(() => order.user); + return ( ) : isInEditMode && canEditCustomer ? ( -
+ {({ change, data }) => { const handleChange = (event: React.ChangeEvent) => { change(event); + const value = event.target.value; + onCustomerEdit({ - [event.target.value.value.includes("@") - ? "userEmail" - : "user"]: event.target.value.value + [value.includes("@") ? "userEmail" : "user"]: value }); toggleEditMode(); }; + const userChoices = maybe(() => users, []).map(user => ({ + label: user.email, + value: user.id + })); + const handleUserChange = createSingleAutocompleteSelectHandler( + handleChange, + setUserDisplayName, + userChoices + ); return ( users, []).map(user => ({ - label: user.email, - value: user.id - }))} + allowCustomValues={true} + choices={userChoices} + displayValue={userDisplayName} fetchChoices={fetchUsers} loading={loading} placeholder={i18n.t("Search Customers")} - onChange={handleChange} + onChange={handleUserChange} name="query" value={data.query} /> diff --git a/src/orders/components/OrderCustomerEditDialog/OrderCustomerEditDialog.tsx b/src/orders/components/OrderCustomerEditDialog/OrderCustomerEditDialog.tsx index 3cd9804bb..7000c2289 100644 --- a/src/orders/components/OrderCustomerEditDialog/OrderCustomerEditDialog.tsx +++ b/src/orders/components/OrderCustomerEditDialog/OrderCustomerEditDialog.tsx @@ -38,10 +38,8 @@ const styles = (theme: Theme) => interface OrderCustomerEditDialogProps extends WithStyles { confirmButtonState: ConfirmButtonTransitionState; open: boolean; - user?: { - label: string; - value: string; - }; + user: string; + userDisplayValue: string; users?: Array<{ id: string; email: string; @@ -62,6 +60,7 @@ const OrderCustomerEditDialog = withStyles(styles, { open, loading, user, + userDisplayValue, users, fetchUsers, onChange, @@ -81,8 +80,9 @@ const OrderCustomerEditDialog = withStyles(styles, { createStyles({ - iconCell: { + colAction: { "&:last-child": { paddingRight: 0 }, width: 48 + theme.spacing.unit / 2 }, + colName: { + width: "auto" + }, + colNameLabel: { + marginLeft: AVATAR_MARGIN + }, + colPrice: { + textAlign: "right", + width: 150 + }, + colQuantity: { + textAlign: "right", + width: 80 + }, + colTotal: { + textAlign: "right", + width: 150 + }, quantityField: { "& input": { padding: "12px 12px 10px", @@ -43,8 +63,8 @@ const styles = (theme: Theme) => }, width: 60 }, - textRight: { - textAlign: "right" + table: { + tableLayout: "fixed" } }); @@ -63,23 +83,25 @@ const OrderDraftDetailsProducts = withStyles(styles, { onOrderLineChange, onOrderLineRemove }: OrderDraftDetailsProductsProps) => ( -
+
{maybe(() => !!lines.length) && ( - - {i18n.t("Product", { context: "table header" })} + + + {i18n.t("Product", { context: "table header" })} + - + {i18n.t("Quantity", { context: "table header" })} - + {i18n.t("Price", { context: "table header" })} - + {i18n.t("Total", { context: "table header" })} - + )} @@ -93,8 +115,10 @@ const OrderDraftDetailsProducts = withStyles(styles, { ) : ( renderCollection(lines, line => ( - line.thumbnail.url)} /> - + line.thumbnail.url)} + > {maybe(() => line.productName && line.productSku) ? ( <> {line.productName} @@ -103,8 +127,8 @@ const OrderDraftDetailsProducts = withStyles(styles, { ) : ( )} - - + + {maybe(() => line.quantity) ? ( )} - + {maybe(() => line.unitPrice.net) ? ( ) : ( )} - + {maybe(() => line.unitPrice.net && line.quantity) ? ( )} - + onOrderLineRemove(line.id)}> diff --git a/src/orders/components/OrderDraftList/OrderDraftList.tsx b/src/orders/components/OrderDraftList/OrderDraftList.tsx index e6ab22d0b..8ebfde633 100644 --- a/src/orders/components/OrderDraftList/OrderDraftList.tsx +++ b/src/orders/components/OrderDraftList/OrderDraftList.tsx @@ -17,14 +17,14 @@ import Money from "@saleor/components/Money"; import Skeleton from "@saleor/components/Skeleton"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; +import i18n from "@saleor/i18n"; import { maybe, renderCollection, transformOrderStatus, transformPaymentStatus -} from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; +} from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList"; const styles = (theme: Theme) => @@ -59,14 +59,18 @@ interface OrderDraftListProps orders: OrderDraftList_draftOrders_edges_node[]; } +const numberOfColumns = 5; + export const OrderDraftList = withStyles(styles, { name: "OrderDraftList" })( ({ classes, disabled, + settings, orders, pageInfo, onPreviousPage, onNextPage, + onUpdateListSettings, onRowClick, isChecked, selected, @@ -84,6 +88,7 @@ export const OrderDraftList = withStyles(styles, { name: "OrderDraftList" })( return (
toggle(order.id)} /> @@ -176,7 +184,9 @@ export const OrderDraftList = withStyles(styles, { name: "OrderDraftList" })( }, () => ( - {i18n.t("No orders found")} + + {i18n.t("No orders found")} + ) )} diff --git a/src/orders/components/OrderDraftListPage/OrderDraftListPage.tsx b/src/orders/components/OrderDraftListPage/OrderDraftListPage.tsx index 09b7063c2..a172986a6 100644 --- a/src/orders/components/OrderDraftListPage/OrderDraftListPage.tsx +++ b/src/orders/components/OrderDraftListPage/OrderDraftListPage.tsx @@ -5,8 +5,8 @@ import React from "react"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList"; import OrderDraftList from "../OrderDraftList"; diff --git a/src/orders/components/OrderFulfillment/OrderFulfillment.tsx b/src/orders/components/OrderFulfillment/OrderFulfillment.tsx index ee4d4241d..041c4acb3 100644 --- a/src/orders/components/OrderFulfillment/OrderFulfillment.tsx +++ b/src/orders/components/OrderFulfillment/OrderFulfillment.tsx @@ -20,7 +20,9 @@ import CardTitle from "@saleor/components/CardTitle"; import Money from "@saleor/components/Money"; import Skeleton from "@saleor/components/Skeleton"; import StatusLabel from "@saleor/components/StatusLabel"; -import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import TableCellAvatar, { + AVATAR_MARGIN +} from "@saleor/components/TableCellAvatar"; import i18n from "../../../i18n"; import { maybe, renderCollection } from "../../../misc"; import { FulfillmentStatus } from "../../../types/globalTypes"; @@ -31,6 +33,25 @@ const styles = (theme: Theme) => clickableRow: { cursor: "pointer" }, + colName: { + width: "auto" + }, + colNameLabel: { + marginLeft: AVATAR_MARGIN + }, + colPrice: { + textAlign: "right", + width: 120 + }, + colQuantity: { + textAlign: "center", + width: 120 + }, + colTotal: { + textAlign: "right", + width: 120 + }, + orderNumber: { display: "inline", marginLeft: theme.spacing.unit @@ -38,14 +59,8 @@ const styles = (theme: Theme) => statusBar: { paddingTop: 0 }, - textCenter: { - textAlign: "center" - }, - textRight: { - textAlign: "right" - }, - wideCell: { - width: "50%" + table: { + tableLayout: "fixed" } }); @@ -56,6 +71,8 @@ interface OrderFulfillmentProps extends WithStyles { onTrackingCodeAdd: () => void; } +const numberOfColumns = 3; + const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })( ({ classes, @@ -115,19 +132,21 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })( ) } /> -
+
- - {i18n.t("Product")} + + + {i18n.t("Product")} + - + {i18n.t("Quantity")} - + {i18n.t("Price")} - + {i18n.t("Total")} @@ -140,22 +159,22 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })( key={maybe(() => line.id)} > line.orderLine.thumbnail.url)} - /> - + > {maybe(() => line.orderLine.productName) || } - - + + {maybe(() => line.quantity) || } - + {maybe(() => line.orderLine.unitPrice.gross) ? ( ) : ( )} - + {maybe( () => line.quantity * line.orderLine.unitPrice.gross.amount ) ? ( @@ -174,7 +193,7 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })( ))} {maybe(() => fulfillment.trackingNumber) && ( - + {i18n.t("Tracking Number: {{ trackingNumber }}", { trackingNumber: fulfillment.trackingNumber })} diff --git a/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx b/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx index 92657cdb8..8be5156cc 100644 --- a/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx +++ b/src/orders/components/OrderFulfillmentDialog/OrderFulfillmentDialog.tsx @@ -22,7 +22,9 @@ import ConfirmButton, { } from "@saleor/components/ConfirmButton"; import Form from "@saleor/components/Form"; import { FormSpacer } from "@saleor/components/FormSpacer"; -import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import TableCellAvatar, { + AVATAR_MARGIN +} from "@saleor/components/TableCellAvatar"; import i18n from "../../../i18n"; import { maybe } from "../../../misc"; import { OrderDetails_order_lines } from "../../types/OrderDetails"; @@ -34,23 +36,32 @@ export interface FormData { const styles = (theme: Theme) => createStyles({ - avatarCell: { - paddingLeft: theme.spacing.unit * 2, - paddingRight: theme.spacing.unit * 3, - width: theme.spacing.unit * 5 + colName: { + width: "auto" + }, + colNameLabel: { + marginLeft: AVATAR_MARGIN + }, + colQuantity: { + textAlign: "right", + width: 150 + }, + colQuantityContent: { + alignItems: "center", + display: "inline-flex" + }, + colSku: { + width: 120 }, quantityInput: { width: "4rem" }, - quantityInputContainer: { - paddingRight: theme.spacing.unit, - textAlign: "right" as "right" - }, remainingQuantity: { - paddingBottom: 4 + marginLeft: theme.spacing.unit, + paddingTop: 14 }, - textRight: { - textAlign: "right" as "right" + table: { + tableLayout: "fixed" } }); @@ -104,13 +115,18 @@ const OrderFulfillmentDialog = withStyles(styles, { return ( <> {i18n.t("Fulfill products")} -
+
- - {i18n.t("Product name")} - {i18n.t("SKU")} - + + + {i18n.t("Product name")} + + + + {i18n.t("SKU")} + + {i18n.t("Quantity")} @@ -122,28 +138,34 @@ const OrderFulfillmentDialog = withStyles(styles, { return ( product.thumbnail.url)} - /> - {product.productName} - {product.productSku} - - - handleQuantityChange(productIndex, event) - } - error={remainingQuantity < data.lines[productIndex]} - /> + > + {product.productName} + + + {product.productSku} - -
- / {remainingQuantity} + +
+ + handleQuantityChange(productIndex, event) + } + error={ + remainingQuantity < data.lines[productIndex] + } + /> +
+ / {remainingQuantity} +
diff --git a/src/orders/components/OrderList/OrderList.tsx b/src/orders/components/OrderList/OrderList.tsx index b64b8c872..c0b772ab7 100644 --- a/src/orders/components/OrderList/OrderList.tsx +++ b/src/orders/components/OrderList/OrderList.tsx @@ -18,14 +18,14 @@ import Skeleton from "@saleor/components/Skeleton"; import StatusLabel from "@saleor/components/StatusLabel"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; +import i18n from "@saleor/i18n"; import { maybe, renderCollection, transformOrderStatus, transformPaymentStatus -} from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; +} from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; import { OrderList_orders_edges_node } from "../../types/OrderList"; const styles = (theme: Theme) => @@ -66,14 +66,18 @@ interface OrderListProps orders: OrderList_orders_edges_node[]; } +const numberOfColumns = 7; + export const OrderList = withStyles(styles, { name: "OrderList" })( ({ classes, disabled, + settings, orders, pageInfo, onPreviousPage, onNextPage, + onUpdateListSettings, onRowClick, isChecked, selected, @@ -91,6 +95,7 @@ export const OrderList = withStyles(styles, { name: "OrderList" })( return (
toggle(order.id)} /> @@ -211,7 +219,9 @@ export const OrderList = withStyles(styles, { name: "OrderList" })( }, () => ( - {i18n.t("No orders found")} + + {i18n.t("No orders found")} + ) )} diff --git a/src/orders/components/OrderListFilter/OrderListFilter.tsx b/src/orders/components/OrderListFilter/OrderListFilter.tsx index 8651b6b0a..a76489479 100644 --- a/src/orders/components/OrderListFilter/OrderListFilter.tsx +++ b/src/orders/components/OrderListFilter/OrderListFilter.tsx @@ -1,4 +1,4 @@ -import * as moment from "moment-timezone"; +import moment from "moment-timezone"; import React from "react"; import { DateContext } from "@saleor/components/Date/DateContext"; diff --git a/src/orders/components/OrderPaymentDialog/OrderPaymentDialog.tsx b/src/orders/components/OrderPaymentDialog/OrderPaymentDialog.tsx index bdb77880d..5e9f8b068 100644 --- a/src/orders/components/OrderPaymentDialog/OrderPaymentDialog.tsx +++ b/src/orders/components/OrderPaymentDialog/OrderPaymentDialog.tsx @@ -72,9 +72,9 @@ const OrderPaymentDialog: React.StatelessComponent = ({ transitionState={confirmButtonState} color="primary" variant="contained" - onClick={data => { + onClick={() => { onClose(); - submit(data); + submit(); }} > {i18n.t("Confirm", { context: "button" })} diff --git a/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx b/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx index 1acc3292a..7b5f4f557 100644 --- a/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx +++ b/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx @@ -16,34 +16,29 @@ import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import TextField from "@material-ui/core/TextField"; import React from "react"; -import * as InfiniteScroll from "react-infinite-scroller"; +import InfiniteScroll from "react-infinite-scroller"; import Checkbox from "@saleor/components/Checkbox"; import ConfirmButton, { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; -import Form from "@saleor/components/Form"; import Money from "@saleor/components/Money"; import TableCellAvatar from "@saleor/components/TableCellAvatar"; -import i18n from "../../../i18n"; -import { maybe, renderCollection } from "../../../misc"; +import useSearchQuery from "@saleor/hooks/useSearchQuery"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection } from "@saleor/misc"; +import { FetchMoreProps } from "@saleor/types"; import { SearchOrderVariant_products_edges_node, SearchOrderVariant_products_edges_node_variants } from "../../types/SearchOrderVariant"; -export interface FormData { - variants: SearchOrderVariant_products_edges_node_variants[]; - query: string; -} - const styles = (theme: Theme) => createStyles({ avatar: { paddingLeft: 0 }, content: { - maxHeight: 600, overflowY: "scroll" }, grayText: { @@ -76,31 +71,26 @@ const styles = (theme: Theme) => } }); -interface OrderProductAddDialogProps extends WithStyles { +type SetVariantsAction = ( + data: SearchOrderVariant_products_edges_node_variants[] +) => void; + +interface OrderProductAddDialogProps extends FetchMoreProps { confirmButtonState: ConfirmButtonTransitionState; open: boolean; products: SearchOrderVariant_products_edges_node[]; - loading: boolean; - hasMore: boolean; onClose: () => void; - onFetch: (value: string) => void; - onFetchMore: () => void; - onSubmit: (data: FormData) => void; + onSubmit: (data: SearchOrderVariant_products_edges_node_variants[]) => void; } -const initialForm: FormData = { - query: "", - variants: [] -}; - function hasAllVariantsSelected( productVariants: SearchOrderVariant_products_edges_node_variants[], - selectedVariants: SearchOrderVariant_products_edges_node_variants[] + selectedVariantsToProductsMap: SearchOrderVariant_products_edges_node_variants[] ): boolean { return productVariants.reduce( (acc, productVariant) => acc && - !!selectedVariants.find( + !!selectedVariantsToProductsMap.find( selectedVariant => selectedVariant.id === productVariant.id ), true @@ -109,13 +99,53 @@ function hasAllVariantsSelected( function isVariantSelected( variant: SearchOrderVariant_products_edges_node_variants, - selectedVariants: SearchOrderVariant_products_edges_node_variants[] + selectedVariantsToProductsMap: SearchOrderVariant_products_edges_node_variants[] ): boolean { - return !!selectedVariants.find( + return !!selectedVariantsToProductsMap.find( selectedVariant => selectedVariant.id === variant.id ); } +const onProductAdd = ( + product: SearchOrderVariant_products_edges_node, + productIndex: number, + productsWithAllVariantsSelected: boolean[], + variants: SearchOrderVariant_products_edges_node_variants[], + setVariants: SetVariantsAction +) => + productsWithAllVariantsSelected[productIndex] + ? setVariants( + variants.filter( + selectedVariant => + !product.variants.find( + productVariant => productVariant.id === selectedVariant.id + ) + ) + ) + : setVariants([ + ...variants, + ...product.variants.filter( + productVariant => + !variants.find( + selectedVariant => selectedVariant.id === productVariant.id + ) + ) + ]); + +const onVariantAdd = ( + variant: SearchOrderVariant_products_edges_node_variants, + variantIndex: number, + productIndex: number, + variants: SearchOrderVariant_products_edges_node_variants[], + selectedVariantsToProductsMap: boolean[][], + setVariants: SetVariantsAction +) => + selectedVariantsToProductsMap[productIndex][variantIndex] + ? setVariants( + variants.filter(selectedVariant => selectedVariant.id !== variant.id) + ) + : setVariants([...variants, variant]); + const OrderProductAddDialog = withStyles(styles, { name: "OrderProductAddDialog" })( @@ -130,220 +160,173 @@ const OrderProductAddDialog = withStyles(styles, { onFetchMore, onClose, onSubmit - }: OrderProductAddDialogProps) => ( - - - {({ data, change }) => { - const selectedVariants = products - ? products.map(product => - product.variants.map(variant => - isVariantSelected(variant, data.variants) - ) - ) - : []; - const selectedProducts = products - ? products.map(product => - hasAllVariantsSelected(product.variants, data.variants) - ) - : []; + }: OrderProductAddDialogProps & WithStyles) => { + const [query, onQueryChange] = useSearchQuery(onFetch); + const [variants, setVariants] = React.useState< + SearchOrderVariant_products_edges_node_variants[] + >([]); - const onProductAdd = ( - product: SearchOrderVariant_products_edges_node, - productIndex: number - ) => - selectedProducts[productIndex] - ? change({ - target: { - name: "variants", - value: data.variants.filter( - selectedVariant => - !product.variants.find( - productVariant => - productVariant.id === selectedVariant.id - ) - ) - } - } as any) - : change({ - target: { - name: "variants", - value: [ - ...data.variants, - ...product.variants.filter( - productVariant => - !data.variants.find( - selectedVariant => - selectedVariant.id === productVariant.id - ) - ) - ] - } - } as any); - const onVariantAdd = ( - variant: SearchOrderVariant_products_edges_node_variants, - variantIndex: number, - productIndex: number - ) => - selectedVariants[productIndex][variantIndex] - ? change({ - target: { - name: "variants", - value: data.variants.filter( - selectedVariant => selectedVariant.id !== variant.id - ) - } - } as any) - : change({ - target: { - name: "variants", - value: [...data.variants, variant] - } - } as any); + const selectedVariantsToProductsMap = products + ? products.map(product => + product.variants.map(variant => isVariantSelected(variant, variants)) + ) + : []; + const productsWithAllVariantsSelected = products + ? products.map(product => + hasAllVariantsSelected(product.variants, variants) + ) + : []; - return ( - <> - {i18n.t("Add product")} - - 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 && - }} - /> - - - - - - } - threshold={10} - > -
- - {renderCollection( - products, - (product, productIndex) => ( - - - - - onProductAdd(product, productIndex) - } - /> - - product.thumbnail.url)} - /> - - {maybe(() => product.name)} - - - {maybe(() => product.variants, []).map( - (variant, variantIndex) => ( - - - - - onVariantAdd( - variant, - variantIndex, - productIndex - ) - } - /> - - -
{variant.name}
-
- {i18n.t("SKU {{ sku }}", { - sku: variant.sku - })} -
-
- - - -
+ const handleSubmit = () => onSubmit(variants); + + return ( + + {i18n.t("Add product")} + + + }} + /> + + + + + + } + threshold={10} + > +
+ + {renderCollection( + products, + (product, productIndex) => ( + + + + + onProductAdd( + product, + productIndex, + productsWithAllVariantsSelected, + variants, + setVariants ) - )} - - ), - () => ( - - - {i18n.t("No products matching given query")} + } + /> + + product.thumbnail.url)} + /> + + {maybe(() => product.name)} + + + {maybe(() => product.variants, []).map( + (variant, variantIndex) => ( + + + + + onVariantAdd( + variant, + variantIndex, + productIndex, + variants, + selectedVariantsToProductsMap, + setVariants + ) + } + /> + + +
{variant.name}
+
+ {i18n.t("SKU {{ sku }}", { + sku: variant.sku + })} +
+
+ +
) )} -
-
- - - - - - {i18n.t("Confirm", { context: "button" })} - - - - ); - }} - - - ) + + ), + () => ( + + + {i18n.t("No products matching given query")} + + + ) + )} + + + + + + + + {i18n.t("Confirm", { context: "button" })} + + + + ); + } ); OrderProductAddDialog.displayName = "OrderProductAddDialog"; export default OrderProductAddDialog; diff --git a/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx b/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx index 7a20e34b0..ade726e67 100644 --- a/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx +++ b/src/orders/components/OrderUnfulfilledItems/OrderUnfulfilledItems.tsx @@ -13,7 +13,9 @@ import CardTitle from "@saleor/components/CardTitle"; import Money from "@saleor/components/Money"; import Skeleton from "@saleor/components/Skeleton"; import StatusLabel from "@saleor/components/StatusLabel"; -import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import TableCellAvatar, { + AVATAR_MARGIN +} from "@saleor/components/TableCellAvatar"; import i18n from "../../../i18n"; import { maybe } from "../../../misc"; import { OrderDetails_order_lines } from "../../types/OrderDetails"; @@ -22,14 +24,29 @@ const styles = createStyles({ clickableRow: { cursor: "pointer" }, - textCenter: { - textAlign: "center" + colName: { + width: "auto" }, - textRight: { - textAlign: "right" + colNameLabel: { + marginLeft: AVATAR_MARGIN }, - wideCell: { - width: "50%" + colPrice: { + textAlign: "right", + width: 120 + }, + colQuantity: { + textAlign: "center", + width: 120 + }, + colTotal: { + textAlign: "right", + width: 120 + }, + statusBar: { + paddingTop: 0 + }, + table: { + tableLayout: "fixed" } }); @@ -55,17 +72,17 @@ const OrderUnfulfilledItems = withStyles(styles, { /> } /> - +
- - {i18n.t("Product")} + + {i18n.t("Product")} - + {i18n.t("Quantity")} - {i18n.t("Price")} - {i18n.t("Total")} + {i18n.t("Price")} + {i18n.t("Total")} @@ -75,23 +92,25 @@ const OrderUnfulfilledItems = withStyles(styles, { hover={!!line} key={maybe(() => line.id)} > - line.thumbnail.url)} /> - + line.thumbnail.url)} + > {maybe(() => line.productName) || } - - + + {maybe(() => line.quantity - line.quantityFulfilled) || ( )} - + {maybe(() => line.unitPrice.gross) ? ( ) : ( )} - + {maybe( () => (line.quantity - line.quantityFulfilled) * diff --git a/src/orders/views/OrderDetails/index.tsx b/src/orders/views/OrderDetails/index.tsx index a1cb70178..8a4052c42 100644 --- a/src/orders/views/OrderDetails/index.tsx +++ b/src/orders/views/OrderDetails/index.tsx @@ -567,15 +567,13 @@ export const OrderDetails: React.StatelessComponent = ({ onClose={closeModal} onFetch={variantSearch} onFetchMore={fetchMore} - onSubmit={formData => + onSubmit={variants => orderLinesAdd.mutate({ id, - input: formData.variants.map( - variant => ({ - quantity: 1, - variantId: variant.id - }) - ) + input: variants.map(variant => ({ + quantity: 1, + variantId: variant.id + })) }) } /> @@ -608,14 +606,11 @@ export const OrderDetails: React.StatelessComponent = ({ open={params.action === "edit-shipping-address"} variant="shipping" onClose={closeModal} - onConfirm={variables => + onConfirm={shippingAddress => orderUpdate.mutate({ id, input: { - shippingAddress: { - ...variables, - country: variables.country.value - } + shippingAddress } }) } @@ -644,14 +639,11 @@ export const OrderDetails: React.StatelessComponent = ({ open={params.action === "edit-billing-address"} variant="billing" onClose={closeModal} - onConfirm={variables => + onConfirm={billingAddress => orderUpdate.mutate({ id, input: { - billingAddress: { - ...variables, - country: variables.country.value - } + billingAddress } }) } diff --git a/src/orders/views/OrderDraftList.tsx b/src/orders/views/OrderDraftList.tsx index 17e9ebf2a..129ba3fcf 100644 --- a/src/orders/views/OrderDraftList.tsx +++ b/src/orders/views/OrderDraftList.tsx @@ -5,14 +5,15 @@ import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import { PAGINATE_BY } from "../../config"; -import i18n from "../../i18n"; -import { getMutationState, maybe } from "../../misc"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import OrderDraftListPage from "../components/OrderDraftListPage"; import { TypedOrderDraftBulkCancelMutation, @@ -40,6 +41,9 @@ export const OrderDraftList: React.StatelessComponent = ({ const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); + const { updateListSettings, settings } = useListSettings( + ListViews.DRAFT_LIST + ); const closeModal = () => navigate( @@ -57,7 +61,7 @@ export const OrderDraftList: React.StatelessComponent = ({ navigate(orderUrl(data.draftOrderCreate.order.id)); }; - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); return ( @@ -106,6 +110,7 @@ export const OrderDraftList: React.StatelessComponent = ({ <> data.draftOrders.edges.map(edge => edge.node) )} @@ -113,6 +118,7 @@ export const OrderDraftList: React.StatelessComponent = ({ onAdd={createOrder} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} onRowClick={id => () => navigate(orderUrl(id))} isChecked={isSelected} selected={listElements.length} diff --git a/src/orders/views/OrderList/OrderList.tsx b/src/orders/views/OrderList/OrderList.tsx index 7c40ef420..c099b6815 100644 --- a/src/orders/views/OrderList/OrderList.tsx +++ b/src/orders/views/OrderList/OrderList.tsx @@ -5,9 +5,9 @@ import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; import SaveFilterTabDialog, { SaveFilterTabDialogFormData } from "@saleor/components/SaveFilterTabDialog"; -import { PAGINATE_BY } from "@saleor/config"; import useBulkActions from "@saleor/hooks/useBulkActions"; import useDateLocalize from "@saleor/hooks/useDateLocalize"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { @@ -16,6 +16,7 @@ import usePaginator, { import useShop from "@saleor/hooks/useShop"; import i18n from "@saleor/i18n"; import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import OrderBulkCancelDialog from "../../components/OrderBulkCancelDialog"; import OrderListPage from "../../components/OrderListPage/OrderListPage"; import { @@ -58,6 +59,9 @@ export const OrderList: React.StatelessComponent = ({ const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); + const { updateListSettings, settings } = useListSettings( + ListViews.ORDER_LIST + ); const tabs = getFilterTabs(); @@ -124,7 +128,7 @@ export const OrderList: React.StatelessComponent = ({ handleTabChange(tabs.length + 1); }; - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); const handleCreateOrderCreateSuccess = (data: OrderDraftCreate) => { @@ -139,7 +143,7 @@ export const OrderList: React.StatelessComponent = ({ ...paginationState, filter: getFilterVariables(params) }), - [params] + [params, settings.rowNumber] ); return ( @@ -186,6 +190,7 @@ export const OrderList: React.StatelessComponent = ({ <> = ({ onAdd={createOrder} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} onRowClick={id => () => navigate(orderUrl(id))} isChecked={isSelected} selected={listElements.length} diff --git a/src/pages/components/PageList/PageList.tsx b/src/pages/components/PageList/PageList.tsx index a02195c84..dc3ac0b36 100644 --- a/src/pages/components/PageList/PageList.tsx +++ b/src/pages/components/PageList/PageList.tsx @@ -17,9 +17,9 @@ import Skeleton from "@saleor/components/Skeleton"; import StatusLabel from "@saleor/components/StatusLabel"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; -import { maybe, renderCollection } from "../../../misc"; -import { ListActions, ListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection } from "@saleor/misc"; +import { ListActions, ListProps } from "@saleor/types"; import { PageList_pages_edges_node } from "../../types/PageList"; export interface PageListProps extends ListProps, ListActions { @@ -44,14 +44,19 @@ const styles = (theme: Theme) => cursor: "pointer" } }); + +const numberOfColumns = 4; + const PageList = withStyles(styles, { name: "PageList" })( ({ classes, + settings, pages, disabled, onNextPage, pageInfo, onRowClick, + onUpdateListSettings, onPreviousPage, isChecked, selected, @@ -62,6 +67,7 @@ const PageList = withStyles(styles, { name: "PageList" })(
toggle(page.id)} /> @@ -138,7 +147,9 @@ const PageList = withStyles(styles, { name: "PageList" })( }, () => ( - {i18n.t("No pages found")} + + {i18n.t("No pages found")} + ) )} diff --git a/src/pages/components/PageListPage/PageListPage.tsx b/src/pages/components/PageListPage/PageListPage.tsx index d5666cb8c..282058a50 100644 --- a/src/pages/components/PageListPage/PageListPage.tsx +++ b/src/pages/components/PageListPage/PageListPage.tsx @@ -5,8 +5,8 @@ import React from "react"; import AppHeader from "@saleor/components/AppHeader"; import Container from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; import { PageList_pages_edges_node } from "../../types/PageList"; import PageList from "../PageList/PageList"; @@ -17,11 +17,13 @@ export interface PageListPageProps extends PageListProps, ListActions { const PageListPage: React.StatelessComponent = ({ disabled, + settings, onAdd, onBack, onNextPage, onPreviousPage, onRowClick, + onUpdateListSettings, pageInfo, pages, isChecked, @@ -45,9 +47,11 @@ const PageListPage: React.StatelessComponent = ({ = ({ const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); - - const paginationState = createPaginationState(PAGINATE_BY, params); + const { updateListSettings, settings } = useListSettings( + ListViews.PAGES_LIST + ); + const paginationState = createPaginationState(settings.rowNumber, params); return ( @@ -115,6 +118,7 @@ export const PageList: React.StatelessComponent = ({ <> data.pages.edges.map(edge => edge.node) )} @@ -123,6 +127,7 @@ export const PageList: React.StatelessComponent = ({ onBack={() => navigate(configurationMenuUrl)} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} onRowClick={id => () => navigate(pageUrl(id))} toolbar={ <> diff --git a/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx b/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx index 68bec7865..99087212c 100644 --- a/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx +++ b/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx @@ -8,18 +8,23 @@ import { WithStyles } from "@material-ui/core/styles"; import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; -import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import DeleteIcon from "@material-ui/icons/Delete"; import React from "react"; import CardTitle from "@saleor/components/CardTitle"; +import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; -import i18n from "../../../i18n"; -import { maybe, renderCollection } from "../../../misc"; -import { AttributeTypeEnum } from "../../../types/globalTypes"; +import { + SortableTableBody, + SortableTableRow +} from "@saleor/components/SortableTable"; +import TableHead from "@saleor/components/TableHead"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection, stopPropagation } from "@saleor/misc"; +import { ListActions, ReorderAction } from "@saleor/types"; +import { AttributeTypeEnum } from "@saleor/types/globalTypes"; import { ProductTypeDetails_productType_productAttributes, ProductTypeDetails_productType_variantAttributes @@ -27,11 +32,15 @@ import { const styles = (theme: Theme) => createStyles({ + colName: {}, + colSlug: { + width: 300 + }, iconCell: { "&:last-child": { paddingRight: 0 }, - width: 48 + theme.spacing.unit / 2 + width: 48 + theme.spacing.unit * 1.5 }, link: { cursor: "pointer" @@ -41,27 +50,38 @@ const styles = (theme: Theme) => } }); -interface ProductTypeAttributesProps extends WithStyles { +interface ProductTypeAttributesProps extends ListActions { attributes: | ProductTypeDetails_productType_productAttributes[] | ProductTypeDetails_productType_variantAttributes[]; - type: AttributeTypeEnum; - onAttributeAdd: (type: AttributeTypeEnum) => void; - onAttributeDelete: (id: string, event: React.MouseEvent) => void; - onAttributeUpdate: (id: string) => void; + disabled: boolean; + type: string; + onAttributeAssign: (type: AttributeTypeEnum) => void; + onAttributeClick: (id: string) => void; + onAttributeReorder: ReorderAction; + onAttributeUnassign: (id: string) => void; } +const numberOfColumns = 5; + const ProductTypeAttributes = withStyles(styles, { name: "ProductTypeAttributes" })( ({ attributes, classes, + disabled, + isChecked, + selected, + toggle, + toggleAll, + toolbar, type, - onAttributeAdd, - onAttributeDelete, - onAttributeUpdate - }: ProductTypeAttributesProps) => ( + onAttributeAssign, + onAttributeClick, + onAttributeReorder, + onAttributeUnassign + }: ProductTypeAttributesProps & WithStyles) => ( onAttributeAdd(type)} + onClick={() => onAttributeAssign(AttributeTypeEnum[type])} > - {i18n.t("Add attribute", { context: "button" })} + {i18n.t("Assign attribute", { context: "button" })} } />
- - - {i18n.t("Attribute name")} - - {i18n.t("Values")} - - - + + + {i18n.t("Attribute name")} + + {i18n.t("Slug")} + - + {renderCollection( attributes, - attribute => ( - onAttributeUpdate(attribute.id) - : undefined - } - key={maybe(() => attribute.id)} - > - - {maybe(() => attribute.name) ? attribute.name : } - - - {maybe(() => attribute.values) !== undefined ? ( - attribute.values.map(value => value.name).join(", ") - ) : ( - - )} - - - onAttributeDelete(attribute.id, event)} - > - - - - - ), + (attribute, attributeIndex) => { + const isSelected = attribute ? isChecked(attribute.id) : false; + + return ( + onAttributeClick(attribute.id) + : undefined + } + key={maybe(() => attribute.id)} + index={attributeIndex || 0} + > + + toggle(attribute.id)} + /> + + + {maybe(() => attribute.name) ? ( + attribute.name + ) : ( + + )} + + + {maybe(() => attribute.slug) ? ( + attribute.slug + ) : ( + + )} + + + + onAttributeUnassign(attribute.id) + )} + > + + + + + ); + }, () => ( - + {i18n.t("No attributes found")} ) )} - +
) diff --git a/src/productTypes/components/ProductTypeCreatePage/ProductTypeCreatePage.tsx b/src/productTypes/components/ProductTypeCreatePage/ProductTypeCreatePage.tsx index d59b23daf..10ce39d20 100644 --- a/src/productTypes/components/ProductTypeCreatePage/ProductTypeCreatePage.tsx +++ b/src/productTypes/components/ProductTypeCreatePage/ProductTypeCreatePage.tsx @@ -8,33 +8,53 @@ import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; -import i18n from "../../../i18n"; -import { TaxRateType, WeightUnitsEnum } from "../../../types/globalTypes"; +import { ChangeEvent, FormChange } from "@saleor/hooks/useForm"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import i18n from "@saleor/i18n"; +import { ProductTypeDetails_taxTypes } from "@saleor/productTypes/types/ProductTypeDetails"; +import { UserError } from "@saleor/types"; +import { WeightUnitsEnum } from "@saleor/types/globalTypes"; import ProductTypeDetails from "../ProductTypeDetails/ProductTypeDetails"; import ProductTypeShipping from "../ProductTypeShipping/ProductTypeShipping"; import ProductTypeTaxes from "../ProductTypeTaxes/ProductTypeTaxes"; export interface ProductTypeForm { - chargeTaxes: boolean; name: string; isShippingRequired: boolean; - taxRate: TaxRateType; + taxType: string; weight: number; } export interface ProductTypeCreatePageProps { - errors: Array<{ - field: string; - message: string; - }>; + errors: UserError[]; defaultWeightUnit: WeightUnitsEnum; disabled: boolean; pageTitle: string; saveButtonBarState: ConfirmButtonTransitionState; + taxTypes: ProductTypeDetails_taxTypes[]; onBack: () => void; onSubmit: (data: ProductTypeForm) => void; } +const formInitialData: ProductTypeForm = { + isShippingRequired: false, + name: "", + taxType: "", + weight: 0 +}; + +function handleTaxTypeChange( + event: ChangeEvent, + taxTypes: ProductTypeDetails_taxTypes[], + formChange: FormChange, + displayChange: (name: string) => void +) { + formChange(event); + displayChange( + taxTypes.find(taxType => taxType.taxCode === event.target.value).description + ); +} + const ProductTypeCreatePage: React.StatelessComponent< ProductTypeCreatePageProps > = ({ @@ -43,16 +63,12 @@ const ProductTypeCreatePage: React.StatelessComponent< errors, pageTitle, saveButtonBarState, + taxTypes, onBack, onSubmit }: ProductTypeCreatePageProps) => { - const formInitialData: ProductTypeForm = { - chargeTaxes: true, - isShippingRequired: false, - name: "", - taxRate: TaxRateType.STANDARD, - weight: 0 - }; + const [taxTypeDisplayName, setTaxTypeDisplayName] = useStateFromProps(""); + return (
- {({ change, data, hasChanged, submit }) => ( + {({ change, data, errors: formErrors, hasChanged, submit }) => ( {i18n.t("Product Types")} @@ -69,8 +85,24 @@ const ProductTypeCreatePage: React.StatelessComponent< + + + handleTaxTypeChange( + event, + taxTypes, + change, + setTaxTypeDisplayName + ) + } + />
- -
{ name: string; }; disabled: boolean; + errors: FormErrors<"name">; onChange: (event: React.ChangeEvent) => void; } const ProductTypeDetails = withStyles(styles, { name: "ProductTypeDetails" })( - ({ classes, data, disabled, onChange }: ProductTypeDetailsProps) => ( + ({ classes, data, disabled, errors, onChange }: ProductTypeDetailsProps) => ( ; + errors: UserError[]; productType: ProductTypeDetails_productType; defaultWeightUnit: WeightUnitsEnum; disabled: boolean; pageTitle: string; + productAttributeList: ListActions; saveButtonBarState: ConfirmButtonTransitionState; + taxTypes: ProductTypeDetails_taxTypes[]; + variantAttributeList: ListActions; onAttributeAdd: (type: AttributeTypeEnum) => void; - onAttributeDelete: (id: string, event: React.MouseEvent) => void; - onAttributeUpdate: (id: string) => void; + onAttributeClick: (id: string) => void; + onAttributeReorder: (event: ReorderEvent, type: AttributeTypeEnum) => void; + onAttributeUnassign: (id: string) => void; onBack: () => void; onDelete: () => void; onSubmit: (data: ProductTypeForm) => void; } +function handleTaxTypeChange( + event: ChangeEvent, + taxTypes: ProductTypeDetails_taxTypes[], + formChange: FormChange, + displayChange: (name: string) => void +) { + formChange(event); + displayChange( + taxTypes.find(taxType => taxType.taxCode === event.target.value).description + ); +} + const ProductTypeDetailsPage: React.StatelessComponent< ProductTypeDetailsPageProps > = ({ @@ -63,14 +78,21 @@ const ProductTypeDetailsPage: React.StatelessComponent< errors, pageTitle, productType, + productAttributeList, saveButtonBarState, + taxTypes, + variantAttributeList, onAttributeAdd, - onAttributeDelete, - onAttributeUpdate, + onAttributeUnassign, + onAttributeReorder, + onAttributeClick, onBack, onDelete, onSubmit }) => { + const [taxTypeDisplayName, setTaxTypeDisplayName] = useStateFromProps( + maybe(() => productType.taxType.description) + ); const formInitialData: ProductTypeForm = { hasVariants: maybe(() => productType.hasVariants) !== undefined @@ -88,10 +110,7 @@ const ProductTypeDetailsPage: React.StatelessComponent< value: attribute.id })) : [], - taxRate: - maybe(() => productType.taxRate) !== undefined - ? productType.taxRate - : null, + taxType: maybe(() => productType.taxType.taxCode, ""), variantAttributes: maybe(() => productType.variantAttributes) !== undefined ? productType.variantAttributes.map(attribute => ({ @@ -108,7 +127,7 @@ const ProductTypeDetailsPage: React.StatelessComponent< onSubmit={onSubmit} confirmLeave > - {({ change, data, hasChanged, submit }) => ( + {({ change, data, errors: formErrors, hasChanged, submit }) => ( {i18n.t("Product Types")} @@ -117,15 +136,36 @@ const ProductTypeDetailsPage: React.StatelessComponent< + + handleTaxTypeChange( + event, + taxTypes, + change, + setTaxTypeDisplayName + ) + } + /> + productType.productAttributes)} + disabled={disabled} type={AttributeTypeEnum.PRODUCT} - onAttributeAdd={onAttributeAdd} - onAttributeDelete={onAttributeDelete} - onAttributeUpdate={onAttributeUpdate} + onAttributeAssign={onAttributeAdd} + onAttributeClick={onAttributeClick} + onAttributeReorder={(event: ReorderEvent) => + onAttributeReorder(event, AttributeTypeEnum.PRODUCT) + } + onAttributeUnassign={onAttributeUnassign} + {...productAttributeList} /> productType.variantAttributes)} + disabled={disabled} type={AttributeTypeEnum.VARIANT} - onAttributeAdd={onAttributeAdd} - onAttributeDelete={onAttributeDelete} - onAttributeUpdate={onAttributeUpdate} + onAttributeAssign={onAttributeAdd} + onAttributeClick={onAttributeClick} + onAttributeReorder={(event: ReorderEvent) => + onAttributeReorder(event, AttributeTypeEnum.VARIANT) + } + onAttributeUnassign={onAttributeUnassign} + {...variantAttributeList} /> )} @@ -155,12 +200,6 @@ const ProductTypeDetailsPage: React.StatelessComponent< defaultWeightUnit={defaultWeightUnit} onChange={change} /> - -
toggle(productType.id)} /> @@ -148,8 +152,8 @@ const ProductTypeList = withStyles(styles, { name: "ProductTypeList" })( )} - {maybe(() => productType.taxRate) ? ( - translatedTaxRates()[productType.taxRate] + {maybe(() => productType.taxType) ? ( + productType.taxType.description ) : ( )} @@ -159,7 +163,7 @@ const ProductTypeList = withStyles(styles, { name: "ProductTypeList" })( }, () => ( - + {i18n.t("No product types found")} diff --git a/src/productTypes/components/ProductTypeTaxes/ProductTypeTaxes.tsx b/src/productTypes/components/ProductTypeTaxes/ProductTypeTaxes.tsx index 77dff8964..a03edfd79 100644 --- a/src/productTypes/components/ProductTypeTaxes/ProductTypeTaxes.tsx +++ b/src/productTypes/components/ProductTypeTaxes/ProductTypeTaxes.tsx @@ -1,44 +1,62 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; +import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import React from "react"; import CardTitle from "@saleor/components/CardTitle"; -import SingleSelectField from "@saleor/components/SingleSelectField"; +import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField"; +import { ProductTypeDetails_taxTypes } from "@saleor/productTypes/types/ProductTypeDetails"; import i18n from "../../../i18n"; -import { translatedTaxRates as taxRates } from "../../../misc"; -import { TaxRateType } from "../../../types/globalTypes"; +import { maybe } from "../../../misc"; +import { ProductTypeForm } from "../ProductTypeDetailsPage/ProductTypeDetailsPage"; -interface ProductTypeTaxesProps { +interface ProductTypeTaxesProps extends WithStyles { data: { - taxRate: TaxRateType | null; + taxType: string; }; + taxTypeDisplayName: string; + taxTypes: ProductTypeDetails_taxTypes[]; disabled: boolean; onChange: (event: React.ChangeEvent) => void; } -const taxRateChoices = Object.keys(taxRates()).map(key => ({ - label: taxRates()[key], - value: key -})); -const ProductTypeTaxes: React.StatelessComponent = ({ - data, - disabled, - onChange -}) => ( - - - - - - + +const styles = createStyles({ + root: { + overflow: "visible" + } +}); + +const ProductTypeTaxes = withStyles(styles, { name: "ProductTypeTaxes" })( + ({ + classes, + data, + disabled, + taxTypes, + taxTypeDisplayName, + onChange + }: ProductTypeTaxesProps) => ( + + + + + taxTypes.map(c => ({ label: c.description, value: c.taxCode })), + [] + )} + InputProps={{ + autoComplete: "off" + }} + /> + + + ) ); ProductTypeTaxes.displayName = "ProductTypeTaxes"; export default ProductTypeTaxes; diff --git a/src/productTypes/containers/ProductTypeOperations.tsx b/src/productTypes/containers/ProductTypeOperations.tsx index ce34e2660..7a7ea2931 100644 --- a/src/productTypes/containers/ProductTypeOperations.tsx +++ b/src/productTypes/containers/ProductTypeOperations.tsx @@ -1,63 +1,91 @@ import React from "react"; +import { MutationFn } from "react-apollo"; +import { AttributeTypeEnum, ReorderInput } from "@saleor/types/globalTypes"; import { getMutationProviderData } from "../../misc"; import { PartialMutationProviderOutput } from "../../types"; import { - TypedAttributeCreateMutation, - TypedAttributeDeleteMutation, - TypedAttributeUpdateMutation, + ProductTypeAttributeReorderMutation, + TypedAssignAttributeMutation, TypedProductTypeDeleteMutation, - TypedProductTypeUpdateMutation + TypedProductTypeUpdateMutation, + TypedUnassignAttributeMutation } from "../mutations"; import { - AttributeCreate, - AttributeCreateVariables -} from "../types/AttributeCreate"; + AssignAttribute, + AssignAttributeVariables +} from "../types/AssignAttribute"; import { - AttributeDelete, - AttributeDeleteVariables -} from "../types/AttributeDelete"; -import { - AttributeUpdate, - AttributeUpdateVariables -} from "../types/AttributeUpdate"; + ProductTypeAttributeReorder, + ProductTypeAttributeReorderVariables +} from "../types/ProductTypeAttributeReorder"; import { ProductTypeDelete, ProductTypeDeleteVariables } from "../types/ProductTypeDelete"; +import { + ProductTypeDetailsFragment, + ProductTypeDetailsFragment_productAttributes +} from "../types/ProductTypeDetailsFragment"; import { ProductTypeUpdate, ProductTypeUpdateVariables } from "../types/ProductTypeUpdate"; +import { + UnassignAttribute, + UnassignAttributeVariables +} from "../types/UnassignAttribute"; + +function moveAttribute( + attributes: + | ProductTypeDetailsFragment_productAttributes[] + | ProductTypeDetailsFragment_productAttributes[], + move: ReorderInput +) { + const attributeIndex = attributes.findIndex( + attribute => attribute.id === move.id + ); + const newIndex = attributeIndex + move.sortOrder; + + const attributesWithoutMovedOne = [ + ...attributes.slice(0, attributeIndex), + ...attributes.slice(attributeIndex + 1) + ]; + + return [ + ...attributesWithoutMovedOne.slice(0, newIndex), + attributes[attributeIndex], + ...attributesWithoutMovedOne.slice(newIndex) + ]; +} interface ProductTypeOperationsProps { - children: ( - props: { - attributeCreate: PartialMutationProviderOutput< - AttributeCreate, - AttributeCreateVariables - >; - deleteAttribute: PartialMutationProviderOutput< - AttributeDelete, - AttributeDeleteVariables - >; - deleteProductType: PartialMutationProviderOutput< - ProductTypeDelete, - ProductTypeDeleteVariables - >; - updateAttribute: PartialMutationProviderOutput< - AttributeUpdate, - AttributeUpdateVariables - >; - updateProductType: PartialMutationProviderOutput< - ProductTypeUpdate, - ProductTypeUpdateVariables - >; - } - ) => React.ReactNode; - onAttributeCreate: (data: AttributeCreate) => void; - onAttributeDelete: (data: AttributeDelete) => void; - onAttributeUpdate: (data: AttributeUpdate) => void; + children: (props: { + assignAttribute: PartialMutationProviderOutput< + AssignAttribute, + AssignAttributeVariables + >; + unassignAttribute: PartialMutationProviderOutput< + UnassignAttribute, + UnassignAttributeVariables + >; + deleteProductType: PartialMutationProviderOutput< + ProductTypeDelete, + ProductTypeDeleteVariables + >; + reorderAttribute: PartialMutationProviderOutput< + ProductTypeAttributeReorder, + ProductTypeAttributeReorderVariables + >; + updateProductType: PartialMutationProviderOutput< + ProductTypeUpdate, + ProductTypeUpdateVariables + >; + }) => React.ReactNode; + productType: ProductTypeDetailsFragment; + onAssignAttribute: (data: AssignAttribute) => void; + onUnassignAttribute: (data: UnassignAttribute) => void; + onProductTypeAttributeReorder: (data: ProductTypeAttributeReorder) => void; onProductTypeDelete: (data: ProductTypeDelete) => void; onProductTypeUpdate: (data: ProductTypeUpdate) => void; } @@ -66,9 +94,10 @@ const ProductTypeOperations: React.StatelessComponent< ProductTypeOperationsProps > = ({ children, - onAttributeCreate, - onAttributeDelete, - onAttributeUpdate, + productType, + onAssignAttribute, + onUnassignAttribute, + onProductTypeAttributeReorder, onProductTypeDelete, onProductTypeUpdate }) => { @@ -77,37 +106,78 @@ const ProductTypeOperations: React.StatelessComponent< {(...deleteProductType) => ( {(...updateProductType) => ( - - {(...createAttribute) => ( - - {(...deleteAttribute) => ( - + {(...assignAttribute) => ( + + {(...unassignAttribute) => ( + - {(...updateAttribute) => - children({ - attributeCreate: getMutationProviderData( - ...createAttribute - ), - deleteAttribute: getMutationProviderData( - ...deleteAttribute + {( + reorderAttributeMutation, + reorderAttributeMutationResult + ) => { + const reorderAttributeMutationFn: MutationFn< + ProductTypeAttributeReorder, + ProductTypeAttributeReorderVariables + > = opts => { + const optimisticResponse: ProductTypeAttributeReorder = { + productTypeReorderAttributes: { + __typename: "ProductTypeReorderAttributes" as "ProductTypeReorderAttributes", + errors: [], + productType: { + ...productType, + productAttributes: + opts.variables.type === + AttributeTypeEnum.PRODUCT + ? moveAttribute( + productType.productAttributes, + opts.variables.move + ) + : productType.productAttributes, + variantAttributes: + opts.variables.type === + AttributeTypeEnum.VARIANT + ? moveAttribute( + productType.variantAttributes, + opts.variables.move + ) + : productType.variantAttributes + } + } + }; + return reorderAttributeMutation({ + ...opts, + optimisticResponse + }); + }; + + return children({ + assignAttribute: getMutationProviderData( + ...assignAttribute ), deleteProductType: getMutationProviderData( ...deleteProductType ), - updateAttribute: getMutationProviderData( - ...updateAttribute + reorderAttribute: getMutationProviderData( + reorderAttributeMutationFn, + reorderAttributeMutationResult + ), + unassignAttribute: getMutationProviderData( + ...unassignAttribute ), updateProductType: getMutationProviderData( ...updateProductType ) - }) - } - + }); + }} + )} - + )} - + )} )} diff --git a/src/productTypes/fixtures.ts b/src/productTypes/fixtures.ts index 169f08b8f..fe214f7bd 100644 --- a/src/productTypes/fixtures.ts +++ b/src/productTypes/fixtures.ts @@ -2,7 +2,7 @@ import { ProductCreateData_productTypes_edges_node, ProductCreateData_productTypes_edges_node_productAttributes } from "../products/types/ProductCreateData"; -import { TaxRateType } from "../types/globalTypes"; +import { AttributeInputTypeEnum } from "../types/globalTypes"; import { ProductTypeDetails_productType } from "./types/ProductTypeDetails"; import { ProductTypeList_productTypes_edges_node } from "./types/ProductTypeList"; @@ -11,8 +11,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZTo5", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Author", slug: "author", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -39,8 +41,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZTo2", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Box Size", slug: "box-size", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -85,8 +89,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZToz", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Brand", slug: "brand", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -104,8 +110,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZTo4", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Candy Box Size", slug: "candy-box-size", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -141,8 +149,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZTo1", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Coffee Genre", slug: "coffee-genre", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -169,8 +179,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZToy", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Collar", slug: "collar", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -206,8 +218,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZTox", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Color", slug: "color", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -234,8 +248,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZToxMg==", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Cover", slug: "cover", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -298,8 +314,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZTo3", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Flavor", slug: "flavor", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -326,8 +344,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZToxMQ==", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Language", slug: "language", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -354,8 +374,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZToxMA==", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Publisher", slug: "publisher", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -382,8 +404,10 @@ export const attributes: ProductCreateData_productTypes_edges_node_productAttrib node: { __typename: "Attribute" as "Attribute", id: "UHJvZHVjdEF0dHJpYnV0ZTo0", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Size", slug: "size", + valueRequired: true, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -455,7 +479,11 @@ export const productTypes: Array< isShippingRequired: true, name: "Candy", productAttributes: [attributes[0]], - taxRate: "FOODSTUFFS" as TaxRateType, + taxType: { + __typename: "TaxType" as "TaxType", + description: "PB100000", + taxCode: "Books / Manuals" + }, variantAttributes: [attributes[1], attributes[2]] }, { @@ -465,7 +493,11 @@ export const productTypes: Array< isShippingRequired: false, name: "E-books", productAttributes: [attributes[5]], - taxRate: "STANDARD" as TaxRateType, + taxType: { + __typename: "TaxType" as "TaxType", + description: "PH403682", + taxCode: "Holters" + }, variantAttributes: [attributes[0], attributes[3]] }, { @@ -475,7 +507,11 @@ export const productTypes: Array< isShippingRequired: true, name: "Mugs", productAttributes: [attributes[7]], - taxRate: "STANDARD" as TaxRateType, + taxType: { + __typename: "TaxType" as "TaxType", + description: "PC077426", + taxCode: "Cabling" + }, variantAttributes: [attributes[2], attributes[5]] }, { @@ -485,7 +521,11 @@ export const productTypes: Array< isShippingRequired: true, name: "Coffee", productAttributes: [attributes[8]], - taxRate: "STANDARD" as TaxRateType, + taxType: { + __typename: "TaxType" as "TaxType", + description: "PB100000", + taxCode: "Books / Manuals" + }, variantAttributes: [attributes[1], attributes[4]] }, { @@ -495,7 +535,11 @@ export const productTypes: Array< isShippingRequired: true, name: "T-Shirt", productAttributes: [attributes[4]], - taxRate: "STANDARD" as TaxRateType, + taxType: { + __typename: "TaxType" as "TaxType", + description: "PH403970", + taxCode: "Wheelchair" + }, variantAttributes: [attributes[1], attributes[6]] } ].map(productType => ({ @@ -511,67 +555,51 @@ export const productType: ProductTypeDetails_productType = { productAttributes: [ { __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: false, id: "UHJvZHVjdEF0dHJpYnV0ZTo5", name: "Author", slug: "author", - values: [ - { - __typename: "AttributeValue" as "AttributeValue", - id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI0", - name: "John Doe", - slug: "john-doe" - }, - { - __typename: "AttributeValue" as "AttributeValue", - id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI1", - name: "Milionare Pirate", - slug: "milionare-pirate" - } - ] + visibleInStorefront: true }, { __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: false, id: "UHJvZHVjdEF0dHJpYnV0ZToxMQ==", name: "Language", slug: "language", - values: [ - { - __typename: "AttributeValue" as "AttributeValue", - id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI4", - name: "English", - slug: "english" - }, - { - __typename: "AttributeValue" as "AttributeValue", - id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI5", - name: "Pirate", - slug: "pirate" - } - ] + visibleInStorefront: true }, { __typename: "Attribute" as "Attribute", + filterableInDashboard: true, + filterableInStorefront: false, id: "UHJvZHVjdEF0dHJpYnV0ZToxMA==", name: "Publisher", slug: "publisher", - values: [ - { - __typename: "AttributeValue" as "AttributeValue", - id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI2", - name: "Mirumee Press", - slug: "mirumee-press" - }, - { - __typename: "AttributeValue" as "AttributeValue", - id: "UHJvZHVjdEF0dHJpYnV0ZVZhbHVlOjI3", - name: "Saleor Publishing", - slug: "saleor-publishing" - } - ] + visibleInStorefront: true + } + ], + taxType: { + __typename: "TaxType" as "TaxType", + description: "PH405458", + taxCode: "Shields" + }, + variantAttributes: [ + { + ...attributes[1], + filterableInDashboard: true, + filterableInStorefront: false, + visibleInStorefront: true + }, + { + ...attributes[6], + filterableInDashboard: true, + filterableInStorefront: false, + visibleInStorefront: true } ], - taxRate: "STANDARD" as TaxRateType, - variantAttributes: [], weight: { __typename: "Weight", unit: "kg", diff --git a/src/productTypes/mutations.ts b/src/productTypes/mutations.ts index de7cc9543..7d103d756 100644 --- a/src/productTypes/mutations.ts +++ b/src/productTypes/mutations.ts @@ -1,19 +1,15 @@ import gql from "graphql-tag"; import { TypedMutation } from "../mutations"; -import { attributeFragment, productTypeDetailsFragment } from "./queries"; +import { productTypeDetailsFragment } from "./queries"; import { - AttributeCreate, - AttributeCreateVariables -} from "./types/AttributeCreate"; + AssignAttribute, + AssignAttributeVariables +} from "./types/AssignAttribute"; import { - AttributeDelete, - AttributeDeleteVariables -} from "./types/AttributeDelete"; -import { - AttributeUpdate, - AttributeUpdateVariables -} from "./types/AttributeUpdate"; + ProductTypeAttributeReorder, + ProductTypeAttributeReorderVariables +} from "./types/ProductTypeAttributeReorder"; import { ProductTypeBulkDelete, ProductTypeBulkDeleteVariables @@ -30,6 +26,10 @@ import { ProductTypeUpdate, ProductTypeUpdateVariables } from "./types/ProductTypeUpdate"; +import { + UnassignAttribute, + UnassignAttributeVariables +} from "./types/UnassignAttribute"; export const productTypeDeleteMutation = gql` mutation ProductTypeDelete($id: ID!) { @@ -83,6 +83,44 @@ export const TypedProductTypeUpdateMutation = TypedMutation< ProductTypeUpdateVariables >(productTypeUpdateMutation); +export const assignAttributeMutation = gql` + ${productTypeDetailsFragment} + mutation AssignAttribute($id: ID!, $operations: [AttributeAssignInput!]!) { + attributeAssign(productTypeId: $id, operations: $operations) { + errors { + field + message + } + productType { + ...ProductTypeDetailsFragment + } + } + } +`; +export const TypedAssignAttributeMutation = TypedMutation< + AssignAttribute, + AssignAttributeVariables +>(assignAttributeMutation); + +export const unassignAttributeMutation = gql` + ${productTypeDetailsFragment} + mutation UnassignAttribute($id: ID!, $ids: [ID]!) { + attributeUnassign(productTypeId: $id, attributeIds: $ids) { + errors { + field + message + } + productType { + ...ProductTypeDetailsFragment + } + } + } +`; +export const TypedUnassignAttributeMutation = TypedMutation< + UnassignAttribute, + UnassignAttributeVariables +>(unassignAttributeMutation); + export const productTypeCreateMutation = gql` ${productTypeDetailsFragment} mutation ProductTypeCreate($input: ProductTypeInput!) { @@ -102,14 +140,18 @@ export const TypedProductTypeCreateMutation = TypedMutation< ProductTypeCreateVariables >(productTypeCreateMutation); -export const attributeCreateMutation = gql` +const productTypeAttributeReorder = gql` ${productTypeDetailsFragment} - mutation AttributeCreate( - $id: ID! - $input: AttributeCreateInput! + mutation ProductTypeAttributeReorder( + $move: ReorderInput! + $productTypeId: ID! $type: AttributeTypeEnum! ) { - attributeCreate(id: $id, input: $input, type: $type) { + productTypeReorderAttributes( + moves: [$move] + productTypeId: $productTypeId + type: $type + ) { errors { field message @@ -120,45 +162,7 @@ export const attributeCreateMutation = gql` } } `; -export const TypedAttributeCreateMutation = TypedMutation< - AttributeCreate, - AttributeCreateVariables ->(attributeCreateMutation); - -export const attributeUpdateMutation = gql` - ${attributeFragment} - mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) { - attributeUpdate(id: $id, input: $input) { - errors { - field - message - } - attribute { - ...AttributeFragment - } - } - } -`; -export const TypedAttributeUpdateMutation = TypedMutation< - AttributeUpdate, - AttributeUpdateVariables ->(attributeUpdateMutation); - -export const attributeDeleteMutation = gql` - ${productTypeDetailsFragment} - mutation AttributeDelete($id: ID!) { - attributeDelete(id: $id) { - errors { - field - message - } - productType { - ...ProductTypeDetailsFragment - } - } - } -`; -export const TypedAttributeDeleteMutation = TypedMutation< - AttributeDelete, - AttributeDeleteVariables ->(attributeDeleteMutation); +export const ProductTypeAttributeReorderMutation = TypedMutation< + ProductTypeAttributeReorder, + ProductTypeAttributeReorderVariables +>(productTypeAttributeReorder); diff --git a/src/productTypes/queries.ts b/src/productTypes/queries.ts index f3012c73a..63bbbbec0 100644 --- a/src/productTypes/queries.ts +++ b/src/productTypes/queries.ts @@ -1,6 +1,7 @@ import gql from "graphql-tag"; -import { TypedQuery } from "../queries"; +import { attributeFragment } from "@saleor/attributes/queries"; +import { pageInfoFragment, TypedQuery } from "../queries"; import { ProductTypeCreateData } from "./types/ProductTypeCreateData"; import { ProductTypeDetails, @@ -11,25 +12,16 @@ import { ProductTypeListVariables } from "./types/ProductTypeList"; -export const attributeFragment = gql` - fragment AttributeFragment on Attribute { - id - name - slug - values { - id - name - slug - } - } -`; export const productTypeFragment = gql` fragment ProductTypeFragment on ProductType { id name hasVariants isShippingRequired - taxRate + taxType { + description + taxCode + } } `; @@ -52,6 +44,7 @@ export const productTypeDetailsFragment = gql` `; export const productTypeListQuery = gql` + ${pageInfoFragment} ${productTypeFragment} query ProductTypeList( $after: String @@ -66,10 +59,7 @@ export const productTypeListQuery = gql` } } pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor + ...PageInfoFragment } } } @@ -88,6 +78,10 @@ export const productTypeDetailsQuery = gql` shop { defaultWeightUnit } + taxTypes { + taxCode + description + } } `; export const TypedProductTypeDetailsQuery = TypedQuery< @@ -100,6 +94,10 @@ export const productTypeCreateDataQuery = gql` shop { defaultWeightUnit } + taxTypes { + taxCode + description + } } `; export const TypedProductTypeCreateDataQuery = TypedQuery< diff --git a/src/productTypes/types/ProductTypeCreate.ts b/src/productTypes/types/ProductTypeCreate.ts index a146ea60f..b2c9256c8 100644 --- a/src/productTypes/types/ProductTypeCreate.ts +++ b/src/productTypes/types/ProductTypeCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductTypeInput, TaxRateType } from "./../../types/globalTypes"; +import { ProductTypeInput } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductTypeCreate @@ -14,11 +14,10 @@ export interface ProductTypeCreate_productTypeCreate_errors { message: string | null; } -export interface ProductTypeCreate_productTypeCreate_productType_productAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; +export interface ProductTypeCreate_productTypeCreate_productType_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; } export interface ProductTypeCreate_productTypeCreate_productType_productAttributes { @@ -26,14 +25,9 @@ export interface ProductTypeCreate_productTypeCreate_productType_productAttribut id: string; name: string | null; slug: string | null; - values: (ProductTypeCreate_productTypeCreate_productType_productAttributes_values | null)[] | null; -} - -export interface ProductTypeCreate_productTypeCreate_productType_variantAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; } export interface ProductTypeCreate_productTypeCreate_productType_variantAttributes { @@ -41,7 +35,9 @@ export interface ProductTypeCreate_productTypeCreate_productType_variantAttribut id: string; name: string | null; slug: string | null; - values: (ProductTypeCreate_productTypeCreate_productType_variantAttributes_values | null)[] | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; } export interface ProductTypeCreate_productTypeCreate_productType_weight { @@ -56,7 +52,7 @@ export interface ProductTypeCreate_productTypeCreate_productType { name: string; hasVariants: boolean; isShippingRequired: boolean; - taxRate: TaxRateType | null; + taxType: ProductTypeCreate_productTypeCreate_productType_taxType | null; productAttributes: (ProductTypeCreate_productTypeCreate_productType_productAttributes | null)[] | null; variantAttributes: (ProductTypeCreate_productTypeCreate_productType_variantAttributes | null)[] | null; weight: ProductTypeCreate_productTypeCreate_productType_weight | null; diff --git a/src/productTypes/types/ProductTypeCreateData.ts b/src/productTypes/types/ProductTypeCreateData.ts index 483dedd6a..b50c7f29c 100644 --- a/src/productTypes/types/ProductTypeCreateData.ts +++ b/src/productTypes/types/ProductTypeCreateData.ts @@ -13,6 +13,13 @@ export interface ProductTypeCreateData_shop { defaultWeightUnit: WeightUnitsEnum | null; } +export interface ProductTypeCreateData_taxTypes { + __typename: "TaxType"; + taxCode: string | null; + description: string | null; +} + export interface ProductTypeCreateData { shop: ProductTypeCreateData_shop | null; + taxTypes: (ProductTypeCreateData_taxTypes | null)[] | null; } diff --git a/src/productTypes/types/ProductTypeDetails.ts b/src/productTypes/types/ProductTypeDetails.ts index 38cbc73a6..ec3d8a568 100644 --- a/src/productTypes/types/ProductTypeDetails.ts +++ b/src/productTypes/types/ProductTypeDetails.ts @@ -2,17 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { TaxRateType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductTypeDetails // ==================================================== -export interface ProductTypeDetails_productType_productAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; +export interface ProductTypeDetails_productType_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; } export interface ProductTypeDetails_productType_productAttributes { @@ -20,14 +19,9 @@ export interface ProductTypeDetails_productType_productAttributes { id: string; name: string | null; slug: string | null; - values: (ProductTypeDetails_productType_productAttributes_values | null)[] | null; -} - -export interface ProductTypeDetails_productType_variantAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; } export interface ProductTypeDetails_productType_variantAttributes { @@ -35,7 +29,9 @@ export interface ProductTypeDetails_productType_variantAttributes { id: string; name: string | null; slug: string | null; - values: (ProductTypeDetails_productType_variantAttributes_values | null)[] | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; } export interface ProductTypeDetails_productType_weight { @@ -50,7 +46,7 @@ export interface ProductTypeDetails_productType { name: string; hasVariants: boolean; isShippingRequired: boolean; - taxRate: TaxRateType | null; + taxType: ProductTypeDetails_productType_taxType | null; productAttributes: (ProductTypeDetails_productType_productAttributes | null)[] | null; variantAttributes: (ProductTypeDetails_productType_variantAttributes | null)[] | null; weight: ProductTypeDetails_productType_weight | null; @@ -61,9 +57,16 @@ export interface ProductTypeDetails_shop { defaultWeightUnit: WeightUnitsEnum | null; } +export interface ProductTypeDetails_taxTypes { + __typename: "TaxType"; + taxCode: string | null; + description: string | null; +} + export interface ProductTypeDetails { productType: ProductTypeDetails_productType | null; shop: ProductTypeDetails_shop | null; + taxTypes: (ProductTypeDetails_taxTypes | null)[] | null; } export interface ProductTypeDetailsVariables { diff --git a/src/productTypes/types/ProductTypeDetailsFragment.ts b/src/productTypes/types/ProductTypeDetailsFragment.ts index 85b673786..87cd9d547 100644 --- a/src/productTypes/types/ProductTypeDetailsFragment.ts +++ b/src/productTypes/types/ProductTypeDetailsFragment.ts @@ -2,17 +2,14 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { TaxRateType } from "./../../types/globalTypes"; - // ==================================================== // GraphQL fragment: ProductTypeDetailsFragment // ==================================================== -export interface ProductTypeDetailsFragment_productAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; +export interface ProductTypeDetailsFragment_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; } export interface ProductTypeDetailsFragment_productAttributes { @@ -20,14 +17,9 @@ export interface ProductTypeDetailsFragment_productAttributes { id: string; name: string | null; slug: string | null; - values: (ProductTypeDetailsFragment_productAttributes_values | null)[] | null; -} - -export interface ProductTypeDetailsFragment_variantAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; } export interface ProductTypeDetailsFragment_variantAttributes { @@ -35,7 +27,9 @@ export interface ProductTypeDetailsFragment_variantAttributes { id: string; name: string | null; slug: string | null; - values: (ProductTypeDetailsFragment_variantAttributes_values | null)[] | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; } export interface ProductTypeDetailsFragment_weight { @@ -50,7 +44,7 @@ export interface ProductTypeDetailsFragment { name: string; hasVariants: boolean; isShippingRequired: boolean; - taxRate: TaxRateType | null; + taxType: ProductTypeDetailsFragment_taxType | null; productAttributes: (ProductTypeDetailsFragment_productAttributes | null)[] | null; variantAttributes: (ProductTypeDetailsFragment_variantAttributes | null)[] | null; weight: ProductTypeDetailsFragment_weight | null; diff --git a/src/productTypes/types/ProductTypeFragment.ts b/src/productTypes/types/ProductTypeFragment.ts index f1443f1ff..d96c18668 100644 --- a/src/productTypes/types/ProductTypeFragment.ts +++ b/src/productTypes/types/ProductTypeFragment.ts @@ -2,17 +2,21 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { TaxRateType } from "./../../types/globalTypes"; - // ==================================================== // GraphQL fragment: ProductTypeFragment // ==================================================== +export interface ProductTypeFragment_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; +} + export interface ProductTypeFragment { __typename: "ProductType"; id: string; name: string; hasVariants: boolean; isShippingRequired: boolean; - taxRate: TaxRateType | null; + taxType: ProductTypeFragment_taxType | null; } diff --git a/src/productTypes/types/ProductTypeList.ts b/src/productTypes/types/ProductTypeList.ts index 89d1f26d0..64fa7e917 100644 --- a/src/productTypes/types/ProductTypeList.ts +++ b/src/productTypes/types/ProductTypeList.ts @@ -2,19 +2,23 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { TaxRateType } from "./../../types/globalTypes"; - // ==================================================== // GraphQL query operation: ProductTypeList // ==================================================== +export interface ProductTypeList_productTypes_edges_node_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; +} + export interface ProductTypeList_productTypes_edges_node { __typename: "ProductType"; id: string; name: string; hasVariants: boolean; isShippingRequired: boolean; - taxRate: TaxRateType | null; + taxType: ProductTypeList_productTypes_edges_node_taxType | null; } export interface ProductTypeList_productTypes_edges { @@ -24,10 +28,10 @@ export interface ProductTypeList_productTypes_edges { export interface ProductTypeList_productTypes_pageInfo { __typename: "PageInfo"; + endCursor: string | null; hasNextPage: boolean; hasPreviousPage: boolean; startCursor: string | null; - endCursor: string | null; } export interface ProductTypeList_productTypes { diff --git a/src/productTypes/types/ProductTypeUpdate.ts b/src/productTypes/types/ProductTypeUpdate.ts index 6c7f4ce11..b0859f729 100644 --- a/src/productTypes/types/ProductTypeUpdate.ts +++ b/src/productTypes/types/ProductTypeUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ProductTypeInput, TaxRateType } from "./../../types/globalTypes"; +import { ProductTypeInput } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductTypeUpdate @@ -14,11 +14,10 @@ export interface ProductTypeUpdate_productTypeUpdate_errors { message: string | null; } -export interface ProductTypeUpdate_productTypeUpdate_productType_productAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; +export interface ProductTypeUpdate_productTypeUpdate_productType_taxType { + __typename: "TaxType"; + description: string | null; + taxCode: string | null; } export interface ProductTypeUpdate_productTypeUpdate_productType_productAttributes { @@ -26,14 +25,9 @@ export interface ProductTypeUpdate_productTypeUpdate_productType_productAttribut id: string; name: string | null; slug: string | null; - values: (ProductTypeUpdate_productTypeUpdate_productType_productAttributes_values | null)[] | null; -} - -export interface ProductTypeUpdate_productTypeUpdate_productType_variantAttributes_values { - __typename: "AttributeValue"; - id: string; - name: string | null; - slug: string | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; } export interface ProductTypeUpdate_productTypeUpdate_productType_variantAttributes { @@ -41,7 +35,9 @@ export interface ProductTypeUpdate_productTypeUpdate_productType_variantAttribut id: string; name: string | null; slug: string | null; - values: (ProductTypeUpdate_productTypeUpdate_productType_variantAttributes_values | null)[] | null; + visibleInStorefront: boolean; + filterableInDashboard: boolean; + filterableInStorefront: boolean; } export interface ProductTypeUpdate_productTypeUpdate_productType_weight { @@ -56,7 +52,7 @@ export interface ProductTypeUpdate_productTypeUpdate_productType { name: string; hasVariants: boolean; isShippingRequired: boolean; - taxRate: TaxRateType | null; + taxType: ProductTypeUpdate_productTypeUpdate_productType_taxType | null; productAttributes: (ProductTypeUpdate_productTypeUpdate_productType_productAttributes | null)[] | null; variantAttributes: (ProductTypeUpdate_productTypeUpdate_productType_variantAttributes | null)[] | null; weight: ProductTypeUpdate_productTypeUpdate_productType_weight | null; diff --git a/src/productTypes/urls.ts b/src/productTypes/urls.ts index 2ba27d635..86251789d 100644 --- a/src/productTypes/urls.ts +++ b/src/productTypes/urls.ts @@ -2,7 +2,6 @@ import { stringify as stringifyQs } from "qs"; import urlJoin from "url-join"; import { BulkAction, Dialog, Pagination, SingleAction } from "../types"; -import { AttributeTypeEnum } from "../types/globalTypes"; const productTypeSection = "/product-types/"; @@ -19,12 +18,14 @@ export const productTypeAddUrl = productTypeAddPath; export const productTypePath = (id: string) => urlJoin(productTypeSection, id); export type ProductTypeUrlDialog = - | "add-attribute" - | "edit-attribute" + | "assign-attribute" + | "unassign-attribute" + | "unassign-attributes" | "remove"; -export type ProductTypeUrlQueryParams = Dialog & +export type ProductTypeUrlQueryParams = BulkAction & + Dialog & SingleAction & { - type?: AttributeTypeEnum; + type?: string; }; export const productTypeUrl = ( id: string, diff --git a/src/productTypes/views/ProductTypeCreate.tsx b/src/productTypes/views/ProductTypeCreate.tsx index 51bc8cab0..2427377f1 100644 --- a/src/productTypes/views/ProductTypeCreate.tsx +++ b/src/productTypes/views/ProductTypeCreate.tsx @@ -4,7 +4,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import i18n from "../../i18n"; -import { maybe } from "../../misc"; +import { getMutationState, maybe } from "../../misc"; import ProductTypeCreatePage, { ProductTypeForm } from "../components/ProductTypeCreatePage"; @@ -29,8 +29,9 @@ export const ProductTypeCreate: React.StatelessComponent = () => { {( createProductType, - { loading: loadingCreate, data: createProductTypeData } + { called, loading, data: createProductTypeData } ) => { + const formTransitionState = getMutationState(loading, called); const handleCreate = (formData: ProductTypeForm) => createProductType({ variables: { @@ -38,7 +39,7 @@ export const ProductTypeCreate: React.StatelessComponent = () => { hasVariants: false, isShippingRequired: formData.isShippingRequired, name: formData.name, - taxRate: formData.chargeTaxes ? formData.taxRate : null, + taxCode: formData.taxType, weight: formData.weight } } @@ -50,7 +51,7 @@ export const ProductTypeCreate: React.StatelessComponent = () => { data.shop.defaultWeightUnit)} - disabled={loadingCreate || loading} + disabled={loading} errors={ createProductTypeData ? createProductTypeData.productTypeCreate.errors @@ -59,7 +60,8 @@ export const ProductTypeCreate: React.StatelessComponent = () => { pageTitle={i18n.t("Create Product Type", { context: "page title" })} - saveButtonBarState={loadingCreate ? "loading" : "default"} + saveButtonBarState={formTransitionState} + taxTypes={maybe(() => data.taxTypes, [])} onBack={() => navigate(productTypeListUrl())} onSubmit={handleCreate} /> diff --git a/src/productTypes/views/ProductTypeUpdate/index.tsx b/src/productTypes/views/ProductTypeUpdate/index.tsx index 25348b6ec..1e59ccb7c 100644 --- a/src/productTypes/views/ProductTypeUpdate/index.tsx +++ b/src/productTypes/views/ProductTypeUpdate/index.tsx @@ -1,26 +1,29 @@ -import DialogContentText from "@material-ui/core/DialogContentText"; +import Button from "@material-ui/core/Button"; import React from "react"; -import ActionDialog from "@saleor/components/ActionDialog"; +import { attributeUrl } from "@saleor/attributes/urls"; import { WindowTitle } from "@saleor/components/WindowTitle"; +import useBulkActions from "@saleor/hooks/useBulkActions"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; -import i18n from "../../../i18n"; -import { getMutationState, maybe } from "../../../misc"; -import { AttributeTypeEnum } from "../../../types/globalTypes"; -import ProductTypeAttributeEditDialog, { - FormData as AttributeForm -} from "../../components/ProductTypeAttributeEditDialog"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import AssignAttributeDialog from "@saleor/productTypes/components/AssignAttributeDialog"; +import { ReorderEvent } from "@saleor/types"; +import { AttributeTypeEnum } from "@saleor/types/globalTypes"; +import ProductTypeAttributeUnassignDialog from "../../components/ProductTypeAttributeUnassignDialog"; +import ProductTypeBulkAttributeUnassignDialog from "../../components/ProductTypeBulkAttributeUnassignDialog"; +import ProductTypeDeleteDialog from "../../components/ProductTypeDeleteDialog"; import ProductTypeDetailsPage, { ProductTypeForm } from "../../components/ProductTypeDetailsPage"; import ProductTypeOperations from "../../containers/ProductTypeOperations"; +import SearchAttributes from "../../containers/SearchAttributes"; import { TypedProductTypeDetailsQuery } from "../../queries"; -import { AttributeCreate } from "../../types/AttributeCreate"; -import { AttributeDelete } from "../../types/AttributeDelete"; -import { AttributeUpdate } from "../../types/AttributeUpdate"; +import { AssignAttribute } from "../../types/AssignAttribute"; import { ProductTypeDelete } from "../../types/ProductTypeDelete"; import { ProductTypeUpdate as ProductTypeUpdateMutation } from "../../types/ProductTypeUpdate"; +import { UnassignAttribute } from "../../types/UnassignAttribute"; import { productTypeListUrl, productTypeUrl, @@ -33,11 +36,14 @@ interface ProductTypeUpdateProps { params: ProductTypeUrlQueryParams; } -export const ProductTypeUpdate: React.StatelessComponent< - ProductTypeUpdateProps -> = ({ id, params }) => { +export const ProductTypeUpdate: React.FC = ({ + id, + params +}) => { const navigate = useNavigator(); const notify = useNotifier(); + const productAttributeListActions = useBulkActions(); + const variantAttributeListActions = useBulkActions(); return ( @@ -48,48 +54,35 @@ export const ProductTypeUpdate: React.StatelessComponent< require={["productType"]} > {({ data, loading: dataLoading }) => { - const closeModal = () => { - navigate(productTypeUrl(id), true); - setErrors.addAttributeErrors([]); - setErrors.editAttributeErrors([]); - }; - const handleAttributeCreateSuccess = (data: AttributeCreate) => { - if (data.attributeCreate.errors.length === 0) { + const closeModal = () => navigate(productTypeUrl(id), true); + + const handleAttributeAssignSuccess = (data: AssignAttribute) => { + if (data.attributeAssign.errors.length === 0) { notify({ - text: i18n.t("Attribute created", { + text: i18n.t("Attributes assigned", { context: "notification" }) }); closeModal(); } else if ( - data.attributeCreate.errors !== null && - data.attributeCreate.errors.length > 0 + data.attributeAssign.errors !== null && + data.attributeAssign.errors.length > 0 ) { - setErrors.addAttributeErrors(data.attributeCreate.errors); + setErrors.addAttributeErrors(data.attributeAssign.errors); } }; - const handleAttributeDeleteSuccess = (data: AttributeDelete) => { - if (!data.attributeDelete.errors) { + const handleAttributeUnassignSuccess = ( + data: UnassignAttribute + ) => { + if (data.attributeUnassign.errors.length === 0) { notify({ - text: i18n.t("Attribute deleted", { - context: "notification" - }) - }); - } - }; - const handleAttributeUpdateSuccess = (data: AttributeUpdate) => { - if (data.attributeUpdate.errors.length === 0) { - notify({ - text: i18n.t("Attribute updated", { + text: i18n.t("Attribute unassigned", { context: "notification" }) }); closeModal(); - } else if ( - data.attributeUpdate.errors !== null && - data.attributeUpdate.errors.length > 0 - ) { - setErrors.editAttributeErrors(data.attributeUpdate.errors); + productAttributeListActions.reset(); + variantAttributeListActions.reset(); } }; const handleProductTypeDeleteSuccess = ( @@ -126,18 +119,19 @@ export const ProductTypeUpdate: React.StatelessComponent< return ( data.productType)} + onAssignAttribute={handleAttributeAssignSuccess} + onUnassignAttribute={handleAttributeUnassignSuccess} onProductTypeDelete={handleProductTypeDeleteSuccess} onProductTypeUpdate={handleProductTypeUpdateSuccess} + onProductTypeAttributeReorder={() => undefined} > {({ - attributeCreate, - deleteAttribute, + assignAttribute, deleteProductType, - updateAttribute, - updateProductType + unassignAttribute, + updateProductType, + reorderAttribute }) => { const handleProductTypeDelete = () => deleteProductType.mutate({ id }); @@ -153,7 +147,7 @@ export const ProductTypeUpdate: React.StatelessComponent< productAttributes: formData.productAttributes.map( choice => choice.value ), - taxRate: formData.taxRate, + taxCode: formData.taxType, variantAttributes: formData.variantAttributes.map( choice => choice.value ), @@ -161,60 +155,45 @@ export const ProductTypeUpdate: React.StatelessComponent< } }); }; - const handleAttributeCreate = ( - data: AttributeForm, - type: AttributeTypeEnum - ) => - attributeCreate.mutate({ + const handleAssignAttribute = () => + assignAttribute.mutate({ id, - input: { - name: data.name, - values: data.values.map(value => ({ - name: value.label - })) - }, - type + operations: params.ids.map(id => ({ + id, + type: AttributeTypeEnum[params.type] + })) }); - const handleAttributeDelete = ( - id: string, - event: React.MouseEvent - ) => { - event.stopPropagation(); - deleteAttribute.mutate({ id }); - }; - const handleAttributeUpdate = ( - id: string, - formData: AttributeForm - ) => { - const attribute = data.productType.variantAttributes - .concat(data.productType.productAttributes) - .filter(attribute => attribute.id === id)[0]; - updateAttribute.mutate({ + + const handleAttributeUnassign = () => + unassignAttribute.mutate({ id, - input: { - addValues: formData.values - .filter( - value => - !attribute.values - .map(value => value.id) - .includes(value.value) - ) - .map(value => ({ - name: value.label - })), - name: formData.name, - removeValues: attribute.values - .filter( - value => - !formData.values - .map(value => value.value) - .includes(value.id) - ) - .map(value => value.id) - } + ids: [params.id] }); - }; + + const handleBulkAttributeUnassign = () => + unassignAttribute.mutate({ + id, + ids: params.ids + }); + const loading = updateProductType.opts.loading || dataLoading; + + const assignTransactionState = getMutationState( + assignAttribute.opts.called, + assignAttribute.opts.loading, + maybe( + () => assignAttribute.opts.data.attributeAssign.errors + ) + ); + + const unassignTransactionState = getMutationState( + unassignAttribute.opts.called, + unassignAttribute.opts.loading, + maybe( + () => unassignAttribute.opts.data.attributeUnassign.errors + ) + ); + const deleteTransactionState = getMutationState( deleteProductType.opts.called, deleteProductType.opts.loading, @@ -223,12 +202,33 @@ export const ProductTypeUpdate: React.StatelessComponent< ) ); - const attribute = maybe(() => - data.productType.productAttributes - .concat(data.productType.variantAttributes) - .find(attribute => attribute.id === params.id) + const formTransitionState = getMutationState( + updateProductType.opts.called, + updateProductType.opts.loading, + maybe( + () => updateProductType.opts.data.productTypeUpdate.errors + ) ); + const handleAttributeReorder = ( + event: ReorderEvent, + type: AttributeTypeEnum + ) => { + const attributes = + type === AttributeTypeEnum.PRODUCT + ? data.productType.productAttributes + : data.productType.variantAttributes; + + reorderAttribute.mutate({ + move: { + id: attributes[event.oldIndex].id, + sortOrder: event.newIndex - event.oldIndex + }, + productTypeId: id, + type + }); + }; + return ( <> data.productType.name)} /> @@ -240,20 +240,24 @@ export const ProductTypeUpdate: React.StatelessComponent< errors={errors.formErrors} pageTitle={maybe(() => data.productType.name)} productType={maybe(() => data.productType)} - saveButtonBarState={loading ? "loading" : "default"} + saveButtonBarState={formTransitionState} + taxTypes={maybe(() => data.taxTypes, [])} onAttributeAdd={type => navigate( productTypeUrl(id, { - action: "add-attribute", + action: "assign-attribute", type }) ) } - onAttributeDelete={handleAttributeDelete} - onAttributeUpdate={attributeId => + onAttributeClick={attributeId => + navigate(attributeUrl(attributeId)) + } + onAttributeReorder={handleAttributeReorder} + onAttributeUnassign={attributeId => navigate( productTypeUrl(id, { - action: "edit-attribute", + action: "unassign-attribute", id: attributeId }) ) @@ -267,75 +271,200 @@ export const ProductTypeUpdate: React.StatelessComponent< ) } onSubmit={handleProductTypeUpdate} + productAttributeList={{ + isChecked: productAttributeListActions.isSelected, + selected: + productAttributeListActions.listElements.length, + toggle: productAttributeListActions.toggle, + toggleAll: productAttributeListActions.toggleAll, + toolbar: ( + + ) + }} + variantAttributeList={{ + isChecked: variantAttributeListActions.isSelected, + selected: + variantAttributeListActions.listElements.length, + toggle: variantAttributeListActions.toggle, + toggleAll: variantAttributeListActions.toggleAll, + toolbar: ( + + ) + }} /> {!dataLoading && ( - <> - {Object.keys(AttributeTypeEnum).map(key => ( - - handleAttributeCreate( - data, - AttributeTypeEnum[key] - ) - } - opened={ - params.action === "add-attribute" && - params.type === AttributeTypeEnum[key] - } - title={i18n.t("Add Attribute", { - context: "modal title" - })} - key={key} - /> - ))} - attribute.name)} - values={maybe(() => - attribute.values.map(value => ({ - label: value.name, - value: value.id - })) - )} - onClose={closeModal} - onConfirm={data => - handleAttributeUpdate(params.id, data) - } - opened={params.action === "edit-attribute"} - title={i18n.t("Edit Attribute", { - context: "modal title" - })} - /> - navigate(productTypeUrl(id))} - onConfirm={handleProductTypeDelete} - title={i18n.t("Remove product type")} - variant="delete" - > - {{ name }}?", - { - name: maybe( - () => data.productType.name, - "..." - ) + + {({ search, result }) => { + const fetchMore = () => + result.loadMore( + (prev, next) => { + if ( + prev.productType.availableAttributes + .pageInfo.endCursor === + next.productType.availableAttributes + .pageInfo.endCursor + ) { + return prev; } - ) - }} - /> - - + return { + ...prev, + productType: { + ...prev.productType, + availableAttributes: { + ...prev.productType.availableAttributes, + edges: [ + ...prev.productType + .availableAttributes.edges, + ...next.productType + .availableAttributes.edges + ], + pageInfo: + next.productType.availableAttributes + .pageInfo + } + } + }; + }, + { + after: + result.data.productType.availableAttributes + .pageInfo.endCursor + } + ); + + return ( + <> + {Object.keys(AttributeTypeEnum).map(key => ( + + result.data.productType.availableAttributes.edges.map( + edge => edge.node + ) + )} + confirmButtonState={assignTransactionState} + errors={maybe( + () => + assignAttribute.opts.data.attributeAssign.errors.map( + err => err.message + ), + [] + )} + loading={result.loading} + onClose={closeModal} + onSubmit={handleAssignAttribute} + onFetch={search} + onFetchMore={fetchMore} + onOpen={result.refetch} + hasMore={maybe( + () => + result.data.productType + .availableAttributes.pageInfo + .hasNextPage, + false + )} + open={ + params.action === "assign-attribute" && + params.type === AttributeTypeEnum[key] + } + selected={maybe(() => params.ids, [])} + onToggle={attributeId => { + const ids = maybe(() => params.ids, []); + navigate( + productTypeUrl(id, { + ...params, + ids: ids.includes(attributeId) + ? params.ids.filter( + selectedId => + selectedId !== attributeId + ) + : [...ids, attributeId] + }) + ); + }} + key={key} + /> + ))} + + ); + }} + )} + data.productType.name, "...")} + open={params.action === "remove"} + onClose={() => navigate(productTypeUrl(id))} + onConfirm={handleProductTypeDelete} + /> + params.ids.length.toString(), + "..." + )} + confirmButtonState={unassignTransactionState} + onClose={closeModal} + onConfirm={handleBulkAttributeUnassign} + open={params.action === "unassign-attributes"} + productTypeName={maybe( + () => data.productType.name, + "..." + )} + /> + + [ + ...data.productType.productAttributes, + ...data.productType.variantAttributes + ].find(attribute => attribute.id === params.id) + .name, + "..." + )} + confirmButtonState={unassignTransactionState} + onClose={closeModal} + onConfirm={handleAttributeUnassign} + open={params.action === "unassign-attribute"} + productTypeName={maybe( + () => data.productType.name, + "..." + )} + /> ); }} diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 216e575a4..cd9724688 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -1,4 +1,4 @@ -import { RawDraftContentState } from "draft-js"; +import { ContentState, convertToRaw, RawDraftContentState } from "draft-js"; import React from "react"; import AppHeader from "@saleor/components/AppHeader"; @@ -7,62 +7,63 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; +import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SeoForm from "@saleor/components/SeoForm"; import VisibilityCard from "@saleor/components/VisibilityCard"; +import { SearchCategories_categories_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories"; +import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections"; +import useFormset from "@saleor/hooks/useFormset"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { + getChoices, + ProductAttributeValueChoices, + ProductType +} from "@saleor/products/utils/data"; +import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import i18n from "../../../i18n"; -import { maybe } from "../../../misc"; import { UserError } from "../../../types"; import { ProductCreateData_productTypes_edges_node_productAttributes } from "../../types/ProductCreateData"; +import { + createAttributeChangeHandler, + createAttributeMultiChangeHandler, + createProductTypeSelectHandler +} from "../../utils/handlers"; +import ProductAttributes, { + ProductAttributeInput, + ProductAttributeInputData +} from "../ProductAttributes"; import ProductDetailsForm from "../ProductDetailsForm"; import ProductOrganization from "../ProductOrganization"; import ProductPricing from "../ProductPricing"; import ProductStock from "../ProductStock"; -interface ChoiceType { - label: string; - value: string; -} -export interface FormData { - attributes: Array<{ - slug: string; - value: string; - }>; +interface FormData { basePrice: number; publicationDate: string; - category: ChoiceType; + category: string; + collections: string[]; chargeTaxes: boolean; - collections: ChoiceType[]; description: RawDraftContentState; isPublished: boolean; name: string; - productType: { - label: string; - value: { - hasVariants: boolean; - id: string; - name: string; - productAttributes: ProductCreateData_productTypes_edges_node_productAttributes[]; - }; - }; + productType: string; seoDescription: string; seoTitle: string; sku: string; stockQuantity: number; } +export interface ProductCreatePageSubmitData extends FormData { + attributes: ProductAttributeInput[]; +} interface ProductCreatePageProps { errors: UserError[]; - collections?: Array<{ - id: string; - name: string; - }>; + collections: SearchCollections_collections_edges_node[]; + categories: SearchCategories_categories_edges_node[]; currency: string; - categories?: Array<{ - id: string; - name: string; - }>; disabled: boolean; productTypes?: Array<{ id: string; @@ -76,7 +77,7 @@ interface ProductCreatePageProps { fetchCollections: (data: string) => void; onAttributesEdit: () => void; onBack?(); - onSubmit?(data: FormData); + onSubmit?(data: ProductCreatePageSubmitData); } export const ProductCreatePage: React.StatelessComponent< @@ -84,38 +85,34 @@ export const ProductCreatePage: React.StatelessComponent< > = ({ currency, disabled, - categories, - collections, + categories: categoryChoiceList, + collections: collectionChoiceList, errors: userErrors, fetchCategories, fetchCollections, header, - productTypes, + productTypes: productTypeChoiceList, saveButtonBarState, onBack, onSubmit }: ProductCreatePageProps) => { + // Form values + const { + change: changeAttributeData, + data: attributes, + set: setAttributeData + } = useFormset([]); + + const initialDescription = convertToRaw(ContentState.createFromText("")); const initialData: FormData = { - attributes: [], basePrice: 0, - category: { - label: "", - value: "" - }, + category: "", chargeTaxes: false, collections: [], description: {} as any, isPublished: false, name: "", - productType: { - label: "", - value: { - hasVariants: false, - id: "", - name: "", - productAttributes: [] as ProductCreateData_productTypes_edges_node_productAttributes[] - } - }, + productType: "", publicationDate: "", seoDescription: "", seoTitle: "", @@ -123,16 +120,84 @@ export const ProductCreatePage: React.StatelessComponent< stockQuantity: null }; + // Display values + const [selectedAttributes, setSelectedAttributes] = useStateFromProps< + ProductAttributeValueChoices[] + >([]); + + const [selectedCategory, setSelectedCategory] = useStateFromProps(""); + + const [selectedCollections, setSelectedCollections] = useStateFromProps< + MultiAutocompleteChoiceType[] + >([]); + + const [productType, setProductType] = React.useState({ + hasVariants: false, + id: "", + name: "", + productAttributes: [] as ProductCreateData_productTypes_edges_node_productAttributes[] + }); + + const categories = getChoices(categoryChoiceList); + const collections = getChoices(collectionChoiceList); + const productTypes = getChoices(productTypeChoiceList); + + const handleSubmit = (data: FormData) => + onSubmit({ + attributes, + ...data + }); + return ( - {({ change, data, errors, hasChanged, submit }) => { - const hasVariants = - data.productType && data.productType.value.hasVariants; + {({ + change, + data, + errors, + hasChanged, + submit, + triggerChange, + toggleValue + }) => { + const handleCollectionSelect = createMultiAutocompleteSelectHandler( + toggleValue, + setSelectedCollections, + selectedCollections, + collections + ); + const handleCategorySelect = createSingleAutocompleteSelectHandler( + change, + setSelectedCategory, + categories + ); + const handleAttributeChange = createAttributeChangeHandler( + changeAttributeData, + setSelectedAttributes, + selectedAttributes, + attributes, + triggerChange + ); + const handleAttributeMultiChange = createAttributeMultiChangeHandler( + changeAttributeData, + setSelectedAttributes, + selectedAttributes, + attributes, + triggerChange + ); + + const handleProductTypeSelect = createProductTypeSelectHandler( + change, + setAttributeData, + setSelectedAttributes, + setProductType, + productTypeChoiceList + ); + return ( {i18n.t("Products")} @@ -143,9 +208,17 @@ export const ProductCreatePage: React.StatelessComponent< data={data} disabled={disabled} errors={errors} + initialDescription={initialDescription} onChange={change} /> + + - {!hasVariants && ( + {!productType.hasVariants && ( <> - categories.map(category => ({ - label: category.name, - value: category.id - })), - [] - )} + categories={categories} + categoryInputDisplayValue={selectedCategory} + collections={collections} + data={data} + disabled={disabled} errors={errors} fetchCategories={fetchCategories} fetchCollections={fetchCollections} - collections={maybe( - () => - collections.map(collection => ({ - label: collection.name, - value: collection.id - })), - [] - )} + productType={productType} + productTypeInputDisplayValue={productType.name} productTypes={productTypes} - data={data} - disabled={disabled} - onChange={change} + onCategoryChange={handleCategorySelect} + onCollectionChange={handleCollectionSelect} + onProductTypeChange={handleProductTypeSelect} + collectionsInputDisplayValue={selectedCollections} /> createStyles({ @@ -28,10 +25,16 @@ const styles = (theme: Theme) => }); interface ProductDetailsFormProps extends WithStyles { - data: CreateFormData & UpdateFormData; + data: { + description: RawDraftContentState; + name: string; + }; disabled?: boolean; errors: { [key: string]: string }; - product?: ProductDetails_product; + // Draftail isn't controlled - it needs only initial input + // because it's autosaving on its own. + // Ref https://github.com/mirumee/saleor/issues/4470 + initialDescription: RawDraftContentState; onChange(event: any); } @@ -43,7 +46,7 @@ export const ProductDetailsForm = withStyles(styles, { data, disabled, errors, - product, + initialDescription, onChange }: ProductDetailsFormProps) => ( @@ -67,7 +70,7 @@ export const ProductDetailsForm = withStyles(styles, { disabled={disabled} error={!!errors.descriptionJson} helperText={errors.descriptionJson} - initial={maybe(() => JSON.parse(product.descriptionJson), null)} + initial={initialDescription} label={i18n.t("Description")} name="description" onChange={onChange} diff --git a/src/products/components/ProductImageNavigation/ProductImageNavigation.tsx b/src/products/components/ProductImageNavigation/ProductImageNavigation.tsx index ad9d8937c..452e3bc7e 100644 --- a/src/products/components/ProductImageNavigation/ProductImageNavigation.tsx +++ b/src/products/components/ProductImageNavigation/ProductImageNavigation.tsx @@ -6,7 +6,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import CardTitle from "@saleor/components/CardTitle"; diff --git a/src/products/components/ProductImages/ProductImages.tsx b/src/products/components/ProductImages/ProductImages.tsx index a4727eca5..0f259f6a4 100644 --- a/src/products/components/ProductImages/ProductImages.tsx +++ b/src/products/components/ProductImages/ProductImages.tsx @@ -12,6 +12,7 @@ import { import CardTitle from "@saleor/components/CardTitle"; import ImageTile from "@saleor/components/ImageTile"; import ImageUpload from "@saleor/components/ImageUpload"; +import { ReorderAction } from "@saleor/types"; import React from "react"; import { SortableContainer, SortableElement } from "react-sortable-hoc"; import i18n from "../../../i18n"; @@ -113,17 +114,21 @@ interface ProductImagesProps extends WithStyles { loading?: boolean; onImageDelete: (id: string) => () => void; onImageEdit: (id: string) => () => void; + onImageReorder?: ReorderAction; onImageUpload(file: File); - onImageReorder?(event: { oldIndex: number; newIndex: number }); } -interface ImageListContainerProps extends WithStyles { - items: any; - onImageDelete: (id: string) => () => void; - onImageEdit: (id: string) => () => void; +interface SortableImageProps { + image: { + id: string; + alt?: string; + url: string; + }; + onImageEdit: (id: string) => void; + onImageDelete: () => void; } -const SortableImage = SortableElement( +const SortableImage = SortableElement( ({ image, onImageEdit, onImageDelete }) => ( () => void; + onImageEdit: (id: string) => () => void; +} + +const ImageListContainer = SortableContainer( withStyles(styles, { name: "ImageListContainer" })( ({ classes, @@ -141,7 +153,7 @@ const ImageListContainer = SortableContainer( onImageDelete, onImageEdit, ...props - }: ImageListContainerProps) => { + }: ImageListContainerProps & WithStyles) => { return (
{items.map((image, index) => ( diff --git a/src/products/components/ProductOrganization/ProductOrganization.tsx b/src/products/components/ProductOrganization/ProductOrganization.tsx index 2c757074f..31e010bba 100644 --- a/src/products/components/ProductOrganization/ProductOrganization.tsx +++ b/src/products/components/ProductOrganization/ProductOrganization.tsx @@ -11,24 +11,23 @@ import React from "react"; import CardSpacer from "@saleor/components/CardSpacer"; import CardTitle from "@saleor/components/CardTitle"; -import Chip from "@saleor/components/Chip"; import { FormSpacer } from "@saleor/components/FormSpacer"; -import MultiAutocompleteSelectField from "@saleor/components/MultiAutocompleteSelectField"; -import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField"; -import Skeleton from "@saleor/components/Skeleton"; -import i18n from "../../../i18n"; -import { maybe } from "../../../misc"; -import { ProductCreateData_productTypes_edges_node_productAttributes } from "../../types/ProductCreateData"; +import Hr from "@saleor/components/Hr"; +import MultiAutocompleteSelectField, { + MultiAutocompleteChoiceType +} from "@saleor/components/MultiAutocompleteSelectField"; +import SingleAutocompleteSelectField, { + SingleAutocompleteChoiceType +} from "@saleor/components/SingleAutocompleteSelectField"; +import { ChangeEvent } from "@saleor/hooks/useForm"; +import i18n from "@saleor/i18n"; +import { maybe } from "@saleor/misc"; +import { FormErrors } from "@saleor/types"; -interface ChoiceType { - label: string; - value: string; -} interface ProductType { hasVariants: boolean; id: string; name: string; - productAttributes: ProductCreateData_productTypes_edges_node_productAttributes[]; } const styles = (theme: Theme) => @@ -40,12 +39,6 @@ const styles = (theme: Theme) => fontSize: "1rem", marginBottom: theme.spacing.unit / 2 }, - hr: { - backgroundColor: theme.overrides.MuiCard.root.borderColor, - border: "none", - height: 1, - margin: `0 -${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px` - }, label: { marginBottom: theme.spacing.unit / 2 } @@ -53,267 +46,116 @@ const styles = (theme: Theme) => interface ProductOrganizationProps extends WithStyles { canChangeType: boolean; - categories?: Array<{ value: string; label: string }>; - collections?: Array<{ value: string; label: string }>; + categories?: SingleAutocompleteChoiceType[]; + categoryInputDisplayValue: string; + collections?: MultiAutocompleteChoiceType[]; + collectionsInputDisplayValue: MultiAutocompleteChoiceType[]; data: { - attributes: Array<{ - slug: string; - value: string; - }>; - category: ChoiceType; - collections: ChoiceType[]; - productType: { - label: string; - value: { - hasVariants: boolean; - id: string; - name: string; - productAttributes: ProductCreateData_productTypes_edges_node_productAttributes[]; - }; - }; + category: string; + collections: string[]; + productType?: string; }; disabled: boolean; - errors: { [key: string]: string }; - product?: { - productType?: { - hasVariants?: boolean; - name?: string; - }; - }; - productTypes?: ProductType[]; + errors: FormErrors<"productType" | "category">; + productType?: ProductType; + productTypeInputDisplayValue?: string; + productTypes?: SingleAutocompleteChoiceType[]; fetchCategories: (query: string) => void; fetchCollections: (query: string) => void; - onChange: (event: React.ChangeEvent, cb?: () => void) => void; + onCategoryChange: (event: ChangeEvent) => void; + onCollectionChange: (event: ChangeEvent) => void; + onProductTypeChange?: (event: ChangeEvent) => void; } const ProductOrganization = withStyles(styles, { name: "ProductOrganization" })( ({ canChangeType, categories, + categoryInputDisplayValue, classes, collections, + collectionsInputDisplayValue, data, disabled, errors, fetchCategories, fetchCollections, - product, + productType, + productTypeInputDisplayValue, productTypes, - onChange - }: ProductOrganizationProps) => { - const unrolledAttributes = maybe( - () => data.productType.value.productAttributes, - [] - ); - const getAttributeName = (slug: string) => { - const match = unrolledAttributes.find(a => a.slug === slug); - if (!match) { - return ""; - } - return match.name; - }; - const getAttributeValue = (slug: string) => { - if (unrolledAttributes.length > 0) { - const value = data.attributes.find(a => a.slug === slug); - const attributeMatch = unrolledAttributes.find(a => a.slug === slug); - if (!attributeMatch) { - return { - label: "", - value: "" - }; - } - const attributeValueMatch = attributeMatch.values.find( - v => v.slug === value.value - ); - const label = !!attributeValueMatch - ? attributeValueMatch.name - : value.value; - return { - label, - value - }; - } - return { - label: "", - value: "" - }; - }; - const getAttributeValues = (slug: string) => { - const match = unrolledAttributes.find(a => a.slug === slug); - if (match) { - return match.values; - } - - return []; - }; - const handleProductTypeSelect = ( - event: React.ChangeEvent<{ - name: string; - value: { - label: string; - value: ProductType; - }; - }> - ) => { - onChange(event, () => - onChange({ - ...event, - target: { - ...event.target, - name: "attributes", - value: event.target.value.value.productAttributes.map( - attribute => ({ - slug: attribute.slug, - value: "" - }) - ) - } - }) - ); - }; - const handleAttributeValueSelect = ( - event: React.ChangeEvent<{ - name: string; - value: { - label: string; - value: string; - }; - }> - ) => { - onChange({ - ...event, - target: { - ...event.target, - name: "attributes", - value: data.attributes.map(a => - a.slug === event.target.name - ? { slug: a.slug, value: event.target.value.value } - : a - ) - } - }); - }; - return ( - - - - {canChangeType ? ( - ({ label: pt.name, value: pt })) - : [] - } - value={data.productType} - onChange={handleProductTypeSelect} - /> - ) : ( - <> - - {i18n.t("Product Type")} - - - {maybe(() => product.productType.name, "...")} - - - - {i18n.t("Product Type")} - - - {maybe( - () => - product.productType.hasVariants - ? i18n.t("Configurable") - : i18n.t("Simple"), - "..." - )} - - - )} - {!(data && data.attributes && data.attributes.length === 0) ? ( - <> - - - {i18n.t("Attributes")} - -
- - ) : ( - - )} - {data.attributes ? ( - data.attributes.map((item, index) => { - return ( - - ({ - label: v.name, - value: v.slug - }))} - custom - /> - - - ); - }) - ) : ( - - )} -
+ onCategoryChange, + onCollectionChange, + onProductTypeChange + }: ProductOrganizationProps) => ( + + + + {canChangeType ? ( - -
- - {({ deleteItem, items }) => ( - <> - -
- {items.map(item => ( - deleteItem(item)} - /> - ))} -
- - )} -
-
-
- ); - } + ) : ( + <> + + {i18n.t("Product Type")} + + {maybe(() => productType.name, "...")} + + + {i18n.t("Product Type")} + + + {maybe( + () => + productType.hasVariants + ? i18n.t("Configurable") + : i18n.t("Simple"), + "..." + )} + + + )} + +
+ + + +
+ + +
+
+ ) ); ProductOrganization.displayName = "ProductOrganization"; export default ProductOrganization; diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 93db6711c..169aa649e 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -11,15 +11,33 @@ import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SeoForm from "@saleor/components/SeoForm"; import VisibilityCard from "@saleor/components/VisibilityCard"; -import i18n from "../../../i18n"; -import { maybe } from "../../../misc"; -import { ListActions, UserError } from "../../../types"; +import { SearchCategories_categories_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories"; +import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections"; +import useFormset from "@saleor/hooks/useFormset"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import i18n from "@saleor/i18n"; +import { maybe } from "@saleor/misc"; +import { ListActions, UserError } from "@saleor/types"; +import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import { ProductDetails_product, - ProductDetails_product_attributes_attribute, ProductDetails_product_images, ProductDetails_product_variants } from "../../types/ProductDetails"; +import { + getAttributeInputFromProduct, + getChoices, + getProductUpdatePageFormData, + getSelectedAttributesFromProduct, + ProductAttributeValueChoices, + ProductUpdatePageFormData +} from "../../utils/data"; +import { + createAttributeChangeHandler, + createAttributeMultiChangeHandler +} from "../../utils/handlers"; +import ProductAttributes, { ProductAttributeInput } from "../ProductAttributes"; import ProductDetailsForm from "../ProductDetailsForm"; import ProductImages from "../ProductImages"; import ProductOrganization from "../ProductOrganization"; @@ -27,25 +45,15 @@ import ProductPricing from "../ProductPricing"; import ProductStock from "../ProductStock"; import ProductVariants from "../ProductVariants"; -interface ProductUpdateProps extends ListActions { +export interface ProductUpdatePageProps extends ListActions { errors: UserError[]; placeholderImage: string; - collections?: Array<{ - id: string; - name: string; - }>; - categories?: Array<{ - id: string; - name: string; - }>; - disabled?: boolean; - productCollections?: Array<{ - id: string; - name: string; - }>; + collections: SearchCollections_collections_edges_node[]; + categories: SearchCategories_categories_edges_node[]; + disabled: boolean; variants: ProductDetails_product_variants[]; - images?: ProductDetails_product_images[]; - product?: ProductDetails_product; + images: ProductDetails_product_images[]; + product: ProductDetails_product; header: string; saveButtonBarState: ConfirmButtonTransitionState; fetchCategories: (query: string) => void; @@ -60,45 +68,16 @@ interface ProductUpdateProps extends ListActions { onImageUpload(file: File); onProductShow?(); onSeoClick?(); - onSubmit?(data: any); + onSubmit?(data: ProductUpdatePageSubmitData); onVariantAdd?(); } -interface ChoiceType { - label: string; - value: string; -} -export interface FormData { - attributes: Array<{ - slug: string; - value: string; - }>; - basePrice: number; - category: ChoiceType | null; - chargeTaxes: boolean; - collections: ChoiceType[]; - description: RawDraftContentState; - isPublished: boolean; - name: string; - productType: { - label: string; - value: { - hasVariants: boolean; - id: string; - name: string; - productAttributes: Array< - Exclude - >; - }; - } | null; - publicationDate: string; - seoDescription: string; - seoTitle: string; - sku: string; - stockQuantity: number; +export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData { + attributes: ProductAttributeInput[]; + collections: string[]; } -export const ProductUpdate: React.StatelessComponent = ({ +export const ProductUpdatePage: React.FC = ({ disabled, categories: categoryChoiceList, collections: collectionChoiceList, @@ -109,7 +88,6 @@ export const ProductUpdate: React.StatelessComponent = ({ header, placeholderImage, product, - productCollections, saveButtonBarState, variants, onAttributesEdit, @@ -129,186 +107,198 @@ export const ProductUpdate: React.StatelessComponent = ({ toggleAll, toolbar }) => { - const initialData: FormData = { - attributes: maybe( - () => - product.attributes.map(a => ({ - slug: a.attribute.slug, - value: a.value ? a.value.slug : null - })), - [] - ), - basePrice: maybe(() => product.basePrice.amount), - category: maybe(() => ({ - label: product.category.name, - value: product.category.id - })), - chargeTaxes: maybe(() => product.chargeTaxes, false), - collections: productCollections - ? productCollections.map(collection => ({ - label: collection.name, - value: collection.id - })) - : [], - description: maybe(() => JSON.parse(product.descriptionJson)), - isPublished: maybe(() => product.isPublished, false), - name: maybe(() => product.name), - productType: maybe(() => ({ - label: product.productType.name, - value: { - hasVariants: product.productType.hasVariants, - id: product.productType.id, - name: product.productType.name, - productAttributes: product.attributes.map(a => a.attribute) - } - })), - publicationDate: maybe(() => product.publicationDate), - seoDescription: maybe(() => product.seoDescription) || "", - seoTitle: maybe(() => product.seoTitle) || "", - sku: maybe(() => - product.productType.hasVariants - ? undefined - : variants && variants[0] - ? variants[0].sku - : undefined - ), - stockQuantity: maybe(() => - product.productType.hasVariants - ? undefined - : variants && variants[0] - ? variants[0].quantity - : undefined - ) - }; - const categories = - categoryChoiceList !== undefined - ? categoryChoiceList.map(category => ({ - label: category.name, - value: category.id - })) - : []; - const collections = - collectionChoiceList !== undefined - ? collectionChoiceList.map(collection => ({ - label: collection.name, - value: collection.id - })) - : []; - const currency = - product && product.basePrice ? product.basePrice.currency : undefined; - const hasVariants = - product && product.productType && product.productType.hasVariants; + const attributeInput = React.useMemo( + () => getAttributeInputFromProduct(product), + [product] + ); + const { change: changeAttributeData, data: attributes } = useFormset( + attributeInput + ); + + const [selectedAttributes, setSelectedAttributes] = useStateFromProps< + ProductAttributeValueChoices[] + >(getSelectedAttributesFromProduct(product)); + + const [selectedCategory, setSelectedCategory] = useStateFromProps( + maybe(() => product.category.name, "") + ); + + const [selectedCollections, setSelectedCollections] = useStateFromProps( + getChoices(maybe(() => product.collections, [])) + ); + + const initialData = getProductUpdatePageFormData(product, variants); + const initialDescription = maybe(() => + JSON.parse(product.descriptionJson) + ); + + const categories = getChoices(categoryChoiceList); + const collections = getChoices(collectionChoiceList); + const currency = maybe(() => product.basePrice.currency); + const hasVariants = maybe(() => product.productType.hasVariants, false); + + const handleSubmit = (data: ProductUpdatePageFormData) => + onSubmit({ + attributes, + ...data + }); return ( - {({ change, data, errors, hasChanged, submit }) => ( - <> - - {i18n.t("Products")} - - -
- - - - - - - {hasVariants ? ( - - ) : ( - { + const handleCollectionSelect = createMultiAutocompleteSelectHandler( + toggleValue, + setSelectedCollections, + selectedCollections, + collections + ); + const handleCategorySelect = createSingleAutocompleteSelectHandler( + change, + setSelectedCategory, + categories + ); + const handleAttributeChange = createAttributeChangeHandler( + changeAttributeData, + setSelectedAttributes, + selectedAttributes, + attributes, + triggerChange + ); + const handleAttributeMultiChange = createAttributeMultiChangeHandler( + changeAttributeData, + setSelectedAttributes, + selectedAttributes, + attributes, + triggerChange + ); + + return ( + <> + + {i18n.t("Products")} + + +
+ - )} - - - convertFromRaw(data.description) - .getPlainText() - .slice(0, 300) + + + + + + + + {hasVariants ? ( + + ) : ( + )} - loading={disabled} - onClick={onSeoClick} - onChange={change} - /> -
-
- - - -
-
- -
- - )} + + + convertFromRaw(data.description) + .getPlainText() + .slice(0, 300) + )} + loading={disabled} + onClick={onSeoClick} + onChange={change} + /> +
+
+ product.productType)} + onCategoryChange={handleCategorySelect} + onCollectionChange={handleCollectionSelect} + /> + + +
+
+ +
+ + ); + }} ); }; -ProductUpdate.displayName = "ProductUpdate"; -export default ProductUpdate; +ProductUpdatePage.displayName = "ProductUpdatePage"; +export default ProductUpdatePage; diff --git a/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx b/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx index 1d45faf12..3693e0224 100644 --- a/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx +++ b/src/products/components/ProductVariantAttributes/ProductVariantAttributes.tsx @@ -1,139 +1,105 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; -import { - createStyles, - Theme, - withStyles, - WithStyles -} from "@material-ui/core/styles"; import React from "react"; import CardTitle from "@saleor/components/CardTitle"; -import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField"; +import Grid from "@saleor/components/Grid"; +import SingleAutocompleteSelectField, { + SingleAutocompleteChoiceType +} from "@saleor/components/SingleAutocompleteSelectField"; import Skeleton from "@saleor/components/Skeleton"; +import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset"; import i18n from "../../../i18n"; -import { ProductVariant_attributes_attribute } from "../../types/ProductVariant"; +import { ProductVariant_attributes_attribute_values } from "../../types/ProductVariant"; -const styles = (theme: Theme) => - createStyles({ - card: { - overflow: "visible" - }, - grid: { - display: "grid", - gridColumnGap: `${theme.spacing.unit * 2}px`, - gridRowGap: `${theme.spacing.unit * 3}px`, - gridTemplateColumns: "1fr 1fr" - } - }); +export interface VariantAttributeInputData { + values: ProductVariant_attributes_attribute_values[]; +} +export type VariantAttributeInput = FormsetAtomicData< + VariantAttributeInputData, + string +>; -interface ProductVariantAttributesProps extends WithStyles { - attributes?: ProductVariant_attributes_attribute[]; - data: { - attributes?: Array<{ - slug: string; - value: string; - }>; - }; +interface ProductVariantAttributesProps { + attributes: VariantAttributeInput[]; disabled: boolean; - onChange: ( - event: React.ChangeEvent<{ - name: string; - value: Array<{ - slug: string; - value: string; - }>; - }> - ) => void; + errors: Record; + onChange: FormsetChange; } -const ProductVariantAttributes = withStyles(styles, { - name: "ProductVariantAttributes" -})( - ({ - attributes, - classes, - data, - disabled, - onChange - }: ProductVariantAttributesProps) => { - return ( - - - - {attributes === undefined ? ( - - ) : ( - attributes.map((item, index) => { - const getAttributeValue = (slug: string) => { - const valueMatches = attributes.filter(a => a.slug === slug); - if (valueMatches.length > 0) { - const value = data.attributes.filter(a => a.slug === slug)[0] - .value; - const labelMatches = valueMatches[0].values.filter( - v => v.slug === value - ); - const label = - labelMatches.length > 0 ? labelMatches[0].name : value; - return { - label, - value - }; - } - return { - label: "", - value: "" - }; - }; - const getAttributeValues = (slug: string) => { - const matches = attributes.filter(a => a.slug === slug); - return matches.length > 0 - ? matches[0].values.map(v => ({ - label: v.name, - value: v.slug - })) - : []; - }; - const handleAttributeValueSelect = ( - event: React.ChangeEvent<{ - name: string; - value: { - label: string; - value: string; - }; - }> - ) => - onChange({ - ...(event as any), - target: { - ...event.target, - name: "attributes", - value: data.attributes.map(a => - a.slug === event.target.name - ? { slug: a.slug, value: event.target.value.value } - : a - ) - } - }); - - return ( - - ); - }) - )} - - - ); +function getAttributeDisplayValue( + id: string, + slug: string, + attributes: VariantAttributeInput[] +): string { + const attribute = attributes.find(attr => attr.id === id); + const attributeValue = attribute.data.values.find( + value => value.slug === slug + ); + if (!!attributeValue) { + return attributeValue.name; } + + return slug; +} + +function getAttributeValue( + id: string, + attributes: VariantAttributeInput[] +): string { + const attribute = attributes.find(attr => attr.id === id); + return attribute.value; +} + +function getAttributeValueChoices( + id: string, + attributes: VariantAttributeInput[] +): SingleAutocompleteChoiceType[] { + const attribute = attributes.find(attr => attr.id === id); + return attribute.data.values.map(attributeValue => ({ + label: attributeValue.name, + value: attributeValue.slug + })); +} + +const ProductVariantAttributes: React.FC = ({ + attributes, + disabled, + errors, + onChange +}) => ( + + + + + {attributes === undefined ? ( + + ) : ( + attributes.map((attribute, attributeIndex) => { + return ( + onChange(attribute.id, event.target.value)} + value={getAttributeValue(attribute.id, attributes)} + choices={getAttributeValueChoices(attribute.id, attributes)} + allowCustomValues + /> + ); + }) + )} + + + ); ProductVariantAttributes.displayName = "ProductVariantAttributes"; export default ProductVariantAttributes; diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 5ea1ef7fb..1c70303b2 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -8,25 +8,36 @@ import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import useFormset, { + FormsetChange, + FormsetData +} from "@saleor/hooks/useFormset"; +import { + getVariantAttributeErrors, + getVariantAttributeInputFromProduct +} from "@saleor/products/utils/data"; import i18n from "../../../i18n"; import { maybe } from "../../../misc"; import { UserError } from "../../../types"; import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData"; -import ProductVariantAttributes from "../ProductVariantAttributes"; +import ProductVariantAttributes, { + VariantAttributeInputData +} from "../ProductVariantAttributes"; import ProductVariantNavigation from "../ProductVariantNavigation"; import ProductVariantPrice from "../ProductVariantPrice"; import ProductVariantStock from "../ProductVariantStock"; -interface FormData { - attributes?: Array<{ - slug: string; - value: string; - }>; - costPrice?: string; - images?: string[]; - priceOverride?: string; - quantity?: number; - sku?: string; +interface ProductVariantCreatePageFormData { + costPrice: string; + images: string[]; + priceOverride: string; + quantity: number; + sku: string; +} + +export interface ProductVariantCreatePageSubmitData + extends ProductVariantCreatePageFormData { + attributes: FormsetData; } interface ProductVariantCreatePageProps { @@ -37,13 +48,11 @@ interface ProductVariantCreatePageProps { product: ProductVariantCreateData_product; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; - onSubmit: (data: FormData) => void; + onSubmit: (data: ProductVariantCreatePageSubmitData) => void; onVariantClick: (variantId: string) => void; } -const ProductVariantCreatePage: React.StatelessComponent< - ProductVariantCreatePageProps -> = ({ +const ProductVariantCreatePage: React.FC = ({ currencySymbol, errors: formErrors, loading, @@ -54,12 +63,23 @@ const ProductVariantCreatePage: React.StatelessComponent< onSubmit, onVariantClick }) => { + const attributeInput = React.useMemo( + () => getVariantAttributeInputFromProduct(product), + [product] + ); + const { change: changeAttributeData, data: attributes } = useFormset( + attributeInput + ); + const initialForm = { - attributes: maybe(() => - product.productType.variantAttributes.map(attribute => ({ - slug: attribute.slug, - value: "" - })) + attributes: maybe( + () => + product.productType.variantAttributes.map(attribute => ({ + name: attribute.name, + slug: attribute.slug, + values: [""] + })), + [] ), costPrice: "", images: maybe(() => product.images.map(image => image.id)), @@ -67,62 +87,79 @@ const ProductVariantCreatePage: React.StatelessComponent< quantity: 0, sku: "" }; + + const handleSubmit = (data: ProductVariantCreatePageFormData) => + onSubmit({ + ...data, + attributes + }); + return ( -
- {({ change, data, errors, hasChanged, submit }) => ( - - {maybe(() => product.name)} - - -
- product.thumbnail.url)} - variants={maybe(() => product.variants)} - onRowClick={(variantId: string) => { - if (product && product.variants) { - return onVariantClick(variantId); - } - }} - /> -
-
- product.productType.variantAttributes)} - data={data} - disabled={loading} - onChange={change} - /> - - - - -
-
- -
- )} + + {({ change, data, errors, hasChanged, submit, triggerChange }) => { + const handleAttributeChange: FormsetChange = (id, value) => { + changeAttributeData(id, value); + triggerChange(); + }; + + return ( + + {maybe(() => product.name)} + + +
+ product.thumbnail.url)} + variants={maybe(() => product.variants)} + onRowClick={(variantId: string) => { + if (product && product.variants) { + return onVariantClick(variantId); + } + }} + /> +
+
+ product.productType.variantAttributes) + )} + onChange={handleAttributeChange} + /> + + + + +
+
+ +
+ ); + }} ); }; diff --git a/src/products/components/ProductVariantImageSelectDialog/ProductVariantImageSelectDialog.tsx b/src/products/components/ProductVariantImageSelectDialog/ProductVariantImageSelectDialog.tsx index 1708e288e..028a5eacd 100644 --- a/src/products/components/ProductVariantImageSelectDialog/ProductVariantImageSelectDialog.tsx +++ b/src/products/components/ProductVariantImageSelectDialog/ProductVariantImageSelectDialog.tsx @@ -9,7 +9,7 @@ import { withStyles, WithStyles } from "@material-ui/core/styles"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import i18n from "../../../i18n"; diff --git a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx index 45252d754..96f534a15 100644 --- a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx +++ b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx @@ -10,7 +10,7 @@ import Table from "@material-ui/core/Table"; import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import CardTitle from "@saleor/components/CardTitle"; @@ -68,35 +68,27 @@ const ProductVariantNavigation = withStyles(styles, {
- {renderCollection( - variants, - variant => ( - onRowClick(variant.id) : undefined} - > - variant.images[0].url, - fallbackThumbnail - )} - /> - - {variant ? variant.name || variant.sku : } - - - ), - () => ( - - {i18n.t("This product has no variants")} - - ) - )} + {renderCollection(variants, variant => ( + onRowClick(variant.id) : undefined} + > + variant.images[0].url, + fallbackThumbnail + )} + /> + + {variant ? variant.name || variant.sku : } + + + ))} {onAdd ? ( diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 1dd8be01e..56e3b6499 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -8,16 +8,38 @@ import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import useFormset, { + FormsetChange, + FormsetData +} from "@saleor/hooks/useFormset"; +import { + getAttributeInputFromVariant, + getVariantAttributeErrors +} from "@saleor/products/utils/data"; import { maybe } from "../../../misc"; import { UserError } from "../../../types"; import { ProductVariant } from "../../types/ProductVariant"; -import ProductVariantAttributes from "../ProductVariantAttributes"; +import ProductVariantAttributes, { + VariantAttributeInputData +} from "../ProductVariantAttributes"; import ProductVariantImages from "../ProductVariantImages"; import ProductVariantImageSelectDialog from "../ProductVariantImageSelectDialog"; import ProductVariantNavigation from "../ProductVariantNavigation"; import ProductVariantPrice from "../ProductVariantPrice"; import ProductVariantStock from "../ProductVariantStock"; +export interface ProductVariantPageFormData { + costPrice: string; + priceOverride: string; + quantity: number; + sku: string; +} + +export interface ProductVariantPageSubmitData + extends ProductVariantPageFormData { + attributes: FormsetData; +} + interface ProductVariantPageProps { variant?: ProductVariant; errors: UserError[]; @@ -28,12 +50,12 @@ interface ProductVariantPageProps { onAdd(); onBack(); onDelete(); - onSubmit(data: any); + onSubmit(data: ProductVariantPageSubmitData); onImageSelect(id: string); onVariantClick(variantId: string); } -const ProductVariantPage: React.StatelessComponent = ({ +const ProductVariantPage: React.FC = ({ errors: formErrors, loading, header, @@ -47,20 +69,42 @@ const ProductVariantPage: React.StatelessComponent = ({ onSubmit, onVariantClick }) => { + const attributeInput = React.useMemo( + () => getAttributeInputFromVariant(variant), + [variant] + ); + const { change: changeAttributeData, data: attributes } = useFormset( + attributeInput + ); + const [isModalOpened, setModalStatus] = React.useState(false); const toggleModal = () => setModalStatus(!isModalOpened); - const variantImages = variant ? variant.images.map(image => image.id) : []; - const productImages = variant - ? variant.product.images.sort((prev, next) => - prev.sortOrder > next.sortOrder ? 1 : -1 - ) - : undefined; - const images = productImages - ? productImages - .filter(image => variantImages.indexOf(image.id) !== -1) - .sort((prev, next) => (prev.sortOrder > next.sortOrder ? 1 : -1)) - : undefined; + const variantImages = maybe(() => variant.images.map(image => image.id), []); + const productImages = maybe(() => + variant.product.images.sort((prev, next) => + prev.sortOrder > next.sortOrder ? 1 : -1 + ) + ); + const images = maybe(() => + productImages + .filter(image => variantImages.indexOf(image.id) !== -1) + .sort((prev, next) => (prev.sortOrder > next.sortOrder ? 1 : -1)) + ); + + const initialForm: ProductVariantPageFormData = { + costPrice: maybe(() => variant.costPrice.amount.toString(), ""), + priceOverride: maybe(() => variant.priceOverride.amount.toString(), ""), + quantity: maybe(() => variant.quantity, 0), + sku: maybe(() => variant.sku, "") + }; + + const handleSubmit = (data: ProductVariantPageFormData) => + onSubmit({ + ...data, + attributes + }); + return ( <> @@ -69,102 +113,94 @@ const ProductVariantPage: React.StatelessComponent = ({
({ - slug: a.attribute.slug, - value: a.value.slug - })) - : [], - costPrice: - variant && variant.costPrice - ? variant.costPrice.amount.toString() - : null, - priceOverride: - variant && variant.priceOverride - ? variant.priceOverride.amount.toString() - : null, - quantity: variant && variant.quantity ? variant.quantity : "", - sku: variant && variant.sku - }} + initial={initialForm} errors={formErrors} - onSubmit={onSubmit} + onSubmit={handleSubmit} confirmLeave > - {({ change, data, errors, hasChanged, submit }) => ( - <> - -
- variant.product.thumbnail.url - )} - variants={maybe(() => variant.product.variants)} - onAdd={onAdd} - onRowClick={(variantId: string) => { - if (variant) { - return onVariantClick(variantId); + {({ change, data, errors, hasChanged, submit, triggerChange }) => { + const handleAttributeChange: FormsetChange = (id, value) => { + changeAttributeData(id, value); + triggerChange(); + }; + + return ( + <> + +
+ variant.product.thumbnail.url + )} + variants={maybe(() => variant.product.variants)} + onAdd={onAdd} + onRowClick={(variantId: string) => { + if (variant) { + return onVariantClick(variantId); + } + }} + /> +
+
+ + variant.attributes.map( + attribute => attribute.attribute + ) + ) + )} + onChange={handleAttributeChange} + /> + + + + -
-
- a.attribute) - : undefined - } - data={data} - disabled={loading} - onChange={change} - /> - - - - - - -
-
- - - )} + costPrice={data.costPrice} + loading={loading} + onChange={change} + /> + + +
+
+ + + ); + }}
{variant && ( diff --git a/src/products/components/ProductVariants/ProductVariants.tsx b/src/products/components/ProductVariants/ProductVariants.tsx index 5e7acbe77..6784d352d 100644 --- a/src/products/components/ProductVariants/ProductVariants.tsx +++ b/src/products/components/ProductVariants/ProductVariants.tsx @@ -72,6 +72,8 @@ interface ProductVariantsProps extends ListActions, WithStyles { onVariantAdd?(); } +const numberOfColumns = 5; + export const ProductVariants = withStyles(styles, { name: "ProductVariants" })( ({ classes, @@ -110,6 +112,7 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
toggle(variant.id)} /> @@ -188,7 +192,7 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })( }, () => ( - + {i18n.t("This product has no variants")} diff --git a/src/products/fixtures.ts b/src/products/fixtures.ts index 2fc48bc95..0ae25921b 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -1,3 +1,4 @@ +import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { content } from "../storybook/stories/components/RichTextEditor"; import { ProductDetails_product } from "./types/ProductDetails"; import { ProductVariant } from "./types/ProductVariant"; @@ -7,87 +8,85 @@ export const product: ( placeholderImage: string ) => ProductDetails_product & ProductVariantCreateData_product = placeholderImage => ({ - __typename: "Product", + __typename: "Product" as "Product", attributes: [ { __typename: "SelectedAttribute", attribute: { - __typename: "Attribute", + __typename: "Attribute" as "Attribute", id: "pta18161", + inputType: AttributeInputTypeEnum.DROPDOWN, name: "Borders", slug: "Borders", - sortNumber: 0, + valueRequired: true, values: [ { __typename: "AttributeValue", id: "ptav47282", name: "portals", - slug: "portals", - sortNumber: 0 + slug: "portals" }, { __typename: "AttributeValue", id: "ptav17253", name: "Baht", - slug: "Baht", - sortNumber: 1 + slug: "Baht" } ] }, - value: { - __typename: "AttributeValue", - id: "ptav47282", - name: "portals", - slug: "portals", - sortNumber: 0 - } + values: [ + { + __typename: "AttributeValue", + id: "ptav47282", + name: "portals", + slug: "portals" + } + ] }, { __typename: "SelectedAttribute", attribute: { - __typename: "Attribute", + __typename: "Attribute" as "Attribute", id: "pta22785", + inputType: AttributeInputTypeEnum.MULTISELECT, name: "Legacy", slug: "Legacy", - sortNumber: 1, + valueRequired: true, values: [ { __typename: "AttributeValue", id: "ptav31282", name: "payment", - slug: "payment", - sortNumber: 0 + slug: "payment" }, { __typename: "AttributeValue", id: "ptav14907", name: "Auto Loan Account", - slug: "Auto-Loan-Account", - sortNumber: 1 + slug: "Auto-Loan-Account" }, { __typename: "AttributeValue", id: "ptav27366", name: "Garden", - slug: "Garden", - sortNumber: 2 + slug: "Garden" }, { __typename: "AttributeValue", id: "ptav11873", name: "override", - slug: "override", - sortNumber: 3 + slug: "override" } ] }, - value: { - __typename: "AttributeValue", - id: "ptav14907", - name: "Auto Loan Account", - slug: "Auto-Loan-Account", - sortNumber: 1 - } + values: [ + { + __typename: "AttributeValue", + id: "ptav14907", + name: "Auto Loan Account", + slug: "Auto-Loan-Account" + } + ] } ], basePrice: { @@ -195,6 +194,7 @@ export const product: ( name: "Color", slug: "color", sortOrder: 0, + valueRequired: true, values: [ { __typename: "AttributeValue", @@ -233,7 +233,7 @@ export const product: ( seoDescription: "Seo description", seoTitle: "Seo title", sku: "59661-34207", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, url: "/example-url", variants: [ { @@ -290,9 +290,11 @@ export const product: ( }); export const products = (placeholderImage: string) => [ { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta37372", name: "expedite", slug: "expedite", @@ -323,6 +325,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta3183", name: "system", slug: "system", @@ -349,6 +352,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta47147", name: "interactive", slug: "interactive", @@ -371,6 +375,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta34499", name: "deposit", slug: "deposit", @@ -418,6 +423,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -445,7 +451,7 @@ export const products = (placeholderImage: string) => [ "Autem odit tempora nesciunt quaerat enim reprehenderit eius. Excepturi nemo quos veritatis laboriosam aperiam atque natus soluta", seoTitle: "Generic Rubber Pants", sku: "57599-97473", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -472,9 +478,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta1529", name: "Granite", slug: "Granite", @@ -504,6 +512,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta44120", name: "wireless", slug: "wireless", @@ -540,6 +549,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta83114", name: "6th generation", slug: "6th-generation", @@ -579,6 +589,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -601,7 +612,7 @@ export const products = (placeholderImage: string) => [ seoDescription: "enim est recusandae soluta aperiam", seoTitle: "Handcrafted Wooden Towels", sku: "6526-89350", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -658,9 +669,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta40940", name: "monitor", slug: "monitor", @@ -707,6 +720,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta33894", name: "Assurance", slug: "Assurance", @@ -743,6 +757,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta35528", name: "Function-based", slug: "Function-based", @@ -776,6 +791,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -798,7 +814,7 @@ export const products = (placeholderImage: string) => [ seoDescription: "quas minima error repudiandae corrupti", seoTitle: "Handcrafted Metal Cheese", sku: "9937-5954", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -845,9 +861,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta58079", name: "Cotton", slug: "Cotton", @@ -874,6 +892,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta51731", name: "Assistant", slug: "Assistant", @@ -886,6 +905,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta53013", name: "target", slug: "target", @@ -921,6 +941,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta98307", name: "Brand", slug: "Brand", @@ -943,6 +964,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta52191", name: "invoice", slug: "invoice", @@ -984,6 +1006,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -1007,7 +1030,7 @@ export const products = (placeholderImage: string) => [ "Veniam quasi temporibus ab similique. Praesentium vero repudiandae", seoTitle: "Awesome Granite Car", sku: "96777-48145", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -1084,9 +1107,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta96115", name: "Officer", slug: "Officer", @@ -1099,6 +1124,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta74440", name: "driver", slug: "driver", @@ -1119,6 +1145,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta95292", name: "SDD", slug: "SDD", @@ -1147,6 +1174,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta28064", name: "firewall", slug: "firewall", @@ -1165,6 +1193,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta90527", name: "Jamaica", slug: "Jamaica", @@ -1201,6 +1230,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -1224,7 +1254,7 @@ export const products = (placeholderImage: string) => [ "Minima quaerat error incidunt qui quis vitae maxime. Est ab et", seoTitle: "Gorgeous Metal Gloves", sku: "60799-40023", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -1311,9 +1341,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta77866", name: "well-modulated", slug: "well-modulated", @@ -1335,6 +1367,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta46984", name: "magenta", slug: "magenta", @@ -1357,6 +1390,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta58329", name: "District", slug: "District", @@ -1380,6 +1414,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta24725", name: "payment", slug: "payment", @@ -1415,6 +1450,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta20828", name: "functionalities", slug: "functionalities", @@ -1457,6 +1493,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -1480,7 +1517,7 @@ export const products = (placeholderImage: string) => [ "Esse quia voluptates veniam eveniet. Velit laborum possimus eveniet consequuntur magnam eveniet provident et voluptatem", seoTitle: "Unbranded Rubber Ball", sku: "2840-48373", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -1567,9 +1604,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta66889", name: "Compatible", slug: "Compatible", @@ -1610,6 +1649,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta85893", name: "program", slug: "program", @@ -1652,6 +1692,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -1675,7 +1716,7 @@ export const products = (placeholderImage: string) => [ "Sed tempore autem voluptas voluptas aut sit et. Molestiae rem quo cupiditate consectetur", seoTitle: "Fantastic Cotton Tuna", sku: "63913-27946", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -1722,9 +1763,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta3756", name: "directional", slug: "directional", @@ -1761,6 +1804,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -1784,7 +1828,7 @@ export const products = (placeholderImage: string) => [ "Voluptatem veniam aut rerum. Illum veniam asperiores ut et iusto enim vel sapiente quia", seoTitle: "Tasty Steel Pants", sku: "4110-47925", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -1861,9 +1905,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta93061", name: "Cotton", slug: "Cotton", @@ -1895,6 +1941,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta58822", name: "Shore", slug: "Shore", @@ -1909,6 +1956,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta33738", name: "Communications", slug: "Communications", @@ -1932,6 +1980,7 @@ export const products = (placeholderImage: string) => [ }, { attribute: { + __typename: "Attribute" as "Attribute", id: "pta46618", name: "Credit Card Account", slug: "Credit-Card-Account", @@ -1968,6 +2017,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -1991,7 +2041,7 @@ export const products = (placeholderImage: string) => [ "Sapiente qui error. Placeat hic nulla repudiandae delectus et", seoTitle: "Ergonomic Cotton Shoes", sku: "9207-4523", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -2058,9 +2108,11 @@ export const products = (placeholderImage: string) => [ } }, { + __typename: "Product" as "Product", attributes: [ { attribute: { + __typename: "Attribute" as "Attribute", id: "pta1842", name: "Small", slug: "Small", @@ -2100,6 +2152,7 @@ export const products = (placeholderImage: string) => [ edges: [ { node: { + __typename: "Image" as "Image", id: "UHJvZHVjdEltYWdlOjE=", image: placeholderImage, sortOrder: 0, @@ -2122,7 +2175,7 @@ export const products = (placeholderImage: string) => [ seoDescription: "adipisci laboriosam autem vel soluta", seoTitle: "Unbranded Steel Tuna", sku: "84653-71539", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: { edges: [ { @@ -2185,10 +2238,11 @@ export const variant = (placeholderImage: string): ProductVariant => ({ { __typename: "SelectedAttribute", attribute: { - __typename: "Attribute", + __typename: "Attribute" as "Attribute", id: "pta18161", name: "Borders", slug: "Borders", + valueRequired: true, values: [ { __typename: "AttributeValue", @@ -2214,10 +2268,11 @@ export const variant = (placeholderImage: string): ProductVariant => ({ { __typename: "SelectedAttribute", attribute: { - __typename: "Attribute", + __typename: "Attribute" as "Attribute", id: "pta22785", name: "Legacy", slug: "Legacy", + valueRequired: true, values: [ { __typename: "AttributeValue", @@ -2288,7 +2343,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ currency: "USD" }, product: { - __typename: "Product", + __typename: "Product" as "Product", id: "prod1", images: [ { @@ -2356,7 +2411,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ } ], name: "Our Awesome Book", - thumbnail: { __typename: "Image", url: placeholderImage }, + thumbnail: { __typename: "Image" as "Image", url: placeholderImage }, variants: [ { __typename: "ProductVariant", diff --git a/src/products/mutations.ts b/src/products/mutations.ts index b8845d331..ef7024d69 100644 --- a/src/products/mutations.ts +++ b/src/products/mutations.ts @@ -124,6 +124,7 @@ export const productUpdateMutation = gql` $isPublished: Boolean! $name: String $basePrice: Decimal + $seo: SeoInput ) { productUpdate( id: $id @@ -137,6 +138,7 @@ export const productUpdateMutation = gql` isPublished: $isPublished name: $name basePrice: $basePrice + seo: $seo } ) { errors { @@ -170,6 +172,7 @@ export const simpleProductUpdateMutation = gql` $basePrice: Decimal $productVariantId: ID! $productVariantInput: ProductVariantInput! + $seo: SeoInput ) { productUpdate( id: $id @@ -183,6 +186,7 @@ export const simpleProductUpdateMutation = gql` isPublished: $isPublished name: $name basePrice: $basePrice + seo: $seo } ) { errors { @@ -224,6 +228,7 @@ export const productCreateMutation = gql` $productType: ID! $sku: String $stockQuantity: Int + $seo: SeoInput ) { productCreate( input: { @@ -239,6 +244,7 @@ export const productCreateMutation = gql` productType: $productType sku: $sku quantity: $stockQuantity + seo: $seo } ) { errors { diff --git a/src/products/queries.ts b/src/products/queries.ts index 84c3c345d..6bf4516f7 100644 --- a/src/products/queries.ts +++ b/src/products/queries.ts @@ -95,12 +95,15 @@ export const productFragmentDetails = gql` id slug name + inputType + valueRequired values { + id name slug } } - value { + values { id name slug @@ -154,6 +157,7 @@ export const fragmentVariant = gql` id name slug + valueRequired values { id name @@ -273,11 +277,12 @@ const productCreateQuery = gql` hasVariants productAttributes { id + inputType slug name + valueRequired values { id - sortOrder name slug } @@ -307,9 +312,9 @@ const productVariantCreateQuery = gql` id slug name + valueRequired values { id - sortOrder name slug } diff --git a/src/products/types/Product.ts b/src/products/types/Product.ts index 2396b200b..35d5a5eba 100644 --- a/src/products/types/Product.ts +++ b/src/products/types/Product.ts @@ -2,6 +2,8 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + // ==================================================== // GraphQL fragment: Product // ==================================================== @@ -50,6 +52,7 @@ export interface Product_purchaseCost { export interface Product_attributes_attribute_values { __typename: "AttributeValue"; + id: string; name: string | null; slug: string | null; } @@ -59,10 +62,12 @@ export interface Product_attributes_attribute { id: string; slug: string | null; name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; values: (Product_attributes_attribute_values | null)[] | null; } -export interface Product_attributes_value { +export interface Product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; @@ -72,7 +77,7 @@ export interface Product_attributes_value { export interface Product_attributes { __typename: "SelectedAttribute"; attribute: Product_attributes_attribute; - value: Product_attributes_value; + values: (Product_attributes_values | null)[]; } export interface Product_pricing_priceRange_start_net { diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index 2ea047957..bb2706b5f 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput } from "./../../types/globalTypes"; +import { AttributeValueInput, SeoInput, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductCreate @@ -58,6 +58,7 @@ export interface ProductCreate_productCreate_product_purchaseCost { export interface ProductCreate_productCreate_product_attributes_attribute_values { __typename: "AttributeValue"; + id: string; name: string | null; slug: string | null; } @@ -67,10 +68,12 @@ export interface ProductCreate_productCreate_product_attributes_attribute { id: string; slug: string | null; name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; values: (ProductCreate_productCreate_product_attributes_attribute_values | null)[] | null; } -export interface ProductCreate_productCreate_product_attributes_value { +export interface ProductCreate_productCreate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; @@ -80,7 +83,7 @@ export interface ProductCreate_productCreate_product_attributes_value { export interface ProductCreate_productCreate_product_attributes { __typename: "SelectedAttribute"; attribute: ProductCreate_productCreate_product_attributes_attribute; - value: ProductCreate_productCreate_product_attributes_value; + values: (ProductCreate_productCreate_product_attributes_values | null)[]; } export interface ProductCreate_productCreate_product_pricing_priceRange_start_net { @@ -196,4 +199,5 @@ export interface ProductCreateVariables { productType: string; sku?: string | null; stockQuantity?: number | null; + seo?: SeoInput | null; } diff --git a/src/products/types/ProductCreateData.ts b/src/products/types/ProductCreateData.ts index a19865341..4e6513b86 100644 --- a/src/products/types/ProductCreateData.ts +++ b/src/products/types/ProductCreateData.ts @@ -2,6 +2,8 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + // ==================================================== // GraphQL query operation: ProductCreateData // ==================================================== @@ -9,7 +11,6 @@ export interface ProductCreateData_productTypes_edges_node_productAttributes_values { __typename: "AttributeValue"; id: string; - sortOrder: number | null; name: string | null; slug: string | null; } @@ -17,8 +18,10 @@ export interface ProductCreateData_productTypes_edges_node_productAttributes_val export interface ProductCreateData_productTypes_edges_node_productAttributes { __typename: "Attribute"; id: string; + inputType: AttributeInputTypeEnum | null; slug: string | null; name: string | null; + valueRequired: boolean; values: (ProductCreateData_productTypes_edges_node_productAttributes_values | null)[] | null; } diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index 4e0326d16..5094afd59 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -2,6 +2,8 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + // ==================================================== // GraphQL query operation: ProductDetails // ==================================================== @@ -50,6 +52,7 @@ export interface ProductDetails_product_purchaseCost { export interface ProductDetails_product_attributes_attribute_values { __typename: "AttributeValue"; + id: string; name: string | null; slug: string | null; } @@ -59,10 +62,12 @@ export interface ProductDetails_product_attributes_attribute { id: string; slug: string | null; name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; values: (ProductDetails_product_attributes_attribute_values | null)[] | null; } -export interface ProductDetails_product_attributes_value { +export interface ProductDetails_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; @@ -72,7 +77,7 @@ export interface ProductDetails_product_attributes_value { export interface ProductDetails_product_attributes { __typename: "SelectedAttribute"; attribute: ProductDetails_product_attributes_attribute; - value: ProductDetails_product_attributes_value; + values: (ProductDetails_product_attributes_values | null)[]; } export interface ProductDetails_product_pricing_priceRange_start_net { diff --git a/src/products/types/ProductImageCreate.ts b/src/products/types/ProductImageCreate.ts index 1e4baa729..db1c34871 100644 --- a/src/products/types/ProductImageCreate.ts +++ b/src/products/types/ProductImageCreate.ts @@ -2,6 +2,8 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: ProductImageCreate // ==================================================== @@ -56,6 +58,7 @@ export interface ProductImageCreate_productImageCreate_product_purchaseCost { export interface ProductImageCreate_productImageCreate_product_attributes_attribute_values { __typename: "AttributeValue"; + id: string; name: string | null; slug: string | null; } @@ -65,10 +68,12 @@ export interface ProductImageCreate_productImageCreate_product_attributes_attrib id: string; slug: string | null; name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; values: (ProductImageCreate_productImageCreate_product_attributes_attribute_values | null)[] | null; } -export interface ProductImageCreate_productImageCreate_product_attributes_value { +export interface ProductImageCreate_productImageCreate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; @@ -78,7 +83,7 @@ export interface ProductImageCreate_productImageCreate_product_attributes_value export interface ProductImageCreate_productImageCreate_product_attributes { __typename: "SelectedAttribute"; attribute: ProductImageCreate_productImageCreate_product_attributes_attribute; - value: ProductImageCreate_productImageCreate_product_attributes_value; + values: (ProductImageCreate_productImageCreate_product_attributes_values | null)[]; } export interface ProductImageCreate_productImageCreate_product_pricing_priceRange_start_net { diff --git a/src/products/types/ProductImageUpdate.ts b/src/products/types/ProductImageUpdate.ts index bd2a9d178..045296fa0 100644 --- a/src/products/types/ProductImageUpdate.ts +++ b/src/products/types/ProductImageUpdate.ts @@ -2,6 +2,8 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { AttributeInputTypeEnum } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: ProductImageUpdate // ==================================================== @@ -56,6 +58,7 @@ export interface ProductImageUpdate_productImageUpdate_product_purchaseCost { export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute_values { __typename: "AttributeValue"; + id: string; name: string | null; slug: string | null; } @@ -65,10 +68,12 @@ export interface ProductImageUpdate_productImageUpdate_product_attributes_attrib id: string; slug: string | null; name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; values: (ProductImageUpdate_productImageUpdate_product_attributes_attribute_values | null)[] | null; } -export interface ProductImageUpdate_productImageUpdate_product_attributes_value { +export interface ProductImageUpdate_productImageUpdate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; @@ -78,7 +83,7 @@ export interface ProductImageUpdate_productImageUpdate_product_attributes_value export interface ProductImageUpdate_productImageUpdate_product_attributes { __typename: "SelectedAttribute"; attribute: ProductImageUpdate_productImageUpdate_product_attributes_attribute; - value: ProductImageUpdate_productImageUpdate_product_attributes_value; + values: (ProductImageUpdate_productImageUpdate_product_attributes_values | null)[]; } export interface ProductImageUpdate_productImageUpdate_product_pricing_priceRange_start_net { diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index 0b8795836..97189239a 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput } from "./../../types/globalTypes"; +import { AttributeValueInput, SeoInput, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductUpdate @@ -58,6 +58,7 @@ export interface ProductUpdate_productUpdate_product_purchaseCost { export interface ProductUpdate_productUpdate_product_attributes_attribute_values { __typename: "AttributeValue"; + id: string; name: string | null; slug: string | null; } @@ -67,10 +68,12 @@ export interface ProductUpdate_productUpdate_product_attributes_attribute { id: string; slug: string | null; name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; values: (ProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; } -export interface ProductUpdate_productUpdate_product_attributes_value { +export interface ProductUpdate_productUpdate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; @@ -80,7 +83,7 @@ export interface ProductUpdate_productUpdate_product_attributes_value { export interface ProductUpdate_productUpdate_product_attributes { __typename: "SelectedAttribute"; attribute: ProductUpdate_productUpdate_product_attributes_attribute; - value: ProductUpdate_productUpdate_product_attributes_value; + values: (ProductUpdate_productUpdate_product_attributes_values | null)[]; } export interface ProductUpdate_productUpdate_product_pricing_priceRange_start_net { @@ -194,4 +197,5 @@ export interface ProductUpdateVariables { isPublished: boolean; name?: string | null; basePrice?: any | null; + seo?: SeoInput | null; } diff --git a/src/products/types/ProductVariant.ts b/src/products/types/ProductVariant.ts index 86239a599..bcc912d50 100644 --- a/src/products/types/ProductVariant.ts +++ b/src/products/types/ProductVariant.ts @@ -18,6 +18,7 @@ export interface ProductVariant_attributes_attribute { id: string; name: string | null; slug: string | null; + valueRequired: boolean; values: (ProductVariant_attributes_attribute_values | null)[] | null; } @@ -31,7 +32,7 @@ export interface ProductVariant_attributes_value { export interface ProductVariant_attributes { __typename: "SelectedAttribute"; attribute: ProductVariant_attributes_attribute; - value: ProductVariant_attributes_value; + value: ProductVariant_attributes_value | null; } export interface ProductVariant_costPrice { diff --git a/src/products/types/ProductVariantCreateData.ts b/src/products/types/ProductVariantCreateData.ts index 956873a6a..248ec0fe1 100644 --- a/src/products/types/ProductVariantCreateData.ts +++ b/src/products/types/ProductVariantCreateData.ts @@ -16,7 +16,6 @@ export interface ProductVariantCreateData_product_images { export interface ProductVariantCreateData_product_productType_variantAttributes_values { __typename: "AttributeValue"; id: string; - sortOrder: number | null; name: string | null; slug: string | null; } @@ -26,6 +25,7 @@ export interface ProductVariantCreateData_product_productType_variantAttributes id: string; slug: string | null; name: string | null; + valueRequired: boolean; values: (ProductVariantCreateData_product_productType_variantAttributes_values | null)[] | null; } diff --git a/src/products/types/ProductVariantDetails.ts b/src/products/types/ProductVariantDetails.ts index d7337cbac..116136551 100644 --- a/src/products/types/ProductVariantDetails.ts +++ b/src/products/types/ProductVariantDetails.ts @@ -18,6 +18,7 @@ export interface ProductVariantDetails_productVariant_attributes_attribute { id: string; name: string | null; slug: string | null; + valueRequired: boolean; values: (ProductVariantDetails_productVariant_attributes_attribute_values | null)[] | null; } @@ -31,7 +32,7 @@ export interface ProductVariantDetails_productVariant_attributes_value { export interface ProductVariantDetails_productVariant_attributes { __typename: "SelectedAttribute"; attribute: ProductVariantDetails_productVariant_attributes_attribute; - value: ProductVariantDetails_productVariant_attributes_value; + value: ProductVariantDetails_productVariant_attributes_value | null; } export interface ProductVariantDetails_productVariant_costPrice { diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index 66563c9aa..d958ad3c3 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueInput, ProductVariantInput } from "./../../types/globalTypes"; +import { AttributeValueInput, ProductVariantInput, SeoInput, AttributeInputTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: SimpleProductUpdate @@ -58,6 +58,7 @@ export interface SimpleProductUpdate_productUpdate_product_purchaseCost { export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_values { __typename: "AttributeValue"; + id: string; name: string | null; slug: string | null; } @@ -67,10 +68,12 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_attribute id: string; slug: string | null; name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; values: (SimpleProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; } -export interface SimpleProductUpdate_productUpdate_product_attributes_value { +export interface SimpleProductUpdate_productUpdate_product_attributes_values { __typename: "AttributeValue"; id: string; name: string | null; @@ -80,7 +83,7 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_value { export interface SimpleProductUpdate_productUpdate_product_attributes { __typename: "SelectedAttribute"; attribute: SimpleProductUpdate_productUpdate_product_attributes_attribute; - value: SimpleProductUpdate_productUpdate_product_attributes_value; + values: (SimpleProductUpdate_productUpdate_product_attributes_values | null)[]; } export interface SimpleProductUpdate_productUpdate_product_pricing_priceRange_start_net { @@ -197,6 +200,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_attribu id: string; name: string | null; slug: string | null; + valueRequired: boolean; values: (SimpleProductUpdate_productVariantUpdate_productVariant_attributes_attribute_values | null)[] | null; } @@ -210,7 +214,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_attribu export interface SimpleProductUpdate_productVariantUpdate_productVariant_attributes { __typename: "SelectedAttribute"; attribute: SimpleProductUpdate_productVariantUpdate_productVariant_attributes_attribute; - value: SimpleProductUpdate_productVariantUpdate_productVariant_attributes_value; + value: SimpleProductUpdate_productVariantUpdate_productVariant_attributes_value | null; } export interface SimpleProductUpdate_productVariantUpdate_productVariant_costPrice { @@ -305,4 +309,5 @@ export interface SimpleProductUpdateVariables { basePrice?: any | null; productVariantId: string; productVariantInput: ProductVariantInput; + seo?: SeoInput | null; } diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index 3c8d9f64c..4dcfdd5d1 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -26,6 +26,7 @@ export interface VariantCreate_productVariantCreate_productVariant_attributes_at id: string; name: string | null; slug: string | null; + valueRequired: boolean; values: (VariantCreate_productVariantCreate_productVariant_attributes_attribute_values | null)[] | null; } @@ -39,7 +40,7 @@ export interface VariantCreate_productVariantCreate_productVariant_attributes_va export interface VariantCreate_productVariantCreate_productVariant_attributes { __typename: "SelectedAttribute"; attribute: VariantCreate_productVariantCreate_productVariant_attributes_attribute; - value: VariantCreate_productVariantCreate_productVariant_attributes_value; + value: VariantCreate_productVariantCreate_productVariant_attributes_value | null; } export interface VariantCreate_productVariantCreate_productVariant_costPrice { diff --git a/src/products/types/VariantImageAssign.ts b/src/products/types/VariantImageAssign.ts index dfc6bf05c..b3051bcbb 100644 --- a/src/products/types/VariantImageAssign.ts +++ b/src/products/types/VariantImageAssign.ts @@ -24,6 +24,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_attributes id: string; name: string | null; slug: string | null; + valueRequired: boolean; values: (VariantImageAssign_variantImageAssign_productVariant_attributes_attribute_values | null)[] | null; } @@ -37,7 +38,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_attributes export interface VariantImageAssign_variantImageAssign_productVariant_attributes { __typename: "SelectedAttribute"; attribute: VariantImageAssign_variantImageAssign_productVariant_attributes_attribute; - value: VariantImageAssign_variantImageAssign_productVariant_attributes_value; + value: VariantImageAssign_variantImageAssign_productVariant_attributes_value | null; } export interface VariantImageAssign_variantImageAssign_productVariant_costPrice { diff --git a/src/products/types/VariantImageUnassign.ts b/src/products/types/VariantImageUnassign.ts index 420d03223..56a791674 100644 --- a/src/products/types/VariantImageUnassign.ts +++ b/src/products/types/VariantImageUnassign.ts @@ -24,6 +24,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_attrib id: string; name: string | null; slug: string | null; + valueRequired: boolean; values: (VariantImageUnassign_variantImageUnassign_productVariant_attributes_attribute_values | null)[] | null; } @@ -37,7 +38,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_attrib export interface VariantImageUnassign_variantImageUnassign_productVariant_attributes { __typename: "SelectedAttribute"; attribute: VariantImageUnassign_variantImageUnassign_productVariant_attributes_attribute; - value: VariantImageUnassign_variantImageUnassign_productVariant_attributes_value; + value: VariantImageUnassign_variantImageUnassign_productVariant_attributes_value | null; } export interface VariantImageUnassign_variantImageUnassign_productVariant_costPrice { diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index e979e4edf..f58467f0d 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -26,6 +26,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_attributes_at id: string; name: string | null; slug: string | null; + valueRequired: boolean; values: (VariantUpdate_productVariantUpdate_productVariant_attributes_attribute_values | null)[] | null; } @@ -39,7 +40,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_attributes_va export interface VariantUpdate_productVariantUpdate_productVariant_attributes { __typename: "SelectedAttribute"; attribute: VariantUpdate_productVariantUpdate_productVariant_attributes_attribute; - value: VariantUpdate_productVariantUpdate_productVariant_attributes_value; + value: VariantUpdate_productVariantUpdate_productVariant_attributes_value | null; } export interface VariantUpdate_productVariantUpdate_productVariant_costPrice { diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 78db87e84..47b70babe 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -9,7 +9,9 @@ import SearchCategories from "../../containers/SearchCategories"; import SearchCollections from "../../containers/SearchCollections"; import i18n from "../../i18n"; import { decimal, getMutationState, maybe } from "../../misc"; -import ProductCreatePage, { FormData } from "../components/ProductCreatePage"; +import ProductCreatePage, { + ProductCreatePageSubmitData +} from "../components/ProductCreatePage"; import { TypedProductCreateMutation } from "../mutations"; import { TypedProductCreateQuery } from "../queries"; import { ProductCreate } from "../types/ProductCreate"; @@ -42,6 +44,13 @@ export const ProductUpdate: React.StatelessComponent< text: i18n.t("Product created") }); navigate(productUrl(data.productCreate.product.id)); + } else { + const attributeError = data.productCreate.errors.find( + err => err.field === "attributes" + ); + if (!!attributeError) { + notify({ text: attributeError.message }); + } } }; @@ -55,26 +64,33 @@ export const ProductUpdate: React.StatelessComponent< loading: productCreateDataLoading } ) => { - const handleSubmit = (formData: FormData) => { + const handleSubmit = ( + formData: ProductCreatePageSubmitData + ) => { productCreate({ variables: { - attributes: formData.attributes, + attributes: formData.attributes.map(attribute => ({ + id: attribute.id, + values: attribute.value + })), basePrice: decimal(formData.basePrice), - category: formData.category.value, + category: formData.category, chargeTaxes: formData.chargeTaxes, - collections: formData.collections.map( - collection => collection.value - ), + collections: formData.collections, descriptionJson: JSON.stringify( formData.description ), isPublished: formData.isPublished, name: formData.name, - productType: formData.productType.value.id, + productType: formData.productType, publicationDate: formData.publicationDate !== "" ? formData.publicationDate : null, + seo: { + description: formData.seoDescription, + title: formData.seoTitle + }, sku: formData.sku, stockQuantity: formData.stockQuantity !== null diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index 7494e6053..17f6d7e00 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -9,7 +9,9 @@ import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; import SaveFilterTabDialog, { SaveFilterTabDialogFormData } from "@saleor/components/SaveFilterTabDialog"; +import { defaultListSettings, ProductListColumns } from "@saleor/config"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useListSettings from "@saleor/hooks/useListSettings"; import useLocale from "@saleor/hooks/useLocale"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; @@ -17,10 +19,10 @@ import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; import useShop from "@saleor/hooks/useShop"; -import { PAGINATE_BY } from "../../../config"; -import i18n from "../../../i18n"; -import { getMutationState, maybe } from "../../../misc"; -import ProductListCard from "../../components/ProductListCard"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; +import ProductListPage from "../../components/ProductListPage"; import { TypedProductBulkDeleteMutation, TypedProductBulkPublishMutation @@ -62,7 +64,9 @@ export const ProductList: React.StatelessComponent = ({ const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); - + const { updateListSettings, settings } = useListSettings( + ListViews.PRODUCT_LIST + ); const tabs = getFilterTabs(); const currentTab = @@ -128,14 +132,14 @@ export const ProductList: React.StatelessComponent = ({ handleTabChange(tabs.length + 1); }; - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); const queryVariables = React.useMemo( () => ({ ...paginationState, filter: getFilterVariables(params) }), - [params] + [params, settings.rowNumber] ); return ( @@ -193,9 +197,13 @@ export const ProductList: React.StatelessComponent = ({ return ( <> - = ({ )} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} pageInfo={pageInfo} onRowClick={id => () => navigate(productUrl(id))} onAll={() => @@ -261,7 +270,9 @@ export const ProductList: React.StatelessComponent = ({ confirmButtonState={bulkDeleteMutationState} onClose={closeModal} onConfirm={() => - productBulkDelete({ variables: { ids: params.ids } }) + productBulkDelete({ + variables: { ids: params.ids } + }) } title={i18n.t("Remove products")} variant="delete" diff --git a/src/products/views/ProductUpdate.tsx b/src/products/views/ProductUpdate.tsx index c67671c79..9080c2738 100644 --- a/src/products/views/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate.tsx @@ -4,7 +4,7 @@ import DeleteIcon from "@material-ui/icons/Delete"; import React from "react"; import { arrayMove } from "react-sortable-hoc"; -import * as placeholderImg from "@assets/images/placeholder255x255.png"; +import placeholderImg from "@assets/images/placeholder255x255.png"; import ActionDialog from "@saleor/components/ActionDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useBulkActions from "@saleor/hooks/useBulkActions"; diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 846a85654..cd6919a50 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -8,7 +8,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle"; import useBulkActions from "@saleor/hooks/useBulkActions"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; -import placeholderImg from "../../../../images/placeholder255x255.png"; +import placeholderImg from "../@assets/images/placeholder255x255.png"; import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config"; import SearchCategories from "../../../containers/SearchCategories"; import SearchCollections from "../../../containers/SearchCollections"; diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index 6af9aa183..45a95b4e9 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -1,13 +1,15 @@ import React from "react"; -import * as placeholderImg from "@assets/images/placeholder255x255.png"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; +import placeholderImg from "@assets/images/placeholder255x255.png"; import i18n from "../../i18n"; import { decimal, getMutationState, maybe } from "../../misc"; import ProductVariantDeleteDialog from "../components/ProductVariantDeleteDialog"; -import ProductVariantPage from "../components/ProductVariantPage"; +import ProductVariantPage, { + ProductVariantPageSubmitData +} from "../components/ProductVariantPage"; import ProductVariantOperations from "../containers/ProductVariantOperations"; import { TypedProductVariantQuery } from "../queries"; import { VariantUpdate } from "../types/VariantUpdate"; @@ -24,18 +26,6 @@ interface ProductUpdateProps { params: ProductVariantEditUrlQueryParams; } -interface FormData { - id: string; - attributes?: Array<{ - slug: string; - value: string; - }>; - costPrice?: string; - priceOverride?: string; - quantity: number; - sku: string; -} - export const ProductVariant: React.StatelessComponent = ({ variantId, productId, @@ -127,10 +117,13 @@ export const ProductVariant: React.StatelessComponent = ({ ) } onImageSelect={handleImageSelect} - onSubmit={(data: FormData) => { + onSubmit={(data: ProductVariantPageSubmitData) => { if (variant) { updateVariant.mutate({ - attributes: data.attributes ? data.attributes : null, + attributes: data.attributes.map(attribute => ({ + id: attribute.id, + values: [attribute.value] + })), costPrice: decimal(data.costPrice), id: variantId, priceOverride: decimal(data.priceOverride), diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 848f1480e..ede273a8e 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -6,7 +6,9 @@ import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import i18n from "../../i18n"; import { decimal, getMutationState, maybe } from "../../misc"; -import ProductVariantCreatePage from "../components/ProductVariantCreatePage"; +import ProductVariantCreatePage, { + ProductVariantCreatePageSubmitData +} from "../components/ProductVariantCreatePage"; import { TypedVariantCreateMutation } from "../mutations"; import { TypedProductVariantCreateQuery } from "../queries"; import { VariantCreate } from "../types/VariantCreate"; @@ -16,17 +18,6 @@ interface ProductUpdateProps { productId: string; } -interface FormData { - attributes?: Array<{ - slug: string; - value: string; - }>; - costPrice?: string; - priceOverride?: string; - quantity: number; - sku: string; -} - export const ProductVariant: React.StatelessComponent = ({ productId }) => { @@ -57,10 +48,17 @@ export const ProductVariant: React.StatelessComponent = ({ {(variantCreate, variantCreateResult) => { const handleBack = () => navigate(productUrl(productId)); - const handleSubmit = (formData: FormData) => + const handleSubmit = ( + formData: ProductVariantCreatePageSubmitData + ) => variantCreate({ variables: { - attributes: formData.attributes, + attributes: formData.attributes + .filter(attribute => attribute.value !== "") + .map(attribute => ({ + id: attribute.id, + values: [attribute.value] + })), costPrice: decimal(formData.costPrice), priceOverride: decimal(formData.priceOverride), product: productId, diff --git a/src/shipping/components/ShippingZonesList/ShippingZonesList.tsx b/src/shipping/components/ShippingZonesList/ShippingZonesList.tsx index 838fe4f14..f38a21ab6 100644 --- a/src/shipping/components/ShippingZonesList/ShippingZonesList.tsx +++ b/src/shipping/components/ShippingZonesList/ShippingZonesList.tsx @@ -20,10 +20,10 @@ import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; import TableHead from "@saleor/components/TableHead"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; -import { maybe, renderCollection } from "../../../misc"; -import { ICONBUTTON_SIZE } from "../../../theme"; -import { ListActions, ListProps } from "../../../types"; +import i18n from "@saleor/i18n"; +import { maybe, renderCollection } from "@saleor/misc"; +import { ICONBUTTON_SIZE } from "@saleor/theme"; +import { ListActions, ListProps } from "@saleor/types"; import { ShippingZoneFragment } from "../../types/ShippingZoneFragment"; export interface ShippingZonesListProps extends ListProps, ListActions { @@ -50,14 +50,19 @@ const styles = (theme: Theme) => cursor: "pointer" } }); + +const numberOfColumns = 4; + const ShippingZonesList = withStyles(styles, { name: "ShippingZonesList" })( ({ classes, disabled, + settings, onAdd, onNextPage, onPreviousPage, onRemove, + onUpdateListSettings, onRowClick, pageInfo, shippingZones, @@ -81,6 +86,7 @@ const ShippingZonesList = withStyles(styles, { name: "ShippingZonesList" })( />
{i18n.t("Countries", { context: "object" })} + toggle(shippingZone.id)} /> @@ -159,7 +169,7 @@ const ShippingZonesList = withStyles(styles, { name: "ShippingZonesList" })( }, () => ( - + {i18n.t("No shipping zones found")} diff --git a/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx b/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx index f63fddfe1..b9aa74b37 100644 --- a/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx +++ b/src/shipping/components/ShippingZonesListPage/ShippingZonesListPage.tsx @@ -4,9 +4,9 @@ import AppHeader from "@saleor/components/AppHeader"; import Container from "@saleor/components/Container"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; -import i18n from "../../../i18n"; -import { ListActions, PageListProps } from "../../../types"; -import { WeightUnitsEnum } from "../../../types/globalTypes"; +import i18n from "@saleor/i18n"; +import { ListActions, PageListProps } from "@saleor/types"; +import { WeightUnitsEnum } from "@saleor/types/globalTypes"; import { ShippingZoneFragment } from "../../types/ShippingZoneFragment"; import ShippingWeightUnitForm from "../ShippingWeightUnitForm"; import ShippingZonesList from "../ShippingZonesList"; diff --git a/src/shipping/views/ShippingZonesList.tsx b/src/shipping/views/ShippingZonesList.tsx index 6559a4fa9..501ec532a 100644 --- a/src/shipping/views/ShippingZonesList.tsx +++ b/src/shipping/views/ShippingZonesList.tsx @@ -4,17 +4,18 @@ import DeleteIcon from "@material-ui/icons/Delete"; import React from "react"; import ActionDialog from "@saleor/components/ActionDialog"; +import { configurationMenuUrl } from "@saleor/configuration"; import useBulkActions from "@saleor/hooks/useBulkActions"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; import useShop from "@saleor/hooks/useShop"; -import { PAGINATE_BY } from "../../config"; -import { configurationMenuUrl } from "../../configuration"; -import i18n from "../../i18n"; -import { getMutationState, maybe } from "../../misc"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import ShippingZonesListPage from "../components/ShippingZonesListPage"; import { TypedBulkDeleteShippingZone, @@ -46,8 +47,11 @@ export const ShippingZonesList: React.StatelessComponent< const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); + const { updateListSettings, settings } = useListSettings( + ListViews.SHIPPING_METHODS_LIST + ); - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); return ( @@ -145,6 +149,7 @@ export const ShippingZonesList: React.StatelessComponent< defaultWeightUnit={maybe( () => shop.defaultWeightUnit )} + settings={settings} disabled={ loading || deleteShippingZoneOpts.loading || @@ -156,6 +161,7 @@ export const ShippingZonesList: React.StatelessComponent< pageInfo={pageInfo} onAdd={() => navigate(shippingZoneAddUrl)} onBack={() => navigate(configurationMenuUrl)} + onUpdateListSettings={updateListSettings} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} onRemove={id => diff --git a/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx b/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx index 5cfa4c6f3..794b7ac1a 100644 --- a/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx +++ b/src/siteSettings/components/SiteSettingsPage/SiteSettingsPage.tsx @@ -8,14 +8,31 @@ import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { UserError } from "@saleor/types"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; +import { mapCountriesToChoices } from "@saleor/utils/maps"; import i18n from "../../../i18n"; import { maybe } from "../../../misc"; import { AuthorizationKeyType } from "../../../types/globalTypes"; import { SiteSettings_shop } from "../../types/SiteSettings"; +import SiteSettingsAddress from "../SiteSettingsAddress/SiteSettingsAddress"; import SiteSettingsDetails from "../SiteSettingsDetails/SiteSettingsDetails"; import SiteSettingsKeys from "../SiteSettingsKeys/SiteSettingsKeys"; -export interface SiteSettingsPageFormData { +export interface SiteSettingsPageAddressFormData { + city: string; + companyName: string; + country: string; + countryArea: string; + phone: string; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface SiteSettingsPageFormData + extends SiteSettingsPageAddressFormData { description: string; domain: string; name: string; @@ -23,10 +40,7 @@ export interface SiteSettingsPageFormData { export interface SiteSettingsPageProps { disabled: boolean; - errors: Array<{ - field: string; - message: string; - }>; + errors: UserError[]; shop: SiteSettings_shop; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; @@ -45,11 +59,24 @@ const SiteSettingsPage: React.StatelessComponent = ({ onKeyRemove, onSubmit }) => { + const [displayCountry, setDisplayCountry] = useStateFromProps( + maybe(() => shop.companyAddress.country.code, "") + ); + const initialForm: SiteSettingsPageFormData = { + city: maybe(() => shop.companyAddress.city, ""), + companyName: maybe(() => shop.companyAddress.companyName, ""), + country: maybe(() => shop.companyAddress.country.code, ""), + countryArea: maybe(() => shop.companyAddress.countryArea, ""), description: maybe(() => shop.description, ""), domain: maybe(() => shop.domain.host, ""), - name: maybe(() => shop.name, "") + name: maybe(() => shop.name, ""), + phone: maybe(() => shop.companyAddress.phone, ""), + postalCode: maybe(() => shop.companyAddress.postalCode, ""), + streetAddress1: maybe(() => shop.companyAddress.streetAddress1, ""), + streetAddress2: maybe(() => shop.companyAddress.streetAddress2, "") }; + return (
= ({ onSubmit={onSubmit} confirmLeave > - {({ change, data, errors: formErrors, hasChanged, submit }) => ( - - {i18n.t("Configuration")} - - - {i18n.t("Site Settings")} - { + const countryChoices = mapCountriesToChoices( + maybe(() => shop.countries, []) + ); + const handleCountryChange = createSingleAutocompleteSelectHandler( + change, + setDisplayCountry, + countryChoices + ); + + return ( + + {i18n.t("Configuration")} + - - {i18n.t("Authentication keys")} - - shop.authorizationKeys)} - onAdd={onKeyAdd} - onRemove={onKeyRemove} + + {i18n.t("Site Settings")} + + + {i18n.t("Company information")} + + + + {i18n.t("Authentication keys")} + + shop.authorizationKeys)} + onAdd={onKeyAdd} + onRemove={onKeyRemove} + /> + + - - - - )} + + ); + }} ); }; diff --git a/src/siteSettings/fixtures.ts b/src/siteSettings/fixtures.ts index b41da4c79..59e41ca41 100644 --- a/src/siteSettings/fixtures.ts +++ b/src/siteSettings/fixtures.ts @@ -10,6 +10,32 @@ export const shop: SiteSettings_shop = { name: AuthorizationKeyType.FACEBOOK } ], + companyAddress: { + __typename: "Address", + city: "Kenstad", + cityArea: "Alabama", + companyName: "Saleor e-commerce", + country: { + __typename: "CountryDisplay", + code: "UA", + country: "United Arab Emirates" + }, + countryArea: null, + firstName: null, + id: "1", + lastName: null, + phone: "+41 876-373-9137", + postalCode: "89880-6342", + streetAddress1: "01419 Bernhard Plain", + streetAddress2: null + }, + countries: [ + { + __typename: "CountryDisplay", + code: "UA", + country: "United Arab Emirates" + } + ], description: "Lorem ipsum dolor sit amet", domain: { __typename: "Domain", diff --git a/src/siteSettings/mutations.ts b/src/siteSettings/mutations.ts index 31b531d85..048217b7a 100644 --- a/src/siteSettings/mutations.ts +++ b/src/siteSettings/mutations.ts @@ -1,6 +1,7 @@ import gql from "graphql-tag"; import { TypedMutation } from "../mutations"; +import { fragmentAddress } from "../orders/queries"; import { shopFragment } from "./queries"; import { AuthorizationKeyAdd, @@ -58,9 +59,11 @@ export const TypedAuthorizationKeyDelete = TypedMutation< const shopSettingsUpdate = gql` ${shopFragment} + ${fragmentAddress} mutation ShopSettingsUpdate( $shopDomainInput: SiteDomainInput! $shopSettingsInput: ShopSettingsInput! + $addressInput: AddressInput! ) { shopSettingsUpdate(input: $shopSettingsInput) { errors { @@ -83,6 +86,17 @@ const shopSettingsUpdate = gql` } } } + shopAddressUpdate(input: $addressInput) { + errors { + field + message + } + shop { + companyAddress { + ...AddressFragment + } + } + } } `; export const TypedShopSettingsUpdate = TypedMutation< diff --git a/src/siteSettings/queries.ts b/src/siteSettings/queries.ts index 07128b873..b6a57c61a 100644 --- a/src/siteSettings/queries.ts +++ b/src/siteSettings/queries.ts @@ -1,13 +1,22 @@ import gql from "graphql-tag"; +import { fragmentAddress } from "../orders/queries"; import { TypedQuery } from "../queries"; import { SiteSettings } from "./types/SiteSettings"; export const shopFragment = gql` + ${fragmentAddress} fragment ShopFragment on Shop { authorizationKeys { key name } + companyAddress { + ...AddressFragment + } + countries { + code + country + } description domain { host diff --git a/src/siteSettings/types/AuthorizationKeyAdd.ts b/src/siteSettings/types/AuthorizationKeyAdd.ts index c6524fdbf..a326a9cca 100644 --- a/src/siteSettings/types/AuthorizationKeyAdd.ts +++ b/src/siteSettings/types/AuthorizationKeyAdd.ts @@ -20,6 +20,34 @@ export interface AuthorizationKeyAdd_authorizationKeyAdd_shop_authorizationKeys name: AuthorizationKeyType; } +export interface AuthorizationKeyAdd_authorizationKeyAdd_shop_companyAddress_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface AuthorizationKeyAdd_authorizationKeyAdd_shop_companyAddress { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: AuthorizationKeyAdd_authorizationKeyAdd_shop_companyAddress_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface AuthorizationKeyAdd_authorizationKeyAdd_shop_countries { + __typename: "CountryDisplay"; + code: string; + country: string; +} + export interface AuthorizationKeyAdd_authorizationKeyAdd_shop_domain { __typename: "Domain"; host: string; @@ -28,6 +56,8 @@ export interface AuthorizationKeyAdd_authorizationKeyAdd_shop_domain { export interface AuthorizationKeyAdd_authorizationKeyAdd_shop { __typename: "Shop"; authorizationKeys: (AuthorizationKeyAdd_authorizationKeyAdd_shop_authorizationKeys | null)[]; + companyAddress: AuthorizationKeyAdd_authorizationKeyAdd_shop_companyAddress | null; + countries: (AuthorizationKeyAdd_authorizationKeyAdd_shop_countries | null)[]; description: string | null; domain: AuthorizationKeyAdd_authorizationKeyAdd_shop_domain; name: string; diff --git a/src/siteSettings/types/AuthorizationKeyDelete.ts b/src/siteSettings/types/AuthorizationKeyDelete.ts index a79185000..024e7f599 100644 --- a/src/siteSettings/types/AuthorizationKeyDelete.ts +++ b/src/siteSettings/types/AuthorizationKeyDelete.ts @@ -20,6 +20,34 @@ export interface AuthorizationKeyDelete_authorizationKeyDelete_shop_authorizatio name: AuthorizationKeyType; } +export interface AuthorizationKeyDelete_authorizationKeyDelete_shop_companyAddress_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface AuthorizationKeyDelete_authorizationKeyDelete_shop_companyAddress { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: AuthorizationKeyDelete_authorizationKeyDelete_shop_companyAddress_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface AuthorizationKeyDelete_authorizationKeyDelete_shop_countries { + __typename: "CountryDisplay"; + code: string; + country: string; +} + export interface AuthorizationKeyDelete_authorizationKeyDelete_shop_domain { __typename: "Domain"; host: string; @@ -28,6 +56,8 @@ export interface AuthorizationKeyDelete_authorizationKeyDelete_shop_domain { export interface AuthorizationKeyDelete_authorizationKeyDelete_shop { __typename: "Shop"; authorizationKeys: (AuthorizationKeyDelete_authorizationKeyDelete_shop_authorizationKeys | null)[]; + companyAddress: AuthorizationKeyDelete_authorizationKeyDelete_shop_companyAddress | null; + countries: (AuthorizationKeyDelete_authorizationKeyDelete_shop_countries | null)[]; description: string | null; domain: AuthorizationKeyDelete_authorizationKeyDelete_shop_domain; name: string; diff --git a/src/siteSettings/types/ShopFragment.ts b/src/siteSettings/types/ShopFragment.ts index dcea4c527..d27566ab8 100644 --- a/src/siteSettings/types/ShopFragment.ts +++ b/src/siteSettings/types/ShopFragment.ts @@ -14,6 +14,34 @@ export interface ShopFragment_authorizationKeys { name: AuthorizationKeyType; } +export interface ShopFragment_companyAddress_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface ShopFragment_companyAddress { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: ShopFragment_companyAddress_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface ShopFragment_countries { + __typename: "CountryDisplay"; + code: string; + country: string; +} + export interface ShopFragment_domain { __typename: "Domain"; host: string; @@ -22,6 +50,8 @@ export interface ShopFragment_domain { export interface ShopFragment { __typename: "Shop"; authorizationKeys: (ShopFragment_authorizationKeys | null)[]; + companyAddress: ShopFragment_companyAddress | null; + countries: (ShopFragment_countries | null)[]; description: string | null; domain: ShopFragment_domain; name: string; diff --git a/src/siteSettings/types/ShopSettingsUpdate.ts b/src/siteSettings/types/ShopSettingsUpdate.ts index e78f9c121..2fa80ac0d 100644 --- a/src/siteSettings/types/ShopSettingsUpdate.ts +++ b/src/siteSettings/types/ShopSettingsUpdate.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { SiteDomainInput, ShopSettingsInput, AuthorizationKeyType } from "./../../types/globalTypes"; +import { SiteDomainInput, ShopSettingsInput, AddressInput, AuthorizationKeyType } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ShopSettingsUpdate @@ -20,6 +20,34 @@ export interface ShopSettingsUpdate_shopSettingsUpdate_shop_authorizationKeys { name: AuthorizationKeyType; } +export interface ShopSettingsUpdate_shopSettingsUpdate_shop_companyAddress_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface ShopSettingsUpdate_shopSettingsUpdate_shop_companyAddress { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: ShopSettingsUpdate_shopSettingsUpdate_shop_companyAddress_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface ShopSettingsUpdate_shopSettingsUpdate_shop_countries { + __typename: "CountryDisplay"; + code: string; + country: string; +} + export interface ShopSettingsUpdate_shopSettingsUpdate_shop_domain { __typename: "Domain"; host: string; @@ -28,6 +56,8 @@ export interface ShopSettingsUpdate_shopSettingsUpdate_shop_domain { export interface ShopSettingsUpdate_shopSettingsUpdate_shop { __typename: "Shop"; authorizationKeys: (ShopSettingsUpdate_shopSettingsUpdate_shop_authorizationKeys | null)[]; + companyAddress: ShopSettingsUpdate_shopSettingsUpdate_shop_companyAddress | null; + countries: (ShopSettingsUpdate_shopSettingsUpdate_shop_countries | null)[]; description: string | null; domain: ShopSettingsUpdate_shopSettingsUpdate_shop_domain; name: string; @@ -62,12 +92,53 @@ export interface ShopSettingsUpdate_shopDomainUpdate { shop: ShopSettingsUpdate_shopDomainUpdate_shop | null; } +export interface ShopSettingsUpdate_shopAddressUpdate_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface ShopSettingsUpdate_shopAddressUpdate_shop_companyAddress_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface ShopSettingsUpdate_shopAddressUpdate_shop_companyAddress { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: ShopSettingsUpdate_shopAddressUpdate_shop_companyAddress_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface ShopSettingsUpdate_shopAddressUpdate_shop { + __typename: "Shop"; + companyAddress: ShopSettingsUpdate_shopAddressUpdate_shop_companyAddress | null; +} + +export interface ShopSettingsUpdate_shopAddressUpdate { + __typename: "ShopAddressUpdate"; + errors: ShopSettingsUpdate_shopAddressUpdate_errors[] | null; + shop: ShopSettingsUpdate_shopAddressUpdate_shop | null; +} + export interface ShopSettingsUpdate { shopSettingsUpdate: ShopSettingsUpdate_shopSettingsUpdate | null; shopDomainUpdate: ShopSettingsUpdate_shopDomainUpdate | null; + shopAddressUpdate: ShopSettingsUpdate_shopAddressUpdate | null; } export interface ShopSettingsUpdateVariables { shopDomainInput: SiteDomainInput; shopSettingsInput: ShopSettingsInput; + addressInput: AddressInput; } diff --git a/src/siteSettings/types/SiteSettings.ts b/src/siteSettings/types/SiteSettings.ts index 873124078..ee049f738 100644 --- a/src/siteSettings/types/SiteSettings.ts +++ b/src/siteSettings/types/SiteSettings.ts @@ -14,6 +14,34 @@ export interface SiteSettings_shop_authorizationKeys { name: AuthorizationKeyType; } +export interface SiteSettings_shop_companyAddress_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface SiteSettings_shop_companyAddress { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: SiteSettings_shop_companyAddress_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface SiteSettings_shop_countries { + __typename: "CountryDisplay"; + code: string; + country: string; +} + export interface SiteSettings_shop_domain { __typename: "Domain"; host: string; @@ -22,6 +50,8 @@ export interface SiteSettings_shop_domain { export interface SiteSettings_shop { __typename: "Shop"; authorizationKeys: (SiteSettings_shop_authorizationKeys | null)[]; + companyAddress: SiteSettings_shop_companyAddress | null; + countries: (SiteSettings_shop_countries | null)[]; description: string | null; domain: SiteSettings_shop_domain; name: string; diff --git a/src/siteSettings/views/index.tsx b/src/siteSettings/views/index.tsx index a1032f38e..bb593d907 100644 --- a/src/siteSettings/views/index.tsx +++ b/src/siteSettings/views/index.tsx @@ -65,7 +65,9 @@ export const SiteSettings: React.StatelessComponent = ({ (!data.shopDomainUpdate.errors || data.shopDomainUpdate.errors.length === 0) && (!data.shopSettingsUpdate.errors || - data.shopSettingsUpdate.errors.length === 0) + data.shopSettingsUpdate.errors.length === 0) && + (!data.shopAddressUpdate.errors || + data.shopAddressUpdate.errors.length === 0) ) { notify({ text: i18n.t("Site settings updated", { @@ -95,6 +97,11 @@ export const SiteSettings: React.StatelessComponent = ({ () => updateShopSettingsOpts.data.shopSettingsUpdate.errors, [] + ), + ...maybe( + () => + updateShopSettingsOpts.data.shopAddressUpdate.errors, + [] ) ]; const loading = @@ -119,6 +126,16 @@ export const SiteSettings: React.StatelessComponent = ({ ) => updateShopSettings({ variables: { + addressInput: { + city: data.city, + companyName: data.companyName, + country: data.country, + countryArea: data.countryArea, + phone: data.phone, + postalCode: data.postalCode, + streetAddress1: data.streetAddress1, + streetAddress2: data.streetAddress2 + }, shopDomainInput: { domain: data.domain, name: data.name diff --git a/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx b/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx index 7156935e3..a104b703b 100644 --- a/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx +++ b/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx @@ -58,8 +58,8 @@ const StaffDetailsPage: React.StatelessComponent = ({ onSubmit }: StaffDetailsPageProps) => { const initialForm: FormData = { - email: maybe(() => staffMember.email), - firstName: maybe(() => staffMember.firstName), + email: maybe(() => staffMember.email, ""), + firstName: maybe(() => staffMember.firstName, ""), hasFullAccess: maybe( () => permissions.filter( @@ -71,7 +71,7 @@ const StaffDetailsPage: React.StatelessComponent = ({ false ), isActive: maybe(() => staffMember.isActive, false), - lastName: maybe(() => staffMember.lastName), + lastName: maybe(() => staffMember.lastName, ""), permissions: maybe(() => staffMember.permissions, []).map(perm => perm.code) }; return ( diff --git a/src/staff/components/StaffList/StaffList.tsx b/src/staff/components/StaffList/StaffList.tsx index f15a69e6a..a3c67c56a 100644 --- a/src/staff/components/StaffList/StaffList.tsx +++ b/src/staff/components/StaffList/StaffList.tsx @@ -12,19 +12,19 @@ import TableFooter from "@material-ui/core/TableFooter"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import Typography from "@material-ui/core/Typography"; -import * as classNames from "classnames"; +import classNames from "classnames"; import React from "react"; import Skeleton from "@saleor/components/Skeleton"; import TablePagination from "@saleor/components/TablePagination"; -import i18n from "../../../i18n"; +import i18n from "@saleor/i18n"; import { getUserInitials, getUserName, maybe, renderCollection -} from "../../../misc"; -import { ListProps } from "../../../types"; +} from "@saleor/misc"; +import { ListProps } from "@saleor/types"; import { StaffList_staffUsers_edges_node } from "../../types/StaffList"; const styles = (theme: Theme) => @@ -72,9 +72,11 @@ interface StaffListProps extends ListProps, WithStyles { const StaffList = withStyles(styles, { name: "StaffList" })( ({ classes, + settings, disabled, onNextPage, onPreviousPage, + onUpdateListSettings, onRowClick, pageInfo, staffMembers @@ -95,10 +97,12 @@ const StaffList = withStyles(styles, { name: "StaffList" })( marginTop: theme.spacing.unit }, hr: { - backgroundColor: "#eaeaea", + backgroundColor: theme.overrides.MuiCard.root.borderColor, border: "none", height: 1, marginBottom: 0, diff --git a/src/staff/components/StaffProperties/StaffProperties.tsx b/src/staff/components/StaffProperties/StaffProperties.tsx index e4312e044..17a1a0517 100644 --- a/src/staff/components/StaffProperties/StaffProperties.tsx +++ b/src/staff/components/StaffProperties/StaffProperties.tsx @@ -16,7 +16,7 @@ import i18n from "../../../i18n"; import { getUserInitials, maybe } from "../../../misc"; import { StaffMemberDetails_user } from "../../types/StaffMemberDetails"; -import * as photoIcon from "@assets/images/photo-icon.svg"; +import photoIcon from "../@assets/images/photo-icon.svg"; const styles = (theme: Theme) => createStyles({ diff --git a/src/staff/views/StaffList.tsx b/src/staff/views/StaffList.tsx index eb3d94f50..a730d8695 100644 --- a/src/staff/views/StaffList.tsx +++ b/src/staff/views/StaffList.tsx @@ -1,14 +1,16 @@ import React from "react"; +import useListSettings from "@saleor/hooks/useListSettings"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; -import { PAGINATE_BY } from "../../config"; -import { configurationMenuUrl } from "../../configuration"; -import i18n from "../../i18n"; -import { getMutationState, maybe } from "../../misc"; + +import { configurationMenuUrl } from "@saleor/configuration"; +import i18n from "@saleor/i18n"; +import { getMutationState, maybe } from "@saleor/misc"; +import { ListViews } from "@saleor/types"; import StaffAddMemberDialog, { FormData as AddStaffMemberForm } from "../components/StaffAddMemberDialog"; @@ -32,6 +34,9 @@ export const StaffList: React.StatelessComponent = ({ const navigate = useNavigator(); const notify = useNotifier(); const paginate = usePaginator(); + const { updateListSettings, settings } = useListSettings( + ListViews.STAFF_MEMBERS_LIST + ); const closeModal = () => navigate( @@ -43,7 +48,7 @@ export const StaffList: React.StatelessComponent = ({ true ); - const paginationState = createPaginationState(PAGINATE_BY, params); + const paginationState = createPaginationState(settings.rowNumber, params); return ( {({ data, loading }) => { @@ -91,6 +96,7 @@ export const StaffList: React.StatelessComponent = ({ <> data.staffUsers.edges.map(edge => edge.node) @@ -105,6 +111,7 @@ export const StaffList: React.StatelessComponent = ({ onBack={() => navigate(configurationMenuUrl)} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} onRowClick={id => () => navigate(staffMemberDetailsUrl(id))} /> ( - - - - - -
- {storyFn()} -
-
-
-
-
-
+ + + + +
+ {storyFn()} +
+
+
+
+
); export default Decorator; diff --git a/src/storybook/Stories.test.ts b/src/storybook/Stories.test.ts index e6f85b866..3cb047195 100644 --- a/src/storybook/Stories.test.ts +++ b/src/storybook/Stories.test.ts @@ -1,4 +1,5 @@ import createGenerateClassName from "@material-ui/core/styles/createGenerateClassName"; +import createHookGenerateClassName from "@material-ui/styles/createGenerateClassName"; import initStoryshots from "@storybook/addon-storyshots"; // tslint:disable no-submodule-imports import generateRandomKey from "draft-js/lib/generateRandomKey"; @@ -15,11 +16,18 @@ jest.mock("@material-ui/core/styles/createGenerateClassName"); } ); +jest.mock("@material-ui/styles/createGenerateClassName"); +(createHookGenerateClassName as any).mockImplementation( + () => (rule, stylesheet) => { + return [stylesheet.options.meta, rule.key, "id"].join("-"); + } +); + jest.mock("draft-js/lib/generateRandomKey"); (generateRandomKey as any).mockImplementation(() => "testKey"); initStoryshots({ - configPath: "src/storybook/", + configPath: "saleor/static/dashboard-next/storybook/", test({ story }) { const result = render(story.render() as any); expect(toJSON(result)).toMatchSnapshot(); diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 17bd0d0a3..f8eba83b1 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -1,886 +1,33 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Storyshots Categories / CategoryProducts when loading data 1`] = ` +exports[`Storyshots Attributes / Attribute delete default 1`] = `
-
-
- - Products - -
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - -
- - Name - - Type -
-
- -
-
- - ‌ - - - - ‌ - -
- - +/> `; -exports[`Storyshots Categories / CategoryProducts with clickable rows 1`] = ` +exports[`Storyshots Attributes / Attribute value delete default 1`] = `
-
-
- - Products - -
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Name - - Type -
-
- -
-
- - Gardner, Graham and King - - - T-Shirt -
-
- -
-
- - Gardner, Graham and King - - - T-Shirt -
-
- -
-
- - Gardner, Graham and King - - - T-Shirt -
-
- -
-
- - Gardner, Graham and King - - - T-Shirt -
-
-
+/> `; -exports[`Storyshots Categories / CategoryProducts with initial data 1`] = ` +exports[`Storyshots Attributes / Attribute value edit default 1`] = `
-
-
- - Products - -
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Name - - Type -
-
- -
-
- - Gardner, Graham and King - - - T-Shirt -
-
- -
-
- - Gardner, Graham and King - - - T-Shirt -
-
- -
-
- - Gardner, Graham and King - - - T-Shirt -
-
- -
-
- - Gardner, Graham and King - - - T-Shirt -
-
-
+/> `; -exports[`Storyshots Categories / CategoryProducts without initial data 1`] = ` +exports[`Storyshots Attributes / Attribute value edit form errors 1`] = `
-
-
- - Products - -
- -
-
-
-
- - - - - - - - - - - - - - - - - -
- Name - - Type -
- No products found -
-
-
+/> +`; + +exports[`Storyshots Attributes / Delete multiple attributes default 1`] = ` +
`; exports[`Storyshots Customers / Address editing dialog default 1`] = ` @@ -926,17 +73,31 @@ exports[`Storyshots Generics / AddressEdit default 1`] = ` class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >
+
+
+
+
+
+
+
+
@@ -1178,17 +453,31 @@ exports[`Storyshots Generics / AddressEdit default 1`] = ` class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >
+ `; +exports[`Storyshots Generics / Assign attributes dialog default 1`] = ` +
+`; + +exports[`Storyshots Generics / Assign attributes dialog errors 1`] = ` +
+`; + +exports[`Storyshots Generics / Assign attributes dialog loading 1`] = ` +
+`; + +exports[`Storyshots Generics / Assign product default 1`] = ` +
+`; + exports[`Storyshots Generics / Autocomplete Menu default 1`] = `
+
+
+
+ `; +exports[`Storyshots Generics / Column picker default 1`] = ` +
+
+
+
+
+ +
+
+
+
+
+`; + exports[`Storyshots Generics / Date default 1`] = `
-
- - Some editable text - -
+ + Some editable text + @@ -2114,6 +1529,129 @@ exports[`Storyshots Generics / Money range to 1`] = `
`; +exports[`Storyshots Generics / MultiAutocompleteSelectField with custom option 1`] = ` +
+
+
+
+
+ + +

+ Value: Afghanistan +

+
+
+
+
+
+ + Afghanistan + + +
+
+
+
+
+
+`; + exports[`Storyshots Generics / MultiAutocompleteSelectField with loaded data 1`] = `
-
+
+ + +

+ Value: Afghanistan +

+
+
+
+
- - -

- Value: afghanistan -

-
-
-
-

- Afghanistan -

-
- -
+ +
- +
@@ -2235,104 +1786,117 @@ exports[`Storyshots Generics / MultiAutocompleteSelectField with loading data 1`
-
+
-
-
-
-
+
+
+
+
+
+ -

+ -

+ + + + +
- +
@@ -2831,7 +2395,11 @@ exports[`Storyshots Generics / PageHeader with title 1`] = `
+ > +
+
`; @@ -2851,31 +2419,35 @@ exports[`Storyshots Generics / PageHeader with title icon bar 1`] = `
- + + + +
@@ -2900,7 +2472,11 @@ exports[`Storyshots Generics / PageHeader without title 1`] = `
+ > +
+
`; @@ -2982,17 +2558,31 @@ exports[`Storyshots Generics / PhoneField default 1`] = ` class="MuiFormControl-root-id" >
+
+
+

+ @@ -3127,17 +2759,31 @@ exports[`Storyshots Generics / Price input with label 1`] = ` class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >
+ @@ -3164,17 +2810,31 @@ exports[`Storyshots Generics / Price input with label and hint 1`] = ` class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >
+ @@ -3206,11 +2866,25 @@ exports[`Storyshots Generics / Price input with no value 1`] = ` class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >
+ @@ -3237,11 +2911,25 @@ exports[`Storyshots Generics / Price input with value 1`] = ` class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >
+
+

+
@@ -3963,7 +3671,7 @@ exports[`Storyshots Generics / SingleAutocompleteSelectField with custom option class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >

`; -exports[`Storyshots Navigation / Menu item create default 1`] = ` +exports[`Storyshots Navigation / Menu item default 1`] = ` +

+`; + +exports[`Storyshots Navigation / Menu item edit 1`] = ` +
+`; + +exports[`Storyshots Navigation / Menu item errors 1`] = `
@@ -6554,19 +6326,7 @@ exports[`Storyshots Orders / OrderShippingMethodEditDialog default 1`] = ` /> `; -exports[`Storyshots Product types / Edit attribute default 1`] = ` -
-`; - -exports[`Storyshots Product types / Edit attribute form errors 1`] = ` -
-`; - -exports[`Storyshots Product types / Edit attribute loading 1`] = ` +exports[`Storyshots Product types / ProductTypeDeleteDialog default 1`] = `
@@ -6622,6 +6382,5164 @@ exports[`Storyshots SiteSettings / Add key dialog form errors 1`] = ` /> `; +exports[`Storyshots Views / Attributes / Attribute details create 1`] = ` +
+
+
+
+
+ Create New Attribute +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+

+ This is used internally. Make sure you don’t use spaces +

+
+
+
+ +
+
+
+ Dropdown +
+ + +
+
+
+
+
+
+
+
+ + Attribute Values + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + Admin + + Default Store View + +
+
+
+
+
+
+ + Properties + +
+
+
+
+
+
+ Storefront Properties +
+
+
+`; + +exports[`Storyshots Views / Attributes / Attribute details default 1`] = ` +
+
+
+
+
+ Author +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+

+ This is used internally. Make sure you don’t use spaces +

+
+
+
+ +
+
+
+ Dropdown +
+ + +
+
+
+
+
+
+
+
+ + Attribute Values + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + Admin + + Default Store View + +
+
+
+
+
+
+ + Properties + +
+
+
+
+
+
+ Storefront Properties +
+
+
+`; + +exports[`Storyshots Views / Attributes / Attribute details form errors 1`] = ` +
+
+
+
+
+ Author +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+

+ Generic form error +

+
+
+
+ +
+ + +
+

+ Generic form error +

+
+
+
+ +
+
+
+ Dropdown +
+ + +
+
+
+
+
+
+
+
+ + Attribute Values + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + Admin + + Default Store View + +
+
+
+
+
+
+ + Properties + +
+
+
+
+
+
+ Storefront Properties +
+
+
+`; + +exports[`Storyshots Views / Attributes / Attribute details loading 1`] = ` +
+
+
+
+
+ + ‌ + +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+

+ This is used internally. Make sure you don’t use spaces +

+
+
+
+ +
+
+
+ Dropdown +
+ + +
+
+
+
+
+
+
+
+ + Attribute Values + +
+ +
+
+
+
+ + + + + + + + + + + + + + + +
+ + Admin + + Default Store View + +
+ + + + ‌ + + + + ‌ + + + +
+
+
+
+
+
+ + Properties + +
+
+
+
+
+
+ Storefront Properties +
+
+
+`; + +exports[`Storyshots Views / Attributes / Attribute details multiple select input 1`] = ` +
+
+
+
+
+ Author +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+

+ This is used internally. Make sure you don’t use spaces +

+
+
+
+ +
+
+
+ Multiple Select +
+ + +
+
+
+
+
+
+
+
+ + Attribute Values + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + Admin + + Default Store View + +
+
+
+
+
+
+ + Properties + +
+
+
+
+
+
+ Storefront Properties +
+
+
+`; + +exports[`Storyshots Views / Attributes / Attribute details no values 1`] = ` +
+
+
+
+
+ Author +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+

+ This is used internally. Make sure you don’t use spaces +

+
+
+
+ +
+
+
+ Dropdown +
+ + +
+
+
+
+
+
+
+
+ + Attribute Values + +
+ +
+
+
+
+ + + + + + + + + + + + + + + +
+ + Admin + + Default Store View + +
+ + + + ‌ + + + + ‌ + + + +
+
+
+
+
+
+ + Properties + +
+
+
+
+
+
+ Storefront Properties +
+
+
+`; + +exports[`Storyshots Views / Attributes / Attribute list default 1`] = ` +
+
+
+
+ Attributes +
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + Attribute Code + + Default Label + + Visible + + Searchable + + Use in faceted search +
+
+
+
+`; + +exports[`Storyshots Views / Attributes / Attribute list loading 1`] = ` +
+
+
+
+ Attributes +
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + Attribute Code + + Default Label + + Visible + + Searchable + + Use in faceted search +
+
+
+
+`; + +exports[`Storyshots Views / Attributes / Attribute list no data 1`] = ` +
+
+
+
+ Attributes +
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ Attribute Code + + Default Label + + Visible + + Searchable + + Use in faceted search +
+ No attributes found +
+
+
+
+`; + exports[`Storyshots Views / Authentication / Log in default 1`] = `
+
+
+
+
+
+ - + Add category + + + +
+ > +
+ + No of Rows: + +
+
+
+ 20 +
+ + +
+
+
+
@@ -7430,32 +12476,36 @@ exports[`Storyshots Views / Categories / Category list empty 1`] = `
- + Add category + + + +
-
+ > +
+ + No of Rows: + +
+
+
+ 20 +
+ + +
+
+
+
@@ -7607,32 +12693,36 @@ exports[`Storyshots Views / Categories / Category list loading 1`] = `
- + Add category + + + +
+ > + +
+ > +
+ + No of Rows: + +
+
+
+ 20 +
+ + +
+
+
+
@@ -7820,7 +12962,11 @@ exports[`Storyshots Views / Categories / Create category When loading 1`] = `
+ > +
+
+
+ > +
+
+
+ > +
+
+ -
@@ -9370,14 +14558,28 @@ Ctrl + K" class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >
+
@@ -9399,7 +14601,7 @@ Ctrl + K" + +
+
+

+ Optional +

+
+
+
+
+
+
+ + Search Engine Preview + +
+ +
+
+
+
+
+

+ Add search engine title and description to make this category easier to find +

+
+
+
+
+ + Subcategories + + + Products + +
+
+
+
+ + Products in Coffees + +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Name + + + Type + + Published + + Price +
+
+
+
+ +
+`; + exports[`Storyshots Views / Collections / Collection details default 1`] = `
+ > +
+
+ -
@@ -13960,14 +20890,28 @@ Ctrl + K" class="MuiFormControl-root-id MuiFormControl-fullWidth-id" >
+
@@ -13989,7 +20933,7 @@ Ctrl + K"