Merge pull request #231 from mirumee/fix/permissions
Fix permission handling
This commit is contained in:
commit
3c88c49010
31 changed files with 3053 additions and 1144 deletions
|
@ -48,3 +48,4 @@ All notable, unreleased changes to this project will be documented in this file.
|
|||
- Add readonly mode - #229 by @dominik-zeglen
|
||||
- Add mailing configuration - #222 by @dominik-zeglen
|
||||
- Fix minor bugs - #230 by @dominik-zeglen
|
||||
- Fix permission handling - #231 by @dominik-zeglen
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"POT-Creation-Date: 2019-10-24T13:17:27.157Z\n"
|
||||
"POT-Creation-Date: 2019-10-25T13:36:05.943Z\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@ -9667,14 +9667,6 @@ msgctxt "button"
|
|||
msgid "{languageName} - {languageCode}"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/translations/components/TranslationFields/TranslationFields.json
|
||||
#. [src.translations.components.TranslationFields.282734765]
|
||||
#. defaultMessage is:
|
||||
#. {numberOFields} Translations, {numberOfTranslatedFields} Completed
|
||||
msgctxt "description"
|
||||
msgid "{numberOFields} Translations, {numberOfTranslatedFields} Completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/SeoForm/SeoForm.json
|
||||
#. [src.components.SeoForm.3877274856] - character limit
|
||||
#. defaultMessage is:
|
||||
|
@ -9683,6 +9675,14 @@ msgctxt "character limit"
|
|||
msgid "{numberOfCharacters} of {maxCharacters} characters"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/translations/components/TranslationFields/TranslationFields.json
|
||||
#. [src.translations.components.TranslationFields.1308081812]
|
||||
#. defaultMessage is:
|
||||
#. {numberOfFields} Translations, {numberOfTranslatedFields} Completed
|
||||
msgctxt "description"
|
||||
msgid "{numberOfFields} Translations, {numberOfTranslatedFields} Completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/ColumnPicker/ColumnPickerContent.json
|
||||
#. [src.components.ColumnPicker.2715399461] - pick columns to display
|
||||
#. defaultMessage is:
|
||||
|
|
|
@ -15,8 +15,6 @@ export const fragmentUser = gql`
|
|||
email
|
||||
firstName
|
||||
lastName
|
||||
isStaff
|
||||
note
|
||||
permissions {
|
||||
code
|
||||
name
|
||||
|
|
|
@ -31,8 +31,6 @@ export interface SetPassword_setPassword_user {
|
|||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
isStaff: boolean;
|
||||
note: string | null;
|
||||
permissions: (SetPassword_setPassword_user_permissions | null)[] | null;
|
||||
avatar: SetPassword_setPassword_user_avatar | null;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,6 @@ export interface TokenAuth_tokenCreate_user {
|
|||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
isStaff: boolean;
|
||||
note: string | null;
|
||||
permissions: (TokenAuth_tokenCreate_user_permissions | null)[] | null;
|
||||
avatar: TokenAuth_tokenCreate_user_avatar | null;
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ export interface User {
|
|||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
isStaff: boolean;
|
||||
note: string | null;
|
||||
permissions: (User_permissions | null)[] | null;
|
||||
avatar: User_avatar | null;
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ export interface VerifyToken_tokenVerify_user {
|
|||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
isStaff: boolean;
|
||||
note: string | null;
|
||||
permissions: (VerifyToken_tokenVerify_user_permissions | null)[] | null;
|
||||
avatar: VerifyToken_tokenVerify_user_avatar | null;
|
||||
}
|
||||
|
|
|
@ -24,11 +24,13 @@ 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 { createConfigurationMenu } from "@saleor/configuration";
|
||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useTheme from "@saleor/hooks/useTheme";
|
||||
import useUser from "@saleor/hooks/useUser";
|
||||
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { staffMemberDetailsUrl } from "@saleor/staff/urls";
|
||||
import Container from "../Container";
|
||||
import AppActionContext from "./AppActionContext";
|
||||
|
@ -297,6 +299,17 @@ const AppLayout = withStyles(styles, {
|
|||
const intl = useIntl();
|
||||
|
||||
const menuStructure = createMenuStructure(intl);
|
||||
const configurationMenu = createConfigurationMenu(intl);
|
||||
const userPermissions = maybe(() => user.permissions, []);
|
||||
|
||||
const renderConfigure = configurationMenu.some(section =>
|
||||
section.menuItems.some(
|
||||
menuItem =>
|
||||
!!userPermissions.find(
|
||||
userPermission => userPermission.code === menuItem.permission
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const handleLogout = () => {
|
||||
setMenuState(false);
|
||||
|
@ -365,7 +378,7 @@ const AppLayout = withStyles(styles, {
|
|||
isMenuSmall={!isMenuSmall}
|
||||
location={location.pathname}
|
||||
user={user}
|
||||
renderConfigure={true}
|
||||
renderConfigure={renderConfigure}
|
||||
onMenuItemClick={handleMenuItemClick}
|
||||
/>
|
||||
</ResponsiveDrawer>
|
||||
|
|
31
src/components/RequirePermissions.tsx
Normal file
31
src/components/RequirePermissions.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import React from "react";
|
||||
|
||||
import { User_permissions } from "@saleor/auth/types/User";
|
||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||
|
||||
export function hasPermissions(
|
||||
userPermissions: User_permissions[],
|
||||
requiredPermissions: PermissionEnum[]
|
||||
): boolean {
|
||||
return requiredPermissions.reduce(
|
||||
(acc, perm) =>
|
||||
acc && !!userPermissions.find(userPerm => userPerm.code === perm),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
export interface RequirePermissionsProps {
|
||||
children: React.ReactNode | React.ReactNodeArray;
|
||||
requiredPermissions: PermissionEnum[];
|
||||
userPermissions: User_permissions[];
|
||||
}
|
||||
|
||||
const RequirePermissions: React.FC<RequirePermissionsProps> = ({
|
||||
children,
|
||||
requiredPermissions,
|
||||
userPermissions
|
||||
}) =>
|
||||
hasPermissions(userPermissions, requiredPermissions) ? <>{children}</> : null;
|
||||
|
||||
RequirePermissions.displayName = "RequirePermissions";
|
||||
export default RequirePermissions;
|
|
@ -99,7 +99,7 @@ export const ConfigurationPage = withStyles(styles, {
|
|||
})(
|
||||
({
|
||||
classes,
|
||||
menu,
|
||||
menu: menus,
|
||||
user,
|
||||
onSectionClick
|
||||
}: ConfigurationPageProps & WithStyles<typeof styles>) => {
|
||||
|
@ -110,9 +110,11 @@ export const ConfigurationPage = withStyles(styles, {
|
|||
className={classes.header}
|
||||
title={intl.formatMessage(sectionNames.configuration)}
|
||||
/>
|
||||
{menu
|
||||
{menus
|
||||
.filter(menu =>
|
||||
menu.menuItems.map(item => hasPermission(item.permission, user))
|
||||
menu.menuItems.some(menuItem =>
|
||||
hasPermission(menuItem.permission, user)
|
||||
)
|
||||
)
|
||||
.map((menu, menuIndex) => (
|
||||
<div className={classes.configurationCategory} key={menuIndex}>
|
||||
|
@ -120,28 +122,30 @@ export const ConfigurationPage = withStyles(styles, {
|
|||
<Typography>{menu.label}</Typography>
|
||||
</div>
|
||||
<div className={classes.configurationItem}>
|
||||
{menu.menuItems.map((item, itemIndex) => (
|
||||
<Card
|
||||
className={item.url ? classes.card : classes.cardDisabled}
|
||||
onClick={() => onSectionClick(item.url)}
|
||||
key={itemIndex}
|
||||
>
|
||||
<CardContent className={classes.cardContent}>
|
||||
<div className={classes.icon}>{item.icon}</div>
|
||||
<div>
|
||||
<Typography
|
||||
className={classes.sectionTitle}
|
||||
color="primary"
|
||||
>
|
||||
{item.title}
|
||||
</Typography>
|
||||
<Typography className={classes.sectionDescription}>
|
||||
{item.description}
|
||||
</Typography>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
{menu.menuItems
|
||||
.filter(menuItem => hasPermission(menuItem.permission, user))
|
||||
.map((item, itemIndex) => (
|
||||
<Card
|
||||
className={item.url ? classes.card : classes.cardDisabled}
|
||||
onClick={() => onSectionClick(item.url)}
|
||||
key={itemIndex}
|
||||
>
|
||||
<CardContent className={classes.cardContent}>
|
||||
<div className={classes.icon}>{item.icon}</div>
|
||||
<div>
|
||||
<Typography
|
||||
className={classes.sectionTitle}
|
||||
color="primary"
|
||||
>
|
||||
{item.title}
|
||||
</Typography>
|
||||
<Typography className={classes.sectionDescription}>
|
||||
{item.description}
|
||||
</Typography>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
@ -81,7 +81,7 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
|||
id: "configurationMenuTaxes"
|
||||
}),
|
||||
icon: <Taxes fontSize="inherit" viewBox="0 0 44 44" />,
|
||||
permission: PermissionEnum.MANAGE_PRODUCTS,
|
||||
permission: PermissionEnum.MANAGE_SETTINGS,
|
||||
title: intl.formatMessage(sectionNames.taxes),
|
||||
url: taxSection
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
|||
preserveAspectRatio="xMinYMin meet"
|
||||
/>
|
||||
),
|
||||
permission: PermissionEnum.MANAGE_SETTINGS,
|
||||
permission: PermissionEnum.MANAGE_PLUGINS,
|
||||
title: intl.formatMessage(sectionNames.plugins),
|
||||
url: pluginsListUrl()
|
||||
},
|
||||
|
|
|
@ -430,6 +430,18 @@ export const permissions: ShopInfo_shop_permissions[] = [
|
|||
{
|
||||
code: PermissionEnum.MANAGE_USERS,
|
||||
name: "Manage customers."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_PLUGINS,
|
||||
name: "Manage plugins."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_SERVICE_ACCOUNTS,
|
||||
name: "Manage service accounts."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_WEBHOOKS,
|
||||
name: "Manage webhooks."
|
||||
}
|
||||
].map(perm => ({
|
||||
__typename: "PermissionDisplay" as "PermissionDisplay",
|
||||
|
|
|
@ -14,7 +14,10 @@ import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight";
|
|||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import { UserPermissionProps } from "@saleor/types";
|
||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
|
@ -26,7 +29,7 @@ const styles = (theme: Theme) =>
|
|||
}
|
||||
});
|
||||
|
||||
interface HomeNotificationTableProps extends WithStyles<typeof styles> {
|
||||
interface HomeNotificationTableProps extends UserPermissionProps {
|
||||
ordersToCapture: number;
|
||||
ordersToFulfill: number;
|
||||
productsOutOfStock: number;
|
||||
|
@ -45,101 +48,112 @@ const HomeNotificationTable = withStyles(styles, {
|
|||
onProductsOutOfStockClick,
|
||||
ordersToCapture,
|
||||
ordersToFulfill,
|
||||
productsOutOfStock
|
||||
}: HomeNotificationTableProps) => (
|
||||
productsOutOfStock,
|
||||
userPermissions
|
||||
}: HomeNotificationTableProps & WithStyles<typeof styles>) => (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableBody className={classes.tableRow}>
|
||||
<TableRow hover={true} onClick={onOrdersToFulfillClick}>
|
||||
<TableCell>
|
||||
{ordersToFulfill === undefined ? (
|
||||
<Skeleton />
|
||||
) : ordersToFulfill === 0 ? (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="No orders ready to fulfill"
|
||||
id="homeNotificationTableNoOrders"
|
||||
/>
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="{amount, plural,
|
||||
<RequirePermissions
|
||||
userPermissions={userPermissions}
|
||||
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
|
||||
>
|
||||
<TableRow hover={true} onClick={onOrdersToFulfillClick}>
|
||||
<TableCell>
|
||||
{ordersToFulfill === undefined ? (
|
||||
<Skeleton />
|
||||
) : ordersToFulfill === 0 ? (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="No orders ready to fulfill"
|
||||
id="homeNotificationTableNoOrders"
|
||||
/>
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="{amount, plural,
|
||||
one {One order}
|
||||
other {{amount} Orders}
|
||||
} are ready to fulfill"
|
||||
id="homeNotificationTableOrders"
|
||||
values={{
|
||||
amount: <strong>{ordersToFulfill}</strong>
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.arrowIcon}>
|
||||
<KeyboardArrowRight />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow hover={true} onClick={onOrdersToCaptureClick}>
|
||||
<TableCell>
|
||||
{ordersToCapture === undefined ? (
|
||||
<Skeleton />
|
||||
) : ordersToCapture === 0 ? (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="No payments waiting for capture"
|
||||
id="homeNotificationsNoPayments"
|
||||
/>
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="{amount, plural,
|
||||
id="homeNotificationTableOrders"
|
||||
values={{
|
||||
amount: <strong>{ordersToFulfill}</strong>
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.arrowIcon}>
|
||||
<KeyboardArrowRight />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow hover={true} onClick={onOrdersToCaptureClick}>
|
||||
<TableCell>
|
||||
{ordersToCapture === undefined ? (
|
||||
<Skeleton />
|
||||
) : ordersToCapture === 0 ? (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="No payments waiting for capture"
|
||||
id="homeNotificationsNoPayments"
|
||||
/>
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="{amount, plural,
|
||||
one {One payment}
|
||||
other {{amount} Payments}
|
||||
} to capture"
|
||||
id="homeNotificationTablePayments"
|
||||
values={{
|
||||
amount: <strong>{ordersToCapture}</strong>
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.arrowIcon}>
|
||||
<KeyboardArrowRight />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow hover={true} onClick={onProductsOutOfStockClick}>
|
||||
<TableCell>
|
||||
{productsOutOfStock === undefined ? (
|
||||
<Skeleton />
|
||||
) : productsOutOfStock === 0 ? (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="No products out of stock"
|
||||
id="homeNotificationsTableNoProducts"
|
||||
/>
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="{amount, plural,
|
||||
id="homeNotificationTablePayments"
|
||||
values={{
|
||||
amount: <strong>{ordersToCapture}</strong>
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.arrowIcon}>
|
||||
<KeyboardArrowRight />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</RequirePermissions>
|
||||
<RequirePermissions
|
||||
userPermissions={userPermissions}
|
||||
requiredPermissions={[PermissionEnum.MANAGE_PRODUCTS]}
|
||||
>
|
||||
<TableRow hover={true} onClick={onProductsOutOfStockClick}>
|
||||
<TableCell>
|
||||
{productsOutOfStock === undefined ? (
|
||||
<Skeleton />
|
||||
) : productsOutOfStock === 0 ? (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="No products out of stock"
|
||||
id="homeNotificationsTableNoProducts"
|
||||
/>
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="{amount, plural,
|
||||
one {One product}
|
||||
other {{amount} Products}
|
||||
} out of stock"
|
||||
id="homeNotificationTableProducts"
|
||||
values={{
|
||||
amount: <strong>{productsOutOfStock}</strong>
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.arrowIcon}>
|
||||
<KeyboardArrowRight />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
id="homeNotificationTableProducts"
|
||||
values={{
|
||||
amount: <strong>{productsOutOfStock}</strong>
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.arrowIcon}>
|
||||
<KeyboardArrowRight />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</RequirePermissions>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
|
|
|
@ -10,7 +10,10 @@ import CardSpacer from "@saleor/components/CardSpacer";
|
|||
import Container from "@saleor/components/Container";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Money from "@saleor/components/Money";
|
||||
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import { UserPermissionProps } from "@saleor/types";
|
||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||
import Orders from "../../../icons/Orders";
|
||||
import Sales from "../../../icons/Sales";
|
||||
import {
|
||||
|
@ -39,7 +42,7 @@ const styles = (theme: Theme) =>
|
|||
}
|
||||
});
|
||||
|
||||
export interface HomePageProps extends WithStyles<typeof styles> {
|
||||
export interface HomePageProps extends UserPermissionProps {
|
||||
activities: Home_activities_edges_node[];
|
||||
orders: number;
|
||||
ordersToCapture: number;
|
||||
|
@ -68,35 +71,41 @@ const HomePage = withStyles(styles, { name: "HomePage" })(
|
|||
onProductsOutOfStockClick,
|
||||
ordersToCapture,
|
||||
ordersToFulfill,
|
||||
productsOutOfStock
|
||||
}: HomePageProps) => (
|
||||
productsOutOfStock,
|
||||
userPermissions
|
||||
}: HomePageProps & WithStyles<typeof styles>) => (
|
||||
<Container>
|
||||
<HomeHeader userName={userName} />
|
||||
<CardSpacer />
|
||||
<Grid>
|
||||
<div>
|
||||
<div className={classes.cardContainer}>
|
||||
<HomeAnalyticsCard
|
||||
title={"Sales"}
|
||||
icon={<Sales fontSize={"inherit"} viewBox="0 0 64 64" />}
|
||||
>
|
||||
{sales ? (
|
||||
<Money money={sales} />
|
||||
) : (
|
||||
<Skeleton style={{ width: "5em" }} />
|
||||
)}
|
||||
</HomeAnalyticsCard>
|
||||
<HomeAnalyticsCard
|
||||
title={"Orders"}
|
||||
icon={<Orders fontSize={"inherit"} viewBox="0 0 64 64" />}
|
||||
>
|
||||
{orders === undefined ? (
|
||||
<Skeleton style={{ width: "5em" }} />
|
||||
) : (
|
||||
orders
|
||||
)}
|
||||
</HomeAnalyticsCard>
|
||||
</div>
|
||||
<RequirePermissions
|
||||
userPermissions={userPermissions}
|
||||
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
|
||||
>
|
||||
<div className={classes.cardContainer}>
|
||||
<HomeAnalyticsCard
|
||||
title={"Sales"}
|
||||
icon={<Sales fontSize={"inherit"} viewBox="0 0 64 64" />}
|
||||
>
|
||||
{sales ? (
|
||||
<Money money={sales} />
|
||||
) : (
|
||||
<Skeleton style={{ width: "5em" }} />
|
||||
)}
|
||||
</HomeAnalyticsCard>
|
||||
<HomeAnalyticsCard
|
||||
title={"Orders"}
|
||||
icon={<Orders fontSize={"inherit"} viewBox="0 0 64 64" />}
|
||||
>
|
||||
{orders === undefined ? (
|
||||
<Skeleton style={{ width: "5em" }} />
|
||||
) : (
|
||||
orders
|
||||
)}
|
||||
</HomeAnalyticsCard>
|
||||
</div>
|
||||
</RequirePermissions>
|
||||
<HomeNotificationTable
|
||||
onOrdersToCaptureClick={onOrdersToCaptureClick}
|
||||
onOrdersToFulfillClick={onOrdersToFulfillClick}
|
||||
|
@ -104,16 +113,30 @@ const HomePage = withStyles(styles, { name: "HomePage" })(
|
|||
ordersToCapture={ordersToCapture}
|
||||
ordersToFulfill={ordersToFulfill}
|
||||
productsOutOfStock={productsOutOfStock}
|
||||
userPermissions={userPermissions}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<HomeProductListCard
|
||||
onRowClick={onProductClick}
|
||||
topProducts={topProducts}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<RequirePermissions
|
||||
userPermissions={userPermissions}
|
||||
requiredPermissions={[
|
||||
PermissionEnum.MANAGE_ORDERS,
|
||||
PermissionEnum.MANAGE_PRODUCTS
|
||||
]}
|
||||
>
|
||||
<HomeProductListCard
|
||||
onRowClick={onProductClick}
|
||||
topProducts={topProducts}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</RequirePermissions>
|
||||
</div>
|
||||
<div>
|
||||
<HomeActivityCard activities={activities} />
|
||||
<RequirePermissions
|
||||
userPermissions={userPermissions}
|
||||
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
|
||||
>
|
||||
<HomeActivityCard activities={activities} />
|
||||
</RequirePermissions>
|
||||
</div>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
|
|
@ -56,6 +56,7 @@ const HomeSection = () => {
|
|||
ordersToFulfill={maybe(() => data.ordersToFulfill.totalCount)}
|
||||
productsOutOfStock={maybe(() => data.productsOutOfStock.totalCount)}
|
||||
userName={getUserName(user, true)}
|
||||
userPermissions={maybe(() => user.permissions, [])}
|
||||
/>
|
||||
)}
|
||||
</HomePageQuery>
|
||||
|
|
|
@ -16,11 +16,13 @@ import ExternalLink from "@saleor/components/ExternalLink";
|
|||
import Form from "@saleor/components/Form";
|
||||
import Hr from "@saleor/components/Hr";
|
||||
import Link from "@saleor/components/Link";
|
||||
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
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 { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
|
||||
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;
|
||||
users?: SearchCustomers_search_edges_node[];
|
||||
loading?: boolean;
|
||||
|
@ -72,6 +76,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
|||
loading,
|
||||
order,
|
||||
users,
|
||||
userPermissions,
|
||||
onCustomerEdit,
|
||||
onBillingAddressEdit,
|
||||
onFetchMore: onFetchMoreUsers,
|
||||
|
@ -81,6 +86,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
|||
const intl = useIntl();
|
||||
|
||||
const user = maybe(() => order.user);
|
||||
const userEmail = maybe(()=>order.userEmail)
|
||||
|
||||
const [userDisplayName, setUserDisplayName] = useStateFromProps(
|
||||
maybe(() => user.email, "")
|
||||
|
@ -100,14 +106,19 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
|||
})}
|
||||
toolbar={
|
||||
!!canEditCustomer && (
|
||||
<Button
|
||||
color="primary"
|
||||
variant="text"
|
||||
disabled={!onCustomerEdit}
|
||||
onClick={toggleEditMode}
|
||||
<RequirePermissions
|
||||
userPermissions={userPermissions}
|
||||
requiredPermissions={[PermissionEnum.MANAGE_USERS]}
|
||||
>
|
||||
{intl.formatMessage(buttonMessages.edit)}
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="text"
|
||||
disabled={!onCustomerEdit}
|
||||
onClick={toggleEditMode}
|
||||
>
|
||||
{intl.formatMessage(buttonMessages.edit)}
|
||||
</Button>
|
||||
</RequirePermissions>
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -155,26 +166,35 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
|||
}}
|
||||
</Form>
|
||||
) : user === null ? (
|
||||
<Typography>
|
||||
<FormattedMessage defaultMessage="Anonymous user" />
|
||||
</Typography>
|
||||
userEmail === null ? (
|
||||
<Typography>
|
||||
<FormattedMessage defaultMessage="Anonymous user" />
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography className={classes.userEmail}>{userEmail}</Typography>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<Typography className={classes.userEmail}>
|
||||
{user.email}
|
||||
</Typography>
|
||||
<div>
|
||||
<Link
|
||||
underline={false}
|
||||
href={createHref(customerUrl(user.id))}
|
||||
onClick={onProfileView}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="View Profile"
|
||||
description="link"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<RequirePermissions
|
||||
userPermissions={userPermissions}
|
||||
requiredPermissions={[PermissionEnum.MANAGE_USERS]}
|
||||
>
|
||||
<div>
|
||||
<Link
|
||||
underline={false}
|
||||
href={createHref(customerUrl(user.id))}
|
||||
onClick={onProfileView}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="View Profile"
|
||||
description="link"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</RequirePermissions>
|
||||
{/* TODO: Uncomment it after adding ability to filter
|
||||
orders by customer */}
|
||||
{/* <div>
|
||||
|
@ -187,36 +207,40 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
|||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
<Hr />
|
||||
<CardContent>
|
||||
<div className={classes.sectionHeader}>
|
||||
<Typography className={classes.sectionHeaderTitle}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Contact Information"
|
||||
description="subheader"
|
||||
/>
|
||||
</Typography>
|
||||
</div>
|
||||
{!!user && (
|
||||
<>
|
||||
<Hr />
|
||||
<CardContent>
|
||||
<div className={classes.sectionHeader}>
|
||||
<Typography className={classes.sectionHeaderTitle}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Contact Information"
|
||||
description="subheader"
|
||||
/>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
{maybe(() => order.userEmail) === undefined ? (
|
||||
<Skeleton />
|
||||
) : order.userEmail === null ? (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="Not set"
|
||||
description="customer is not set in draft order"
|
||||
id="orderCustomerCustomerNotSet"
|
||||
/>
|
||||
</Typography>
|
||||
) : (
|
||||
<ExternalLink
|
||||
href={`mailto:${maybe(() => order.userEmail)}`}
|
||||
typographyProps={{ color: "primary" }}
|
||||
>
|
||||
{maybe(() => order.userEmail)}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</CardContent>
|
||||
{maybe(() => order.userEmail) === undefined ? (
|
||||
<Skeleton />
|
||||
) : order.userEmail === null ? (
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
defaultMessage="Not set"
|
||||
description="customer is not set in draft order"
|
||||
id="orderCustomerCustomerNotSet"
|
||||
/>
|
||||
</Typography>
|
||||
) : (
|
||||
<ExternalLink
|
||||
href={`mailto:${maybe(() => order.userEmail)}`}
|
||||
typographyProps={{ color: "primary" }}
|
||||
>
|
||||
{maybe(() => order.userEmail)}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</CardContent>
|
||||
</>
|
||||
)}
|
||||
<Hr />
|
||||
<CardContent>
|
||||
<div className={classes.sectionHeader}>
|
||||
|
|
|
@ -17,6 +17,7 @@ import Grid from "@saleor/components/Grid";
|
|||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { UserPermissionProps } from "@saleor/types";
|
||||
import { maybe, renderCollection } from "../../../misc";
|
||||
import { OrderStatus } from "../../../types/globalTypes";
|
||||
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;
|
||||
shippingMethods?: Array<{
|
||||
id: string;
|
||||
|
@ -68,12 +69,13 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
|
|||
({
|
||||
classes,
|
||||
order,
|
||||
onOrderCancel,
|
||||
userPermissions,
|
||||
onBack,
|
||||
onBillingAddressEdit,
|
||||
onFulfillmentCancel,
|
||||
onFulfillmentTrackingNumberUpdate,
|
||||
onNoteAdd,
|
||||
onOrderCancel,
|
||||
onOrderFulfill,
|
||||
onPaymentCapture,
|
||||
onPaymentPaid,
|
||||
|
@ -81,7 +83,7 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
|
|||
onPaymentVoid,
|
||||
onShippingAddressEdit,
|
||||
onProfileView
|
||||
}: OrderDetailsPageProps) => {
|
||||
}: OrderDetailsPageProps & WithStyles<typeof styles>) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const canCancel = maybe(() => order.status) !== OrderStatus.CANCELED;
|
||||
|
@ -170,6 +172,7 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
|
|||
canEditAddresses={canEditAddresses}
|
||||
canEditCustomer={false}
|
||||
order={order}
|
||||
userPermissions={userPermissions}
|
||||
onBillingAddressEdit={onBillingAddressEdit}
|
||||
onShippingAddressEdit={onShippingAddressEdit}
|
||||
onProfileView={onProfileView}
|
||||
|
|
|
@ -18,7 +18,7 @@ import PageHeader from "@saleor/components/PageHeader";
|
|||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
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 { maybe } from "../../../misc";
|
||||
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;
|
||||
order: OrderDetails_order;
|
||||
users: SearchCustomers_search_edges_node[];
|
||||
|
@ -90,7 +92,8 @@ const OrderDraftPage = withStyles(styles, { name: "OrderDraftPage" })(
|
|||
onProfileView,
|
||||
order,
|
||||
users,
|
||||
usersLoading
|
||||
usersLoading,
|
||||
userPermissions
|
||||
}: OrderDraftPageProps & WithStyles<typeof styles>) => {
|
||||
const intl = useIntl();
|
||||
|
||||
|
@ -147,6 +150,7 @@ const OrderDraftPage = withStyles(styles, { name: "OrderDraftPage" })(
|
|||
loading={usersLoading}
|
||||
order={order}
|
||||
users={users}
|
||||
userPermissions={userPermissions}
|
||||
onBillingAddressEdit={onBillingAddressEdit}
|
||||
onCustomerEdit={onCustomerEdit}
|
||||
onFetchMore={onFetchMore}
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useUser from "@saleor/hooks/useUser";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
|
||||
import SearchCustomers from "../../../containers/SearchCustomers";
|
||||
import { customerUrl } from "../../../customers/urls";
|
||||
|
@ -80,6 +81,7 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
|
|||
params
|
||||
}) => {
|
||||
const navigate = useNavigator();
|
||||
const { user } = useUser();
|
||||
|
||||
return (
|
||||
<TypedOrderDetailsQuery
|
||||
|
@ -183,6 +185,10 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
|
|||
() => data.order.availableShippingMethods,
|
||||
[]
|
||||
)}
|
||||
userPermissions={maybe(
|
||||
() => user.permissions,
|
||||
[]
|
||||
)}
|
||||
onOrderCancel={() => openModal("cancel")}
|
||||
onOrderFulfill={() => openModal("fulfill")}
|
||||
onFulfillmentCancel={fulfillmentId =>
|
||||
|
@ -466,6 +472,10 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
|
|||
onProfileView={() =>
|
||||
navigate(customerUrl(order.user.id))
|
||||
}
|
||||
userPermissions={maybe(
|
||||
() => user.permissions,
|
||||
[]
|
||||
)}
|
||||
/>
|
||||
<OrderDraftCancelDialog
|
||||
confirmButtonState={getMutationState(
|
||||
|
|
|
@ -10,7 +10,7 @@ import ErrorPage from "./components/ErrorPage/ErrorPage";
|
|||
import useNavigator from "./hooks/useNavigator";
|
||||
import useNotifier from "./hooks/useNotifier";
|
||||
import { commonMessages } from "./intl";
|
||||
import { RequireAtLeastOne } from "./misc";
|
||||
import { maybe, RequireAtLeastOne } from "./misc";
|
||||
|
||||
export interface LoadMore<TData, TVariables> {
|
||||
loadMore: (
|
||||
|
@ -81,12 +81,21 @@ export function TypedQuery<TData, TVariables>(
|
|||
variables={variables}
|
||||
skip={skip}
|
||||
context={{ useBatching: true }}
|
||||
errorPolicy="all"
|
||||
>
|
||||
{(queryData: QueryResult<TData, TVariables>) => {
|
||||
if (queryData.error) {
|
||||
pushMessage({
|
||||
text: intl.formatMessage(commonMessages.somethingWentWrong)
|
||||
});
|
||||
if (
|
||||
!queryData.error.graphQLErrors.every(
|
||||
err =>
|
||||
maybe(() => err.extensions.exception.code) ===
|
||||
"PermissionDenied"
|
||||
)
|
||||
) {
|
||||
pushMessage({
|
||||
text: intl.formatMessage(commonMessages.somethingWentWrong)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const loadMore = (
|
||||
|
|
|
@ -5,14 +5,18 @@ import AppHeader from "@saleor/components/AppHeader";
|
|||
import Container from "@saleor/components/Container";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ListActions, PageListProps } from "@saleor/types";
|
||||
import { WeightUnitsEnum } from "@saleor/types/globalTypes";
|
||||
import { ListActions, PageListProps, UserPermissionProps } from "@saleor/types";
|
||||
import { PermissionEnum, WeightUnitsEnum } from "@saleor/types/globalTypes";
|
||||
import { ShippingZoneFragment } from "../../types/ShippingZoneFragment";
|
||||
import ShippingWeightUnitForm from "../ShippingWeightUnitForm";
|
||||
import ShippingZonesList from "../ShippingZonesList";
|
||||
|
||||
export interface ShippingZonesListPageProps extends PageListProps, ListActions {
|
||||
export interface ShippingZonesListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
UserPermissionProps {
|
||||
defaultWeightUnit: WeightUnitsEnum;
|
||||
shippingZones: ShippingZoneFragment[];
|
||||
onBack: () => void;
|
||||
|
@ -22,7 +26,14 @@ export interface ShippingZonesListPageProps extends PageListProps, ListActions {
|
|||
|
||||
const ShippingZonesListPage: React.StatelessComponent<
|
||||
ShippingZonesListPageProps
|
||||
> = ({ defaultWeightUnit, disabled, onBack, onSubmit, ...listProps }) => {
|
||||
> = ({
|
||||
defaultWeightUnit,
|
||||
disabled,
|
||||
userPermissions,
|
||||
onBack,
|
||||
onSubmit,
|
||||
...listProps
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
|
@ -41,11 +52,16 @@ const ShippingZonesListPage: React.StatelessComponent<
|
|||
<ShippingZonesList disabled={disabled} {...listProps} />
|
||||
</div>
|
||||
<div>
|
||||
<ShippingWeightUnitForm
|
||||
defaultWeightUnit={defaultWeightUnit}
|
||||
disabled={disabled}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
<RequirePermissions
|
||||
userPermissions={userPermissions}
|
||||
requiredPermissions={[PermissionEnum.MANAGE_SETTINGS]}
|
||||
>
|
||||
<ShippingWeightUnitForm
|
||||
defaultWeightUnit={defaultWeightUnit}
|
||||
disabled={disabled}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
</RequirePermissions>
|
||||
</div>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
|
|
@ -14,6 +14,7 @@ import usePaginator, {
|
|||
createPaginationState
|
||||
} from "@saleor/hooks/usePaginator";
|
||||
import useShop from "@saleor/hooks/useShop";
|
||||
import useUser from "@saleor/hooks/useUser";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { getMutationState, maybe } from "@saleor/misc";
|
||||
import { ListViews } from "@saleor/types";
|
||||
|
@ -45,6 +46,7 @@ export const ShippingZonesList: React.StatelessComponent<
|
|||
const notify = useNotifier();
|
||||
const paginate = usePaginator();
|
||||
const shop = useShop();
|
||||
const { user } = useUser();
|
||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||
params.ids
|
||||
);
|
||||
|
@ -195,6 +197,7 @@ export const ShippingZonesList: React.StatelessComponent<
|
|||
<DeleteIcon />
|
||||
</IconButton>
|
||||
}
|
||||
userPermissions={maybe(() => user.permissions, [])}
|
||||
/>
|
||||
|
||||
<ActionDialog
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import avatarImage from "@assets/images/avatars/avatar1.png";
|
||||
import { PermissionEnum } from "../types/globalTypes";
|
||||
import { permissions } from "@saleor/fixtures";
|
||||
import { StaffList_staffUsers_edges_node } from "./types/StaffList";
|
||||
import { StaffMemberDetails_user } from "./types/StaffMemberDetails";
|
||||
|
||||
|
@ -145,49 +145,5 @@ export const staffMember: StaffMemberDetails_user = {
|
|||
id: "VXNlcjoyMQ==",
|
||||
isActive: true,
|
||||
lastName: "Smith",
|
||||
permissions: [
|
||||
{
|
||||
code: PermissionEnum.IMPERSONATE_USERS,
|
||||
name: "Impersonate customers."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_DISCOUNTS,
|
||||
name: "Manage sales and vouchers."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_MENUS,
|
||||
name: "Manage navigation."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_ORDERS,
|
||||
name: "Manage orders."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_PAGES,
|
||||
name: "Manage pages."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_PRODUCTS,
|
||||
name: "Manage products."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_SETTINGS,
|
||||
name: "Manage settings."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_SHIPPING,
|
||||
name: "Manage shipping."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_STAFF,
|
||||
name: "Manage staff."
|
||||
},
|
||||
{
|
||||
code: PermissionEnum.MANAGE_USERS,
|
||||
name: "Manage customers."
|
||||
}
|
||||
].map(perm => ({
|
||||
__typename: "PermissionDisplay" as "PermissionDisplay",
|
||||
...perm
|
||||
}))
|
||||
permissions
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,8 @@ import { storiesOf } from "@storybook/react";
|
|||
import React from "react";
|
||||
|
||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||
import { permissions } from "@saleor/fixtures";
|
||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||
import HomePage, { HomePageProps } from "../../../home/components/HomePage";
|
||||
import { shop as shopFixture } from "../../../home/fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
@ -21,7 +23,8 @@ const homePageProps: Omit<HomePageProps, "classes"> = {
|
|||
productsOutOfStock: shop.productsOutOfStock.totalCount,
|
||||
sales: shop.salesToday.gross,
|
||||
topProducts: shop.productTopToday.edges.map(edge => edge.node),
|
||||
userName: "admin@example.com"
|
||||
userName: "admin@example.com",
|
||||
userPermissions: permissions
|
||||
};
|
||||
|
||||
storiesOf("Views / HomePage", module)
|
||||
|
@ -42,4 +45,23 @@ storiesOf("Views / HomePage", module)
|
|||
))
|
||||
.add("no data", () => (
|
||||
<HomePage {...homePageProps} topProducts={[]} activities={[]} />
|
||||
))
|
||||
.add("no permissions", () => (
|
||||
<HomePage {...homePageProps} userPermissions={[]} />
|
||||
))
|
||||
.add("product permissions", () => (
|
||||
<HomePage
|
||||
{...homePageProps}
|
||||
userPermissions={permissions.filter(
|
||||
perm => perm.code === PermissionEnum.MANAGE_PRODUCTS
|
||||
)}
|
||||
/>
|
||||
))
|
||||
.add("order permissions", () => (
|
||||
<HomePage
|
||||
{...homePageProps}
|
||||
userPermissions={permissions.filter(
|
||||
perm => perm.code === PermissionEnum.MANAGE_ORDERS
|
||||
)}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Omit } from "@material-ui/core";
|
|||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import { permissions } from "@saleor/fixtures";
|
||||
import OrderCustomer, {
|
||||
OrderCustomerProps
|
||||
} from "../../../orders/components/OrderCustomer";
|
||||
|
@ -19,6 +20,7 @@ const props: Omit<OrderCustomerProps, "classes"> = {
|
|||
onProfileView: () => undefined,
|
||||
onShippingAddressEdit: undefined,
|
||||
order,
|
||||
userPermissions: permissions,
|
||||
users: clients
|
||||
};
|
||||
|
||||
|
@ -37,4 +39,10 @@ storiesOf("Orders / OrderCustomer", module)
|
|||
))
|
||||
.add("editable", () => (
|
||||
<OrderCustomer {...props} canEditAddresses={true} canEditCustomer={true} />
|
||||
))
|
||||
.add("editable", () => (
|
||||
<OrderCustomer {...props} canEditAddresses={true} canEditCustomer={true} />
|
||||
))
|
||||
.add("no user permissions", () => (
|
||||
<OrderCustomer {...props} userPermissions={[]} />
|
||||
));
|
||||
|
|
|
@ -3,6 +3,7 @@ import { storiesOf } from "@storybook/react";
|
|||
import React from "react";
|
||||
|
||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||
import { permissions } from "@saleor/fixtures";
|
||||
import OrderDetailsPage, {
|
||||
OrderDetailsPageProps
|
||||
} from "../../../orders/components/OrderDetailsPage";
|
||||
|
@ -32,7 +33,8 @@ const props: Omit<OrderDetailsPageProps, "classes"> = {
|
|||
onProductClick: undefined,
|
||||
onProfileView: () => undefined,
|
||||
onShippingAddressEdit: undefined,
|
||||
order
|
||||
order,
|
||||
userPermissions: permissions
|
||||
};
|
||||
|
||||
storiesOf("Views / Orders / Order details", module)
|
||||
|
|
|
@ -3,7 +3,7 @@ import { storiesOf } from "@storybook/react";
|
|||
import React from "react";
|
||||
|
||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||
import { fetchMoreProps } from "@saleor/fixtures";
|
||||
import { fetchMoreProps, permissions } from "@saleor/fixtures";
|
||||
import OrderDraftPage, {
|
||||
OrderDraftPageProps
|
||||
} from "../../../orders/components/OrderDraftPage";
|
||||
|
@ -32,6 +32,7 @@ const props: Omit<OrderDraftPageProps, "classes"> = {
|
|||
onShippingMethodEdit: undefined,
|
||||
order,
|
||||
saveButtonBarState: "default",
|
||||
userPermissions: permissions,
|
||||
users: clients,
|
||||
usersLoading: false
|
||||
};
|
||||
|
@ -44,4 +45,7 @@ storiesOf("Views / Orders / Order draft", module)
|
|||
))
|
||||
.add("without lines", () => (
|
||||
<OrderDraftPage {...props} order={{ ...order, lines: [] }} />
|
||||
))
|
||||
.add("no user permissions", () => (
|
||||
<OrderDraftPage {...props} userPermissions={[]} />
|
||||
));
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import { listActionsProps, pageListProps } from "../../../fixtures";
|
||||
import {
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
permissions
|
||||
} from "../../../fixtures";
|
||||
import ShippingZonesListPage, {
|
||||
ShippingZonesListPageProps
|
||||
} from "../../../shipping/components/ShippingZonesListPage";
|
||||
|
@ -17,7 +21,8 @@ const props: ShippingZonesListPageProps = {
|
|||
onBack: () => undefined,
|
||||
onRemove: () => undefined,
|
||||
onSubmit: () => undefined,
|
||||
shippingZones
|
||||
shippingZones,
|
||||
userPermissions: permissions
|
||||
};
|
||||
|
||||
storiesOf("Views / Shipping / Shipping zones list", module)
|
||||
|
@ -30,6 +35,7 @@ storiesOf("Views / Shipping / Shipping zones list", module)
|
|||
shippingZones={undefined}
|
||||
/>
|
||||
))
|
||||
.add("no data", () => (
|
||||
<ShippingZonesListPage {...props} shippingZones={[]} />
|
||||
.add("no data", () => <ShippingZonesListPage {...props} shippingZones={[]} />)
|
||||
.add("no site settings permissions", () => (
|
||||
<ShippingZonesListPage {...props} userPermissions={[]} />
|
||||
));
|
||||
|
|
|
@ -235,7 +235,7 @@ const TranslationFields = withStyles(styles, { name: "TranslationFields" })(
|
|||
<CardContent>
|
||||
<Typography className={classes.cardCaption} variant="caption">
|
||||
<FormattedMessage
|
||||
defaultMessage="{numberOFields} Translations, {numberOfTranslatedFields} Completed"
|
||||
defaultMessage="{numberOfFields} Translations, {numberOfTranslatedFields} Completed"
|
||||
values={{
|
||||
numberOfFields: fields.length,
|
||||
numberOfTranslatedFields: fields.reduce(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { MutationResult } from "react-apollo";
|
||||
|
||||
import { User_permissions } from "./auth/types/User";
|
||||
import { FilterContentSubmitData } from "./components/Filter";
|
||||
import { Filter } from "./components/TableFilter";
|
||||
|
||||
|
@ -158,3 +159,7 @@ export interface FetchMoreProps {
|
|||
}
|
||||
|
||||
export type TabActionDialog = "save-search" | "delete-search";
|
||||
|
||||
export interface UserPermissionProps {
|
||||
userPermissions: User_permissions[];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue