Hide shipping zones when you do not have access (#2333)

* hide shipping zones when you do not have access

* use single style hook

* cover warehouses

* snapshot
This commit is contained in:
Patryk Andrzejewski 2022-09-29 10:40:19 +02:00 committed by GitHub
parent 02c4e8d6e2
commit 2d2151abc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 200 additions and 2633 deletions

View file

@ -17,13 +17,13 @@ const AssignmentListHeader: React.FC<AssignmentListHeaderProps> = ({
itemsName, itemsName,
loading, loading,
}) => { }) => {
const classes = useHeaderStyles(); const { container, skeleton, ...accordion } = useHeaderStyles();
return ( return (
<div className={classes.container}> <div className={container}>
<AccordionSummary expandIcon={<IconChevronDown />} classes={classes}> <AccordionSummary expandIcon={<IconChevronDown />} classes={accordion}>
{loading ? ( {loading ? (
<Skeleton className={classes.skeleton} /> <Skeleton className={skeleton} />
) : ( ) : (
<Typography variant="subtitle2" color="textSecondary"> <Typography variant="subtitle2" color="textSecondary">
{`${assignCount} ${itemsName.toLowerCase()}`} {`${assignCount} ${itemsName.toLowerCase()}`}

View file

@ -5,6 +5,7 @@ import { channelsListUrl } from "@saleor/channels/urls";
import CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import Grid from "@saleor/components/Grid"; import Grid from "@saleor/components/Grid";
import RequirePermissions from "@saleor/components/RequirePermissions";
import Savebar from "@saleor/components/Savebar"; import Savebar from "@saleor/components/Savebar";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import { import {
@ -13,6 +14,7 @@ import {
ChannelErrorFragment, ChannelErrorFragment,
CountryCode, CountryCode,
CountryFragment, CountryFragment,
PermissionEnum,
SearchShippingZonesQuery, SearchShippingZonesQuery,
SearchWarehousesQuery, SearchWarehousesQuery,
StockSettingsInput, StockSettingsInput,
@ -214,6 +216,9 @@ const ChannelDetailsPage = function<TErrors>({
<CardSpacer /> <CardSpacer />
</> </>
)} )}
<RequirePermissions
requiredPermissions={[PermissionEnum.MANAGE_SHIPPING]}
>
<ShippingZones <ShippingZones
shippingZonesChoices={getFilteredShippingZonesChoices( shippingZonesChoices={getFilteredShippingZonesChoices(
data.shippingZonesToDisplay, data.shippingZonesToDisplay,
@ -227,6 +232,14 @@ const ChannelDetailsPage = function<TErrors>({
loading={disabled} loading={disabled}
/> />
<CardSpacer /> <CardSpacer />
</RequirePermissions>
<RequirePermissions
oneOfPermissions={[
PermissionEnum.MANAGE_SHIPPING,
PermissionEnum.MANAGE_ORDERS,
PermissionEnum.MANAGE_PRODUCTS,
]}
>
<Warehouses <Warehouses
warehousesChoices={getFilteredWarehousesChoices( warehousesChoices={getFilteredWarehousesChoices(
data.warehousesToDisplay, data.warehousesToDisplay,
@ -241,6 +254,7 @@ const ChannelDetailsPage = function<TErrors>({
loading={disabled} loading={disabled}
/> />
<CardSpacer /> <CardSpacer />
</RequirePermissions>
<ChannelAllocationStrategy <ChannelAllocationStrategy
data={data} data={data}
disabled={disabled} disabled={disabled}

View file

@ -3,14 +3,11 @@ import { Backlink } from "@saleor/components/Backlink";
import Container from "@saleor/components/Container"; import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import { import {
ChannelCreateMutation, ChannelCreateMutation,
ChannelErrorFragment, ChannelErrorFragment,
useChannelCreateMutation, useChannelCreateMutation,
useChannelReorderWarehousesMutation, useChannelReorderWarehousesMutation,
useShippingZonesCountQuery,
useWarehousesCountQuery,
} from "@saleor/graphql"; } from "@saleor/graphql";
import { getSearchFetchMoreProps } from "@saleor/hooks/makeTopLevelSearch/utils"; import { getSearchFetchMoreProps } from "@saleor/hooks/makeTopLevelSearch/utils";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
@ -19,8 +16,6 @@ import { getDefaultNotifierSuccessErrorData } from "@saleor/hooks/useNotifier/ut
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { extractMutationErrors } from "@saleor/misc"; import { extractMutationErrors } from "@saleor/misc";
import useShippingZonesSearch from "@saleor/searches/useShippingZonesSearch";
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
import getChannelsErrorMessage from "@saleor/utils/errors/channels"; import getChannelsErrorMessage from "@saleor/utils/errors/channels";
import currencyCodes from "currency-codes"; import currencyCodes from "currency-codes";
import React from "react"; import React from "react";
@ -29,6 +24,8 @@ import { useIntl } from "react-intl";
import ChannelDetailsPage from "../../pages/ChannelDetailsPage"; import ChannelDetailsPage from "../../pages/ChannelDetailsPage";
import { channelPath, channelsListUrl } from "../../urls"; import { channelPath, channelsListUrl } from "../../urls";
import { calculateItemsOrderMoves } from "../ChannelDetails/handlers"; import { calculateItemsOrderMoves } from "../ChannelDetails/handlers";
import { useShippingZones } from "../ChannelDetails/useShippingZones";
import { useWarehouses } from "../ChannelDetails/useWarehouses";
export const ChannelCreateView = ({}) => { export const ChannelCreateView = ({}) => {
const navigate = useNavigator(); const navigate = useNavigator();
@ -106,30 +103,20 @@ export const ChannelCreateView = ({}) => {
}; };
const { const {
data: shippingZonesCountData, shippingZonesCountData,
loading: shippingZonesCountLoading, shippingZonesCountLoading,
} = useShippingZonesCountQuery(); fetchMoreShippingZones,
searchShippingZones,
searchShippingZonesResult,
} = useShippingZones();
const { const {
loadMore: fetchMoreShippingZones, warehousesCountData,
search: searchShippingZones, warehousesCountLoading,
result: searchShippingZonesResult, fetchMoreWarehouses,
} = useShippingZonesSearch({ searchWarehouses,
variables: DEFAULT_INITIAL_SEARCH_DATA, searchWarehousesResult,
}); } = useWarehouses();
const {
data: warehousesCountData,
loading: warehousesCountLoading,
} = useWarehousesCountQuery();
const {
loadMore: fetchMoreWarehouses,
search: searchWarehouses,
result: searchWarehousesResult,
} = useWarehouseSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA,
});
const currencyCodeChoices = currencyCodes.data.map(currencyData => ({ const currencyCodeChoices = currencyCodes.data.map(currencyData => ({
label: intl.formatMessage( label: intl.formatMessage(

View file

@ -5,7 +5,6 @@ import { Backlink } from "@saleor/components/Backlink";
import Container from "@saleor/components/Container"; import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import { import {
ChannelDeleteMutation, ChannelDeleteMutation,
ChannelErrorFragment, ChannelErrorFragment,
@ -15,11 +14,8 @@ import {
useChannelDeleteMutation, useChannelDeleteMutation,
useChannelQuery, useChannelQuery,
useChannelReorderWarehousesMutation, useChannelReorderWarehousesMutation,
useChannelShippingZonesQuery,
useChannelsQuery, useChannelsQuery,
useChannelUpdateMutation, useChannelUpdateMutation,
useShippingZonesCountQuery,
useWarehousesCountQuery,
} from "@saleor/graphql"; } from "@saleor/graphql";
import { getSearchFetchMoreProps } from "@saleor/hooks/makeTopLevelSearch/utils"; import { getSearchFetchMoreProps } from "@saleor/hooks/makeTopLevelSearch/utils";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
@ -28,8 +24,6 @@ import { getDefaultNotifierSuccessErrorData } from "@saleor/hooks/useNotifier/ut
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { extractMutationErrors } from "@saleor/misc"; import { extractMutationErrors } from "@saleor/misc";
import useShippingZonesSearch from "@saleor/searches/useShippingZonesSearch";
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
import getChannelsErrorMessage from "@saleor/utils/errors/channels"; import getChannelsErrorMessage from "@saleor/utils/errors/channels";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import { mapEdgesToItems } from "@saleor/utils/maps"; import { mapEdgesToItems } from "@saleor/utils/maps";
@ -44,6 +38,8 @@ import {
ChannelUrlQueryParams, ChannelUrlQueryParams,
} from "../../urls"; } from "../../urls";
import { calculateItemsOrderMoves } from "./handlers"; import { calculateItemsOrderMoves } from "./handlers";
import { useShippingZones } from "./useShippingZones";
import { useWarehouses } from "./useWarehouses";
interface ChannelDetailsProps { interface ChannelDetailsProps {
id: string; id: string;
@ -203,41 +199,22 @@ export const ChannelDetails: React.FC<ChannelDetailsProps> = ({
}; };
const { const {
data: shippingZonesCountData, shippingZonesCountData,
loading: shippingZonesCountLoading, shippingZonesCountLoading,
} = useShippingZonesCountQuery(); channelShippingZonesData,
channelsShippingZonesLoading,
fetchMoreShippingZones,
searchShippingZones,
searchShippingZonesResult,
} = useShippingZones(id);
const { const {
data: channelShippingZonesData, warehousesCountData,
loading: channelsShippingZonesLoading, warehousesCountLoading,
} = useChannelShippingZonesQuery({ fetchMoreWarehouses,
variables: { searchWarehouses,
filter: { searchWarehousesResult,
channels: [id], } = useWarehouses();
},
},
});
const {
loadMore: fetchMoreShippingZones,
search: searchShippingZones,
result: searchShippingZonesResult,
} = useShippingZonesSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA,
});
const {
data: warehousesCountData,
loading: warehousesCountLoading,
} = useWarehousesCountQuery();
const {
loadMore: fetchMoreWarehouses,
search: searchWarehouses,
result: searchWarehousesResult,
} = useWarehouseSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA,
});
const channelWarehouses = data?.channel?.warehouses || []; const channelWarehouses = data?.channel?.warehouses || [];
const channelShippingZones = mapEdgesToItems( const channelShippingZones = mapEdgesToItems(

View file

@ -0,0 +1,52 @@
import { useUserPermissions } from "@saleor/auth/hooks/useUserPermissions";
import { hasPermissions } from "@saleor/components/RequirePermissions";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import {
PermissionEnum,
useChannelShippingZonesQuery,
useShippingZonesCountQuery,
} from "@saleor/graphql";
import useShippingZonesSearch from "@saleor/searches/useShippingZonesSearch";
export const useShippingZones = (channelId?: string) => {
const userPermissions = useUserPermissions();
const canLoadShippingZones = hasPermissions(userPermissions, [
PermissionEnum.MANAGE_SHIPPING,
]);
const {
data: shippingZonesCountData,
loading: shippingZonesCountLoading,
} = useShippingZonesCountQuery({ skip: !canLoadShippingZones });
const {
data: channelShippingZonesData,
loading: channelsShippingZonesLoading,
} = useChannelShippingZonesQuery({
variables: {
filter: {
channels: [channelId],
},
},
skip: !channelId || !canLoadShippingZones,
});
const {
loadMore: fetchMoreShippingZones,
search: searchShippingZones,
result: searchShippingZonesResult,
} = useShippingZonesSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA,
skip: !canLoadShippingZones,
});
return {
shippingZonesCountData,
shippingZonesCountLoading,
channelShippingZonesData,
channelsShippingZonesLoading,
fetchMoreShippingZones,
searchShippingZones,
searchShippingZonesResult,
};
};

View file

@ -0,0 +1,36 @@
import { useUserPermissions } from "@saleor/auth/hooks/useUserPermissions";
import { hasOneOfPermissions } from "@saleor/components/RequirePermissions";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import { PermissionEnum, useWarehousesCountQuery } from "@saleor/graphql";
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
export const useWarehouses = () => {
const userPermissions = useUserPermissions();
const canLoadWarehouses = hasOneOfPermissions(userPermissions, [
PermissionEnum.MANAGE_SHIPPING,
PermissionEnum.MANAGE_ORDERS,
PermissionEnum.MANAGE_PRODUCTS,
]);
const {
data: warehousesCountData,
loading: warehousesCountLoading,
} = useWarehousesCountQuery({ skip: !canLoadWarehouses });
const {
loadMore: fetchMoreWarehouses,
search: searchWarehouses,
result: searchWarehousesResult,
} = useWarehouseSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA,
skip: !canLoadWarehouses,
});
return {
warehousesCountData,
warehousesCountLoading,
fetchMoreWarehouses,
searchWarehouses,
searchWarehousesResult,
};
};

View file

@ -2,32 +2,58 @@ import { useUserPermissions } from "@saleor/auth/hooks/useUserPermissions";
import { PermissionEnum, UserPermissionFragment } from "@saleor/graphql"; import { PermissionEnum, UserPermissionFragment } from "@saleor/graphql";
import React from "react"; import React from "react";
const findPerm = (permList, perm) =>
permList.find(userPerm => userPerm.code === perm);
export function hasPermissions( export function hasPermissions(
userPermissions: UserPermissionFragment[], userPermissions: UserPermissionFragment[],
requiredPermissions: PermissionEnum[], requiredPermissions: PermissionEnum[],
): boolean { ): boolean {
return requiredPermissions.reduce( return requiredPermissions.reduce(
(acc, perm) => (acc, perm) => acc && !!findPerm(userPermissions, perm),
acc && !!userPermissions.find(userPerm => userPerm.code === perm),
true, true,
); );
} }
export function hasOneOfPermissions(
userPermissions: UserPermissionFragment[],
givenPermissions: PermissionEnum[],
): boolean {
return givenPermissions.some(perm => !!findPerm(userPermissions, perm));
}
export interface RequirePermissionsProps { export interface RequirePermissionsProps {
children: React.ReactNode | React.ReactNodeArray; children: React.ReactNode | React.ReactNodeArray;
requiredPermissions: PermissionEnum[]; requiredPermissions?: PermissionEnum[];
oneOfPermissions?: PermissionEnum[];
} }
const RequirePermissions: React.FC<RequirePermissionsProps> = ({ const RequirePermissions: React.FC<RequirePermissionsProps> = ({
children, children,
requiredPermissions, requiredPermissions,
oneOfPermissions,
}) => { }) => {
const userPermissions = useUserPermissions(); const userPermissions = useUserPermissions();
return userPermissions && if (!userPermissions) {
hasPermissions(userPermissions, requiredPermissions) ? ( return null;
<>{children}</> }
) : null;
if (
requiredPermissions &&
hasPermissions(userPermissions, requiredPermissions)
) {
return <>{children}</>;
}
if (
oneOfPermissions &&
hasOneOfPermissions(userPermissions, oneOfPermissions)
) {
return <>{children}</>;
}
return null;
}; };
RequirePermissions.displayName = "RequirePermissions"; RequirePermissions.displayName = "RequirePermissions";

View file

@ -165,7 +165,10 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
if (fetchOnFocus) { if (fetchOnFocus) {
fetchChoices(inputValue); fetchChoices(inputValue);
} }
if (input.current) {
input.current.select(); input.current.select();
}
}; };
const handleToggleMenu = () => { const handleToggleMenu = () => {

File diff suppressed because it is too large Load diff