Fix types
This commit is contained in:
parent
24cd398146
commit
f427edf550
15 changed files with 7 additions and 824 deletions
|
@ -19,9 +19,9 @@ import React from "react";
|
|||
import SVG from "react-inlinesvg";
|
||||
import { RouteComponentProps, withRouter } from "react-router";
|
||||
|
||||
import saleorDarkLogoSmall from "@assets/logo-dark-small.svg";
|
||||
import saleorDarkLogo from "@assets/logo-dark.svg";
|
||||
import menuArrowIcon from "@assets/menu-arrow-icon.svg";
|
||||
import saleorDarkLogoSmall from "@assets/images/logo-dark-small.svg";
|
||||
import saleorDarkLogo from "@assets/images/logo-dark.svg";
|
||||
import menuArrowIcon from "@assets/images/menu-arrow-icon.svg";
|
||||
import AppProgressProvider from "@saleor/components/AppProgress";
|
||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
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 DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
withStyles,
|
||||
WithStyles
|
||||
} from "@material-ui/core/styles";
|
||||
import React from "react";
|
||||
import NavigationPrompt from "react-router-navigation-prompt";
|
||||
|
||||
import i18n from "../i18n";
|
||||
import { FormContext } from "./Form";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
deleteButton: {
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.error.main
|
||||
},
|
||||
backgroundColor: theme.palette.error.main,
|
||||
color: theme.palette.error.contrastText
|
||||
}
|
||||
});
|
||||
|
||||
export const ConfirmFormLeaveDialog = withStyles(styles, {
|
||||
name: "ConfirmFormLeaveDialog"
|
||||
})(({ classes }: WithStyles<typeof styles>) => (
|
||||
<FormContext.Consumer>
|
||||
{({ hasChanged: hasFormChanged }) => (
|
||||
<NavigationPrompt renderIfNotActive={true} when={hasFormChanged}>
|
||||
{({ isActive, onCancel, onConfirm }) => (
|
||||
<Dialog onClose={onCancel} open={isActive}>
|
||||
<DialogTitle>{i18n.t("Unsaved changes")}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
{i18n.t(
|
||||
"If you leave this page, unsaved changes will be lost. Are you sure you want to leave?",
|
||||
{
|
||||
context: "form leave confirmation"
|
||||
}
|
||||
)}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>
|
||||
{i18n.t("Cancel", { context: "button" })}
|
||||
</Button>
|
||||
<Button onClick={onConfirm} className={classes.deleteButton}>
|
||||
{i18n.t("Leave page", { context: "button" })}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)}
|
||||
</NavigationPrompt>
|
||||
)}
|
||||
</FormContext.Consumer>
|
||||
));
|
|
@ -1,40 +0,0 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
import React from "react";
|
||||
|
||||
import i18n from "../../i18n";
|
||||
|
||||
const styles = createStyles({
|
||||
cardActions: {
|
||||
flexDirection: "row-reverse" as "row-reverse"
|
||||
}
|
||||
});
|
||||
|
||||
interface FormActionsProps extends WithStyles<typeof styles> {
|
||||
submitLabel: string;
|
||||
onCancel?();
|
||||
onSubmit?(event: React.FormEvent<any>);
|
||||
}
|
||||
|
||||
const FormActions = withStyles(styles, { name: "FormActions" })(
|
||||
({ classes, onCancel, onSubmit, submitLabel }: FormActionsProps) => (
|
||||
<Toolbar className={classes.cardActions}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={onSubmit}
|
||||
type="submit"
|
||||
>
|
||||
{submitLabel}
|
||||
</Button>
|
||||
{onCancel && (
|
||||
<Button onClick={onCancel}>
|
||||
{i18n.t("Cancel", { context: "button" })}
|
||||
</Button>
|
||||
)}
|
||||
</Toolbar>
|
||||
)
|
||||
);
|
||||
FormActions.displayName = "FormActions";
|
||||
export default FormActions;
|
|
@ -1,54 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import FormComponent, { FormProps } from "./Form";
|
||||
|
||||
interface IFormContext {
|
||||
hasChanged: boolean;
|
||||
toggle: () => void;
|
||||
}
|
||||
|
||||
export const FormContext = React.createContext<IFormContext>(undefined);
|
||||
|
||||
interface FormProviderState {
|
||||
hasChanged: boolean;
|
||||
}
|
||||
|
||||
export class FormProvider extends React.Component<{}, FormProviderState> {
|
||||
state: FormProviderState = {
|
||||
hasChanged: false
|
||||
};
|
||||
|
||||
toggle = () =>
|
||||
this.setState(prevState => ({
|
||||
hasChanged: !prevState.hasChanged
|
||||
}));
|
||||
|
||||
render() {
|
||||
return (
|
||||
<FormContext.Provider
|
||||
value={{
|
||||
hasChanged: this.state.hasChanged,
|
||||
toggle: this.toggle
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
</FormContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function Form<T>(props: FormProps<T>) {
|
||||
return (
|
||||
<FormContext.Consumer>
|
||||
{({ hasChanged, toggle }) => (
|
||||
<FormComponent
|
||||
{...props}
|
||||
toggleFormChangeState={toggle}
|
||||
hasChanged={hasChanged}
|
||||
/>
|
||||
)}
|
||||
</FormContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
export default Form;
|
|
@ -1,4 +1,2 @@
|
|||
export * from "./Form";
|
||||
export { default } from "./Form";
|
||||
export { default as FormActions } from "./FormActions";
|
||||
export * from "./FormActions";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Avatar from "@material-ui/core/Avatar";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import colors from "@material-ui/core/colors";
|
||||
import * as colors from "@material-ui/core/colors";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
|
|
|
@ -1,177 +0,0 @@
|
|||
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 TextField from "@material-ui/core/TextField";
|
||||
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 TextFieldWithChoice from "@saleor/components/TextFieldWithChoice";
|
||||
import i18n from "../../../i18n";
|
||||
import { FormErrors } from "../../../types";
|
||||
import { VoucherDiscountValueType } from "../../../types/globalTypes";
|
||||
import { FormData } from "../VoucherDetailsPage";
|
||||
|
||||
interface VoucherOptionsProps {
|
||||
data: FormData;
|
||||
defaultCurrency: string;
|
||||
disabled: boolean;
|
||||
errors: FormErrors<
|
||||
| "discountType"
|
||||
| "discountValue"
|
||||
| "endDate"
|
||||
| "minAmountSpent"
|
||||
| "startDate"
|
||||
| "usageLimit"
|
||||
>;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
display: "grid",
|
||||
gridColumnGap: theme.spacing.unit * 2 + "px",
|
||||
gridTemplateColumns: "1fr 1fr"
|
||||
}
|
||||
});
|
||||
|
||||
const VoucherOptions = withStyles(styles, {
|
||||
name: "VoucherOptions"
|
||||
})(
|
||||
({
|
||||
classes,
|
||||
data,
|
||||
defaultCurrency,
|
||||
disabled,
|
||||
errors,
|
||||
onChange
|
||||
}: VoucherOptionsProps & WithStyles<typeof styles>) => (
|
||||
<Card>
|
||||
<CardTitle title={i18n.t("Detailed Information")} />
|
||||
<CardContent className={classes.root}>
|
||||
<TextFieldWithChoice
|
||||
disabled={disabled}
|
||||
error={!!errors.discountValue}
|
||||
ChoiceProps={{
|
||||
label:
|
||||
data.discountType === VoucherDiscountValueType.FIXED
|
||||
? defaultCurrency
|
||||
: "%",
|
||||
name: "discountType" as keyof FormData,
|
||||
values: [
|
||||
{
|
||||
label: defaultCurrency,
|
||||
value: VoucherDiscountValueType.FIXED
|
||||
},
|
||||
{
|
||||
label: "%",
|
||||
value: VoucherDiscountValueType.PERCENTAGE
|
||||
}
|
||||
]
|
||||
}}
|
||||
helperText={errors.discountValue}
|
||||
name={"value" as keyof FormData}
|
||||
onChange={onChange}
|
||||
label={i18n.t("Discount Value")}
|
||||
value={data.value}
|
||||
type="number"
|
||||
fullWidth
|
||||
inputProps={{
|
||||
min: 0
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!errors.usageLimit}
|
||||
helperText={errors.usageLimit || i18n.t("Optional")}
|
||||
name={"usageLimit" as keyof FormData}
|
||||
value={data.usageLimit}
|
||||
onChange={onChange}
|
||||
label={i18n.t("Usage Limit")}
|
||||
type="number"
|
||||
inputProps={{
|
||||
min: 0
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
</CardContent>
|
||||
<Hr />
|
||||
<CardContent>
|
||||
<Typography variant="subtitle1">
|
||||
{i18n.t("Discount Specific Information")}
|
||||
</Typography>
|
||||
<FormSpacer />
|
||||
<div className={classes.root}>
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!errors.minAmountSpent}
|
||||
helperText={errors.minAmountSpent || i18n.t("Optional")}
|
||||
name={"minAmountSpent" as keyof FormData}
|
||||
value={data.minAmountSpent}
|
||||
onChange={onChange}
|
||||
label={i18n.t("Minimum order value")}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
<FormSpacer />
|
||||
<ControlledSwitch
|
||||
checked={data.applyOncePerOrder}
|
||||
label={
|
||||
<>
|
||||
{i18n.t("Only once per order", {
|
||||
context: "voucher application"
|
||||
})}
|
||||
<Typography variant="caption">
|
||||
{i18n.t(
|
||||
"If this option is disabled, discount will be counted for every eligible product"
|
||||
)}
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
onChange={onChange}
|
||||
name={"applyOncePerOrder" as keyof FormData}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</CardContent>
|
||||
<Hr />
|
||||
<CardContent>
|
||||
<Typography variant="subtitle1">{i18n.t("Time Frame")}</Typography>
|
||||
<FormSpacer />
|
||||
<div className={classes.root}>
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!errors.startDate}
|
||||
helperText={errors.startDate}
|
||||
name={"startDate" as keyof FormData}
|
||||
onChange={onChange}
|
||||
label={i18n.t("Start Date")}
|
||||
value={data.startDate}
|
||||
type="date"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!errors.endDate}
|
||||
helperText={errors.endDate}
|
||||
name={"endDate" as keyof FormData}
|
||||
onChange={onChange}
|
||||
label={i18n.t("End Date")}
|
||||
value={data.endDate}
|
||||
type="date"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
export default VoucherOptions;
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from "./VoucherOptions";
|
||||
export * from "./VoucherOptions";
|
|
@ -1,68 +0,0 @@
|
|||
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 "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import ProductList from "@saleor/components/ProductList";
|
||||
import { CategoryDetails_category_products_edges_node } from "../../../categories/types/CategoryDetails";
|
||||
import i18n from "../../../i18n";
|
||||
import { FilterPageProps, ListActions, PageListProps } from "../../../types";
|
||||
import { ProductListUrlFilters } from "../../urls";
|
||||
import ProductListFilter from "../ProductListFilter";
|
||||
|
||||
export interface ProductListCardProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
FilterPageProps<ProductListUrlFilters> {
|
||||
currencySymbol: string;
|
||||
products: CategoryDetails_category_products_edges_node[];
|
||||
}
|
||||
|
||||
export const ProductListCard: React.StatelessComponent<
|
||||
ProductListCardProps
|
||||
> = ({
|
||||
currencySymbol,
|
||||
currentTab,
|
||||
filtersList,
|
||||
filterTabs,
|
||||
initialSearch,
|
||||
onAdd,
|
||||
onAll,
|
||||
onSearchChange,
|
||||
onFilterAdd,
|
||||
onFilterSave,
|
||||
onTabChange,
|
||||
onFilterDelete,
|
||||
...listProps
|
||||
}) => (
|
||||
<Container>
|
||||
<PageHeader title={i18n.t("Products")}>
|
||||
<Button onClick={onAdd} color="primary" variant="contained">
|
||||
{i18n.t("Add product")} <AddIcon />
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<Card>
|
||||
<ProductListFilter
|
||||
allTabLabel={i18n.t("All Products")}
|
||||
currencySymbol={currencySymbol}
|
||||
currentTab={currentTab}
|
||||
filterLabel={i18n.t("Select all products where:")}
|
||||
filterTabs={filterTabs}
|
||||
filtersList={filtersList}
|
||||
initialSearch={initialSearch}
|
||||
searchPlaceholder={i18n.t("Search Products...")}
|
||||
onAll={onAll}
|
||||
onSearchChange={onSearchChange}
|
||||
onFilterAdd={onFilterAdd}
|
||||
onFilterSave={onFilterSave}
|
||||
onTabChange={onTabChange}
|
||||
onFilterDelete={onFilterDelete}
|
||||
/>
|
||||
<ProductList {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
ProductListCard.displayName = "ProductListCard";
|
||||
export default ProductListCard;
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from "./ProductListCard";
|
||||
export * from "./ProductListCard";
|
|
@ -1,366 +0,0 @@
|
|||
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 { arrayMove } from "react-sortable-hoc";
|
||||
|
||||
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";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../config";
|
||||
import SearchCategories from "../../containers/SearchCategories";
|
||||
import SearchCollections from "../../containers/SearchCollections";
|
||||
import i18n from "../../i18n";
|
||||
import { decimal, getMutationState, maybe } from "../../misc";
|
||||
import { productTypeUrl } from "../../productTypes/urls";
|
||||
import ProductUpdatePage, { FormData } from "../components/ProductUpdatePage";
|
||||
import ProductUpdateOperations from "../containers/ProductUpdateOperations";
|
||||
import { TypedProductDetailsQuery } from "../queries";
|
||||
import {
|
||||
ProductImageCreate,
|
||||
ProductImageCreateVariables
|
||||
} from "../types/ProductImageCreate";
|
||||
import { ProductVariantBulkDelete } from "../types/ProductVariantBulkDelete";
|
||||
import {
|
||||
productImageUrl,
|
||||
productListUrl,
|
||||
productUrl,
|
||||
ProductUrlQueryParams,
|
||||
productVariantAddUrl,
|
||||
productVariantEditUrl
|
||||
} from "../urls";
|
||||
|
||||
interface ProductUpdateProps {
|
||||
id: string;
|
||||
params: ProductUrlQueryParams;
|
||||
}
|
||||
|
||||
export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||
id,
|
||||
params
|
||||
}) => {
|
||||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||
params.ids
|
||||
);
|
||||
|
||||
return (
|
||||
<SearchCategories variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({ search: searchCategories, result: searchCategoriesOpts }) => (
|
||||
<SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({ search: searchCollections, result: searchCollectionsOpts }) => (
|
||||
<TypedProductDetailsQuery
|
||||
displayLoader
|
||||
require={["product"]}
|
||||
variables={{ id }}
|
||||
>
|
||||
{({ data, loading, refetch }) => {
|
||||
const handleDelete = () => {
|
||||
notify({ text: i18n.t("Product removed") });
|
||||
navigate(productListUrl());
|
||||
};
|
||||
const handleUpdate = () =>
|
||||
notify({ text: i18n.t("Saved changes") });
|
||||
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 (
|
||||
<ProductUpdateOperations
|
||||
product={product}
|
||||
onBulkProductVariantDelete={handleBulkProductVariantDelete}
|
||||
onDelete={handleDelete}
|
||||
onImageCreate={handleImageCreate}
|
||||
onImageDelete={handleImageDeleteSuccess}
|
||||
onUpdate={handleUpdate}
|
||||
>
|
||||
{({
|
||||
bulkProductVariantDelete,
|
||||
createProductImage,
|
||||
deleteProduct,
|
||||
deleteProductImage,
|
||||
reorderProductImages,
|
||||
updateProduct,
|
||||
updateSimpleProduct
|
||||
}) => {
|
||||
const handleImageDelete = (id: string) => () =>
|
||||
deleteProductImage.mutate({ id });
|
||||
const handleImageEdit = (imageId: string) => () =>
|
||||
navigate(productImageUrl(id, imageId));
|
||||
const handleSubmit = (data: FormData) => {
|
||||
if (product) {
|
||||
if (product.productType.hasVariants) {
|
||||
updateProduct.mutate({
|
||||
attributes: data.attributes,
|
||||
basePrice: decimal(data.basePrice),
|
||||
category: data.category.value,
|
||||
chargeTaxes: data.chargeTaxes,
|
||||
collections: data.collections.map(
|
||||
collection => collection.value
|
||||
),
|
||||
descriptionJson: JSON.stringify(data.description),
|
||||
id: product.id,
|
||||
isPublished: data.isPublished,
|
||||
name: data.name,
|
||||
publicationDate:
|
||||
data.publicationDate !== ""
|
||||
? data.publicationDate
|
||||
: null
|
||||
});
|
||||
} else {
|
||||
updateSimpleProduct.mutate({
|
||||
attributes: data.attributes,
|
||||
basePrice: decimal(data.basePrice),
|
||||
category: data.category.value,
|
||||
chargeTaxes: data.chargeTaxes,
|
||||
collections: data.collections.map(
|
||||
collection => collection.value
|
||||
),
|
||||
descriptionJson: JSON.stringify(data.description),
|
||||
id: product.id,
|
||||
isPublished: data.isPublished,
|
||||
name: data.name,
|
||||
productVariantId: product.variants[0].id,
|
||||
productVariantInput: {
|
||||
quantity: data.stockQuantity,
|
||||
sku: data.sku
|
||||
},
|
||||
publicationDate:
|
||||
data.publicationDate !== ""
|
||||
? data.publicationDate
|
||||
: null
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle title={maybe(() => data.product.name)} />
|
||||
<ProductUpdatePage
|
||||
categories={maybe(
|
||||
() => searchCategoriesOpts.data.categories.edges,
|
||||
[]
|
||||
).map(edge => edge.node)}
|
||||
collections={maybe(
|
||||
() =>
|
||||
searchCollectionsOpts.data.collections.edges,
|
||||
[]
|
||||
).map(edge => edge.node)}
|
||||
disabled={disableFormSave}
|
||||
errors={maybe(
|
||||
() =>
|
||||
updateProduct.opts.data.productUpdate.errors,
|
||||
[]
|
||||
)}
|
||||
fetchCategories={searchCategories}
|
||||
fetchCollections={searchCollections}
|
||||
saveButtonBarState={formTransitionState}
|
||||
images={maybe(() => data.product.images)}
|
||||
header={maybe(() => product.name)}
|
||||
placeholderImage={placeholderImg}
|
||||
product={product}
|
||||
productCollections={maybe(
|
||||
() => product.collections
|
||||
)}
|
||||
variants={maybe(() => product.variants)}
|
||||
onAttributesEdit={() =>
|
||||
navigate(
|
||||
productTypeUrl(data.product.productType.id)
|
||||
)
|
||||
}
|
||||
onBack={() => {
|
||||
navigate(productListUrl());
|
||||
}}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
productUrl(id, {
|
||||
action: "remove"
|
||||
})
|
||||
)
|
||||
}
|
||||
onProductShow={() => {
|
||||
if (product) {
|
||||
window.open(product.url);
|
||||
}
|
||||
}}
|
||||
onImageReorder={({ newIndex, oldIndex }) => {
|
||||
if (product) {
|
||||
let ids = product.images.map(image => image.id);
|
||||
ids = arrayMove(ids, oldIndex, newIndex);
|
||||
reorderProductImages.mutate({
|
||||
imagesIds: ids,
|
||||
productId: product.id
|
||||
});
|
||||
}
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
onVariantAdd={handleVariantAdd}
|
||||
onVariantShow={variantId => () =>
|
||||
navigate(
|
||||
productVariantEditUrl(product.id, variantId)
|
||||
)}
|
||||
onImageUpload={file => {
|
||||
if (product) {
|
||||
createProductImage.mutate({
|
||||
alt: "",
|
||||
image: file,
|
||||
product: product.id
|
||||
});
|
||||
}
|
||||
}}
|
||||
onImageEdit={handleImageEdit}
|
||||
onImageDelete={handleImageDelete}
|
||||
toolbar={
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
navigate(
|
||||
productUrl(id, {
|
||||
action: "remove-variants",
|
||||
ids: listElements
|
||||
})
|
||||
)
|
||||
}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
}
|
||||
isChecked={isSelected}
|
||||
selected={listElements.length}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<ActionDialog
|
||||
open={params.action === "remove"}
|
||||
onClose={() => navigate(productUrl(id), true)}
|
||||
confirmButtonState={deleteTransitionState}
|
||||
onConfirm={() => deleteProduct.mutate({ id })}
|
||||
variant="delete"
|
||||
title={i18n.t("Remove product")}
|
||||
>
|
||||
<DialogContentText
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: i18n.t(
|
||||
"Are you sure you want to remove <strong>{{ name }}</strong>?",
|
||||
{
|
||||
name: product ? product.name : undefined
|
||||
}
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</ActionDialog>
|
||||
<ActionDialog
|
||||
open={params.action === "remove-variants"}
|
||||
onClose={() => navigate(productUrl(id), true)}
|
||||
confirmButtonState={
|
||||
bulkProductVariantDeleteTransitionState
|
||||
}
|
||||
onConfirm={() =>
|
||||
bulkProductVariantDelete.mutate({
|
||||
ids: params.ids
|
||||
})
|
||||
}
|
||||
variant="delete"
|
||||
title={i18n.t("Remove product variants")}
|
||||
>
|
||||
<DialogContentText
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: i18n.t(
|
||||
"Are you sure you want to remove <strong>{{ number }}</strong> variants?",
|
||||
{
|
||||
number: maybe(
|
||||
() => params.ids.length.toString(),
|
||||
"..."
|
||||
)
|
||||
}
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</ActionDialog>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</ProductUpdateOperations>
|
||||
);
|
||||
}}
|
||||
</TypedProductDetailsQuery>
|
||||
)}
|
||||
</SearchCollections>
|
||||
)}
|
||||
</SearchCategories>
|
||||
);
|
||||
};
|
||||
export default ProductUpdate;
|
|
@ -3,6 +3,7 @@ import IconButton from "@material-ui/core/IconButton";
|
|||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
import React from "react";
|
||||
|
||||
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";
|
||||
|
@ -32,7 +33,6 @@ import {
|
|||
productVariantAddUrl,
|
||||
productVariantEditUrl
|
||||
} from "../../urls";
|
||||
import placeholderImg from "../@assets/images/placeholder255x255.png";
|
||||
import {
|
||||
createImageReorderHandler,
|
||||
createImageUploadHandler,
|
||||
|
|
|
@ -11,13 +11,12 @@ import Typography from "@material-ui/core/Typography";
|
|||
import React from "react";
|
||||
import SVG from "react-inlinesvg";
|
||||
|
||||
import photoIcon from "@assets/images/photo-icon.svg";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import i18n from "../../../i18n";
|
||||
import { getUserInitials, maybe } from "../../../misc";
|
||||
import { StaffMemberDetails_user } from "../../types/StaffMemberDetails";
|
||||
|
||||
import photoIcon from "../@assets/images/photo-icon.svg";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
avatar: {
|
||||
|
|
|
@ -27,7 +27,7 @@ jest.mock("draft-js/lib/generateRandomKey");
|
|||
(generateRandomKey as any).mockImplementation(() => "testKey");
|
||||
|
||||
initStoryshots({
|
||||
configPath: "saleor/static/dashboard-next/storybook/",
|
||||
configPath: "src/storybook/",
|
||||
test({ story }) {
|
||||
const result = render(story.render() as any);
|
||||
expect(toJSON(result)).toMatchSnapshot();
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import placeholderImage from "@assets/images/placeholder255x255.png";
|
||||
import { category as categoryFixture } from "../../../categories/fixtures";
|
||||
import {
|
||||
filterPageProps,
|
||||
filters,
|
||||
listActionsProps,
|
||||
pageListProps
|
||||
} from "../../../fixtures";
|
||||
import ProductListCard, {
|
||||
ProductListCardProps
|
||||
} from "../../../products/components/ProductListCard";
|
||||
import Decorator from "../../Decorator";
|
||||
|
||||
const products = categoryFixture(placeholderImage).products.edges.map(
|
||||
edge => edge.node
|
||||
);
|
||||
|
||||
const props: ProductListCardProps = {
|
||||
...listActionsProps,
|
||||
...pageListProps.default,
|
||||
...filterPageProps,
|
||||
products
|
||||
};
|
||||
|
||||
storiesOf("Views / Products / Product list", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <ProductListCard {...props} />)
|
||||
.add("with custom filters", () => (
|
||||
<ProductListCard {...props} filtersList={filters} />
|
||||
))
|
||||
.add("loading", () => (
|
||||
<ProductListCard
|
||||
{...props}
|
||||
products={undefined}
|
||||
filtersList={undefined}
|
||||
currentTab={undefined}
|
||||
disabled={true}
|
||||
/>
|
||||
))
|
||||
.add("no data", () => <ProductListCard {...props} products={[]} />);
|
Loading…
Reference in a new issue