Merge branch 'master' into feat/add-slug-to-seo-form

This commit is contained in:
mmarkusik 2020-09-22 09:58:33 +02:00 committed by GitHub
commit c7124ffc41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1764 additions and 446 deletions

View file

@ -40,6 +40,9 @@ All notable, unreleased changes to this project will be documented in this file.
- Update schema with PositiveDecimal type - #695 by @AlicjaSzu
- Restyle side menu - #697 by @dominik-zeglen
- Add error info when fetching taxes - #701 by @dominik-zeglen
- Fix return to previous page on screen size change - #710 by @orzechdev
- Add variants reordering possibility - #716 by @orzechdev
- Fix avatar change button - #719 by @orzechdev
- Add slug field to product, collection, category & page details (update and create) - #720 by @mmarkusik
## 2.10.1

View file

@ -1,9 +1,3 @@
<svg width="37" height="36" viewBox="0 0 37 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.22165 29.7949H0.58465C0.463483 29.7757 0.339087 29.7544 0.241002 29.6832C0.093454 29.576 0.0294675 29.3967 0 29.2102V0.584649C0.03929 0.33635 0.171052 0.104259 0.403986 0.0286256C0.461938 0.00975233 0.524452 0.00954185 0.58465 0H29.2105C29.2702 0.00954185 29.3305 0.0190837 29.3908 0.0286256C29.5592 0.114292 29.7101 0.230548 29.7662 0.403985C29.7851 0.461938 29.7851 0.524451 29.795 0.584649V6.21595L35.4394 6.20683C35.4997 6.21631 35.5601 6.22571 35.6204 6.23518C35.7888 6.32077 35.9397 6.43703 35.9965 6.61061C36.0154 6.6687 36.0154 6.73121 36.0253 6.79148V35.4147C35.986 35.6629 35.8541 35.8951 35.6211 35.9707C35.5629 35.9896 35.5004 35.9898 35.4401 35.9994L6.80637 36C6.68513 35.9808 6.56074 35.9597 6.46265 35.8884C6.3151 35.7812 6.25119 35.6018 6.22165 35.4154V29.7949ZM34.8557 30.2975H7.39102V34.8307L34.8557 34.8301V30.2975ZM28.6633 15.4452L23.063 20.8494L31.1722 28.9086C31.2424 29.0051 31.2852 29.0454 31.3133 29.1281H34.8557V21.229L28.6633 15.4452ZM8.27743 29.0783C8.28087 29.0948 8.28353 29.1114 8.28557 29.1281H29.7339L19.241 18.6996L8.27743 29.0783ZM28.6254 6.21785V1.1693H1.1693V28.6256H6.22165V6.83772C6.2408 6.71669 6.26199 6.59244 6.33307 6.49442C6.44006 6.34694 6.61918 6.28282 6.80539 6.25307L28.6254 6.21785ZM34.8557 19.6289V7.37711C25.7011 7.39192 16.5459 7.40665 7.39102 7.42146V28.3073L18.8489 17.4606C18.996 17.3592 18.9973 17.3585 19.1681 17.3064C19.228 17.3072 19.2878 17.3079 19.3475 17.3086C19.5171 17.3648 19.5185 17.3655 19.663 17.4705L22.233 20.0249L28.2507 14.2183C28.4444 14.0476 28.4507 14.0362 28.7306 14.059C28.9264 14.0927 28.9629 14.1322 29.0562 14.2117L34.8557 19.6289ZM12.9322 9.93019C14.3966 9.94408 15.7827 11.0726 16.0626 12.5321C16.2614 13.5688 15.9096 14.6949 15.1526 15.4341C14.2682 16.2976 12.862 16.5852 11.6988 16.1097C10.6719 15.6898 9.89786 14.7049 9.73691 13.6065C9.53829 12.2509 10.3114 10.8116 11.5556 10.2303C11.9847 10.0299 12.4574 9.92865 12.9322 9.93019ZM12.8982 11.0995C11.9719 11.1083 11.1014 11.8117 10.9137 12.7275C10.653 14 11.8357 15.4123 13.2853 15.1437C14.1119 14.9905 14.8064 14.274 14.929 13.437C15.0953 12.3025 14.1666 11.1191 12.938 11.0996C12.9247 11.0995 12.9115 11.0995 12.8982 11.0995Z" fill="url(#paint0_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="0" y1="0" x2="39.5989" y2="41.1706" gradientUnits="userSpaceOnUse">
<stop stop-color="#13BEBB"/>
<stop offset="1" stop-color="#3EE7CD"/>
</linearGradient>
</defs>
<path d="M6.22165 29.7949H0.58465C0.463483 29.7757 0.339087 29.7544 0.241002 29.6832C0.093454 29.576 0.0294675 29.3967 0 29.2102V0.584649C0.03929 0.33635 0.171052 0.104259 0.403986 0.0286256C0.461938 0.00975233 0.524452 0.00954185 0.58465 0H29.2105C29.2702 0.00954185 29.3305 0.0190837 29.3908 0.0286256C29.5592 0.114292 29.7101 0.230548 29.7662 0.403985C29.7851 0.461938 29.7851 0.524451 29.795 0.584649V6.21595L35.4394 6.20683C35.4997 6.21631 35.5601 6.22571 35.6204 6.23518C35.7888 6.32077 35.9397 6.43703 35.9965 6.61061C36.0154 6.6687 36.0154 6.73121 36.0253 6.79148V35.4147C35.986 35.6629 35.8541 35.8951 35.6211 35.9707C35.5629 35.9896 35.5004 35.9898 35.4401 35.9994L6.80637 36C6.68513 35.9808 6.56074 35.9597 6.46265 35.8884C6.3151 35.7812 6.25119 35.6018 6.22165 35.4154V29.7949ZM34.8557 30.2975H7.39102V34.8307L34.8557 34.8301V30.2975ZM28.6633 15.4452L23.063 20.8494L31.1722 28.9086C31.2424 29.0051 31.2852 29.0454 31.3133 29.1281H34.8557V21.229L28.6633 15.4452ZM8.27743 29.0783C8.28087 29.0948 8.28353 29.1114 8.28557 29.1281H29.7339L19.241 18.6996L8.27743 29.0783ZM28.6254 6.21785V1.1693H1.1693V28.6256H6.22165V6.83772C6.2408 6.71669 6.26199 6.59244 6.33307 6.49442C6.44006 6.34694 6.61918 6.28282 6.80539 6.25307L28.6254 6.21785ZM34.8557 19.6289V7.37711C25.7011 7.39192 16.5459 7.40665 7.39102 7.42146V28.3073L18.8489 17.4606C18.996 17.3592 18.9973 17.3585 19.1681 17.3064C19.228 17.3072 19.2878 17.3079 19.3475 17.3086C19.5171 17.3648 19.5185 17.3655 19.663 17.4705L22.233 20.0249L28.2507 14.2183C28.4444 14.0476 28.4507 14.0362 28.7306 14.059C28.9264 14.0927 28.9629 14.1322 29.0562 14.2117L34.8557 19.6289ZM12.9322 9.93019C14.3966 9.94408 15.7827 11.0726 16.0626 12.5321C16.2614 13.5688 15.9096 14.6949 15.1526 15.4341C14.2682 16.2976 12.862 16.5852 11.6988 16.1097C10.6719 15.6898 9.89786 14.7049 9.73691 13.6065C9.53829 12.2509 10.3114 10.8116 11.5556 10.2303C11.9847 10.0299 12.4574 9.92865 12.9322 9.93019ZM12.8982 11.0995C11.9719 11.1083 11.1014 11.8117 10.9137 12.7275C10.653 14 11.8357 15.4123 13.2853 15.1437C14.1119 14.9905 14.8064 14.274 14.929 13.437C15.0953 12.3025 14.1666 11.1191 12.938 11.0996C12.9247 11.0995 12.9115 11.0995 12.8982 11.0995Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -5890,6 +5890,10 @@
"context": "webhook events",
"string": "Expand or restrict webhooks permissions to register certain events in Saleor system."
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3316426878": {
"context": "event",
"string": "Product updated"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3345061702": {
"context": "event",
"string": "Order fully paid"

