Explicitely return not found page
This commit is contained in:
parent
8ed641eee8
commit
fa8231992f
22 changed files with 337 additions and 268 deletions
|
@ -23,6 +23,7 @@ module.exports = api => {
|
|||
|
||||
const plugins = [
|
||||
"@babel/plugin-proposal-numeric-separator",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
[
|
||||
"@babel/plugin-proposal-decorators",
|
||||
|
@ -49,6 +50,6 @@ module.exports = api => {
|
|||
return {
|
||||
ignore,
|
||||
plugins,
|
||||
presets,
|
||||
presets
|
||||
};
|
||||
};
|
||||
|
|
35
package-lock.json
generated
35
package-lock.json
generated
|
@ -849,6 +849,24 @@
|
|||
"@babel/plugin-syntax-optional-catch-binding": "^7.2.0"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-optional-chaining": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz",
|
||||
"integrity": "sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-syntax-optional-chaining": "^7.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
|
||||
"integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-unicode-property-regex": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.0.tgz",
|
||||
|
@ -940,6 +958,23 @@
|
|||
"@babel/helper-plugin-utils": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-syntax-optional-chaining": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
|
||||
"integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
|
||||
"integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/plugin-syntax-top-level-await": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.0.tgz",
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
"@babel/plugin-proposal-decorators": "^7.4.4",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.2.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.5.4",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
|
||||
"@babel/preset-env": "^7.5.4",
|
||||
"@babel/preset-react": "^7.7.4",
|
||||
"@babel/preset-typescript": "^7.7.4",
|
||||
|
|
|
@ -14,6 +14,7 @@ import usePaginator, {
|
|||
} from "@saleor/hooks/usePaginator";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { PAGINATE_BY } from "../../config";
|
||||
import { maybe } from "../../misc";
|
||||
import { TypedProductBulkDeleteMutation } from "../../products/mutations";
|
||||
|
@ -67,10 +68,15 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
|||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
||||
const { data, loading, refetch } = useCategoryDetailsQuery({
|
||||
displayLoader: true,
|
||||
require: ["category"],
|
||||
variables: { ...paginationState, id }
|
||||
});
|
||||
|
||||
const category = data?.category;
|
||||
|
||||
if (category === null) {
|
||||
return <NotFoundPage onBack={() => navigate(categoryListUrl())} />;
|
||||
}
|
||||
|
||||
const handleCategoryDelete = (data: CategoryDelete) => {
|
||||
if (data.categoryDelete.errors.length === 0) {
|
||||
notify({
|
||||
|
|
|
@ -68,7 +68,6 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
|
|||
);
|
||||
const { data, loading, refetch } = useRootCategoriesQuery({
|
||||
displayLoader: true,
|
||||
require: ["categories"],
|
||||
variables: queryVariables
|
||||
});
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import usePaginator, {
|
|||
import { commonMessages } from "@saleor/intl";
|
||||
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { getMutationState, maybe } from "../../misc";
|
||||
import { productUrl } from "../../products/urls";
|
||||
import { CollectionInput } from "../../types/globalTypes";
|
||||
|
@ -61,14 +62,19 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
|||
>(navigate, params => collectionUrl(id, params), params);
|
||||
|
||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
||||
const handleBack = () => navigate(collectionListUrl());
|
||||
|
||||
return (
|
||||
<TypedCollectionDetailsQuery
|
||||
displayLoader
|
||||
variables={{ id, ...paginationState }}
|
||||
require={["collection"]}
|
||||
>
|
||||
{({ data, loading }) => {
|
||||
const collection = data?.collection;
|
||||
|
||||
if (collection === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
const handleCollectionUpdate = (data: CollectionUpdate) => {
|
||||
if (data.collectionUpdate.errors.length === 0) {
|
||||
notify({
|
||||
|
@ -196,7 +202,7 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
|||
<WindowTitle title={maybe(() => data.collection.name)} />
|
||||
<CollectionDetailsPage
|
||||
onAdd={() => openModal("assign")}
|
||||
onBack={() => navigate(collectionListUrl())}
|
||||
onBack={handleBack}
|
||||
disabled={loading}
|
||||
collection={maybe(() => data.collection)}
|
||||
isFeatured={maybe(
|
||||
|
|
|
@ -29,7 +29,6 @@ import { maybe } from "@saleor/misc";
|
|||
import { staffMemberDetailsUrl } from "@saleor/staff/urls";
|
||||
import Container from "../Container";
|
||||
import ErrorPage from "../ErrorPage";
|
||||
import NotFoundPage from "../NotFoundPage";
|
||||
import AppActionContext from "./AppActionContext";
|
||||
import AppHeaderContext from "./AppHeaderContext";
|
||||
import { appLoaderHeight, drawerWidth, drawerWidthExpanded } from "./consts";
|
||||
|
@ -507,15 +506,11 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
|||
</Container>
|
||||
</div>
|
||||
<main className={classes.view}>
|
||||
{appState.error ? (
|
||||
appState.error === "not-found" ? (
|
||||
<NotFoundPage onBack={handleErrorBack} />
|
||||
) : (
|
||||
<ErrorPage onBack={handleErrorBack} />
|
||||
)
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
{appState.error
|
||||
? appState.error === "unhandled" && (
|
||||
<ErrorPage onBack={handleErrorBack} />
|
||||
)
|
||||
: children}
|
||||
</main>
|
||||
</div>
|
||||
<div className={classes.appAction} ref={appActionAnchor} />
|
||||
|
|
|
@ -21,7 +21,7 @@ const AppStateProvider: React.FC = ({ children }) => {
|
|||
if (!!state.error) {
|
||||
dispatch({
|
||||
payload: {
|
||||
error: null
|
||||
error: undefined
|
||||
},
|
||||
type: "displayError"
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export type AppError = "unhandled" | "not-found";
|
||||
export type AppError = "unhandled";
|
||||
|
||||
interface IAppState {
|
||||
error: AppError | null;
|
||||
|
|
|
@ -7,6 +7,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle";
|
|||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { maybe } from "../../misc";
|
||||
import { orderListUrl, orderUrl } from "../../orders/urls";
|
||||
import CustomerDetailsPage from "../components/CustomerDetailsPage/CustomerDetailsPage";
|
||||
|
@ -54,6 +55,9 @@ export const CustomerDetailsView: React.FC<CustomerDetailsViewProps> = ({
|
|||
navigate(customerListUrl());
|
||||
}
|
||||
};
|
||||
|
||||
const handleBack = () => navigate(customerListUrl());
|
||||
|
||||
return (
|
||||
<TypedRemoveCustomerMutation
|
||||
variables={{ id }}
|
||||
|
@ -62,91 +66,97 @@ export const CustomerDetailsView: React.FC<CustomerDetailsViewProps> = ({
|
|||
{(removeCustomer, removeCustomerOpts) => (
|
||||
<TypedUpdateCustomerMutation onCompleted={handleCustomerUpdateSuccess}>
|
||||
{(updateCustomer, updateCustomerOpts) => (
|
||||
<TypedCustomerDetailsQuery
|
||||
displayLoader
|
||||
variables={{ id }}
|
||||
require={["user"]}
|
||||
>
|
||||
{customerDetails => (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={maybe(() => customerDetails.data.user.email)}
|
||||
/>
|
||||
<CustomerDetailsPage
|
||||
customer={maybe(() => customerDetails.data.user)}
|
||||
disabled={
|
||||
customerDetails.loading ||
|
||||
updateCustomerOpts.loading ||
|
||||
removeCustomerOpts.loading
|
||||
}
|
||||
errors={maybe(
|
||||
() => updateCustomerOpts.data.customerUpdate.errors
|
||||
)}
|
||||
saveButtonBar={updateCustomerOpts.status}
|
||||
onAddressManageClick={() =>
|
||||
navigate(customerAddressesUrl(id))
|
||||
}
|
||||
onBack={() => navigate(customerListUrl())}
|
||||
onRowClick={id => navigate(orderUrl(id))}
|
||||
onSubmit={formData =>
|
||||
updateCustomer({
|
||||
variables: {
|
||||
id,
|
||||
input: {
|
||||
email: formData.email,
|
||||
firstName: formData.firstName,
|
||||
isActive: formData.isActive,
|
||||
lastName: formData.lastName,
|
||||
note: formData.note
|
||||
<TypedCustomerDetailsQuery displayLoader variables={{ id }}>
|
||||
{customerDetails => {
|
||||
const user = customerDetails.data?.user;
|
||||
|
||||
if (user === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={maybe(() => customerDetails.data.user.email)}
|
||||
/>
|
||||
<CustomerDetailsPage
|
||||
customer={maybe(() => customerDetails.data.user)}
|
||||
disabled={
|
||||
customerDetails.loading ||
|
||||
updateCustomerOpts.loading ||
|
||||
removeCustomerOpts.loading
|
||||
}
|
||||
errors={maybe(
|
||||
() => updateCustomerOpts.data.customerUpdate.errors
|
||||
)}
|
||||
saveButtonBar={updateCustomerOpts.status}
|
||||
onAddressManageClick={() =>
|
||||
navigate(customerAddressesUrl(id))
|
||||
}
|
||||
onBack={handleBack}
|
||||
onRowClick={id => navigate(orderUrl(id))}
|
||||
onSubmit={formData =>
|
||||
updateCustomer({
|
||||
variables: {
|
||||
id,
|
||||
input: {
|
||||
email: formData.email,
|
||||
firstName: formData.firstName,
|
||||
isActive: formData.isActive,
|
||||
lastName: formData.lastName,
|
||||
note: formData.note
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
customerUrl(id, {
|
||||
action: "remove"
|
||||
})
|
||||
)
|
||||
}
|
||||
onViewAllOrdersClick={() =>
|
||||
navigate(
|
||||
orderListUrl({
|
||||
customer: maybe(() => customerDetails.data.user.email)
|
||||
})
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ActionDialog
|
||||
confirmButtonState={removeCustomerOpts.status}
|
||||
onClose={() => navigate(customerUrl(id), true)}
|
||||
onConfirm={() => removeCustomer()}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Customer",
|
||||
description: "dialog header"
|
||||
})}
|
||||
variant="delete"
|
||||
open={params.action === "remove"}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {email}?"
|
||||
description="delete customer, dialog content"
|
||||
values={{
|
||||
email: (
|
||||
<strong>
|
||||
{maybe(
|
||||
() => customerDetails.data.user.email,
|
||||
"..."
|
||||
)}
|
||||
</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
</>
|
||||
)}
|
||||
}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
customerUrl(id, {
|
||||
action: "remove"
|
||||
})
|
||||
)
|
||||
}
|
||||
onViewAllOrdersClick={() =>
|
||||
navigate(
|
||||
orderListUrl({
|
||||
customer: maybe(
|
||||
() => customerDetails.data.user.email
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ActionDialog
|
||||
confirmButtonState={removeCustomerOpts.status}
|
||||
onClose={() => navigate(customerUrl(id), true)}
|
||||
onConfirm={() => removeCustomer()}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Customer",
|
||||
description: "dialog header"
|
||||
})}
|
||||
variant="delete"
|
||||
open={params.action === "remove"}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {email}?"
|
||||
description="delete customer, dialog content"
|
||||
values={{
|
||||
email: (
|
||||
<strong>
|
||||
{maybe(
|
||||
() => customerDetails.data.user.email,
|
||||
"..."
|
||||
)}
|
||||
</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedCustomerDetailsQuery>
|
||||
)}
|
||||
</TypedUpdateCustomerMutation>
|
||||
|
|
|
@ -18,14 +18,13 @@ export interface LoadMore<TData, TVariables> {
|
|||
|
||||
export type UseQueryResult<TData, TVariables> = QueryResult<TData, TVariables> &
|
||||
LoadMore<TData, TVariables>;
|
||||
type UseQueryOpts<TData, TVariables> = Partial<{
|
||||
type UseQueryOpts<TVariables> = Partial<{
|
||||
displayLoader: boolean;
|
||||
require: Array<keyof TData>;
|
||||
skip: boolean;
|
||||
variables: TVariables;
|
||||
}>;
|
||||
type UseQueryHook<TData, TVariables> = (
|
||||
opts: UseQueryOpts<TData, TVariables>
|
||||
opts: UseQueryOpts<TVariables>
|
||||
) => UseQueryResult<TData, TVariables>;
|
||||
|
||||
function makeQuery<TData, TVariables>(
|
||||
|
@ -33,10 +32,9 @@ function makeQuery<TData, TVariables>(
|
|||
): UseQueryHook<TData, TVariables> {
|
||||
function useQuery({
|
||||
displayLoader,
|
||||
require,
|
||||
skip,
|
||||
variables
|
||||
}: UseQueryOpts<TData, TVariables>): UseQueryResult<TData, TVariables> {
|
||||
}: UseQueryOpts<TVariables>): UseQueryResult<TData, TVariables> {
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const [, dispatchAppState] = useAppState();
|
||||
|
@ -89,20 +87,6 @@ function makeQuery<TData, TVariables>(
|
|||
variables: { ...variables, ...extraVariables }
|
||||
});
|
||||
|
||||
if (
|
||||
!queryData.loading &&
|
||||
require &&
|
||||
queryData.data &&
|
||||
!require.reduce((acc, key) => acc && queryData.data[key] !== null, true)
|
||||
) {
|
||||
dispatchAppState({
|
||||
payload: {
|
||||
error: "not-found"
|
||||
},
|
||||
type: "displayError"
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...queryData,
|
||||
loadMore
|
||||
|
|
|
@ -380,3 +380,7 @@ export function transformFormToAddress<T>(
|
|||
country: findInEnum(address.country, CountryCode)
|
||||
};
|
||||
}
|
||||
|
||||
export function getStringOrPlaceholder(s: string | undefined): string {
|
||||
return s || "...";
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import useNavigator from "@saleor/hooks/useNavigator";
|
|||
import useUser from "@saleor/hooks/useUser";
|
||||
import useCustomerSearch from "@saleor/searches/useCustomerSearch";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { customerUrl } from "../../../customers/urls";
|
||||
import { getMutationState, maybe, transformAddressToForm } from "../../../misc";
|
||||
import { productUrl } from "../../../products/urls";
|
||||
|
@ -90,14 +91,17 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
|||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const handleBack = () => navigate(orderListUrl());
|
||||
|
||||
return (
|
||||
<TypedOrderDetailsQuery
|
||||
displayLoader
|
||||
variables={{ id }}
|
||||
require={["order"]}
|
||||
>
|
||||
<TypedOrderDetailsQuery displayLoader variables={{ id }}>
|
||||
{({ data, loading }) => {
|
||||
const order = maybe(() => data.order);
|
||||
const order = data?.order;
|
||||
|
||||
if (order === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
||||
const [openModal, closeModal] = createDialogActionHandlers<
|
||||
OrderUrlDialog,
|
||||
OrderUrlQueryParams
|
||||
|
@ -166,7 +170,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
|||
order: id
|
||||
})
|
||||
}
|
||||
onBack={() => navigate(orderListUrl())}
|
||||
onBack={handleBack}
|
||||
order={order}
|
||||
shippingMethods={maybe(
|
||||
() => data.order.availableShippingMethods,
|
||||
|
|
|
@ -13,6 +13,7 @@ import { maybe } from "@saleor/misc";
|
|||
import AssignAttributeDialog from "@saleor/productTypes/components/AssignAttributeDialog";
|
||||
import { ReorderEvent } from "@saleor/types";
|
||||
import { AttributeTypeEnum } from "@saleor/types/globalTypes";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import ProductTypeAttributeUnassignDialog from "../../components/ProductTypeAttributeUnassignDialog";
|
||||
import ProductTypeBulkAttributeUnassignDialog from "../../components/ProductTypeBulkAttributeUnassignDialog";
|
||||
import ProductTypeDeleteDialog from "../../components/ProductTypeDeleteDialog";
|
||||
|
@ -54,15 +55,19 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
|
|||
}
|
||||
});
|
||||
|
||||
const handleBack = () => navigate(productTypeListUrl());
|
||||
|
||||
return (
|
||||
<ProductTypeUpdateErrors>
|
||||
{({ errors, set: setErrors }) => (
|
||||
<TypedProductTypeDetailsQuery
|
||||
displayLoader
|
||||
variables={{ id }}
|
||||
require={["productType"]}
|
||||
>
|
||||
<TypedProductTypeDetailsQuery displayLoader variables={{ id }}>
|
||||
{({ data, loading: dataLoading }) => {
|
||||
const productType = data?.productType;
|
||||
|
||||
if (productType === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
||||
const closeModal = () => navigate(productTypeUrl(id), true);
|
||||
|
||||
const handleAttributeAssignSuccess = (data: AssignAttribute) => {
|
||||
|
@ -242,7 +247,7 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
|
|||
})
|
||||
)
|
||||
}
|
||||
onBack={() => navigate(productTypeListUrl())}
|
||||
onBack={handleBack}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
productTypeUrl(id, {
|
||||
|
|
|
@ -5,6 +5,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { maybe } from "../../misc";
|
||||
import ProductImagePage from "../components/ProductImagePage";
|
||||
import {
|
||||
|
@ -16,7 +17,8 @@ import { ProductImageUpdate } from "../types/ProductImageUpdate";
|
|||
import {
|
||||
productImageUrl,
|
||||
ProductImageUrlQueryParams,
|
||||
productUrl
|
||||
productUrl,
|
||||
productListUrl
|
||||
} from "../urls";
|
||||
|
||||
interface ProductImageProps {
|
||||
|
@ -47,70 +49,77 @@ export const ProductImage: React.FC<ProductImageProps> = ({
|
|||
imageId,
|
||||
productId
|
||||
}}
|
||||
require={["product"]}
|
||||
>
|
||||
{({ data, loading }) => (
|
||||
<TypedProductImageUpdateMutation onCompleted={handleUpdateSuccess}>
|
||||
{(updateImage, updateResult) => (
|
||||
<TypedProductImageDeleteMutation onCompleted={handleBack}>
|
||||
{(deleteImage, deleteResult) => {
|
||||
const handleDelete = () =>
|
||||
deleteImage({ variables: { id: imageId } });
|
||||
const handleImageClick = (id: string) => () =>
|
||||
navigate(productImageUrl(productId, id));
|
||||
const handleUpdate = (formData: { description: string }) => {
|
||||
updateImage({
|
||||
variables: {
|
||||
alt: formData.description,
|
||||
id: imageId
|
||||
}
|
||||
});
|
||||
};
|
||||
const image = data && data.product && data.product.mainImage;
|
||||
{({ data, loading }) => {
|
||||
const product = data?.product;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProductImagePage
|
||||
disabled={loading}
|
||||
product={maybe(() => data.product.name)}
|
||||
image={image || null}
|
||||
images={maybe(() => data.product.images)}
|
||||
onBack={handleBack}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
productImageUrl(productId, imageId, {
|
||||
action: "remove"
|
||||
})
|
||||
)
|
||||
if (product === null) {
|
||||
return <NotFoundPage onBack={() => navigate(productListUrl())} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<TypedProductImageUpdateMutation onCompleted={handleUpdateSuccess}>
|
||||
{(updateImage, updateResult) => (
|
||||
<TypedProductImageDeleteMutation onCompleted={handleBack}>
|
||||
{(deleteImage, deleteResult) => {
|
||||
const handleDelete = () =>
|
||||
deleteImage({ variables: { id: imageId } });
|
||||
const handleImageClick = (id: string) => () =>
|
||||
navigate(productImageUrl(productId, id));
|
||||
const handleUpdate = (formData: { description: string }) => {
|
||||
updateImage({
|
||||
variables: {
|
||||
alt: formData.description,
|
||||
id: imageId
|
||||
}
|
||||
onRowClick={handleImageClick}
|
||||
onSubmit={handleUpdate}
|
||||
saveButtonBarState={updateResult.status}
|
||||
/>
|
||||
<ActionDialog
|
||||
onClose={() =>
|
||||
navigate(productImageUrl(productId, imageId), true)
|
||||
}
|
||||
onConfirm={handleDelete}
|
||||
open={params.action === "remove"}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Image",
|
||||
description: "dialog header"
|
||||
})}
|
||||
variant="delete"
|
||||
confirmButtonState={deleteResult.status}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage defaultMessage="Are you sure you want to delete this image?" />
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedProductImageDeleteMutation>
|
||||
)}
|
||||
</TypedProductImageUpdateMutation>
|
||||
)}
|
||||
});
|
||||
};
|
||||
const image = data && data.product && data.product.mainImage;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProductImagePage
|
||||
disabled={loading}
|
||||
product={maybe(() => data.product.name)}
|
||||
image={image || null}
|
||||
images={maybe(() => data.product.images)}
|
||||
onBack={handleBack}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
productImageUrl(productId, imageId, {
|
||||
action: "remove"
|
||||
})
|
||||
)
|
||||
}
|
||||
onRowClick={handleImageClick}
|
||||
onSubmit={handleUpdate}
|
||||
saveButtonBarState={updateResult.status}
|
||||
/>
|
||||
<ActionDialog
|
||||
onClose={() =>
|
||||
navigate(productImageUrl(productId, imageId), true)
|
||||
}
|
||||
onConfirm={handleDelete}
|
||||
open={params.action === "remove"}
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Image",
|
||||
description: "dialog header"
|
||||
})}
|
||||
variant="delete"
|
||||
confirmButtonState={deleteResult.status}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage defaultMessage="Are you sure you want to delete this image?" />
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedProductImageDeleteMutation>
|
||||
)}
|
||||
</TypedProductImageUpdateMutation>
|
||||
);
|
||||
}}
|
||||
</TypedProductImageQuery>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ import { ProductVariantBulkCreate } from "@saleor/products/types/ProductVariantB
|
|||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { getMutationState, maybe } from "../../../misc";
|
||||
import ProductUpdatePage from "../../components/ProductUpdatePage";
|
||||
import ProductUpdateOperations from "../../containers/ProductUpdateOperations";
|
||||
|
@ -76,13 +77,17 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
|||
ProductUrlQueryParams
|
||||
>(navigate, params => productUrl(id, params), params);
|
||||
|
||||
const handleBack = () => navigate(productListUrl());
|
||||
|
||||
return (
|
||||
<TypedProductDetailsQuery
|
||||
displayLoader
|
||||
require={["product"]}
|
||||
variables={{ id }}
|
||||
>
|
||||
<TypedProductDetailsQuery displayLoader variables={{ id }}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const product = data?.product;
|
||||
|
||||
if (product === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
||||
const handleDelete = () => {
|
||||
notify({
|
||||
text: intl.formatMessage({
|
||||
|
@ -142,7 +147,6 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
|||
}
|
||||
};
|
||||
|
||||
const product = data ? data.product : undefined;
|
||||
return (
|
||||
<ProductUpdateOperations
|
||||
product={product}
|
||||
|
@ -233,9 +237,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
|||
placeholderImage={placeholderImg}
|
||||
product={product}
|
||||
variants={maybe(() => product.variants)}
|
||||
onBack={() => {
|
||||
navigate(productListUrl());
|
||||
}}
|
||||
onBack={handleBack}
|
||||
onDelete={() => openModal("remove")}
|
||||
onProductShow={() => {
|
||||
if (product) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle";
|
|||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { decimal, maybe } from "../../misc";
|
||||
import ProductVariantDeleteDialog from "../components/ProductVariantDeleteDialog";
|
||||
import ProductVariantPage, {
|
||||
|
@ -13,7 +14,10 @@ import ProductVariantPage, {
|
|||
} from "../components/ProductVariantPage";
|
||||
import ProductVariantOperations from "../containers/ProductVariantOperations";
|
||||
import { TypedProductVariantQuery } from "../queries";
|
||||
import { VariantUpdate } from "../types/VariantUpdate";
|
||||
import {
|
||||
VariantUpdate,
|
||||
VariantUpdate_productVariantUpdate_productErrors
|
||||
} from "../types/VariantUpdate";
|
||||
import {
|
||||
productUrl,
|
||||
productVariantAddUrl,
|
||||
|
@ -35,20 +39,24 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
|
|||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const [errors, setErrors] = useState([]);
|
||||
const [errors, setErrors] = useState<
|
||||
VariantUpdate_productVariantUpdate_productErrors[]
|
||||
>([]);
|
||||
useEffect(() => {
|
||||
setErrors([]);
|
||||
}, [variantId]);
|
||||
|
||||
const handleBack = () => navigate(productUrl(productId));
|
||||
|
||||
return (
|
||||
<TypedProductVariantQuery
|
||||
displayLoader
|
||||
variables={{ id: variantId }}
|
||||
require={["productVariant"]}
|
||||
>
|
||||
<TypedProductVariantQuery displayLoader variables={{ id: variantId }}>
|
||||
{({ data, loading }) => {
|
||||
const variant = data ? data.productVariant : undefined;
|
||||
const handleBack = () => navigate(productUrl(productId));
|
||||
const variant = data?.productVariant;
|
||||
|
||||
if (variant === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
||||
const handleDelete = () => {
|
||||
notify({
|
||||
text: intl.formatMessage({
|
||||
|
|
|
@ -5,6 +5,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle";
|
|||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import useShop from "@saleor/hooks/useShop";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { decimal, maybe } from "../../misc";
|
||||
import ProductVariantCreatePage, {
|
||||
ProductVariantCreatePageSubmitData
|
||||
|
@ -12,7 +13,7 @@ import ProductVariantCreatePage, {
|
|||
import { TypedVariantCreateMutation } from "../mutations";
|
||||
import { TypedProductVariantCreateQuery } from "../queries";
|
||||
import { VariantCreate } from "../types/VariantCreate";
|
||||
import { productUrl, productVariantEditUrl } from "../urls";
|
||||
import { productUrl, productVariantEditUrl, productListUrl } from "../urls";
|
||||
|
||||
interface ProductUpdateProps {
|
||||
productId: string;
|
||||
|
@ -25,12 +26,14 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({ productId }) => {
|
|||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<TypedProductVariantCreateQuery
|
||||
displayLoader
|
||||
variables={{ id: productId }}
|
||||
require={["product"]}
|
||||
>
|
||||
<TypedProductVariantCreateQuery displayLoader variables={{ id: productId }}>
|
||||
{({ data, loading: productLoading }) => {
|
||||
const product = data?.product;
|
||||
|
||||
if (product === null) {
|
||||
return <NotFoundPage onBack={() => navigate(productListUrl())} />;
|
||||
}
|
||||
|
||||
const handleCreateSuccess = (data: VariantCreate) => {
|
||||
if (data.productVariantCreate.productErrors.length === 0) {
|
||||
notify({
|
||||
|
|
|
@ -28,7 +28,6 @@ export interface TypedQueryInnerProps<TData, TVariables> {
|
|||
displayLoader?: boolean;
|
||||
skip?: boolean;
|
||||
variables?: TVariables;
|
||||
require?: Array<keyof TData>;
|
||||
}
|
||||
|
||||
interface QueryProgressProps {
|
||||
|
@ -65,7 +64,7 @@ class QueryProgress extends React.Component<QueryProgressProps, {}> {
|
|||
export function TypedQuery<TData, TVariables>(
|
||||
query: DocumentNode
|
||||
): React.FC<TypedQueryInnerProps<TData, TVariables>> {
|
||||
return ({ children, displayLoader, skip, variables, require }) => {
|
||||
return ({ children, displayLoader, skip, variables }) => {
|
||||
const pushMessage = useNotifier();
|
||||
const [, dispatchAppState] = useAppState();
|
||||
const intl = useIntl();
|
||||
|
@ -112,23 +111,6 @@ export function TypedQuery<TData, TVariables>(
|
|||
variables: { ...variables, ...extraVariables }
|
||||
});
|
||||
|
||||
if (
|
||||
!queryData.loading &&
|
||||
require &&
|
||||
queryData.data &&
|
||||
!require.reduce(
|
||||
(acc, key) => acc && queryData.data[key] !== null,
|
||||
true
|
||||
)
|
||||
) {
|
||||
dispatchAppState({
|
||||
payload: {
|
||||
error: "not-found"
|
||||
},
|
||||
type: "displayError"
|
||||
});
|
||||
}
|
||||
|
||||
if (displayLoader) {
|
||||
return (
|
||||
<QueryProgress
|
||||
|
|
|
@ -22,6 +22,7 @@ import { ServiceTokenCreate } from "@saleor/services/types/ServiceTokenCreate";
|
|||
import { ServiceTokenDelete } from "@saleor/services/types/ServiceTokenDelete";
|
||||
import { ServiceUpdate } from "@saleor/services/types/ServiceUpdate";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import ServiceDetailsPage, {
|
||||
ServiceDetailsPageFormData
|
||||
} from "../../components/ServiceDetailsPage";
|
||||
|
@ -77,12 +78,14 @@ export const ServiceDetails: React.FC<OrderListProps> = ({
|
|||
const handleBack = () => navigate(serviceListUrl());
|
||||
|
||||
return (
|
||||
<ServiceDetailsQuery
|
||||
displayLoader
|
||||
variables={{ id }}
|
||||
require={["serviceAccount"]}
|
||||
>
|
||||
<ServiceDetailsQuery displayLoader variables={{ id }}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const service = data?.serviceAccount;
|
||||
|
||||
if (service === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
||||
const onTokenCreate = (data: ServiceTokenCreate) => {
|
||||
if (maybe(() => data.serviceAccountTokenCreate.errors.length === 0)) {
|
||||
refetch();
|
||||
|
@ -151,9 +154,7 @@ export const ServiceDetails: React.FC<OrderListProps> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={maybe(() => data.serviceAccount.name)}
|
||||
/>
|
||||
<WindowTitle title={service?.name || "..."} />
|
||||
<ServiceDetailsPage
|
||||
apiUri={API_URI}
|
||||
disabled={loading}
|
||||
|
|
|
@ -9,6 +9,7 @@ import useNotifier from "@saleor/hooks/useNotifier";
|
|||
import useShop from "@saleor/hooks/useShop";
|
||||
import useUser from "@saleor/hooks/useUser";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { maybe } from "../../misc";
|
||||
import StaffDetailsPage from "../components/StaffDetailsPage/StaffDetailsPage";
|
||||
import {
|
||||
|
@ -63,13 +64,17 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
onCompleted: handleChangePassword
|
||||
});
|
||||
|
||||
const handleBack = () => navigate(staffListUrl());
|
||||
|
||||
return (
|
||||
<TypedStaffMemberDetailsQuery
|
||||
displayLoader
|
||||
variables={{ id }}
|
||||
require={["user"]}
|
||||
>
|
||||
<TypedStaffMemberDetailsQuery displayLoader variables={{ id }}>
|
||||
{({ data, loading }) => {
|
||||
const staffMember = data?.user;
|
||||
|
||||
if (staffMember === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
||||
const handleStaffMemberUpdate = (data: StaffMemberUpdate) => {
|
||||
if (!maybe(() => data.staffUpdate.errors.length !== 0)) {
|
||||
notify({
|
||||
|
@ -100,6 +105,7 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
navigate(staffMemberDetailsUrl(id));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TypedStaffMemberUpdateMutation onCompleted={handleStaffMemberUpdate}>
|
||||
{(updateStaffMember, updateResult) => (
|
||||
|
@ -124,7 +130,7 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={maybe(() => data.user.email)}
|
||||
title={staffMember?.email || "..."}
|
||||
/>
|
||||
<StaffDetailsPage
|
||||
canEditAvatar={isUserSameAsViewer}
|
||||
|
@ -132,7 +138,7 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
canEditStatus={!isUserSameAsViewer}
|
||||
canRemove={!isUserSameAsViewer}
|
||||
disabled={loading}
|
||||
onBack={() => navigate(staffListUrl())}
|
||||
onBack={handleBack}
|
||||
onChangePassword={() =>
|
||||
navigate(
|
||||
staffMemberDetailsUrl(id, {
|
||||
|
@ -176,7 +182,7 @@ export const StaffDetails: React.FC<OrderListProps> = ({ id, params }) => {
|
|||
)
|
||||
}
|
||||
permissions={maybe(() => shop.permissions)}
|
||||
staffMember={maybe(() => data.user)}
|
||||
staffMember={staffMember}
|
||||
saveButtonBarState={updateResult.status}
|
||||
/>
|
||||
<ActionDialog
|
||||
|
|
|
@ -12,7 +12,8 @@ import WebhookDeleteDialog from "@saleor/webhooks/components/WebhookDeleteDialog
|
|||
import { WebhookDelete } from "@saleor/webhooks/types/WebhookDelete";
|
||||
import { WebhookUpdate } from "@saleor/webhooks/types/WebhookUpdate";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import { maybe } from "../../misc";
|
||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||
import { maybe, getStringOrPlaceholder } from "../../misc";
|
||||
import WebhooksDetailsPage from "../components/WebhooksDetailsPage";
|
||||
import { TypedWebhookDelete, TypedWebhookUpdate } from "../mutations";
|
||||
import { TypedWebhooksDetailsQuery } from "../queries";
|
||||
|
@ -48,7 +49,7 @@ export const WebhooksDetails: React.FC<WebhooksDetailsProps> = ({
|
|||
>(navigate, params => webhookUrl(id, params), params);
|
||||
|
||||
const onWebhookDelete = (data: WebhookDelete) => {
|
||||
if (data.webhookDelete.errors.length === 0) {
|
||||
if (data.webhookDelete?.errors.length === 0) {
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
|
@ -57,14 +58,19 @@ export const WebhooksDetails: React.FC<WebhooksDetailsProps> = ({
|
|||
};
|
||||
|
||||
const onWebhookUpdate = (data: WebhookUpdate) => {
|
||||
if (data.webhookUpdate.webhookErrors.length === 0) {
|
||||
const errors = data.webhookUpdate?.webhookErrors;
|
||||
const webhook = data.webhookUpdate?.webhook;
|
||||
|
||||
if (errors.length === 0 && webhook) {
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
navigate(webhookUrl(data.webhookUpdate.webhook.id));
|
||||
navigate(webhookUrl(webhook.id));
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnBack = () => navigate(webhookListUrl());
|
||||
|
||||
return (
|
||||
<TypedWebhookUpdate onCompleted={onWebhookUpdate}>
|
||||
{(webhookUpdate, webhookUpdateOpts) => (
|
||||
|
@ -79,28 +85,33 @@ export const WebhooksDetails: React.FC<WebhooksDetailsProps> = ({
|
|||
}
|
||||
});
|
||||
|
||||
const formErrors = maybe(
|
||||
() => webhookUpdateOpts.data.webhookUpdate.webhookErrors,
|
||||
[]
|
||||
);
|
||||
const webhook = webhookDetails?.data?.webhook;
|
||||
const formErrors =
|
||||
webhookUpdateOpts.data?.webhookUpdate.webhookErrors || [];
|
||||
|
||||
if (webhook === null) {
|
||||
return <NotFoundPage onBack={handleOnBack} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={maybe(() => webhookDetails.data.webhook.name)}
|
||||
title={getStringOrPlaceholder(
|
||||
webhookDetails?.data?.webhook?.name
|
||||
)}
|
||||
/>
|
||||
<WebhooksDetailsPage
|
||||
disabled={webhookDetails.loading}
|
||||
errors={formErrors}
|
||||
saveButtonBarState={webhookUpdateOpts.status}
|
||||
webhook={maybe(() => webhookDetails.data.webhook)}
|
||||
webhook={webhook}
|
||||
fetchServiceAccounts={searchServiceAccount}
|
||||
services={maybe(() =>
|
||||
searchServiceAccountOpt.data.search.edges.map(
|
||||
edge => edge.node
|
||||
)
|
||||
)}
|
||||
onBack={() => navigate(webhookListUrl())}
|
||||
onBack={handleOnBack}
|
||||
onDelete={() => openModal("remove")}
|
||||
onSubmit={data => {
|
||||
webhookUpdate({
|
||||
|
@ -122,10 +133,7 @@ export const WebhooksDetails: React.FC<WebhooksDetailsProps> = ({
|
|||
/>
|
||||
<WebhookDeleteDialog
|
||||
confirmButtonState={webhookDeleteOpts.status}
|
||||
name={maybe(
|
||||
() => webhookDetails.data.webhook.name,
|
||||
"..."
|
||||
)}
|
||||
name={getStringOrPlaceholder(webhook?.name)}
|
||||
onClose={closeModal}
|
||||
onConfirm={handleRemoveConfirm}
|
||||
open={params.action === "remove"}
|
||||
|
|
Loading…
Reference in a new issue