Saleor 1712 dashboard unable to handle empty channel list (#924)

* Use query hook on home page

* Handle no channel

* Handle no channel on pages

* Add navigate channels

* Move messages

* Refactor

* Update storybook and locale

* Remove comment

* Refactor

* Update storybook

* Fix skip

* Fix undefined channel

* Update storybook
This commit is contained in:
Marek Choiński 2021-01-13 10:33:38 +01:00 committed by GitHub
parent 3f5cacb8a0
commit d0be941ade
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 272 additions and 406 deletions

View file

@ -3023,6 +3023,9 @@
"src_dot_home_dot_components_dot_HomeActivityCard_dot_placed": {
"string": "Order #{orderId} was placed"
},
"src_dot_home_dot_components_dot_HomeNotificationTable_dot_createNewChannel": {
"string": "Create new channel"
},
"src_dot_hooks_dot_3382262667": {
"string": "Variant {name} has been set as default."
},

View file

@ -4,6 +4,7 @@ import DeleteIcon from "@material-ui/icons/Delete";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import NotFoundPage from "@saleor/components/NotFoundPage";
import Skeleton from "@saleor/components/Skeleton";
import { WindowTitle } from "@saleor/components/WindowTitle";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useNavigator from "@saleor/hooks/useNavigator";
@ -207,6 +208,10 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
variables => updatePrivateMetadata({ variables })
);
if (typeof channel === "undefined") {
return <Skeleton />;
}
return (
<>
<WindowTitle title={maybe(() => data.category.name)} />
@ -256,7 +261,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
data.category.products.edges.map(edge => edge.node)
)}
saveButtonBarState={updateResult.status}
selectedChannelId={channel.id}
selectedChannelId={channel?.id}
subcategories={maybe(() =>
data.category.children.edges.map(edge => edge.node)
)}

View file