View file

@ -2640,6 +2640,7 @@ type Mutation {
productClearPrivateMetadata(id: ID!, input: MetaPath!): ProductClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.")
productSetAvailabilityForPurchase(isAvailable: Boolean!, productId: ID!, startDate: Date): ProductSetAvailabilityForPurchase
productImageCreate(input: ProductImageCreateInput!): ProductImageCreate
productVariantReorder(moves: [ReorderInput]!, productId: ID!): ProductVariantReorder
productImageDelete(id: ID!): ProductImageDelete
productImageBulkDelete(ids: [ID]!): ProductImageBulkDelete
productImageReorder(imagesIds: [ID]!, productId: ID!): ProductImageReorder
@ -2999,6 +3000,7 @@ enum OrderErrorCode {
REQUIRED
SHIPPING_METHOD_NOT_APPLICABLE
SHIPPING_METHOD_REQUIRED
TAX_ERROR
UNIQUE
VOID_INACTIVE_PAYMENT
ZERO_QUANTITY
@ -4174,6 +4176,12 @@ input ProductVariantInput {
weight: WeightScalar
}
type ProductVariantReorder {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
product: Product
productErrors: [ProductError!]!
}
type ProductVariantStocksCreate {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
productVariant: ProductVariant
@ -5600,6 +5608,7 @@ enum WebhookEventTypeEnum {
INVOICE_SENT
CUSTOMER_CREATED
PRODUCT_CREATED
PRODUCT_UPDATED
CHECKOUT_QUANTITY_CHANGED
CHECKOUT_CREATED
CHECKOUT_UPDATED
@ -5622,6 +5631,7 @@ enum WebhookSampleEventTypeEnum {
INVOICE_SENT
CUSTOMER_CREATED
PRODUCT_CREATED
PRODUCT_UPDATED
CHECKOUT_QUANTITY_CHANGED
CHECKOUT_CREATED
CHECKOUT_UPDATED

View file

@ -34,7 +34,7 @@ const useStyles = makeStyles(
marginTop: theme.spacing(0.5),
transition: theme.transitions.duration.standard + "ms",
[theme.breakpoints.down("sm")]: {
margin: theme.spacing(4, 0, 3, 0)
margin: theme.spacing(4, 0, 0, 0)
}
},
skeleton: {

View file

@ -56,12 +56,24 @@ const useStyles = makeStyles(
marginRight: theme.spacing(2)
},
header: {
display: "grid",
gridTemplateAreas: `"headerAnchor headerToolbar"`,
[theme.breakpoints.down("sm")]: {
gridTemplateAreas: `"headerToolbar"
"headerAnchor"`
},
marginBottom: theme.spacing(3)
},
headerAnchor: {
gridArea: "headerAnchor"
},
headerToolbar: {
display: "flex",
gridArea: "headerToolbar",
height: 40,
[theme.breakpoints.down("sm")]: {
height: "auto"
},
display: "flex",
height: 40,
marginBottom: theme.spacing(3)
}
},
root: {
@ -162,39 +174,43 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
<div>
<Container>
<div className={classes.header}>
{isMdUp && <div ref={appHeaderAnchor} />}
{!isMdUp && (
<SideBarDrawer
menuItems={menuStructure}
location={location.pathname}
user={user}
renderConfigure={renderConfigure}
onMenuItemClick={navigate}
/>
)}
<div className={classes.spacer} />
<div className={classes.userBar}>
<ThemeSwitch
className={classes.darkThemeSwitch}
checked={isDark}
onClick={toggleTheme}
/>
<NavigatorButton
isMac={navigator.platform
.toLowerCase()
.includes("mac")}
onClick={() => setNavigatorVisibility(true)}
/>
<UserChip
onLogout={logout}
onProfileClick={() =>
navigate(staffMemberDetailsUrl(user.id))
}
user={user}
/>
<div
className={classes.headerAnchor}
ref={appHeaderAnchor}
/>
<div className={classes.headerToolbar}>
{!isMdUp && (
<SideBarDrawer
menuItems={menuStructure}
location={location.pathname}
user={user}
renderConfigure={renderConfigure}
onMenuItemClick={navigate}
/>
)}
<div className={classes.spacer} />
<div className={classes.userBar}>
<ThemeSwitch
className={classes.darkThemeSwitch}
checked={isDark}
onClick={toggleTheme}
/>
<NavigatorButton
isMac={navigator.platform
.toLowerCase()
.includes("mac")}
onClick={() => setNavigatorVisibility(true)}
/>
<UserChip
onLogout={logout}
onProfileClick={() =>
navigate(staffMemberDetailsUrl(user.id))
}
user={user}
/>
</div>
</div>
</div>
{!isMdUp && <div ref={appHeaderAnchor} />}
</Container>
</div>
<main className={classes.view}>

View file

@ -15,6 +15,7 @@ interface ImageUploadProps {
isActiveClassName?: string;
iconContainerClassName?: string;
iconContainerActiveClassName?: string;
hideUploadIcon?: boolean;
onImageUpload: (file: FileList) => void;
}
@ -66,6 +67,7 @@ export const ImageUpload: React.FC<ImageUploadProps> = props => {
iconContainerActiveClassName,
iconContainerClassName,
isActiveClassName,
hideUploadIcon,
onImageUpload
} = props;
@ -82,24 +84,26 @@ export const ImageUpload: React.FC<ImageUploadProps> = props => {
[isActiveClassName]: isDragActive
})}
>
<div
className={classNames(iconContainerClassName, {
[iconContainerActiveClassName]: isDragActive
})}
>
<input
{...getInputProps()}
className={classes.fileField}
accept="image/*"
/>
<ImageIcon className={classes.photosIcon} />
<Typography className={classes.uploadText}>
<FormattedMessage
defaultMessage="Drop here to upload"
description="image upload"
{!hideUploadIcon && (
<div
className={classNames(iconContainerClassName, {
[iconContainerActiveClassName]: isDragActive
})}
>
<input
{...getInputProps()}
className={classes.fileField}
accept="image/*"
/>
</Typography>
</div>
<ImageIcon className={classes.photosIcon} />
<Typography className={classes.uploadText}>
<FormattedMessage
defaultMessage="Drop here to upload"
description="image upload"
/>
</Typography>
</div>
)}
</div>
{children && children({ isDragActive })}
</>

View file

@ -241,7 +241,10 @@ export const VisibilityCard: React.FC<VisibilityCardProps> = props => {
const { value } = e.target;
if (!value) {
onChange({
target: { name: "availableForPurchase", value: null }
target: {
name: "availableForPurchase",
value: null
}
});
}
return onChange(e);

View file

@ -83,9 +83,6 @@ const useStyles = makeStyles(
imageUploadActive: {
zIndex: 1
},
imageUploadIcon: {
display: "none"
},
imageUploadIconActive: {
display: "block"
},
@ -253,7 +250,7 @@ const ProductImages: React.FC<ProductImagesProps> = props => {
className={classes.imageUpload}
isActiveClassName={classes.imageUploadActive}
disableClick={true}
iconContainerClassName={classes.imageUploadIcon}
hideUploadIcon={true}
iconContainerActiveClassName={classes.imageUploadIconActive}
onImageUpload={handleImageUpload}
>

View file

@ -18,7 +18,7 @@ import { sectionNames } from "@saleor/intl";
import { maybe } from "@saleor/misc";
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
import { FetchMoreProps, ListActions } from "@saleor/types";
import { FetchMoreProps, ListActions, ReorderAction } from "@saleor/types";
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
@ -73,6 +73,7 @@ export interface ProductUpdatePageProps extends ListActions {
fetchCollections: (query: string) => void;
onVariantsAdd: () => void;
onVariantShow: (id: string) => () => void;
onVariantReorder: ReorderAction;
onImageDelete: (id: string) => () => void;
onBack?();
onDelete();
@ -120,6 +121,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
onVariantAdd,
onVariantsAdd,
onVariantShow,
onVariantReorder,
isChecked,
selected,
toggle,
@ -302,6 +304,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
onRowClick={onVariantShow}
onVariantAdd={onVariantAdd}
onVariantsAdd={onVariantsAdd}
onVariantReorder={onVariantReorder}
toolbar={toolbar}
isChecked={isChecked}
selected={selected}

View file

@ -14,6 +14,7 @@ import useFormset, {
} from "@saleor/hooks/useFormset";
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
import { ReorderAction } from "@saleor/types";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import React from "react";
import { useIntl } from "react-intl";
@ -56,6 +57,7 @@ interface ProductVariantCreatePageProps {
onBack: () => void;
onSubmit: (data: ProductVariantCreatePageSubmitData) => void;
onVariantClick: (variantId: string) => void;
onVariantReorder: ReorderAction;
}
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
@ -69,7 +71,8 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
weightUnit,
onBack,
onSubmit,
onVariantClick
onVariantClick,
onVariantReorder
}) => {
const intl = useIntl();
const attributeInput = React.useMemo(
@ -131,6 +134,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
return onVariantClick(variantId);
}
}}
onReorder={onVariantReorder}
/>
</div>
<div>

View file

@ -1,13 +1,17 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import { makeStyles } from "@material-ui/core/styles";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableRow from "@material-ui/core/TableRow";
import CardTitle from "@saleor/components/CardTitle";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton";
import {
SortableTableBody,
SortableTableRow
} from "@saleor/components/SortableTable";
import TableCellAvatar from "@saleor/components/TableCellAvatar";
import { ReorderAction } from "@saleor/types";
import classNames from "classnames";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -26,16 +30,18 @@ const useStyles = makeStyles(
cursor: "pointer"
},
tabActive: {
"&:before": {
background: theme.palette.primary.main,
content: '""',
height: "100%",
left: 0,
position: "absolute",
top: 0,
width: 2
},
position: "relative"
"& > td:first-child": {
"&:before": {
background: theme.palette.primary.main,
content: '""',
height: "100%",
left: 0,
position: "absolute",
top: 0,
width: 2
},
position: "relative"
}
}
}),
{ name: "ProductVariantNavigation" }
@ -49,10 +55,18 @@ interface ProductVariantNavigationProps {
| ProductVariantCreateData_product_variants[];
onAdd?: () => void;
onRowClick: (variantId: string) => void;
onReorder: ReorderAction;
}
const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props => {
const { current, fallbackThumbnail, variants, onAdd, onRowClick } = props;
const {
current,
fallbackThumbnail,
variants,
onAdd,
onRowClick,
onReorder
} = props;
const classes = useStyles(props);
const intl = useIntl();
@ -66,18 +80,18 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
})}
/>
<ResponsiveTable>
<TableBody>
{renderCollection(variants, variant => (
<TableRow
<SortableTableBody onSortEnd={onReorder}>
{renderCollection(variants, (variant, variantIndex) => (
<SortableTableRow
hover={!!variant}
key={variant ? variant.id : "skeleton"}
className={classes.link}
index={variantIndex}
className={classNames(classes.link, {
[classes.tabActive]: variant && variant.id === current
})}
onClick={variant ? () => onRowClick(variant.id) : undefined}
>
<TableCellAvatar
className={classNames({
[classes.tabActive]: variant && variant.id === current
})}
thumbnail={maybe(
() => variant.images[0].url,
fallbackThumbnail
@ -86,11 +100,11 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
<TableCell className={classes.colName}>
{variant ? variant.name || variant.sku : <Skeleton />}
</TableCell>
</TableRow>
</SortableTableRow>
))}
{onAdd ? (
<TableRow>
<TableCell colSpan={2}>
<TableCell colSpan={3}>
<Button color="primary" onClick={onAdd}>
<FormattedMessage
defaultMessage="Add variant"
@ -102,7 +116,7 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
) : (
<TableRow>
<TableCellAvatar className={classes.tabActive} thumbnail={null} />
<TableCell className={classes.colName}>
<TableCell className={classes.colName} colSpan={2}>
<FormattedMessage
defaultMessage="New Variant"
description="variant name"
@ -110,7 +124,7 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
</TableCell>
</TableRow>
)}
</TableBody>
</SortableTableBody>
</ResponsiveTable>
</Card>
);

View file

@ -19,6 +19,7 @@ import {
getAttributeInputFromVariant,
getStockInputFromVariant
} from "@saleor/products/utils/data";
import { ReorderAction } from "@saleor/types";
import { mapMetadataItemToInput } from "@saleor/utils/maps";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import { diff } from "fast-array-diff";
@ -60,6 +61,7 @@ interface ProductVariantPageProps {
placeholderImage?: string;
header: string;
warehouses: WarehouseFragment[];
onVariantReorder: ReorderAction;
onAdd();
onBack();
onDelete();
@ -82,7 +84,8 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
onDelete,
onImageSelect,
onSubmit,
onVariantClick
onVariantClick,
onVariantReorder
}) => {
const attributeInput = React.useMemo(
() => getAttributeInputFromVariant(variant),
@ -188,6 +191,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
return onVariantClick(variantId);
}
}}
onReorder={onVariantReorder}
/>
</div>
<div>

View file

@ -3,9 +3,7 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Hidden from "@material-ui/core/Hidden";
import { makeStyles } from "@material-ui/core/styles";
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 CardTitle from "@saleor/components/CardTitle";
import Checkbox from "@saleor/components/Checkbox";
@ -14,13 +12,17 @@ import Money from "@saleor/components/Money";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import Skeleton from "@saleor/components/Skeleton";
import {
SortableTableBody,
SortableTableRow
} from "@saleor/components/SortableTable";
import TableHead from "@saleor/components/TableHead";
import { ProductVariant_costPrice } from "@saleor/fragments/types/ProductVariant";
import React from "react";
import { FormattedMessage, IntlShape, useIntl } from "react-intl";
import { maybe, renderCollection } from "../../../misc";
import { ListActions } from "../../../types";
import { ListActions, ReorderAction } from "../../../types";
import {
ProductDetails_product_variants,
ProductDetails_product_variants_stocks_warehouse
@ -171,12 +173,13 @@ interface ProductVariantsProps extends ListActions {
disabled: boolean;
variants: ProductDetails_product_variants[];
fallbackPrice?: ProductVariant_costPrice;
onVariantReorder: ReorderAction;
onRowClick: (id: string) => () => void;
onVariantAdd?();
onVariantsAdd?();
}
const numberOfColumns = 5;
const numberOfColumns = 6;
export const ProductVariants: React.FC<ProductVariantsProps> = props => {
const {
@ -186,6 +189,7 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
onRowClick,
onVariantAdd,
onVariantsAdd,
onVariantReorder,
isChecked,
selected,
toggle,
@ -266,6 +270,7 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
items={variants}
toggleAll={toggleAll}
toolbar={toolbar}
dragRows
>
<TableCell className={classes.colName}>
<FormattedMessage
@ -291,8 +296,8 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
/>
</TableCell>
</TableHead>
<TableBody>
{renderCollection(variants, variant => {
<SortableTableBody onSortEnd={onVariantReorder}>
{renderCollection(variants, (variant, variantIndex) => {
const isSelected = variant ? isChecked(variant.id) : false;
const numAvailable =
variant && variant.stocks
@ -303,11 +308,12 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
: null;
return (
<TableRow
<SortableTableRow
selected={isSelected}
hover={!!variant}
onClick={onRowClick(variant.id)}
key={variant ? variant.id : "skeleton"}
index={variantIndex || 0}
className={classes.link}
>
<TableCell padding="checkbox">
@ -354,10 +360,10 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
)
)}
</TableCell>
</TableRow>
</SortableTableRow>
);
})}
</TableBody>
</SortableTableBody>
</ResponsiveTable>
)}
</Card>

View file

@ -53,6 +53,10 @@ import {
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
} from "./types/ProductVariantBulkDelete";
import {
ProductVariantReorder,
ProductVariantReorderVariables
} from "./types/ProductVariantReorder";
import {
SimpleProductUpdate,
SimpleProductUpdateVariables
@ -615,6 +619,11 @@ const productSetAvailabilityForPurchase = gql`
productId: $productId
startDate: $startDate
) {
product {
id
availableForPurchase
isAvailableForPurchase
}
errors: productErrors {
...ProductErrorFragment
message
@ -627,3 +636,22 @@ export const useProductSetAvailabilityForPurchase = makeMutation<
ProductSetAvailabilityForPurchase,
ProductSetAvailabilityForPurchaseVariables
>(productSetAvailabilityForPurchase);
const productVariantReorder = gql`
${productErrorFragment}
${productFragmentDetails}
mutation ProductVariantReorder($move: ReorderInput!, $productId: ID!) {
productVariantReorder(moves: [$move], productId: $productId) {
errors: productErrors {
...ProductErrorFragment
}
product {
...Product
}
}
}
`;
export const useProductVariantReorderMutation = makeMutation<
ProductVariantReorder,
ProductVariantReorderVariables
>(productVariantReorder);

View file

@ -8,6 +8,13 @@ import { ProductErrorCode } from "./../../types/globalTypes";
// GraphQL mutation operation: ProductSetAvailabilityForPurchase
// ====================================================
export interface ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase_product {
__typename: "Product";
id: string;
availableForPurchase: any | null;
isAvailableForPurchase: boolean | null;
}
export interface ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase_errors {
__typename: "ProductError";
code: ProductErrorCode;
@ -17,6 +24,7 @@ export interface ProductSetAvailabilityForPurchase_productSetAvailabilityForPurc
export interface ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase {
__typename: "ProductSetAvailabilityForPurchase";
product: ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase_product | null;
errors: ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase_errors[];
}

View file

@ -0,0 +1,236 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { ReorderInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: ProductVariantReorder
// ====================================================
export interface ProductVariantReorder_productVariantReorder_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface ProductVariantReorder_productVariantReorder_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductVariantReorder_productVariantReorder_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductVariantReorder_productVariantReorder_product_attributes_attribute_values | null)[] | null;
}
export interface ProductVariantReorder_productVariantReorder_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductVariantReorder_productVariantReorder_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductVariantReorder_productVariantReorder_product_attributes_attribute;
values: (ProductVariantReorder_productVariantReorder_product_attributes_values | null)[];
}
export interface ProductVariantReorder_productVariantReorder_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductVariantReorder_productVariantReorder_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (ProductVariantReorder_productVariantReorder_product_productType_variantAttributes_values | null)[] | null;
}
export interface ProductVariantReorder_productVariantReorder_product_productType {
__typename: "ProductType";
id: string;
variantAttributes: (ProductVariantReorder_productVariantReorder_product_productType_variantAttributes | null)[] | null;
name: string;
hasVariants: boolean;
}
export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start {
__typename: "TaxedMoney";
gross: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start_gross;
}
export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop {
__typename: "TaxedMoney";
gross: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop_gross;
}
export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted {
__typename: "TaxedMoneyRange";
start: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start | null;
stop: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop | null;
}
export interface ProductVariantReorder_productVariantReorder_product_pricing {
__typename: "ProductPricingInfo";
priceRangeUndiscounted: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted | null;
}
export interface ProductVariantReorder_productVariantReorder_product_metadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface ProductVariantReorder_productVariantReorder_product_privateMetadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface ProductVariantReorder_productVariantReorder_product_category {
__typename: "Category";
id: string;
name: string;
}
export interface ProductVariantReorder_productVariantReorder_product_collections {
__typename: "Collection";
id: string;
name: string;
}
export interface ProductVariantReorder_productVariantReorder_product_margin {
__typename: "Margin";
start: number | null;
stop: number | null;
}
export interface ProductVariantReorder_productVariantReorder_product_purchaseCost_start {
__typename: "Money";
amount: number;
currency: string;
}
export interface ProductVariantReorder_productVariantReorder_product_purchaseCost_stop {
__typename: "Money";
amount: number;
currency: string;
}
export interface ProductVariantReorder_productVariantReorder_product_purchaseCost {
__typename: "MoneyRange";
start: ProductVariantReorder_productVariantReorder_product_purchaseCost_start | null;
stop: ProductVariantReorder_productVariantReorder_product_purchaseCost_stop | null;
}
export interface ProductVariantReorder_productVariantReorder_product_images {
__typename: "ProductImage";
id: string;
alt: string;
sortOrder: number | null;
url: string;
}
export interface ProductVariantReorder_productVariantReorder_product_variants_price {
__typename: "Money";
amount: number;
currency: string;
}
export interface ProductVariantReorder_productVariantReorder_product_variants_stocks_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface ProductVariantReorder_productVariantReorder_product_variants_stocks {
__typename: "Stock";
id: string;
quantity: number;
quantityAllocated: number;
warehouse: ProductVariantReorder_productVariantReorder_product_variants_stocks_warehouse;
}
export interface ProductVariantReorder_productVariantReorder_product_variants {
__typename: "ProductVariant";
id: string;
sku: string;
name: string;
price: ProductVariantReorder_productVariantReorder_product_variants_price | null;
margin: number | null;
stocks: (ProductVariantReorder_productVariantReorder_product_variants_stocks | null)[] | null;
trackInventory: boolean;
}
export interface ProductVariantReorder_productVariantReorder_product_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
value: number;
}
export interface ProductVariantReorder_productVariantReorder_product {
__typename: "Product";
id: string;
attributes: ProductVariantReorder_productVariantReorder_product_attributes[];
productType: ProductVariantReorder_productVariantReorder_product_productType;
pricing: ProductVariantReorder_productVariantReorder_product_pricing | null;
metadata: (ProductVariantReorder_productVariantReorder_product_metadata | null)[];
privateMetadata: (ProductVariantReorder_productVariantReorder_product_privateMetadata | null)[];
name: string;
descriptionJson: any;
seoTitle: string | null;
seoDescription: string | null;
category: ProductVariantReorder_productVariantReorder_product_category | null;
collections: (ProductVariantReorder_productVariantReorder_product_collections | null)[] | null;
margin: ProductVariantReorder_productVariantReorder_product_margin | null;
purchaseCost: ProductVariantReorder_productVariantReorder_product_purchaseCost | null;
isAvailableForPurchase: boolean | null;
isAvailable: boolean | null;
isPublished: boolean;
chargeTaxes: boolean;
publicationDate: any | null;
images: (ProductVariantReorder_productVariantReorder_product_images | null)[] | null;
variants: (ProductVariantReorder_productVariantReorder_product_variants | null)[] | null;
weight: ProductVariantReorder_productVariantReorder_product_weight | null;
availableForPurchase: any | null;
visibleInListings: boolean;
}
export interface ProductVariantReorder_productVariantReorder {
__typename: "ProductVariantReorder";
errors: ProductVariantReorder_productVariantReorder_errors[];
product: ProductVariantReorder_productVariantReorder_product | null;
}
export interface ProductVariantReorder {
productVariantReorder: ProductVariantReorder_productVariantReorder | null;
}
export interface ProductVariantReorderVariables {
move: ReorderInput;
productId: string;
}

View file

@ -116,3 +116,30 @@ export function createProductTypeSelectHandler(
);
};
}
interface ProductAvailabilityArgs {
availableForPurchase: string | null;
isAvailableForPurchase: boolean;
productId: string;
}
export function getProductAvailabilityVariables({
isAvailableForPurchase,
availableForPurchase,
productId
}: ProductAvailabilityArgs) {
const isAvailable =
availableForPurchase && !isAvailableForPurchase
? true
: isAvailableForPurchase;
return {
isAvailable,
productId,
startDate: isAvailableForPurchase
? null
: availableForPurchase !== ""
? availableForPurchase
: null
};
}

View file

@ -3,6 +3,7 @@ import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop";
import { getProductAvailabilityVariables } from "@saleor/products/utils/handlers";
import useCategorySearch from "@saleor/searches/useCategorySearch";
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
import useProductTypeSearch from "@saleor/searches/useProductTypeSearch";
@ -19,7 +20,10 @@ import { decimal, weight } from "../../misc";
import ProductCreatePage, {
ProductCreatePageSubmitData
} from "../components/ProductCreatePage";
import { useProductCreateMutation } from "../mutations";
import {
useProductCreateMutation,
useProductSetAvailabilityForPurchase
} from "../mutations";
import { productListUrl, productUrl } from "../urls";
export const ProductCreateView: React.FC = () => {
@ -59,6 +63,18 @@ export const ProductCreateView: React.FC = () => {
const handleBack = () => navigate(productListUrl());
const [
setProductAvailability,
productAvailabilityOpts
] = useProductSetAvailabilityForPurchase({
onCompleted: data => {
const errors = data?.productSetAvailabilityForPurchase?.errors;
if (errors?.length === 0) {
navigate(productUrl(data.productSetAvailabilityForPurchase.product.id));
}
}
});
const [productCreate, productCreateOpts] = useProductCreateMutation({
onCompleted: data => {
if (data.productCreate.errors.length === 0) {
@ -68,7 +84,6 @@ export const ProductCreateView: React.FC = () => {
defaultMessage: "Product created"
})
});
navigate(productUrl(data.productCreate.product.id));
}
}
});
@ -101,11 +116,28 @@ export const ProductCreateView: React.FC = () => {
warehouse: stock.id
})),
trackInventory: formData.trackInventory,
visibleInListings: formData.visibleInListings,
weight: weight(formData.weight)
}
});
return result.data.productCreate?.product?.id || null;
const productId = result.data.productCreate?.product?.id;
if (productId) {
const { isAvailableForPurchase, availableForPurchase } = formData;
const variables = getProductAvailabilityVariables({
availableForPurchase,
isAvailableForPurchase,
productId
});
setProductAvailability({
variables
});
}
return productId || null;
};
const handleSubmit = createMetadataCreateHandler(
handleCreate,
@ -129,7 +161,7 @@ export const ProductCreateView: React.FC = () => {
collections={(searchCollectionOpts.data?.search.edges || []).map(
edge => edge.node
)}
disabled={productCreateOpts.loading}
disabled={productCreateOpts.loading || productAvailabilityOpts.loading}
errors={productCreateOpts.data?.productCreate.errors || []}
fetchCategories={searchCategory}
fetchCollections={searchCollection}

View file

@ -10,6 +10,7 @@ import useBulkActions from "@saleor/hooks/useBulkActions";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { commonMessages } from "@saleor/intl";
import {
useProductDeleteMutation,
@ -19,6 +20,7 @@ import {
useProductSetAvailabilityForPurchase,
useProductUpdateMutation,
useProductVariantBulkDeleteMutation,
useProductVariantReorderMutation,
useSimpleProductUpdateMutation
} from "@saleor/products/mutations";
import useCategorySearch from "@saleor/searches/useCategorySearch";
@ -51,7 +53,8 @@ import {
import {
createImageReorderHandler,
createImageUploadHandler,
createUpdateHandler
createUpdateHandler,
createVariantReorderHandler
} from "./handlers";
interface ProductUpdateProps {
@ -177,6 +180,12 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
onCompleted: data => {
const errors = data?.productSetAvailabilityForPurchase?.errors;
if (errors?.length === 0) {
const updatedProduct = data?.productSetAvailabilityForPurchase?.product;
setProduct(product => ({
...product,
availableForPurchase: updatedProduct.availableForPurchase,
isAvailableForPurchase: updatedProduct.isAvailableForPurchase
}));
notify({
status: "success",
text: intl.formatMessage({
@ -195,7 +204,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
const handleBack = () => navigate(productListUrl());
const product = data?.product;
const [product, setProduct] = useStateFromProps(data?.product);
if (product === null) {
return <NotFoundPage onBack={handleBack} />;
@ -225,12 +234,22 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
reorderProductImages({ variables })
);
const [
reorderProductVariants,
reorderProductVariantsOpts
] = useProductVariantReorderMutation({});
const handleVariantReorder = createVariantReorderHandler(product, variables =>
reorderProductVariants({ variables })
);
const disableFormSave =
createProductImageOpts.loading ||
deleteProductOpts.loading ||
reorderProductImagesOpts.loading ||
updateProductOpts.loading ||
productAvailabilityOpts.loading ||
reorderProductVariantsOpts.loading ||
loading;
const formTransitionState = getMutationState(
@ -282,6 +301,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
onVariantShow={variantId => () =>
navigate(productVariantEditUrl(product.id, variantId))}
onVariantReorder={handleVariantReorder}
onImageUpload={handleImageUpload}
onImageEdit={handleImageEdit}
onImageDelete={handleImageDelete}

View file

@ -14,11 +14,15 @@ import {
ProductUpdate,
ProductUpdateVariables
} from "@saleor/products/types/ProductUpdate";
import { ProductVariantCreateData_product } from "@saleor/products/types/ProductVariantCreateData";
import { ProductVariantDetails_productVariant_product } from "@saleor/products/types/ProductVariantDetails";
import { ProductVariantReorderVariables } from "@saleor/products/types/ProductVariantReorder";
import {
SimpleProductUpdate,
SimpleProductUpdateVariables
} from "@saleor/products/types/SimpleProductUpdate";
import { mapFormsetStockToStockInput } from "@saleor/products/utils/data";
import { getProductAvailabilityVariables } from "@saleor/products/utils/handlers";
import { ReorderEvent } from "@saleor/types";
import { MutationFetchResult } from "react-apollo";
import { arrayMove } from "react-sortable-hoc";
@ -92,20 +96,13 @@ export function createUpdateHandler(
isAvailableForPurchase !== product.isAvailableForPurchase ||
availableForPurchase !== product.availableForPurchase
) {
const isAvailable =
availableForPurchase && !isAvailableForPurchase
? true
: isAvailableForPurchase;
const availabilityResult = await setProductAvailability({
isAvailable,
productId: product.id,
startDate: isAvailableForPurchase
? null
: availableForPurchase !== ""
? availableForPurchase
: null
const variables = getProductAvailabilityVariables({
availableForPurchase,
isAvailableForPurchase,
productId: product.id
});
const availabilityResult = await setProductAvailability(variables);
errors = [
...errors,
...availabilityResult.data.productSetAvailabilityForPurchase.errors
@ -141,3 +138,21 @@ export function createImageReorderHandler(
});
};
}
export function createVariantReorderHandler(
product:
| ProductDetails_product
| ProductVariantDetails_productVariant_product
| ProductVariantCreateData_product,
reorderProductVariants: (variables: ProductVariantReorderVariables) => void
) {
return ({ newIndex, oldIndex }: ReorderEvent) => {
reorderProductVariants({
move: {
id: product.variants[oldIndex].id,
sortOrder: newIndex - oldIndex
},
productId: product.id
});
};
}

View file

@ -21,6 +21,7 @@ import ProductVariantPage, {
ProductVariantPageSubmitData
} from "../components/ProductVariantPage";
import {
useProductVariantReorderMutation,
useVariantDeleteMutation,
useVariantImageAssignMutation,
useVariantImageUnassignMutation,
@ -36,6 +37,7 @@ import {
ProductVariantEditUrlQueryParams
} from "../urls";
import { mapFormsetStockToStockInput } from "../utils/data";
import { createVariantReorderHandler } from "./ProductUpdate/handlers";
interface ProductUpdateProps {
variantId: string;
@ -120,12 +122,23 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
return <NotFoundPage onBack={handleBack} />;
}
const [
reorderProductVariants,
reorderProductVariantsOpts
] = useProductVariantReorderMutation({});
const handleVariantReorder = createVariantReorderHandler(
variant?.product,
variables => reorderProductVariants({ variables })
);
const disableFormSave =
loading ||
deleteVariantOpts.loading ||
updateVariantOpts.loading ||
assignImageOpts.loading ||
unassignImageOpts.loading;
unassignImageOpts.loading ||
reorderProductVariantsOpts.loading;
const handleImageSelect = (id: string) => () => {
if (variant) {
@ -202,6 +215,7 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
onVariantClick={variantId => {
navigate(productVariantEditUrl(productId, variantId));
}}
onVariantReorder={handleVariantReorder}
/>
<ProductVariantDeleteDialog
confirmButtonState={deleteVariantOpts.status}

View file

@ -17,9 +17,13 @@ import { decimal, weight } from "../../misc";
import ProductVariantCreatePage, {
ProductVariantCreatePageSubmitData
} from "../components/ProductVariantCreatePage";
import { useVariantCreateMutation } from "../mutations";
import {
useProductVariantReorderMutation,
useVariantCreateMutation
} from "../mutations";
import { useProductVariantCreateQuery } from "../queries";
import { productListUrl, productUrl, productVariantEditUrl } from "../urls";
import { createVariantReorderHandler } from "./ProductUpdate/handlers";
interface ProductVariantCreateProps {
productId: string;
@ -69,6 +73,15 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
return <NotFoundPage onBack={() => navigate(productListUrl())} />;
}
const [
reorderProductVariants,
reorderProductVariantsOpts
] = useProductVariantReorderMutation({});
const handleVariantReorder = createVariantReorderHandler(product, variables =>
reorderProductVariants({ variables })
);
const handleBack = () => navigate(productUrl(productId));
const handleCreate = async (formData: ProductVariantCreatePageSubmitData) => {
const result = await variantCreate({
@ -104,7 +117,10 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
const handleVariantClick = (id: string) =>
navigate(productVariantEditUrl(productId, id));
const disableForm = productLoading || variantCreateResult.loading;
const disableForm =
productLoading ||
variantCreateResult.loading ||
reorderProductVariantsOpts.loading;
return (
<>
@ -126,6 +142,7 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
onBack={handleBack}
onSubmit={handleSubmit}
onVariantClick={handleVariantClick}
onVariantReorder={handleVariantReorder}
saveButtonBarState={variantCreateResult.status}
warehouses={
warehouses.data?.warehouses.edges.map(edge => edge.node) || []

View file

@ -34,6 +34,14 @@ const useStyles = makeStyles(
position: "relative",
width: 120
},
avatarActionText: {
"&:hover": {
textDecoration: "underline"
},
color: "#fff",
cursor: "pointer",
fontSize: 12
},
avatarDefault: {
"& div": {
color: "#fff",
@ -47,17 +55,10 @@ const useStyles = makeStyles(
width: 120
},
avatarHover: {
"& p": {
"&:hover": {
textDecoration: "underline"
},
color: theme.palette.primary.main,
cursor: "pointer",
fontSize: 12,
fontWeight: 500
},
background: "#00000080",
borderRadius: "100%",
fontSize: 12,
fontWeight: 500,
height: 120,
opacity: 0,
padding: theme.spacing(2.5, 0),
@ -155,13 +156,19 @@ const StaffProperties: React.FC<StaffPropertiesProps> = props => {
{canEditAvatar && (
<div className={classes.avatarHover}>
<SVG src={photoIcon} />
<Typography onClick={clickImgInput}>
<Typography
onClick={clickImgInput}
className={classes.avatarActionText}
>
<FormattedMessage
defaultMessage="Change photo"
description="button"
/>
</Typography>
<Typography onClick={onImageDelete}>
<Typography
onClick={onImageDelete}
className={classes.avatarActionText}
>
<FormattedMessage
defaultMessage="Delete photo"
description="button"

File diff suppressed because it is too large Load diff

View file

@ -34,6 +34,7 @@ const props: ProductUpdatePageProps = {
onImageUpload: () => undefined,
onSubmit: () => undefined,
onVariantAdd: () => undefined,
onVariantReorder: () => undefined,
onVariantShow: () => undefined,
onVariantsAdd: () => undefined,
placeholderImage,

View file

@ -23,6 +23,7 @@ storiesOf("Views / Products / Create product variant", module)
onBack={() => undefined}
onSubmit={() => undefined}
onVariantClick={undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
warehouses={warehouseList}
/>
@ -54,6 +55,7 @@ storiesOf("Views / Products / Create product variant", module)
onBack={() => undefined}
onSubmit={() => undefined}
onVariantClick={undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
warehouses={warehouseList}
/>
@ -69,6 +71,7 @@ storiesOf("Views / Products / Create product variant", module)
onBack={() => undefined}
onSubmit={() => undefined}
onVariantClick={undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
warehouses={warehouseList}
/>
@ -87,6 +90,7 @@ storiesOf("Views / Products / Create product variant", module)
onBack={() => undefined}
onSubmit={() => undefined}
onVariantClick={undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
warehouses={warehouseList}
/>

View file

@ -24,6 +24,7 @@ storiesOf("Views / Products / Product variant details", module)
onImageSelect={() => undefined}
onSubmit={() => undefined}
onVariantClick={() => undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
warehouses={warehouseList}
/>
@ -41,6 +42,7 @@ storiesOf("Views / Products / Product variant details", module)
onImageSelect={() => undefined}
onSubmit={() => undefined}
onVariantClick={() => undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
warehouses={warehouseList}
/>
@ -56,6 +58,7 @@ storiesOf("Views / Products / Product variant details", module)
onImageSelect={() => undefined}
onSubmit={() => undefined}
onVariantClick={() => undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
errors={[
{

View file

@ -548,6 +548,7 @@ export enum OrderErrorCode {
REQUIRED = "REQUIRED",
SHIPPING_METHOD_NOT_APPLICABLE = "SHIPPING_METHOD_NOT_APPLICABLE",
SHIPPING_METHOD_REQUIRED = "SHIPPING_METHOD_REQUIRED",
TAX_ERROR = "TAX_ERROR",
UNIQUE = "UNIQUE",
VOID_INACTIVE_PAYMENT = "VOID_INACTIVE_PAYMENT",
ZERO_QUANTITY = "ZERO_QUANTITY",
@ -905,6 +906,7 @@ export enum WebhookEventTypeEnum {
ORDER_FULLY_PAID = "ORDER_FULLY_PAID",
ORDER_UPDATED = "ORDER_UPDATED",
PRODUCT_CREATED = "PRODUCT_CREATED",
PRODUCT_UPDATED = "PRODUCT_UPDATED",
}
export enum WebhookSortField {

View file

@ -68,6 +68,10 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
defaultMessage: "Product created",
description: "event"
}),
[WebhookEventTypeEnum.PRODUCT_UPDATED]: intl.formatMessage({
defaultMessage: "Product updated",
description: "event"
}),
[WebhookEventTypeEnum.CHECKOUT_QUANTITY_CHANGED]: intl.formatMessage({
defaultMessage: "Changed quantity in checkout",
description: "event"