Add permissions to order management

This commit is contained in:
dominik-zeglen 2019-10-09 12:18:44 +02:00
parent 47b383320d
commit 5a17d4dc4c
4 changed files with 92 additions and 56 deletions

View file

@ -16,11 +16,13 @@ import ExternalLink from "@saleor/components/ExternalLink";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import Link from "@saleor/components/Link"; import Link from "@saleor/components/Link";
import RequirePermissions from "@saleor/components/RequirePermissions";
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField"; import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps, UserPermissionProps } from "@saleor/types";
import { PermissionEnum } from "@saleor/types/globalTypes";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers"; import { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
import { customerUrl } from "../../../customers/urls"; import { customerUrl } from "../../../customers/urls";
@ -49,7 +51,9 @@ const styles = (theme: Theme) =>
} }
}); });
export interface OrderCustomerProps extends Partial<FetchMoreProps> { export interface OrderCustomerProps
extends Partial<FetchMoreProps>,
UserPermissionProps {
order: OrderDetails_order; order: OrderDetails_order;
users?: SearchCustomers_search_edges_node[]; users?: SearchCustomers_search_edges_node[];
loading?: boolean; loading?: boolean;
@ -72,6 +76,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
loading, loading,
order, order,
users, users,
userPermissions,
onCustomerEdit, onCustomerEdit,
onBillingAddressEdit, onBillingAddressEdit,
onFetchMore: onFetchMoreUsers, onFetchMore: onFetchMoreUsers,
@ -100,14 +105,19 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
})} })}
toolbar={ toolbar={
!!canEditCustomer && ( !!canEditCustomer && (
<Button <RequirePermissions
color="primary" userPermissions={userPermissions}
variant="text" requiredPermissions={[PermissionEnum.MANAGE_USERS]}
disabled={!onCustomerEdit}
onClick={toggleEditMode}
> >
{intl.formatMessage(buttonMessages.edit)} <Button
</Button> color="primary"
variant="text"
disabled={!onCustomerEdit}
onClick={toggleEditMode}
>
{intl.formatMessage(buttonMessages.edit)}
</Button>
</RequirePermissions>
) )
} }
/> />
@ -163,18 +173,23 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
<Typography className={classes.userEmail}> <Typography className={classes.userEmail}>
{user.email} {user.email}
</Typography> </Typography>
<div> <RequirePermissions
<Link userPermissions={userPermissions}
underline={false} requiredPermissions={[PermissionEnum.MANAGE_USERS]}
href={createHref(customerUrl(user.id))} >
onClick={onProfileView} <div>
> <Link
<FormattedMessage underline={false}
defaultMessage="View Profile" href={createHref(customerUrl(user.id))}
description="link" onClick={onProfileView}
/> >
</Link> <FormattedMessage
</div> defaultMessage="View Profile"
description="link"
/>
</Link>
</div>
</RequirePermissions>
{/* TODO: Uncomment it after adding ability to filter {/* TODO: Uncomment it after adding ability to filter
orders by customer */} orders by customer */}
{/* <div> {/* <div>
@ -187,36 +202,40 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
</> </>
)} )}
</CardContent> </CardContent>
<Hr /> {!!user && (
<CardContent> <>
<div className={classes.sectionHeader}> <Hr />
<Typography className={classes.sectionHeaderTitle}> <CardContent>
<FormattedMessage <div className={classes.sectionHeader}>
defaultMessage="Contact Information" <Typography className={classes.sectionHeaderTitle}>
description="subheader" <FormattedMessage
/> defaultMessage="Contact Information"
</Typography> description="subheader"
</div> />
</Typography>
</div>
{maybe(() => order.userEmail) === undefined ? ( {maybe(() => order.userEmail) === undefined ? (
<Skeleton /> <Skeleton />
) : order.userEmail === null ? ( ) : order.userEmail === null ? (
<Typography> <Typography>
<FormattedMessage <FormattedMessage
defaultMessage="Not set" defaultMessage="Not set"
description="customer is not set in draft order" description="customer is not set in draft order"
id="orderCustomerCustomerNotSet" id="orderCustomerCustomerNotSet"
/> />
</Typography> </Typography>
) : ( ) : (
<ExternalLink <ExternalLink
href={`mailto:${maybe(() => order.userEmail)}`} href={`mailto:${maybe(() => order.userEmail)}`}
typographyProps={{ color: "primary" }} typographyProps={{ color: "primary" }}
> >
{maybe(() => order.userEmail)} {maybe(() => order.userEmail)}
</ExternalLink> </ExternalLink>
)} )}
</CardContent> </CardContent>
</>
)}
<Hr /> <Hr />
<CardContent> <CardContent>
<div className={classes.sectionHeader}> <div className={classes.sectionHeader}>

View file

@ -17,6 +17,7 @@ import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { UserPermissionProps } from "@saleor/types";
import { maybe, renderCollection } from "../../../misc"; import { maybe, renderCollection } from "../../../misc";
import { OrderStatus } from "../../../types/globalTypes"; import { OrderStatus } from "../../../types/globalTypes";
import { OrderDetails_order } from "../../types/OrderDetails"; import { OrderDetails_order } from "../../types/OrderDetails";
@ -38,7 +39,7 @@ const styles = (theme: Theme) =>
} }
}); });
export interface OrderDetailsPageProps extends WithStyles<typeof styles> { export interface OrderDetailsPageProps extends UserPermissionProps {
order: OrderDetails_order; order: OrderDetails_order;
shippingMethods?: Array<{ shippingMethods?: Array<{
id: string; id: string;
@ -68,12 +69,13 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
({ ({
classes, classes,
order, order,
onOrderCancel, userPermissions,
onBack, onBack,
onBillingAddressEdit, onBillingAddressEdit,
onFulfillmentCancel, onFulfillmentCancel,
onFulfillmentTrackingNumberUpdate, onFulfillmentTrackingNumberUpdate,
onNoteAdd, onNoteAdd,
onOrderCancel,
onOrderFulfill, onOrderFulfill,
onPaymentCapture, onPaymentCapture,
onPaymentPaid, onPaymentPaid,
@ -81,7 +83,7 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
onPaymentVoid, onPaymentVoid,
onShippingAddressEdit, onShippingAddressEdit,
onProfileView onProfileView
}: OrderDetailsPageProps) => { }: OrderDetailsPageProps & WithStyles<typeof styles>) => {
const intl = useIntl(); const intl = useIntl();
const canCancel = maybe(() => order.status) !== OrderStatus.CANCELED; const canCancel = maybe(() => order.status) !== OrderStatus.CANCELED;
@ -170,6 +172,7 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
canEditAddresses={canEditAddresses} canEditAddresses={canEditAddresses}
canEditCustomer={false} canEditCustomer={false}
order={order} order={order}
userPermissions={userPermissions}
onBillingAddressEdit={onBillingAddressEdit} onBillingAddressEdit={onBillingAddressEdit}
onShippingAddressEdit={onShippingAddressEdit} onShippingAddressEdit={onShippingAddressEdit}
onProfileView={onProfileView} onProfileView={onProfileView}

View file

@ -18,7 +18,7 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps, UserPermissionProps } from "@saleor/types";
import { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers"; import { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { DraftOrderInput } from "../../../types/globalTypes"; import { DraftOrderInput } from "../../../types/globalTypes";
@ -39,7 +39,9 @@ const styles = (theme: Theme) =>
} }
}); });
export interface OrderDraftPageProps extends FetchMoreProps { export interface OrderDraftPageProps
extends FetchMoreProps,
UserPermissionProps {
disabled: boolean; disabled: boolean;
order: OrderDetails_order; order: OrderDetails_order;
users: SearchCustomers_search_edges_node[]; users: SearchCustomers_search_edges_node[];
@ -90,7 +92,8 @@ const OrderDraftPage = withStyles(styles, { name: "OrderDraftPage" })(
onProfileView, onProfileView,
order, order,
users, users,
usersLoading usersLoading,
userPermissions
}: OrderDraftPageProps & WithStyles<typeof styles>) => { }: OrderDraftPageProps & WithStyles<typeof styles>) => {
const intl = useIntl(); const intl = useIntl();
@ -147,6 +150,7 @@ const OrderDraftPage = withStyles(styles, { name: "OrderDraftPage" })(
loading={usersLoading} loading={usersLoading}
order={order} order={order}
users={users} users={users}
userPermissions={userPermissions}
onBillingAddressEdit={onBillingAddressEdit} onBillingAddressEdit={onBillingAddressEdit}
onCustomerEdit={onCustomerEdit} onCustomerEdit={onCustomerEdit}
onFetchMore={onFetchMore} onFetchMore={onFetchMore}

View file

@ -2,6 +2,7 @@ import React from "react";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useUser from "@saleor/hooks/useUser";
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config"; import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
import SearchCustomers from "../../../containers/SearchCustomers"; import SearchCustomers from "../../../containers/SearchCustomers";
import { customerUrl } from "../../../customers/urls"; import { customerUrl } from "../../../customers/urls";
@ -80,6 +81,7 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
params params
}) => { }) => {
const navigate = useNavigator(); const navigate = useNavigator();
const { user } = useUser();
return ( return (
<TypedOrderDetailsQuery <TypedOrderDetailsQuery
@ -183,6 +185,10 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
() => data.order.availableShippingMethods, () => data.order.availableShippingMethods,
[] []
)} )}
userPermissions={maybe(
() => user.permissions,
[]
)}
onOrderCancel={() => openModal("cancel")} onOrderCancel={() => openModal("cancel")}
onOrderFulfill={() => openModal("fulfill")} onOrderFulfill={() => openModal("fulfill")}
onFulfillmentCancel={fulfillmentId => onFulfillmentCancel={fulfillmentId =>
@ -466,6 +472,10 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
onProfileView={() => onProfileView={() =>
navigate(customerUrl(order.user.id)) navigate(customerUrl(order.user.id))
} }
userPermissions={maybe(
() => user.permissions,
[]
)}
/> />
<OrderDraftCancelDialog <OrderDraftCancelDialog
confirmButtonState={getMutationState( confirmButtonState={getMutationState(