@ -146,8 +146,8 @@ const CollectionList: React.FC<CollectionListProps> = props => {
collections,
collection => {
const isSelected = collection ? isChecked(collection.id) : false;
const channel = collection?.channelListings.find(
listing => listing.channel.id === selectedChannelId
const channel = collection?.channelListings?.find(
listing => listing?.channel?.id === selectedChannelId
);
return (
<TableRow
@ -184,11 +184,13 @@ const CollectionList: React.FC<CollectionListProps> = props => {
{collection && !collection?.channelListings?.length ? (
"-"
) : collection?.channelListings !== undefined ? (
<ChannelsAvailabilityDropdown
allChannelsCount={channelsCount}
currentChannel={channel}
channels={collection?.channelListings}
/>
channel ? (
<ChannelsAvailabilityDropdown
allChannelsCount={channelsCount}
currentChannel={channel}
channels={collection?.channelListings}
/>
) : null
) : (
<Skeleton />
)}

View file

@ -184,7 +184,7 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
toggle={toggle}
toggleAll={toggleAll}
channelsCount={availableChannels?.length}
selectedChannelId={channel.id}
selectedChannelId={channel?.id}
/>
<ActionDialog
open={params.action === "remove" && maybe(() => params.ids.length > 0)}

View file

@ -30,6 +30,7 @@ export const AppChannelProvider: React.FC = ({ children }) => {
const { data: channelData, refetch } = useChannelsList({
skip: !isAuthenticated
});
const [isPickerActive, setPickerActive] = React.useState(false);
React.useEffect(() => {
if (!selectedChannel) {
@ -38,9 +39,10 @@ export const AppChannelProvider: React.FC = ({ children }) => {
}, [channelData]);
const availableChannels = channelData?.channels || [];
const channel = availableChannels.find(
channel => channel.id === selectedChannel
);
const channel =
channelData &&
(availableChannels.find(channel => channel.id === selectedChannel) || null);
return (
<AppChannelContext.Provider
@ -57,6 +59,7 @@ export const AppChannelProvider: React.FC = ({ children }) => {
</AppChannelContext.Provider>
);
};
AppChannelProvider.displayName = "AppChannelProvider";
function useAppChannel(enablePicker = true): UseAppChannel {

View file

@ -215,12 +215,14 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
.includes("mac")}
onClick={() => setNavigatorVisibility(true)}
/>
<AppChannelSelect
channels={availableChannels}
disabled={!isPickerActive}
selectedChannelId={channel.id}
onChannelSelect={setChannel}
/>
{channel && (
<AppChannelSelect
channels={availableChannels}
disabled={!isPickerActive}
selectedChannelId={channel.id}
onChannelSelect={setChannel}
/>
)}
<UserChip
isDarkThemeEnabled={isDark}
user={user}

View file

@ -78,7 +78,7 @@ const SaleCreatePage: React.FC<SaleCreatePageProps> = ({
triggerChange
);
const formDisabled = data.channelListings?.some(channel =>
validatePrice(channel.discountValue)
validatePrice(channel?.discountValue)
);
return (
<Container>

View file

@ -51,12 +51,12 @@ const SaleSummary: React.FC<SaleSummaryProps> = ({
sale.type === SaleType.FIXED && channel?.discountValue ? (
<Money
money={{
amount: channel.discountValue,
currency: channel.currency
amount: channel?.discountValue,
currency: channel?.currency
}}
/>
) : channel?.discountValue ? (
<Percent amount={channel.discountValue} />
<Percent amount={channel?.discountValue} />
) : (
"-"
)

View file

@ -69,12 +69,12 @@ const VoucherSummary: React.FC<VoucherSummaryProps> = ({
channel?.discountValue ? (
<Money
money={{
amount: channel.discountValue,
currency: channel.channel.currencyCode
amount: channel?.discountValue,
currency: channel?.channel.currencyCode
}}
/>
) : channel?.discountValue ? (
<Percent amount={channel.discountValue} />
<Percent amount={channel?.discountValue} />
) : (
"-"
)

View file

@ -199,7 +199,7 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
<DeleteIcon />
</IconButton>
}
selectedChannelId={channel.id}
selectedChannelId={channel?.id}
/>
<ActionDialog
confirmButtonState={saleBulkDeleteOpts.status}

View file

@ -298,7 +298,7 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
...(updateChannelsOpts.data
?.voucherChannelListingUpdate.errors || [])
]}
selectedChannelId={channel.id}
selectedChannelId={channel?.id}
pageInfo={pageInfo}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}

View file

@ -200,7 +200,7 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
<DeleteIcon />
</IconButton>
}
selectedChannelId={channel.id}
selectedChannelId={channel?.id}
/>
<ActionDialog
confirmButtonState={voucherBulkDeleteOpts.status}

View file

@ -11,7 +11,40 @@ import Skeleton from "@saleor/components/Skeleton";
import { UserPermissionProps } from "@saleor/types";
import { PermissionEnum } from "@saleor/types/globalTypes";
import React from "react";
import { FormattedMessage } from "react-intl";
import { defineMessages, useIntl } from "react-intl";
const messages = defineMessages({
createNewChannel: {
defaultMessage: "Create new channel"
},
noOrders: {
defaultMessage: "No orders ready to fulfill",
id: "homeNotificationTableNoOrders"
},
noPaymentWaiting: {
defaultMessage: "No payments waiting for capture",
id: "homeNotificationsNoPayments"
},
noProductsOut: {
defaultMessage: "No products out of stock",
id: "homeNotificationsTableNoProducts"
},
orderReady: {
defaultMessage:
"{amount, plural,one {One order is ready to fulfill} other {{amount} Orders are ready to fulfill}}",
id: "homeNotificationTableOrders"
},
paymentCapture: {
defaultMessage:
"{amount, plural,one {One payment to capture}other {{amount} Payments to capture}}",
id: "homeNotificationTablePayments"
},
productOut: {
defaultMessage:
"{amount, plural,one {One product out of stock}other {{amount} Products out of stock}}",
id: "homeNotificationTableProducts"
}
});
const useStyles = makeStyles(
() => ({
@ -33,28 +66,46 @@ interface HomeNotificationTableProps extends UserPermissionProps {
ordersToCapture: number;
ordersToFulfill: number;
productsOutOfStock: number;
onCreateNewChannelClick: () => void;
onOrdersToFulfillClick: () => void;
onOrdersToCaptureClick: () => void;
onProductsOutOfStockClick: () => void;
noChannel: boolean;
}
const HomeNotificationTable: React.FC<HomeNotificationTableProps> = props => {
const {
onCreateNewChannelClick,
onOrdersToCaptureClick,
onOrdersToFulfillClick,
onProductsOutOfStockClick,
ordersToCapture,
ordersToFulfill,
productsOutOfStock,
userPermissions
userPermissions,
noChannel
} = props;
const classes = useStyles(props);
const intl = useIntl();
return (
<Card className={classes.tableCard}>
<ResponsiveTable>
<TableBody className={classes.tableRow}>
{noChannel && (
<TableRow hover={true} onClick={onCreateNewChannelClick}>
<TableCell>
<Typography>
{intl.formatMessage(messages.createNewChannel)}
</Typography>
</TableCell>
<TableCell className={classes.arrowIcon}>
<KeyboardArrowRight />
</TableCell>
</TableRow>
)}
<RequirePermissions
userPermissions={userPermissions}
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
@ -65,20 +116,13 @@ const HomeNotificationTable: React.FC<HomeNotificationTableProps> = props => {
<Skeleton />
) : ordersToFulfill === 0 ? (
<Typography>
<FormattedMessage
defaultMessage="No orders ready to fulfill"
id="homeNotificationTableNoOrders"
/>
{intl.formatMessage(messages.noOrders)}
</Typography>
) : (
<Typography>
<FormattedMessage
defaultMessage="{amount, plural,one {One order is ready to fulfill} other {{amount} Orders are ready to fulfill}}"
id="homeNotificationTableOrders"
values={{
amount: <strong>{ordersToFulfill}</strong>
}}
/>
{intl.formatMessage(messages.orderReady, {
amount: <strong>{ordersToFulfill}</strong>
})}
</Typography>
)}
</TableCell>
@ -92,20 +136,13 @@ const HomeNotificationTable: React.FC<HomeNotificationTableProps> = props => {
<Skeleton />
) : ordersToCapture === 0 ? (
<Typography>
<FormattedMessage
defaultMessage="No payments waiting for capture"
id="homeNotificationsNoPayments"
/>
{intl.formatMessage(messages.noPaymentWaiting)}
</Typography>
) : (
<Typography>
<FormattedMessage
defaultMessage="{amount, plural,one {One payment to capture}other {{amount} Payments to capture}}"
id="homeNotificationTablePayments"
values={{
amount: <strong>{ordersToCapture}</strong>
}}
/>
{intl.formatMessage(messages.paymentCapture, {
amount: <strong>{ordersToCapture}</strong>
})}
</Typography>
)}
</TableCell>
@ -124,20 +161,13 @@ const HomeNotificationTable: React.FC<HomeNotificationTableProps> = props => {
<Skeleton />
) : productsOutOfStock === 0 ? (
<Typography>
<FormattedMessage
defaultMessage="No products out of stock"
id="homeNotificationsTableNoProducts"
/>
{intl.formatMessage(messages.noProductsOut)}
</Typography>
) : (
<Typography>
<FormattedMessage
defaultMessage="{amount, plural,one {One product out of stock}other {{amount} Products out of stock}}"
id="homeNotificationTableProducts"
values={{
amount: <strong>{productsOutOfStock}</strong>
}}
/>
{intl.formatMessage(messages.productOut, {
amount: <strong>{productsOutOfStock}</strong>
})}
</Typography>
)}
</TableCell>

View file

@ -46,17 +46,19 @@ const useStyles = makeStyles(
export interface HomePageProps extends UserPermissionProps {
activities: Home_activities_edges_node[];
orders: number;
ordersToCapture: number;
ordersToFulfill: number;
orders: number | null;
ordersToCapture: number | null;
ordersToFulfill: number | null;
productsOutOfStock: number;
sales: Home_salesToday_gross;
topProducts: Home_productTopToday_edges_node[];
topProducts: Home_productTopToday_edges_node[] | null;
userName: string;
onCreateNewChannelClick: () => void;
onOrdersToCaptureClick: () => void;
onOrdersToFulfillClick: () => void;
onProductClick: (productId: string, variantId: string) => void;
onProductsOutOfStockClick: () => void;
noChannel: boolean;
}
const HomePage: React.FC<HomePageProps> = props => {
@ -67,13 +69,15 @@ const HomePage: React.FC<HomePageProps> = props => {
topProducts,
onProductClick,
activities,
onCreateNewChannelClick,
onOrdersToCaptureClick,
onOrdersToFulfillClick,
onProductsOutOfStockClick,
ordersToCapture,
ordersToFulfill,
productsOutOfStock,
userPermissions
ordersToCapture = 0,
ordersToFulfill = 0,
productsOutOfStock = 0,
userPermissions = [],
noChannel
} = props;
const classes = useStyles(props);
@ -99,7 +103,9 @@ const HomePage: React.FC<HomePageProps> = props => {
/>
}
>
{sales ? (
{noChannel ? (
0
) : sales ? (
<Money money={sales} />
) : (
<Skeleton style={{ width: "5em" }} />
@ -115,15 +121,18 @@ const HomePage: React.FC<HomePageProps> = props => {
/>
}
>
{orders === undefined ? (
<Skeleton style={{ width: "5em" }} />
) : (
{noChannel ? (
0
) : orders !== undefined ? (
orders
) : (
<Skeleton style={{ width: "5em" }} />
)}
</HomeAnalyticsCard>
</div>
</RequirePermissions>
<HomeNotificationTable
onCreateNewChannelClick={onCreateNewChannelClick}
onOrdersToCaptureClick={onOrdersToCaptureClick}
onOrdersToFulfillClick={onOrdersToFulfillClick}
onProductsOutOfStockClick={onProductsOutOfStockClick}
@ -131,30 +140,35 @@ const HomePage: React.FC<HomePageProps> = props => {
ordersToFulfill={ordersToFulfill}
productsOutOfStock={productsOutOfStock}
userPermissions={userPermissions}
noChannel={noChannel}
/>
<CardSpacer />
<RequirePermissions
userPermissions={userPermissions}
requiredPermissions={[
PermissionEnum.MANAGE_ORDERS,
PermissionEnum.MANAGE_PRODUCTS
]}
>
<HomeProductListCard
onRowClick={onProductClick}
topProducts={topProducts}
/>
<CardSpacer />
</RequirePermissions>
</div>
<div>
<RequirePermissions
userPermissions={userPermissions}
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
>
<HomeActivityCard activities={activities} />
</RequirePermissions>
{topProducts && (
<RequirePermissions
userPermissions={userPermissions}
requiredPermissions={[
PermissionEnum.MANAGE_ORDERS,
PermissionEnum.MANAGE_PRODUCTS
]}
>
<HomeProductListCard
onRowClick={onProductClick}
topProducts={topProducts}
/>
<CardSpacer />
</RequirePermissions>
)}
</div>
{activities && (
<div>
<RequirePermissions
userPermissions={userPermissions}
requiredPermissions={[PermissionEnum.MANAGE_ORDERS]}
>
<HomeActivityCard activities={activities} />
</RequirePermissions>
</div>
)}
</Grid>
</Container>
);

View file

@ -1,7 +1,7 @@
import makeQuery from "@saleor/hooks/makeQuery";
import gql from "graphql-tag";
import { TypedQuery } from "../queries";
import { Home } from "./types/Home";
import { Home, HomeVariables } from "./types/Home";
const home = gql`
query Home($channel: String!) {
@ -80,4 +80,5 @@ const home = gql`
}
}
`;
export const HomePageQuery = TypedQuery<Home, {}>(home);
export const useHomePage = makeQuery<Home, HomeVariables>(home);

View file

@ -1,64 +1,69 @@
import { channelsListUrl } from "@saleor/channels/urls";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import useNavigator from "@saleor/hooks/useNavigator";
import useUser from "@saleor/hooks/useUser";
import React from "react";
import { getUserName, maybe } from "../../misc";
import { getUserName } from "../../misc";
import { orderListUrl } from "../../orders/urls";
import { productListUrl, productVariantEditUrl } from "../../products/urls";
import { OrderStatusFilter, StockAvailability } from "../../types/globalTypes";
import HomePage from "../components/HomePage";
import { HomePageQuery } from "../queries";
import { useHomePage } from "../queries";
const HomeSection = () => {
const navigate = useNavigator();
const { user } = useUser();
const { channel } = useAppChannel();
const noChannel = !channel && typeof channel !== "undefined";
const { data } = useHomePage({
displayLoader: true,
skip: noChannel,
variables: { channel: channel?.slug }
});
return (
<HomePageQuery displayLoader variables={{ channel: channel.slug }}>
{({ data }) => (
<HomePage
activities={maybe(() =>
data.activities.edges.map(edge => edge.node).reverse()
)}
orders={maybe(() => data.ordersToday.totalCount)}
sales={maybe(() => data.salesToday.gross)}
topProducts={maybe(() =>
data.productTopToday.edges.map(edge => edge.node)
)}
onProductClick={(productId, variantId) =>
navigate(productVariantEditUrl(productId, variantId))
}
onOrdersToCaptureClick={() =>
navigate(
orderListUrl({
status: [OrderStatusFilter.READY_TO_CAPTURE]
})
)
}
onOrdersToFulfillClick={() =>
navigate(
orderListUrl({
status: [OrderStatusFilter.READY_TO_FULFILL]
})
)
}
onProductsOutOfStockClick={() =>
navigate(
productListUrl({
stockStatus: StockAvailability.OUT_OF_STOCK
})
)
}
ordersToCapture={maybe(() => data.ordersToCapture.totalCount)}
ordersToFulfill={maybe(() => data.ordersToFulfill.totalCount)}
productsOutOfStock={maybe(() => data.productsOutOfStock.totalCount)}
userName={getUserName(user, true)}
userPermissions={user?.userPermissions || []}
/>
)}
</HomePageQuery>
<HomePage
activities={data?.activities.edges.map(edge => edge.node).reverse()}
orders={data?.ordersToday.totalCount}
sales={data?.salesToday.gross}
topProducts={data?.productTopToday.edges.map(edge => edge.node)}
onProductClick={(productId, variantId) =>
navigate(productVariantEditUrl(productId, variantId))
}
onCreateNewChannelClick={() => {
navigate(channelsListUrl());
}}
onOrdersToCaptureClick={() =>
navigate(
orderListUrl({
status: [OrderStatusFilter.READY_TO_CAPTURE]
})
)
}
onOrdersToFulfillClick={() =>
navigate(
orderListUrl({
status: [OrderStatusFilter.READY_TO_FULFILL]
})
)
}
onProductsOutOfStockClick={() =>
navigate(
productListUrl({
stockStatus: StockAvailability.OUT_OF_STOCK
})
)
}
ordersToCapture={data?.ordersToCapture.totalCount}
ordersToFulfill={data?.ordersToFulfill.totalCount}
productsOutOfStock={data?.productsOutOfStock.totalCount}
userName={getUserName(user, true)}
userPermissions={user?.userPermissions}
noChannel={noChannel}
/>
);
};

View file

@ -142,13 +142,21 @@ const Routes: React.FC = () => {
} = useAuth();
const { channel } = useAppChannel(false);
const channelLoaded = typeof channel !== "undefined";
const homePageLoaded =
channelLoaded &&
isAuthenticated &&
!tokenAuthLoading &&
!tokenVerifyLoading;
const homePageLoading =
(isAuthenticated && !channelLoaded) || (hasToken && tokenVerifyLoading);
return (
<>
<WindowTitle title={intl.formatMessage(commonMessages.dashboard)} />
{channel &&
isAuthenticated &&
!tokenAuthLoading &&
!tokenVerifyLoading ? (
{homePageLoaded ? (
<AppLayout>
<ErrorBoundary
onError={() =>
@ -284,7 +292,7 @@ const Routes: React.FC = () => {
</Switch>
</ErrorBoundary>
</AppLayout>
) : (isAuthenticated && !channel) || (hasToken && tokenVerifyLoading) ? (
) : homePageLoading ? (
<LoginLoading />
) : (
<Auth />

View file

@ -20,6 +20,7 @@ import { ListViews } from "@saleor/types";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
import createSortHandler from "@saleor/utils/handlers/sortHandler";
import { mapNodeToChoice } from "@saleor/utils/maps";
import { getSortParams } from "@saleor/utils/sort";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -259,12 +260,9 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
/>
<ChannelPickerDialog
channelsChoices={availableChannels.map(channel => ({
label: channel.name,
value: channel.id
}))}
channelsChoices={mapNodeToChoice(availableChannels)}
confirmButtonState="success"
defaultChoice={channel.id}
defaultChoice={channel?.id}
open={params.action === "create-order"}
onClose={closeModal}
onConfirm={channel =>

View file

@ -71,6 +71,8 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
const { channel, availableChannels } = useAppChannel();
const noChannel = !channel && typeof channel !== "undefined";
const tabs = getFilterTabs();
const currentTab =
@ -176,23 +178,25 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
onSubmit={handleFilterTabDelete}
tabName={getStringOrPlaceholder(tabs[currentTab - 1]?.name)}
/>
<ChannelPickerDialog
channelsChoices={availableChannels.map(channel => ({
label: channel.name,
value: channel.id
}))}
confirmButtonState="success"
defaultChoice={channel.id}
open={params.action === "create-order"}
onClose={closeModal}
onConfirm={channel =>
createOrder({
variables: {
input: { channel }
}
})
}
/>
{!noChannel && (
<ChannelPickerDialog
channelsChoices={availableChannels.map(channel => ({
label: channel.name,
value: channel.id
}))}
confirmButtonState="success"
defaultChoice={channel.id}
open={params.action === "create-order"}
onClose={closeModal}
onConfirm={channel =>
createOrder({
variables: {
input: { channel }
}
})
}
/>
)}
</>
);
};

View file

@ -127,6 +127,8 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
});
const { availableChannels, channel } = useAppChannel();
const noChannel = !channel && typeof channel !== "undefined";
const [openModal, closeModal] = createDialogActionHandlers<
ProductListUrlDialog,
ProductListUrlQueryParams
@ -222,8 +224,8 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
);
const paginationState = createPaginationState(settings.rowNumber, params);
const filter = getFilterVariables(params, channel.slug);
const sort = getSortQueryVariables(params, channel.slug);
const filter = !noChannel ? getFilterVariables(params, channel.slug) : null;
const sort = !noChannel ? getSortQueryVariables(params, channel.slug) : null;
const queryVariables = React.useMemo<ProductListVariables>(
() => ({
...paginationState,
@ -302,7 +304,7 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
() => attributes.data.availableInGrid.edges.map(edge => edge.node),
[]
)}
currencySymbol={channel?.currencyCode}
currencySymbol={channel?.currencyCode || ""}
currentTab={currentTab}
defaultSettings={defaultListSettings[ListViews.PRODUCT_LIST]}
filterOpts={filterOpts}
@ -380,7 +382,7 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
tabs={getFilterTabs().map(tab => tab.name)}
onExport={() => openModal("export")}
channelsCount={availableChannels?.length}
selectedChannelId={channel.id}
selectedChannelId={channel?.id}
/>
<ActionDialog
open={params.action === "delete"}

View file

@ -423,7 +423,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
loading: searchCollectionsOpts.loading,
onFetchMore: loadMoreCollections
}}
selectedChannelId={channel.id}
selectedChannelId={channel?.id}
openChannelsModal={handleChannelsModalOpen}
onChannelsChange={setCurrentChannels}
/>

View file

@ -52100,21 +52100,7 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = `
class="MuiTableCell-root-id MuiTableCell-body-id CollectionList-colAvailability-id"
data-test="availability"
data-test-availability="true"
>
<div>
<div
aria-controls="availability-menu"
aria-haspopup="true"
role="button"
>
<div
class="StatusLabel-root-id undefined StatusLabel-errorDot-id"
>
Available in 1/2
</div>
</div>
</div>
</td>
/>
</tr>
<tr
class="MuiTableRow-root-id CollectionList-tableRow-id MuiTableRow-hover-id"
@ -52170,21 +52156,7 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = `
class="MuiTableCell-root-id MuiTableCell-body-id CollectionList-colAvailability-id"
data-test="availability"
data-test-availability="true"
>
<div>
<div
aria-controls="availability-menu"
aria-haspopup="true"
role="button"
>
<div
class="StatusLabel-root-id undefined StatusLabel-errorDot-id"
>
Available in 1/2
</div>
</div>
</div>
</td>
/>
</tr>
<tr
class="MuiTableRow-root-id CollectionList-tableRow-id MuiTableRow-hover-id"
@ -52240,21 +52212,7 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = `
class="MuiTableCell-root-id MuiTableCell-body-id CollectionList-colAvailability-id"
data-test="availability"
data-test-availability="true"
>
<div>
<div
aria-controls="availability-menu"
aria-haspopup="true"
role="button"
>
<div
class="StatusLabel-root-id undefined StatusLabel-errorDot-id"
>
Available in 1/2
</div>
</div>
</div>
</td>
/>
</tr>
<tr
class="MuiTableRow-root-id CollectionList-tableRow-id MuiTableRow-hover-id"
@ -52310,21 +52268,7 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = `
class="MuiTableCell-root-id MuiTableCell-body-id CollectionList-colAvailability-id"
data-test="availability"
data-test-availability="true"
>
<div>
<div
aria-controls="availability-menu"
aria-haspopup="true"
role="button"
>
<div
class="StatusLabel-root-id undefined StatusLabel-errorDot-id"
>
Available in 1/2
</div>
</div>
</div>
</td>
/>
</tr>
<tr
class="MuiTableRow-root-id CollectionList-tableRow-id MuiTableRow-hover-id"
@ -52380,21 +52324,7 @@ exports[`Storyshots Views / Collections / Collection list default 1`] = `
class="MuiTableCell-root-id MuiTableCell-body-id CollectionList-colAvailability-id"
data-test="availability"
data-test-availability="true"
>
<div>
<div
aria-controls="availability-menu"
aria-haspopup="true"
role="button"
>
<div
class="StatusLabel-root-id undefined StatusLabel-errorDot-id"
>
Available in 1/2
</div>
</div>
</div>
</td>
/>
</tr>
</tbody>
</table>
@ -91661,11 +91591,11 @@ exports[`Storyshots Views / HomePage loading 1`] = `
<td
class="MuiTableCell-root-id MuiTableCell-body-id"
>
<span
class="Skeleton-skeleton-id"
<div
class="MuiTypography-root-id MuiTypography-body1-id"
>
</span>
No orders ready to fulfill
</div>
</td>
<td
class="MuiTableCell-root-id MuiTableCell-body-id HomeNotificationTable-arrowIcon-id"
@ -91689,11 +91619,11 @@ exports[`Storyshots Views / HomePage loading 1`] = `
<td
class="MuiTableCell-root-id MuiTableCell-body-id"
>
<span
class="Skeleton-skeleton-id"
<div
class="MuiTypography-root-id MuiTypography-body1-id"
>
</span>
No payments waiting for capture
</div>
</td>
<td
class="MuiTableCell-root-id MuiTableCell-body-id HomeNotificationTable-arrowIcon-id"
@ -91717,11 +91647,11 @@ exports[`Storyshots Views / HomePage loading 1`] = `
<td
class="MuiTableCell-root-id MuiTableCell-body-id"
>
<span
class="Skeleton-skeleton-id"
<div
class="MuiTypography-root-id MuiTypography-body1-id"
>
</span>
No products out of stock
</div>
</td>
<td
class="MuiTableCell-root-id MuiTableCell-body-id HomeNotificationTable-arrowIcon-id"
@ -91746,149 +91676,6 @@ exports[`Storyshots Views / HomePage loading 1`] = `
<div
class="CardSpacer-spacer-id"
/>
<div
class="MuiPaper-root-id MuiPaper-elevation0-id MuiCard-root-id MuiPaper-rounded-id"
>
<div
class="CardTitle-root-id"
>
<span
class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id"
>
Top Products
</span>
<div
class="CardTitle-toolbar-id"
/>
</div>
<div
class="CardTitle-children-id"
/>
<hr
class="CardTitle-hr-id"
/>
<div
class="ResponsiveTable-root-id"
>
<table
class="MuiTable-root-id"
>
<colgroup>
<col
class="HomeProductListCard-colAvatar-id"
/>
<col
class="HomeProductListCard-colName-id"
/>
<col />
</colgroup>
<tbody
class="MuiTableBody-root-id"
>
<tr
class="MuiTableRow-root-id"
>
<td
class="MuiTableCell-root-id MuiTableCell-body-id TableCellAvatar-root-id HomeProductListCard-colAvatar-id"
>
<div
class="TableCellAvatar-content-id"
>
<div
class="MuiAvatar-root-id MuiAvatar-circle-id TableCellAvatar-avatar-id HomeProductListCard-avatarProps-id MuiAvatar-colorDefault-id"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id MuiSvgIcon-colorPrimary-id"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M19 8l-4 4h3c0 3.31-2.69 6-6 6-1.01 0-1.97-.25-2.8-.7l-1.46 1.46C8.97 19.54 10.43 20 12 20c4.42 0 8-3.58 8-8h3l-4-4zM6 12c0-3.31 2.69-6 6-6 1.01 0 1.97.25 2.8.7l1.46-1.46C15.03 4.46 13.57 4 12 4c-4.42 0-8 3.58-8 8H1l4 4 4-4H6z"
/>
</svg>
</div>
<div
class="TableCellAvatar-children-id"
/>
</div>
</td>
<td
class="MuiTableCell-root-id MuiTableCell-body-id HomeProductListCard-label-id"
>
<span
class="Skeleton-skeleton-id"
>
</span>
</td>
<td
class="MuiTableCell-root-id MuiTableCell-body-id"
>
<div
class="MuiTypography-root-id MuiTypography-body1-id MuiTypography-alignRight-id"
>
<span
class="Skeleton-skeleton-id"
>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div
class="CardSpacer-spacer-id"
/>
</div>
<div>
<div
class="MuiPaper-root-id MuiPaper-elevation0-id MuiCard-root-id MuiPaper-rounded-id"
>
<div
class="CardTitle-root-id"
>
<span
class="MuiTypography-root-id CardTitle-title-id MuiTypography-h5-id"
>
Activity
</span>
<div
class="CardTitle-toolbar-id"
/>
</div>
<div
class="CardTitle-children-id"
/>
<hr
class="CardTitle-hr-id"
/>
<ul
class="MuiList-root-id MuiList-dense-id MuiList-padding-id"
>
<li
class="MuiListItem-root-id MuiListItem-dense-id MuiListItem-gutters-id"
>
<div
class="MuiListItemText-root-id HomeActivityCard-loadingProducts-id MuiListItemText-dense-id"
>
<div
class="MuiTypography-root-id MuiTypography-body1-id"
>
<span
class="Skeleton-skeleton-id"
>
</span>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>

View file

@ -13,6 +13,8 @@ const shop = shopFixture(placeholderImage);
const homePageProps: Omit<HomePageProps, "classes"> = {
activities: shop.activities.edges.map(edge => edge.node),
noChannel: false,
onCreateNewChannelClick: () => undefined,
onOrdersToCaptureClick: () => undefined,
onOrdersToFulfillClick: () => undefined,
onProductClick: () => undefined,