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 readonly mode - #229 by @dominik-zeglen
|
||||||
- Add mailing configuration - #222 by @dominik-zeglen
|
- Add mailing configuration - #222 by @dominik-zeglen
|
||||||
- Fix minor bugs - #230 by @dominik-zeglen
|
- Fix minor bugs - #230 by @dominik-zeglen
|
||||||
|
- Fix permission handling - #231 by @dominik-zeglen
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
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-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
@ -9667,14 +9667,6 @@ msgctxt "button"
|
||||||
msgid "{languageName} - {languageCode}"
|
msgid "{languageName} - {languageCode}"
|
||||||
msgstr ""
|
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
|
#: build/locale/src/components/SeoForm/SeoForm.json
|
||||||
#. [src.components.SeoForm.3877274856] - character limit
|
#. [src.components.SeoForm.3877274856] - character limit
|
||||||
#. defaultMessage is:
|
#. defaultMessage is:
|
||||||
|
@ -9683,6 +9675,14 @@ msgctxt "character limit"
|
||||||
msgid "{numberOfCharacters} of {maxCharacters} characters"
|
msgid "{numberOfCharacters} of {maxCharacters} characters"
|
||||||
msgstr ""
|
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
|
#: build/locale/src/components/ColumnPicker/ColumnPickerContent.json
|
||||||
#. [src.components.ColumnPicker.2715399461] - pick columns to display
|
#. [src.components.ColumnPicker.2715399461] - pick columns to display
|
||||||
#. defaultMessage is:
|
#. defaultMessage is:
|
||||||
|
|
|
@ -15,8 +15,6 @@ export const fragmentUser = gql`
|
||||||
email
|
email
|
||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
isStaff
|
|
||||||
note
|
|
||||||
permissions {
|
permissions {
|
||||||
code
|
code
|
||||||
name
|
name
|
||||||
|
|
|
@ -31,8 +31,6 @@ export interface SetPassword_setPassword_user {
|
||||||
email: string;
|
email: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
isStaff: boolean;
|
|
||||||
note: string | null;
|
|
||||||
permissions: (SetPassword_setPassword_user_permissions | null)[] | null;
|
permissions: (SetPassword_setPassword_user_permissions | null)[] | null;
|
||||||
avatar: SetPassword_setPassword_user_avatar | null;
|
avatar: SetPassword_setPassword_user_avatar | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,6 @@ export interface TokenAuth_tokenCreate_user {
|
||||||
email: string;
|
email: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
isStaff: boolean;
|
|
||||||
note: string | null;
|
|
||||||
permissions: (TokenAuth_tokenCreate_user_permissions | null)[] | null;
|
permissions: (TokenAuth_tokenCreate_user_permissions | null)[] | null;
|
||||||
avatar: TokenAuth_tokenCreate_user_avatar | null;
|
avatar: TokenAuth_tokenCreate_user_avatar | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,6 @@ export interface User {
|
||||||
email: string;
|
email: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
isStaff: boolean;
|
|
||||||
note: string | null;
|
|
||||||
permissions: (User_permissions | null)[] | null;
|
permissions: (User_permissions | null)[] | null;
|
||||||
avatar: User_avatar | null;
|
avatar: User_avatar | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,6 @@ export interface VerifyToken_tokenVerify_user {
|
||||||
email: string;
|
email: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
isStaff: boolean;
|
|
||||||
note: string | null;
|
|
||||||
permissions: (VerifyToken_tokenVerify_user_permissions | null)[] | null;
|
permissions: (VerifyToken_tokenVerify_user_permissions | null)[] | null;
|
||||||
avatar: VerifyToken_tokenVerify_user_avatar | 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 saleorDarkLogo from "@assets/images/logo-dark.svg";
|
||||||
import menuArrowIcon from "@assets/images/menu-arrow-icon.svg";
|
import menuArrowIcon from "@assets/images/menu-arrow-icon.svg";
|
||||||
import AppProgressProvider from "@saleor/components/AppProgress";
|
import AppProgressProvider from "@saleor/components/AppProgress";
|
||||||
|
import { createConfigurationMenu } from "@saleor/configuration";
|
||||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useTheme from "@saleor/hooks/useTheme";
|
import useTheme from "@saleor/hooks/useTheme";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
import useUser from "@saleor/hooks/useUser";
|
||||||
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
|
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
|
||||||
|
import { maybe } from "@saleor/misc";
|
||||||
import { staffMemberDetailsUrl } from "@saleor/staff/urls";
|
import { staffMemberDetailsUrl } from "@saleor/staff/urls";
|
||||||
import Container from "../Container";
|
import Container from "../Container";
|
||||||
import AppActionContext from "./AppActionContext";
|
import AppActionContext from "./AppActionContext";
|
||||||
|
@ -297,6 +299,17 @@ const AppLayout = withStyles(styles, {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const menuStructure = createMenuStructure(intl);
|
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 = () => {
|
const handleLogout = () => {
|
||||||
setMenuState(false);
|
setMenuState(false);
|
||||||
|
@ -365,7 +378,7 @@ const AppLayout = withStyles(styles, {
|
||||||
isMenuSmall={!isMenuSmall}
|
isMenuSmall={!isMenuSmall}
|
||||||
location={location.pathname}
|
location={location.pathname}
|
||||||
user={user}
|
user={user}
|
||||||
renderConfigure={true}
|
renderConfigure={renderConfigure}
|
||||||
onMenuItemClick={handleMenuItemClick}
|
onMenuItemClick={handleMenuItemClick}
|
||||||
/>
|
/>
|
||||||
</ResponsiveDrawer>
|
</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,
|
classes,
|
||||||
menu,
|
menu: menus,
|
||||||
user,
|
user,
|
||||||
onSectionClick
|
onSectionClick
|
||||||
}: ConfigurationPageProps & WithStyles<typeof styles>) => {
|
}: ConfigurationPageProps & WithStyles<typeof styles>) => {
|
||||||
|
@ -110,9 +110,11 @@ export const ConfigurationPage = withStyles(styles, {
|
||||||
className={classes.header}
|
className={classes.header}
|
||||||
title={intl.formatMessage(sectionNames.configuration)}
|
title={intl.formatMessage(sectionNames.configuration)}
|
||||||
/>
|
/>
|
||||||
{menu
|
{menus
|
||||||
.filter(menu =>
|
.filter(menu =>
|
||||||
menu.menuItems.map(item => hasPermission(item.permission, user))
|
menu.menuItems.some(menuItem =>
|
||||||
|
hasPermission(menuItem.permission, user)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.map((menu, menuIndex) => (
|
.map((menu, menuIndex) => (
|
||||||
<div className={classes.configurationCategory} key={menuIndex}>
|
<div className={classes.configurationCategory} key={menuIndex}>
|
||||||
|
@ -120,7 +122,9 @@ export const ConfigurationPage = withStyles(styles, {
|
||||||
<Typography>{menu.label}</Typography>
|
<Typography>{menu.label}</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.configurationItem}>
|
<div className={classes.configurationItem}>
|
||||||
{menu.menuItems.map((item, itemIndex) => (
|
{menu.menuItems
|
||||||
|
.filter(menuItem => hasPermission(menuItem.permission, user))
|
||||||
|
.map((item, itemIndex) => (
|
||||||
<Card
|
<Card
|
||||||
className={item.url ? classes.card : classes.cardDisabled}
|
className={item.url ? classes.card : classes.cardDisabled}
|
||||||
onClick={() => onSectionClick(item.url)}
|
onClick={() => onSectionClick(item.url)}
|
||||||
|
|
|
@ -81,7 +81,7 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
||||||
id: "configurationMenuTaxes"
|
id: "configurationMenuTaxes"
|
||||||
}),
|
}),
|
||||||
icon: <Taxes fontSize="inherit" viewBox="0 0 44 44" />,
|
icon: <Taxes fontSize="inherit" viewBox="0 0 44 44" />,
|
||||||
permission: PermissionEnum.MANAGE_PRODUCTS,
|
permission: PermissionEnum.MANAGE_SETTINGS,
|
||||||
title: intl.formatMessage(sectionNames.taxes),
|
title: intl.formatMessage(sectionNames.taxes),
|
||||||
url: taxSection
|
url: taxSection
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
||||||
preserveAspectRatio="xMinYMin meet"
|
preserveAspectRatio="xMinYMin meet"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
permission: PermissionEnum.MANAGE_SETTINGS,
|
permission: PermissionEnum.MANAGE_PLUGINS,
|
||||||
title: intl.formatMessage(sectionNames.plugins),
|
title: intl.formatMessage(sectionNames.plugins),
|
||||||
url: pluginsListUrl()
|
url: pluginsListUrl()
|
||||||
},
|
},
|
||||||
|
|
|
@ -430,6 +430,18 @@ export const permissions: ShopInfo_shop_permissions[] = [
|
||||||
{
|
{
|
||||||
code: PermissionEnum.MANAGE_USERS,
|
code: PermissionEnum.MANAGE_USERS,
|
||||||
name: "Manage customers."
|
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 => ({
|
].map(perm => ({
|
||||||
__typename: "PermissionDisplay" as "PermissionDisplay",
|
__typename: "PermissionDisplay" as "PermissionDisplay",
|
||||||
|
|
|
@ -14,7 +14,10 @@ import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
|
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
|
import { UserPermissionProps } from "@saleor/types";
|
||||||
|
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -26,7 +29,7 @@ const styles = (theme: Theme) =>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface HomeNotificationTableProps extends WithStyles<typeof styles> {
|
interface HomeNotificationTableProps extends UserPermissionProps {
|
||||||
ordersToCapture: number;
|
ordersToCapture: number;
|
||||||
ordersToFulfill: number;
|
ordersToFulfill: number;
|
||||||
productsOutOfStock: number;
|
productsOutOfStock: number;
|
||||||
|
@ -45,11 +48,16 @@ const HomeNotificationTable = withStyles(styles, {
|
||||||
onProductsOutOfStockClick,
|
onProductsOutOfStockClick,
|
||||||
ordersToCapture,
|
ordersToCapture,
|
||||||
ordersToFulfill,
|
ordersToFulfill,
|
||||||
productsOutOfStock
|
productsOutOfStock,
|
||||||
}: HomeNotificationTableProps) => (
|
userPermissions
|
||||||
|
}: HomeNotificationTableProps & WithStyles<typeof styles>) => (
|
||||||
<Card>
|
<Card>
|
||||||
<Table>
|
<Table>
|
||||||
<TableBody className={classes.tableRow}>
|
<TableBody className={classes.tableRow}>
|
||||||
|
<RequirePermissions
|
||||||
|
userPermissions={userPermissions}
|
||||||
|
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
|
||||||
|
>
|
||||||
<TableRow hover={true} onClick={onOrdersToFulfillClick}>
|
<TableRow hover={true} onClick={onOrdersToFulfillClick}>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{ordersToFulfill === undefined ? (
|
{ordersToFulfill === undefined ? (
|
||||||
|
@ -110,6 +118,11 @@ const HomeNotificationTable = withStyles(styles, {
|
||||||
<KeyboardArrowRight />
|
<KeyboardArrowRight />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
</RequirePermissions>
|
||||||
|
<RequirePermissions
|
||||||
|
userPermissions={userPermissions}
|
||||||
|
requiredPermissions={[PermissionEnum.MANAGE_PRODUCTS]}
|
||||||
|
>
|
||||||
<TableRow hover={true} onClick={onProductsOutOfStockClick}>
|
<TableRow hover={true} onClick={onProductsOutOfStockClick}>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{productsOutOfStock === undefined ? (
|
{productsOutOfStock === undefined ? (
|
||||||
|
@ -140,6 +153,7 @@ const HomeNotificationTable = withStyles(styles, {
|
||||||
<KeyboardArrowRight />
|
<KeyboardArrowRight />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
</RequirePermissions>
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -10,7 +10,10 @@ import CardSpacer from "@saleor/components/CardSpacer";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import Grid from "@saleor/components/Grid";
|
import Grid from "@saleor/components/Grid";
|
||||||
import Money from "@saleor/components/Money";
|
import Money from "@saleor/components/Money";
|
||||||
|
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
|
import { UserPermissionProps } from "@saleor/types";
|
||||||
|
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||||
import Orders from "../../../icons/Orders";
|
import Orders from "../../../icons/Orders";
|
||||||
import Sales from "../../../icons/Sales";
|
import Sales from "../../../icons/Sales";
|
||||||
import {
|
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[];
|
activities: Home_activities_edges_node[];
|
||||||
orders: number;
|
orders: number;
|
||||||
ordersToCapture: number;
|
ordersToCapture: number;
|
||||||
|
@ -68,13 +71,18 @@ const HomePage = withStyles(styles, { name: "HomePage" })(
|
||||||
onProductsOutOfStockClick,
|
onProductsOutOfStockClick,
|
||||||
ordersToCapture,
|
ordersToCapture,
|
||||||
ordersToFulfill,
|
ordersToFulfill,
|
||||||
productsOutOfStock
|
productsOutOfStock,
|
||||||
}: HomePageProps) => (
|
userPermissions
|
||||||
|
}: HomePageProps & WithStyles<typeof styles>) => (
|
||||||
<Container>
|
<Container>
|
||||||
<HomeHeader userName={userName} />
|
<HomeHeader userName={userName} />
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<Grid>
|
<Grid>
|
||||||
<div>
|
<div>
|
||||||
|
<RequirePermissions
|
||||||
|
userPermissions={userPermissions}
|
||||||
|
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
|
||||||
|
>
|
||||||
<div className={classes.cardContainer}>
|
<div className={classes.cardContainer}>
|
||||||
<HomeAnalyticsCard
|
<HomeAnalyticsCard
|
||||||
title={"Sales"}
|
title={"Sales"}
|
||||||
|
@ -97,6 +105,7 @@ const HomePage = withStyles(styles, { name: "HomePage" })(
|
||||||
)}
|
)}
|
||||||
</HomeAnalyticsCard>
|
</HomeAnalyticsCard>
|
||||||
</div>
|
</div>
|
||||||
|
</RequirePermissions>
|
||||||
<HomeNotificationTable
|
<HomeNotificationTable
|
||||||
onOrdersToCaptureClick={onOrdersToCaptureClick}
|
onOrdersToCaptureClick={onOrdersToCaptureClick}
|
||||||
onOrdersToFulfillClick={onOrdersToFulfillClick}
|
onOrdersToFulfillClick={onOrdersToFulfillClick}
|
||||||
|
@ -104,16 +113,30 @@ const HomePage = withStyles(styles, { name: "HomePage" })(
|
||||||
ordersToCapture={ordersToCapture}
|
ordersToCapture={ordersToCapture}
|
||||||
ordersToFulfill={ordersToFulfill}
|
ordersToFulfill={ordersToFulfill}
|
||||||
productsOutOfStock={productsOutOfStock}
|
productsOutOfStock={productsOutOfStock}
|
||||||
|
userPermissions={userPermissions}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
|
<RequirePermissions
|
||||||
|
userPermissions={userPermissions}
|
||||||
|
requiredPermissions={[
|
||||||
|
PermissionEnum.MANAGE_ORDERS,
|
||||||
|
PermissionEnum.MANAGE_PRODUCTS
|
||||||
|
]}
|
||||||
|
>
|
||||||
<HomeProductListCard
|
<HomeProductListCard
|
||||||
onRowClick={onProductClick}
|
onRowClick={onProductClick}
|
||||||
topProducts={topProducts}
|
topProducts={topProducts}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
|
</RequirePermissions>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<RequirePermissions
|
||||||
|
userPermissions={userPermissions}
|
||||||
|
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
|
||||||
|
>
|
||||||
<HomeActivityCard activities={activities} />
|
<HomeActivityCard activities={activities} />
|
||||||
|
</RequirePermissions>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -56,6 +56,7 @@ const HomeSection = () => {
|
||||||
ordersToFulfill={maybe(() => data.ordersToFulfill.totalCount)}
|
ordersToFulfill={maybe(() => data.ordersToFulfill.totalCount)}
|
||||||
productsOutOfStock={maybe(() => data.productsOutOfStock.totalCount)}
|
productsOutOfStock={maybe(() => data.productsOutOfStock.totalCount)}
|
||||||
userName={getUserName(user, true)}
|
userName={getUserName(user, true)}
|
||||||
|
userPermissions={maybe(() => user.permissions, [])}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</HomePageQuery>
|
</HomePageQuery>
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -81,6 +86,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const user = maybe(() => order.user);
|
const user = maybe(() => order.user);
|
||||||
|
const userEmail = maybe(()=>order.userEmail)
|
||||||
|
|
||||||
const [userDisplayName, setUserDisplayName] = useStateFromProps(
|
const [userDisplayName, setUserDisplayName] = useStateFromProps(
|
||||||
maybe(() => user.email, "")
|
maybe(() => user.email, "")
|
||||||
|
@ -100,6 +106,10 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
||||||
})}
|
})}
|
||||||
toolbar={
|
toolbar={
|
||||||
!!canEditCustomer && (
|
!!canEditCustomer && (
|
||||||
|
<RequirePermissions
|
||||||
|
userPermissions={userPermissions}
|
||||||
|
requiredPermissions={[PermissionEnum.MANAGE_USERS]}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="text"
|
variant="text"
|
||||||
|
@ -108,6 +118,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
||||||
>
|
>
|
||||||
{intl.formatMessage(buttonMessages.edit)}
|
{intl.formatMessage(buttonMessages.edit)}
|
||||||
</Button>
|
</Button>
|
||||||
|
</RequirePermissions>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -155,14 +166,22 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
||||||
}}
|
}}
|
||||||
</Form>
|
</Form>
|
||||||
) : user === null ? (
|
) : user === null ? (
|
||||||
|
userEmail === null ? (
|
||||||
<Typography>
|
<Typography>
|
||||||
<FormattedMessage defaultMessage="Anonymous user" />
|
<FormattedMessage defaultMessage="Anonymous user" />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography className={classes.userEmail}>{userEmail}</Typography>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Typography className={classes.userEmail}>
|
<Typography className={classes.userEmail}>
|
||||||
{user.email}
|
{user.email}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<RequirePermissions
|
||||||
|
userPermissions={userPermissions}
|
||||||
|
requiredPermissions={[PermissionEnum.MANAGE_USERS]}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
underline={false}
|
underline={false}
|
||||||
|
@ -175,6 +194,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</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,6 +207,8 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
{!!user && (
|
||||||
|
<>
|
||||||
<Hr />
|
<Hr />
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className={classes.sectionHeader}>
|
<div className={classes.sectionHeader}>
|
||||||
|
@ -217,6 +239,8 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Hr />
|
<Hr />
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className={classes.sectionHeader}>
|
<div className={classes.sectionHeader}>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -10,7 +10,7 @@ import ErrorPage from "./components/ErrorPage/ErrorPage";
|
||||||
import useNavigator from "./hooks/useNavigator";
|
import useNavigator from "./hooks/useNavigator";
|
||||||
import useNotifier from "./hooks/useNotifier";
|
import useNotifier from "./hooks/useNotifier";
|
||||||
import { commonMessages } from "./intl";
|
import { commonMessages } from "./intl";
|
||||||
import { RequireAtLeastOne } from "./misc";
|
import { maybe, RequireAtLeastOne } from "./misc";
|
||||||
|
|
||||||
export interface LoadMore<TData, TVariables> {
|
export interface LoadMore<TData, TVariables> {
|
||||||
loadMore: (
|
loadMore: (
|
||||||
|
@ -81,13 +81,22 @@ export function TypedQuery<TData, TVariables>(
|
||||||
variables={variables}
|
variables={variables}
|
||||||
skip={skip}
|
skip={skip}
|
||||||
context={{ useBatching: true }}
|
context={{ useBatching: true }}
|
||||||
|
errorPolicy="all"
|
||||||
>
|
>
|
||||||
{(queryData: QueryResult<TData, TVariables>) => {
|
{(queryData: QueryResult<TData, TVariables>) => {
|
||||||
if (queryData.error) {
|
if (queryData.error) {
|
||||||
|
if (
|
||||||
|
!queryData.error.graphQLErrors.every(
|
||||||
|
err =>
|
||||||
|
maybe(() => err.extensions.exception.code) ===
|
||||||
|
"PermissionDenied"
|
||||||
|
)
|
||||||
|
) {
|
||||||
pushMessage({
|
pushMessage({
|
||||||
text: intl.formatMessage(commonMessages.somethingWentWrong)
|
text: intl.formatMessage(commonMessages.somethingWentWrong)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadMore = (
|
const loadMore = (
|
||||||
mergeFunc: (
|
mergeFunc: (
|
||||||
|
|
|
@ -5,14 +5,18 @@ import AppHeader from "@saleor/components/AppHeader";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import Grid from "@saleor/components/Grid";
|
import Grid from "@saleor/components/Grid";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
|
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { ListActions, PageListProps } from "@saleor/types";
|
import { ListActions, PageListProps, UserPermissionProps } from "@saleor/types";
|
||||||
import { WeightUnitsEnum } from "@saleor/types/globalTypes";
|
import { PermissionEnum, WeightUnitsEnum } from "@saleor/types/globalTypes";
|
||||||
import { ShippingZoneFragment } from "../../types/ShippingZoneFragment";
|
import { ShippingZoneFragment } from "../../types/ShippingZoneFragment";
|
||||||
import ShippingWeightUnitForm from "../ShippingWeightUnitForm";
|
import ShippingWeightUnitForm from "../ShippingWeightUnitForm";
|
||||||
import ShippingZonesList from "../ShippingZonesList";
|
import ShippingZonesList from "../ShippingZonesList";
|
||||||
|
|
||||||
export interface ShippingZonesListPageProps extends PageListProps, ListActions {
|
export interface ShippingZonesListPageProps
|
||||||
|
extends PageListProps,
|
||||||
|
ListActions,
|
||||||
|
UserPermissionProps {
|
||||||
defaultWeightUnit: WeightUnitsEnum;
|
defaultWeightUnit: WeightUnitsEnum;
|
||||||
shippingZones: ShippingZoneFragment[];
|
shippingZones: ShippingZoneFragment[];
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
|
@ -22,7 +26,14 @@ export interface ShippingZonesListPageProps extends PageListProps, ListActions {
|
||||||
|
|
||||||
const ShippingZonesListPage: React.StatelessComponent<
|
const ShippingZonesListPage: React.StatelessComponent<
|
||||||
ShippingZonesListPageProps
|
ShippingZonesListPageProps
|
||||||
> = ({ defaultWeightUnit, disabled, onBack, onSubmit, ...listProps }) => {
|
> = ({
|
||||||
|
defaultWeightUnit,
|
||||||
|
disabled,
|
||||||
|
userPermissions,
|
||||||
|
onBack,
|
||||||
|
onSubmit,
|
||||||
|
...listProps
|
||||||
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -41,11 +52,16 @@ const ShippingZonesListPage: React.StatelessComponent<
|
||||||
<ShippingZonesList disabled={disabled} {...listProps} />
|
<ShippingZonesList disabled={disabled} {...listProps} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<RequirePermissions
|
||||||
|
userPermissions={userPermissions}
|
||||||
|
requiredPermissions={[PermissionEnum.MANAGE_SETTINGS]}
|
||||||
|
>
|
||||||
<ShippingWeightUnitForm
|
<ShippingWeightUnitForm
|
||||||
defaultWeightUnit={defaultWeightUnit}
|
defaultWeightUnit={defaultWeightUnit}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
/>
|
/>
|
||||||
|
</RequirePermissions>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import usePaginator, {
|
||||||
createPaginationState
|
createPaginationState
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
|
import useUser from "@saleor/hooks/useUser";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { getMutationState, maybe } from "@saleor/misc";
|
import { getMutationState, maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
|
@ -45,6 +46,7 @@ export const ShippingZonesList: React.StatelessComponent<
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const paginate = usePaginator();
|
const paginate = usePaginator();
|
||||||
const shop = useShop();
|
const shop = useShop();
|
||||||
|
const { user } = useUser();
|
||||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||||
params.ids
|
params.ids
|
||||||
);
|
);
|
||||||
|
@ -195,6 +197,7 @@ export const ShippingZonesList: React.StatelessComponent<
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
|
userPermissions={maybe(() => user.permissions, [])}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import avatarImage from "@assets/images/avatars/avatar1.png";
|
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 { StaffList_staffUsers_edges_node } from "./types/StaffList";
|
||||||
import { StaffMemberDetails_user } from "./types/StaffMemberDetails";
|
import { StaffMemberDetails_user } from "./types/StaffMemberDetails";
|
||||||
|
|
||||||
|
@ -145,49 +145,5 @@ export const staffMember: StaffMemberDetails_user = {
|
||||||
id: "VXNlcjoyMQ==",
|
id: "VXNlcjoyMQ==",
|
||||||
isActive: true,
|
isActive: true,
|
||||||
lastName: "Smith",
|
lastName: "Smith",
|
||||||
permissions: [
|
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
|
|
||||||
}))
|
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,8 @@ import { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
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 HomePage, { HomePageProps } from "../../../home/components/HomePage";
|
||||||
import { shop as shopFixture } from "../../../home/fixtures";
|
import { shop as shopFixture } from "../../../home/fixtures";
|
||||||
import Decorator from "../../Decorator";
|
import Decorator from "../../Decorator";
|
||||||
|
@ -21,7 +23,8 @@ const homePageProps: Omit<HomePageProps, "classes"> = {
|
||||||
productsOutOfStock: shop.productsOutOfStock.totalCount,
|
productsOutOfStock: shop.productsOutOfStock.totalCount,
|
||||||
sales: shop.salesToday.gross,
|
sales: shop.salesToday.gross,
|
||||||
topProducts: shop.productTopToday.edges.map(edge => edge.node),
|
topProducts: shop.productTopToday.edges.map(edge => edge.node),
|
||||||
userName: "admin@example.com"
|
userName: "admin@example.com",
|
||||||
|
userPermissions: permissions
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Views / HomePage", module)
|
storiesOf("Views / HomePage", module)
|
||||||
|
@ -42,4 +45,23 @@ storiesOf("Views / HomePage", module)
|
||||||
))
|
))
|
||||||
.add("no data", () => (
|
.add("no data", () => (
|
||||||
<HomePage {...homePageProps} topProducts={[]} activities={[]} />
|
<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 { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import { permissions } from "@saleor/fixtures";
|
||||||
import OrderCustomer, {
|
import OrderCustomer, {
|
||||||
OrderCustomerProps
|
OrderCustomerProps
|
||||||
} from "../../../orders/components/OrderCustomer";
|
} from "../../../orders/components/OrderCustomer";
|
||||||
|
@ -19,6 +20,7 @@ const props: Omit<OrderCustomerProps, "classes"> = {
|
||||||
onProfileView: () => undefined,
|
onProfileView: () => undefined,
|
||||||
onShippingAddressEdit: undefined,
|
onShippingAddressEdit: undefined,
|
||||||
order,
|
order,
|
||||||
|
userPermissions: permissions,
|
||||||
users: clients
|
users: clients
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,4 +39,10 @@ storiesOf("Orders / OrderCustomer", module)
|
||||||
))
|
))
|
||||||
.add("editable", () => (
|
.add("editable", () => (
|
||||||
<OrderCustomer {...props} canEditAddresses={true} canEditCustomer={true} />
|
<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 React from "react";
|
||||||
|
|
||||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||||
|
import { permissions } from "@saleor/fixtures";
|
||||||
import OrderDetailsPage, {
|
import OrderDetailsPage, {
|
||||||
OrderDetailsPageProps
|
OrderDetailsPageProps
|
||||||
} from "../../../orders/components/OrderDetailsPage";
|
} from "../../../orders/components/OrderDetailsPage";
|
||||||
|
@ -32,7 +33,8 @@ const props: Omit<OrderDetailsPageProps, "classes"> = {
|
||||||
onProductClick: undefined,
|
onProductClick: undefined,
|
||||||
onProfileView: () => undefined,
|
onProfileView: () => undefined,
|
||||||
onShippingAddressEdit: undefined,
|
onShippingAddressEdit: undefined,
|
||||||
order
|
order,
|
||||||
|
userPermissions: permissions
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Views / Orders / Order details", module)
|
storiesOf("Views / Orders / Order details", module)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||||
import { fetchMoreProps } from "@saleor/fixtures";
|
import { fetchMoreProps, permissions } from "@saleor/fixtures";
|
||||||
import OrderDraftPage, {
|
import OrderDraftPage, {
|
||||||
OrderDraftPageProps
|
OrderDraftPageProps
|
||||||
} from "../../../orders/components/OrderDraftPage";
|
} from "../../../orders/components/OrderDraftPage";
|
||||||
|
@ -32,6 +32,7 @@ const props: Omit<OrderDraftPageProps, "classes"> = {
|
||||||
onShippingMethodEdit: undefined,
|
onShippingMethodEdit: undefined,
|
||||||
order,
|
order,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
|
userPermissions: permissions,
|
||||||
users: clients,
|
users: clients,
|
||||||
usersLoading: false
|
usersLoading: false
|
||||||
};
|
};
|
||||||
|
@ -44,4 +45,7 @@ storiesOf("Views / Orders / Order draft", module)
|
||||||
))
|
))
|
||||||
.add("without lines", () => (
|
.add("without lines", () => (
|
||||||
<OrderDraftPage {...props} order={{ ...order, lines: [] }} />
|
<OrderDraftPage {...props} order={{ ...order, lines: [] }} />
|
||||||
|
))
|
||||||
|
.add("no user permissions", () => (
|
||||||
|
<OrderDraftPage {...props} userPermissions={[]} />
|
||||||
));
|
));
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import { storiesOf } from "@storybook/react";
|
import { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { listActionsProps, pageListProps } from "../../../fixtures";
|
import {
|
||||||
|
listActionsProps,
|
||||||
|
pageListProps,
|
||||||
|
permissions
|
||||||
|
} from "../../../fixtures";
|
||||||
import ShippingZonesListPage, {
|
import ShippingZonesListPage, {
|
||||||
ShippingZonesListPageProps
|
ShippingZonesListPageProps
|
||||||
} from "../../../shipping/components/ShippingZonesListPage";
|
} from "../../../shipping/components/ShippingZonesListPage";
|
||||||
|
@ -17,7 +21,8 @@ const props: ShippingZonesListPageProps = {
|
||||||
onBack: () => undefined,
|
onBack: () => undefined,
|
||||||
onRemove: () => undefined,
|
onRemove: () => undefined,
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
shippingZones
|
shippingZones,
|
||||||
|
userPermissions: permissions
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Views / Shipping / Shipping zones list", module)
|
storiesOf("Views / Shipping / Shipping zones list", module)
|
||||||
|
@ -30,6 +35,7 @@ storiesOf("Views / Shipping / Shipping zones list", module)
|
||||||
shippingZones={undefined}
|
shippingZones={undefined}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
.add("no data", () => (
|
.add("no data", () => <ShippingZonesListPage {...props} shippingZones={[]} />)
|
||||||
<ShippingZonesListPage {...props} shippingZones={[]} />
|
.add("no site settings permissions", () => (
|
||||||
|
<ShippingZonesListPage {...props} userPermissions={[]} />
|
||||||
));
|
));
|
||||||
|
|
|
@ -235,7 +235,7 @@ const TranslationFields = withStyles(styles, { name: "TranslationFields" })(
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography className={classes.cardCaption} variant="caption">
|
<Typography className={classes.cardCaption} variant="caption">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="{numberOFields} Translations, {numberOfTranslatedFields} Completed"
|
defaultMessage="{numberOfFields} Translations, {numberOfTranslatedFields} Completed"
|
||||||
values={{
|
values={{
|
||||||
numberOfFields: fields.length,
|
numberOfFields: fields.length,
|
||||||
numberOfTranslatedFields: fields.reduce(
|
numberOfTranslatedFields: fields.reduce(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { MutationResult } from "react-apollo";
|
import { MutationResult } from "react-apollo";
|
||||||
|
|
||||||
|
import { User_permissions } from "./auth/types/User";
|
||||||
import { FilterContentSubmitData } from "./components/Filter";
|
import { FilterContentSubmitData } from "./components/Filter";
|
||||||
import { Filter } from "./components/TableFilter";
|
import { Filter } from "./components/TableFilter";
|
||||||
|
|
||||||
|
@ -158,3 +159,7 @@ export interface FetchMoreProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TabActionDialog = "save-search" | "delete-search";
|
export type TabActionDialog = "save-search" | "delete-search";
|
||||||
|
|
||||||
|
export interface UserPermissionProps {
|
||||||
|
userPermissions: User_permissions[];
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue