From 0693209f0463ca622e6c363b95a0cccf29425644 Mon Sep 17 00:00:00 2001 From: Wojciech Mista Date: Wed, 6 Oct 2021 14:27:15 +0200 Subject: [PATCH 01/17] Add gift cards card to customer page (#1456) * WIP * WIP * Extract activate/deactivate logic to a hook * add optional side action * Add query for customer's gift cards * Add component for giftcard status chip * Graphql run types * Add gift card card to customer page * Fix status chip header * Revert style change * Unify status chip logic * Fix naming scheme * Add currentOpts to act/deactivate gift cards hook * Add queries to refetch prop * Simplify gift card list component * Fix order status chip * Extract messages to separate file * Remove unused lines of code * Tests and messages * Fix card list rendering * Type fix * Code review fixes * Review changes * Scripts * Change variable name * Fix formatted message * Check if giftcards exist before rendering collection --- locale/defaultMessages.json | 20 + src/components/CardTitle/CardTitle.tsx | 5 + .../CollectionWithDividers.tsx | 12 +- .../PageTitleWithStatusChip.tsx | 9 +- .../CustomerDetailsPage.tsx | 3 + .../GiftCardCreateDialog.tsx | 13 +- .../GiftCardEnableDisableSection.tsx | 61 +- .../GiftCardUpdatePageHeader.tsx | 37 +- .../useGiftCardActivationDeactivation.ts | 92 ++++ .../GiftCardsListTable/GiftCardsListTable.tsx | 192 +++---- .../GiftCardsListTableHeader/messages.ts | 4 + .../GiftCardListDialogsProvider.tsx | 7 +- .../CustomerGiftCardsCard.tsx | 80 +++ .../CustomerGiftCardsCardListItem.tsx | 124 +++++ .../CustomerGiftCardsList.tsx | 33 ++ .../GiftCardCustomerCard/messages.ts | 20 + .../GiftCardCustomerCard/queries.ts | 29 + .../components/GiftCardCustomerCard/styles.ts | 32 ++ .../types/CustomerGiftCardList.ts | 37 ++ .../components/GiftCardCustomerCard/utils.ts | 7 + .../GiftCardListPageDeleteDialog.tsx | 14 +- .../GiftCardStatusChip/GiftCardStatusChip.tsx | 50 ++ .../OrderDetailsPage/OrderDetailsPage.tsx | 9 +- .../OrderStatusChip.tsx} | 16 +- .../__snapshots__/Stories.test.ts.snap | 521 +++++++++++++++++- 25 files changed, 1179 insertions(+), 248 deletions(-) create mode 100644 src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation.ts create mode 100644 src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx create mode 100644 src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx create mode 100644 src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsList.tsx create mode 100644 src/giftCards/components/GiftCardCustomerCard/messages.ts create mode 100644 src/giftCards/components/GiftCardCustomerCard/queries.ts create mode 100644 src/giftCards/components/GiftCardCustomerCard/styles.ts create mode 100644 src/giftCards/components/GiftCardCustomerCard/types/CustomerGiftCardList.ts create mode 100644 src/giftCards/components/GiftCardCustomerCard/utils.ts create mode 100644 src/giftCards/components/GiftCardStatusChip/GiftCardStatusChip.tsx rename src/orders/components/{OrderDetailsPage/Title.tsx => OrderStatusChip/OrderStatusChip.tsx} (53%) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 2c94f19f6b9..e931d7279ec 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3765,6 +3765,10 @@ "context": "GiftCardUpdateDetailsCard title", "string": "Details" }, + "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_deleteLabel": { + "context": "GiftCardEnableDisableSection enable label", + "string": "Delete" + }, "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_disableLabel": { "context": "GiftCardEnableDisableSection enable label", "string": "Deactivate" @@ -3869,6 +3873,22 @@ "context": "GiftCardsListHeader menu item settings", "string": "Settings" }, + "src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsAbsentSubtitle": { + "context": "subtitle", + "string": "There are no gift cards assigned to this customer" + }, + "src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsIssueNewCardButton": { + "context": "button", + "string": "Issue new card" + }, + "src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsPresentSubtitle": { + "context": "subtitle", + "string": "Only five newest gift cards are shown here" + }, + "src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsViewAllButton": { + "context": "button", + "string": "View All" + }, "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_consentLabel": { "context": "GiftCardDeleteDialog consent label", "string": "{selectedItemsCount,plural,one{I am aware that I am removing a gift card with balance} other{I am aware that I am removing gift cards with balance}}" diff --git a/src/components/CardTitle/CardTitle.tsx b/src/components/CardTitle/CardTitle.tsx index 6f4cad33c54..f2d77d9b1e1 100644 --- a/src/components/CardTitle/CardTitle.tsx +++ b/src/components/CardTitle/CardTitle.tsx @@ -27,6 +27,9 @@ const useStyles = makeStyles( fontWeight: 500, lineHeight: 1 }, + subtitle: { + fontWeight: 400 + }, toolbar: { marginRight: theme.spacing(-1) } @@ -39,6 +42,7 @@ interface CardTitleProps { className?: string; height?: "default" | "const"; title: string | React.ReactNode; + subtitle?: string | React.ReactNode; toolbar?: React.ReactNode; onClick?: (event: React.MouseEvent) => void; } @@ -49,6 +53,7 @@ const CardTitle: React.FC = props => { children, height, title, + subtitle, toolbar, onClick, ...rest diff --git a/src/components/CollectionWithDividers/CollectionWithDividers.tsx b/src/components/CollectionWithDividers/CollectionWithDividers.tsx index 2e4d0966264..856406ca9f6 100644 --- a/src/components/CollectionWithDividers/CollectionWithDividers.tsx +++ b/src/components/CollectionWithDividers/CollectionWithDividers.tsx @@ -5,6 +5,7 @@ import React from "react"; interface CollectionWithDividersProps { DividerComponent?: React.FunctionComponent; renderEmpty?: (collection: T[]) => any; + withOuterDividers?: boolean; collection: T[]; renderItem: ( item: T | undefined, @@ -14,6 +15,7 @@ interface CollectionWithDividersProps { } function CollectionWithDividers({ + withOuterDividers = false, collection, renderItem, DividerComponent, @@ -31,7 +33,7 @@ function CollectionWithDividers({ const SelectedDividerComponent = DividerComponent || Divider; - return initial( + const collectionToRender = initial( collection.reduce( (result, item, index) => [ ...result, @@ -41,6 +43,14 @@ function CollectionWithDividers({ [] ) ); + + const collectionWithOuterDividers = () => [ + , + ...collectionToRender, + + ]; + + return withOuterDividers ? collectionWithOuterDividers() : collectionToRender; } export default CollectionWithDividers; diff --git a/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx b/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx index 8f0dd9a3446..e91ce223d3d 100644 --- a/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx +++ b/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx @@ -1,13 +1,9 @@ import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer"; -import StatusChip from "@saleor/components/StatusChip"; -import { StatusType } from "@saleor/components/StatusChip/types"; import { makeStyles } from "@saleor/macaw-ui"; import React from "react"; export interface PageTitleWithStatusChipProps { title: string; - statusLabel: string; - statusType: StatusType; } const useStyles = makeStyles( @@ -22,8 +18,7 @@ const useStyles = makeStyles( const PageTitleWithStatusChip: React.FC = ({ title, - statusLabel, - statusType + children }) => { const classes = useStyles({}); @@ -31,7 +26,7 @@ const PageTitleWithStatusChip: React.FC = ({
{title} - + {children}
); }; diff --git a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx index 3f52167ab06..fd1b5a0d5b6 100644 --- a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx +++ b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx @@ -8,6 +8,7 @@ import { MetadataFormData } from "@saleor/components/Metadata/types"; import PageHeader from "@saleor/components/PageHeader"; import Savebar from "@saleor/components/Savebar"; import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment"; +import CustomerGiftCardsCard from "@saleor/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard"; import { SubmitPromise } from "@saleor/hooks/useForm"; import { sectionNames } from "@saleor/intl"; import { Backlink } from "@saleor/macaw-ui"; @@ -117,6 +118,8 @@ const CustomerDetailsPage: React.FC = ({ /> + + = ({ onClose, open }) => { +interface GiftCardCreateDialogProps extends DialogProps { + refetchQueries: string[]; +} + +const GiftCardCreateDialog: React.FC = ({ + onClose, + open, + refetchQueries +}) => { const intl = useIntl(); const notify = useNotifier(); @@ -80,7 +87,7 @@ const GiftCardCreateDialog: React.FC = ({ onClose, open }) => { const [createGiftCard, createGiftCardOpts] = useGiftCardCreateMutation({ onCompleted, - refetchQueries: [GIFT_CARD_LIST_QUERY] + refetchQueries }); const handleSubmit = (data: GiftCardCreateFormData) => { diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/GiftCardEnableDisableSection.tsx b/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/GiftCardEnableDisableSection.tsx index 81654ae13d5..895219ad46d 100644 --- a/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/GiftCardEnableDisableSection.tsx +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/GiftCardEnableDisableSection.tsx @@ -1,22 +1,13 @@ -import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; import { ConfirmButton } from "@saleor/macaw-ui"; -import commonErrorMessages from "@saleor/utils/errors/common"; import React from "react"; import { useIntl } from "react-intl"; import { bulkEnableDisableSectionMessages as buttonMessages } from "../../GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages"; import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails"; -import { giftCardEnableDisableSectionMessages as messages } from "./messages"; -import { - useGiftCardActivateMutation, - useGiftCardDeactivateMutation -} from "./mutations"; -import { GiftCardActivate } from "./types/GiftCardActivate"; -import { GiftCardDeactivate } from "./types/GiftCardDeactivate"; +import useGiftCardActivationDeactivation from "./hooks/useGiftCardActivationDeactivation"; const GiftCardEnableDisableSection: React.FC = () => { - const notify = useNotifier(); const intl = useIntl(); const { @@ -27,50 +18,12 @@ const GiftCardEnableDisableSection: React.FC = () => { return null; } - const onActivateCompleted = (data: GiftCardActivate) => { - const errors = data?.giftCardActivate?.errors; - - if (!!errors?.length) { - notify({ - status: "error", - text: intl.formatMessage(commonErrorMessages.unknownError) - }); - - return; - } - - notify({ - status: "success", - text: intl.formatMessage(messages.successfullyEnabledTitle) - }); - }; - - const onDeactivateCompleted = (data: GiftCardDeactivate) => { - const errors = data?.giftCardDeactivate?.errors; - - if (!!errors?.length) { - notify({ - status: "error", - text: intl.formatMessage(commonErrorMessages.unknownError) - }); - return; - } - - notify({ - status: "success", - text: intl.formatMessage(messages.successfullyDisabledTitle) - }); - }; - - const [giftCardActivate, giftCardActivateOpts] = useGiftCardActivateMutation({ - onCompleted: onActivateCompleted - }); - - const [ + const { + giftCardActivate, giftCardDeactivate, - giftCardDeactivateOpts - ] = useGiftCardDeactivateMutation({ - onCompleted: onDeactivateCompleted + currentOpts + } = useGiftCardActivationDeactivation({ + isActive }); const handleClick = () => @@ -82,8 +35,6 @@ const GiftCardEnableDisableSection: React.FC = () => { ? buttonMessages.disableLabel : buttonMessages.enableLabel; - const currentOpts = isActive ? giftCardDeactivateOpts : giftCardActivateOpts; - return ( { const { openResendCodeDialog } = useGiftCardUpdateDialogs(); - const { displayCode, isActive, isExpired } = giftCard; + const { displayCode, isExpired } = giftCard; const title = intl.formatMessage(tableMessages.codeEndingWithLabel, { displayCode }); - const getPageTitle = () => { - if (isExpired) { - return ( - - ); - } - - if (!isActive) { - return ( - - ); - } - - return title; - }; - return ( <> {intl.formatMessage(sectionNames.giftCards)} - + + + + } + > {!isExpired && ( diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation.ts b/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation.ts new file mode 100644 index 00000000000..fa9d22ade1f --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation.ts @@ -0,0 +1,92 @@ +import useNotifier from "@saleor/hooks/useNotifier"; +import commonErrorMessages from "@saleor/utils/errors/common"; +import { useIntl } from "react-intl"; + +import { giftCardEnableDisableSectionMessages as messages } from "../messages"; +import { + useGiftCardActivateMutation, + useGiftCardDeactivateMutation +} from "../mutations"; +import { GiftCardActivate } from "../types/GiftCardActivate"; +import { GiftCardDeactivate } from "../types/GiftCardDeactivate"; + +interface UseGiftCardActivationDeactivationProps { + onActivateActionComplete?: () => void | undefined; + onDeactivateActionComplete?: () => void | undefined; + isActive?: boolean; +} + +const useGiftCardActivationDeactivation = ({ + onActivateActionComplete, + onDeactivateActionComplete, + isActive +}: UseGiftCardActivationDeactivationProps) => { + const intl = useIntl(); + const notify = useNotifier(); + + const onActivateCompleted = (data: GiftCardActivate) => { + const errors = data?.giftCardActivate?.errors; + + if (!!errors?.length) { + notify({ + status: "error", + text: intl.formatMessage(commonErrorMessages.unknownError) + }); + + return; + } + + notify({ + status: "success", + text: intl.formatMessage(messages.successfullyEnabledTitle) + }); + + if (!!onActivateActionComplete) { + onActivateActionComplete(); + } + }; + + const onDeactivateCompleted = (data: GiftCardDeactivate) => { + const errors = data?.giftCardDeactivate?.errors; + + if (!!errors?.length) { + notify({ + status: "error", + text: intl.formatMessage(commonErrorMessages.unknownError) + }); + return; + } + + notify({ + status: "success", + text: intl.formatMessage(messages.successfullyDisabledTitle) + }); + + if (!!onDeactivateActionComplete) { + onDeactivateActionComplete(); + } + }; + + const [giftCardActivate, giftCardActivateOpts] = useGiftCardActivateMutation({ + onCompleted: onActivateCompleted + }); + + const [ + giftCardDeactivate, + giftCardDeactivateOpts + ] = useGiftCardDeactivateMutation({ + onCompleted: onDeactivateCompleted + }); + + const currentOpts = isActive ? giftCardDeactivateOpts : giftCardActivateOpts; + + return { + giftCardActivate, + giftCardActivateOpts, + giftCardDeactivate, + giftCardDeactivateOpts, + currentOpts + }; +}; + +export default useGiftCardActivationDeactivation; diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTable.tsx b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTable.tsx index 721b1b7d402..b9b8b7827e6 100644 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTable.tsx +++ b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTable.tsx @@ -11,9 +11,8 @@ import DeleteIconButton from "@saleor/components/DeleteIconButton"; import Link from "@saleor/components/Link"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import Skeleton from "@saleor/components/Skeleton"; -import StatusChip from "@saleor/components/StatusChip"; -import { StatusType } from "@saleor/components/StatusChip/types"; import { customerUrl } from "@saleor/customers/urls"; +import GiftCardStatusChip from "@saleor/giftCards/components/GiftCardStatusChip/GiftCardStatusChip"; import { PLACEHOLDER } from "@saleor/giftCards/GiftCardUpdate/types"; import { giftCardUrl } from "@saleor/giftCards/urls"; import useNavigator from "@saleor/hooks/useNavigator"; @@ -22,7 +21,6 @@ import { productUrl } from "@saleor/products/urls"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages"; import { giftCardsListTableMessages as messages } from "../messages"; import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs"; import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList"; @@ -43,38 +41,6 @@ const GiftCardsListTable: React.FC = () => { const redirectToGiftCardUpdate = (id: string) => () => navigate(giftCardUrl(id)); - const selectGiftCardStatusChip = ({ - isActive, - isExpired - }: { - isActive: boolean; - isExpired: boolean; - }) => { - if (isExpired) { - return ( - - ); - } - - if (!isActive) { - return ( - - ); - } - }; - return ( @@ -83,82 +49,86 @@ const GiftCardsListTable: React.FC = () => { {renderCollection( giftCards, - ({ - id, - displayCode, - usedBy, - usedByEmail, - tag, - isActive, - product, - currentBalance, - isExpired - }) => ( - - - toggle(id)} - /> - - -
- - {intl.formatMessage(messages.codeEndingWithLabel, { - displayCode - })} - - <> - - {selectGiftCardStatusChip({ isActive, isExpired })} - -
-
- - {tag || PLACEHOLDER} - - - {product ? ( - navigate(productUrl(product?.id))}> - {product?.name} - - ) : ( - PLACEHOLDER - )} - - - {usedBy ? ( - navigate(customerUrl(usedBy?.id))}> - {`${usedBy?.firstName} ${usedBy?.lastName}`} - - ) : ( - {usedByEmail || PLACEHOLDER} - )} - - -
- - {currentBalance.currency} - - - {currentBalance.amount} -
-
- - { - event.stopPropagation(); - openDeleteDialog(id); - }} - /> - -
- ), + giftCard => { + const { + id, + displayCode, + usedBy, + usedByEmail, + tag, + product, + currentBalance + } = giftCard; + + return ( + + + toggle(id)} + /> + + +
+ + {intl.formatMessage(messages.codeEndingWithLabel, { + displayCode + })} + + <> + + + +
+
+ + {tag || PLACEHOLDER} + + + {product ? ( + navigate(productUrl(product?.id))}> + {product?.name} + + ) : ( + PLACEHOLDER + )} + + + {usedBy ? ( + navigate(customerUrl(usedBy?.id))}> + {`${usedBy?.firstName} ${usedBy?.lastName}`} + + ) : ( + + {usedByEmail || PLACEHOLDER} + + )} + + +
+ + {currentBalance.currency} + + + {currentBalance.amount} +
+
+ + { + event.stopPropagation(); + openDeleteDialog(id); + }} + /> + +
+ ); + }, () => ( diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts index 1755a4eb391..1a240bc9580 100644 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts +++ b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts @@ -9,6 +9,10 @@ export const bulkEnableDisableSectionMessages = defineMessages({ defaultMessage: "Deactivate", description: "GiftCardEnableDisableSection enable label" }, + deleteLabel: { + defaultMessage: "Delete", + description: "GiftCardEnableDisableSection enable label" + }, successActivateAlertText: { defaultMessage: "Successfully activated gift {count,plural,one{card} other{cards}}", diff --git a/src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx b/src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx index 9b9b3b53bb4..7ef513e31d9 100644 --- a/src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx +++ b/src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx @@ -8,6 +8,7 @@ import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandl import React, { createContext } from "react"; import { + GIFT_CARD_LIST_QUERY, GiftCardListActionParamsEnum, GiftCardListUrlQueryParams } from "../../types"; @@ -67,7 +68,11 @@ const GiftCardListDialogsProvider: React.FC = return ( {children} - + = ({ + customerId +}) => { + const { data, loading } = useCustomerGiftCardQuery({ + variables: { + first: 5, + filter: { + usedBy: [customerId] + } + } + }); + + const giftCards = mapEdgesToItems(data?.giftCards); + + const classes = useCardActionsStyles({ + buttonPosition: giftCards?.length > 0 ? "right" : "left" + }); + + const handleViewAllButton = () => { + // history push to filtered gift cards + }; + + const handleCreateNewCardButton = () => { + // oepn issue new card modal + }; + + return ( + + + + + ) + } + > + + + + + + + + + ); +}; + +export default CustomerGiftCardsCard; diff --git a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx new file mode 100644 index 00000000000..bdc22fcdc61 --- /dev/null +++ b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx @@ -0,0 +1,124 @@ +import CardMenu, { CardMenuItem } from "@saleor/components/CardMenu"; +import { bulkEnableDisableSectionMessages } from "@saleor/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages"; +import { giftCardsListTableMessages } from "@saleor/giftCards/GiftCardsList/messages"; +import useGiftCardActivationDeactivation from "@saleor/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation"; +import { ExtendedGiftCard } from "@saleor/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; +import { GiftCardDetails_giftCard } from "@saleor/giftCards/GiftCardUpdate/types/GiftCardDetails"; +import * as React from "react"; +import { useState } from "react"; +import { FormattedMessage } from "react-intl"; +import { useIntl } from "react-intl"; + +import GiftCardDeleteDialogContent from "../GiftCardDeleteDialog/GiftCardDeleteDialogContent"; +import useGiftCardSingleDelete from "../GiftCardDeleteDialog/useGiftCardSingleDelete"; +import GiftCardStatusChip from "../GiftCardStatusChip/GiftCardStatusChip"; +import { CUSTOMER_GIFT_CARD_LIST_QUERY } from "./queries"; +import { useListWrapperStyles } from "./styles"; +import { CustomerGiftCardList_giftCards_edges_node } from "./types/CustomerGiftCardList"; +import { getGiftCardDisplayCode } from "./utils"; + +interface CustomerGiftCardsCardListItemProps { + giftCard: ExtendedGiftCard; +} + +const CustomerGiftCardsCardListItem: React.FC = ({ + giftCard +}) => { + const intl = useIntl(); + const classes = useListWrapperStyles(); + const [, setIsLoading] = useState(false); + const [openDeleteGiftCard, setOpenDeleteGiftCard] = useState(false); + const { isExpired, isActive } = giftCard; + + const handleActionCompleted = () => setIsLoading(false); + const onGiftCardDeleteDialogClose = () => setOpenDeleteGiftCard(false); + + const { + giftCardActivate, + giftCardDeactivate + } = useGiftCardActivationDeactivation({ + onActivateActionComplete: handleActionCompleted, + onDeactivateActionComplete: handleActionCompleted, + isActive + }); + + const handleGiftCardActivate = () => { + setIsLoading(true); + giftCardActivate({ + variables: { + id: giftCard.id + } + }); + }; + + const handleGiftCardDeactivate = () => { + setIsLoading(true); + giftCardDeactivate({ + variables: { + id: giftCard.id + } + }); + }; + + const handleGiftCardDelete = () => setOpenDeleteGiftCard(true); + + const getMenuItems = (): CardMenuItem[] => { + const items = [ + { + label: intl.formatMessage(bulkEnableDisableSectionMessages.deleteLabel), + onSelect: handleGiftCardDelete + } + ]; + + if (isExpired) { + return items; + } + + const statusButton = isActive + ? { + label: intl.formatMessage( + bulkEnableDisableSectionMessages.disableLabel + ), + onSelect: handleGiftCardDeactivate + } + : { + label: intl.formatMessage( + bulkEnableDisableSectionMessages.enableLabel + ), + onSelect: handleGiftCardActivate + }; + + return [...items, statusButton]; + }; + + const { onDeleteGiftCard, deleteGiftCardOpts } = useGiftCardSingleDelete({ + id: giftCard?.id, + onClose: onGiftCardDeleteDialogClose, + refetchQueries: [CUSTOMER_GIFT_CARD_LIST_QUERY] + }); + + return ( + <> +
+ + + +
+ } + open={openDeleteGiftCard} + onClose={onGiftCardDeleteDialogClose} + onConfirm={onDeleteGiftCard} + confirmButtonState={deleteGiftCardOpts?.status} + /> + + ); +}; + +export default CustomerGiftCardsCardListItem; diff --git a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsList.tsx b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsList.tsx new file mode 100644 index 00000000000..7b3259b5952 --- /dev/null +++ b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsList.tsx @@ -0,0 +1,33 @@ +import CollectionWithDividers from "@saleor/components/CollectionWithDividers"; +import Skeleton from "@saleor/components/Skeleton"; +import { getExtendedGiftCard } from "@saleor/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/utils"; +import React from "react"; + +import CustomerGiftCardsCardListItem from "./CustomerGiftCardsCardListItem"; +import { CustomerGiftCardList_giftCards_edges_node } from "./types/CustomerGiftCardList"; + +interface CustomerGiftCardsListProps { + giftCards: CustomerGiftCardList_giftCards_edges_node[]; + loading: boolean; +} + +const CustomerGiftCardsList: React.FC = ({ + giftCards, + loading +}) => ( + + {!loading && giftCards && ( + ( + + )} + withOuterDividers + /> + )} + +); + +export default CustomerGiftCardsList; diff --git a/src/giftCards/components/GiftCardCustomerCard/messages.ts b/src/giftCards/components/GiftCardCustomerCard/messages.ts new file mode 100644 index 00000000000..b09f80657ca --- /dev/null +++ b/src/giftCards/components/GiftCardCustomerCard/messages.ts @@ -0,0 +1,20 @@ +import { defineMessages } from "react-intl"; + +export const giftCardCustomerCardMessages = defineMessages({ + customerGiftCardsPresentSubtitle: { + defaultMessage: "Only five newest gift cards are shown here", + description: "subtitle" + }, + customerGiftCardsAbsentSubtitle: { + defaultMessage: "There are no gift cards assigned to this customer", + description: "subtitle" + }, + customerGiftCardsViewAllButton: { + defaultMessage: "View All", + description: "button" + }, + customerGiftCardsIssueNewCardButton: { + defaultMessage: "Issue new card", + description: "button" + } +}); diff --git a/src/giftCards/components/GiftCardCustomerCard/queries.ts b/src/giftCards/components/GiftCardCustomerCard/queries.ts new file mode 100644 index 00000000000..f2895ae1e9c --- /dev/null +++ b/src/giftCards/components/GiftCardCustomerCard/queries.ts @@ -0,0 +1,29 @@ +import makeQuery from "@saleor/hooks/makeQuery"; +import gql from "graphql-tag"; + +import { + CustomerGiftCardList, + CustomerGiftCardListVariables +} from "./types/CustomerGiftCardList"; + +const customerGiftCardListQuery = gql` + query CustomerGiftCardList($first: Int, $filter: GiftCardFilterInput) { + giftCards(first: $first, filter: $filter) { + edges { + node { + id + displayCode + expiryDate + isActive + } + } + } + } +`; + +export const useCustomerGiftCardQuery = makeQuery< + CustomerGiftCardList, + CustomerGiftCardListVariables +>(customerGiftCardListQuery); + +export const CUSTOMER_GIFT_CARD_LIST_QUERY = "CustomerGiftCardList"; diff --git a/src/giftCards/components/GiftCardCustomerCard/styles.ts b/src/giftCards/components/GiftCardCustomerCard/styles.ts new file mode 100644 index 00000000000..a33a1ef1bff --- /dev/null +++ b/src/giftCards/components/GiftCardCustomerCard/styles.ts @@ -0,0 +1,32 @@ +import { makeStyles } from "@material-ui/core"; + +interface CustomerGiftCardsCardActionsProps { + buttonPosition: "left" | "right"; +} + +export const useCardActionsStyles = makeStyles( + theme => ({ + cardActions: { + padding: `${theme.spacing(2)} ${theme.spacing(3)}`, + flexDirection: ({ buttonPosition }: CustomerGiftCardsCardActionsProps) => + buttonPosition === "left" ? "row" : "row-reverse" + } + }), + { name: "CustomerGiftCardsCard" } +); + +export const useListWrapperStyles = makeStyles( + theme => ({ + listingWrapper: () => ({ + display: "grid", + gridTemplateColumns: "max-content 1fr min-content", + margin: `${theme.spacing(2)} ${theme.spacing(3)}`, + alignItems: "center", + justifyItems: "center" + }), + listingMenu: { + gridColumn: "3" + } + }), + { name: "CustomerGiftCardListCard" } +); diff --git a/src/giftCards/components/GiftCardCustomerCard/types/CustomerGiftCardList.ts b/src/giftCards/components/GiftCardCustomerCard/types/CustomerGiftCardList.ts new file mode 100644 index 00000000000..f157bc3dd9e --- /dev/null +++ b/src/giftCards/components/GiftCardCustomerCard/types/CustomerGiftCardList.ts @@ -0,0 +1,37 @@ +/* tslint:disable */ +/* eslint-disable */ +// @generated +// This file was automatically generated and should not be edited. + +import { GiftCardFilterInput } from "./../../../../types/globalTypes"; + +// ==================================================== +// GraphQL query operation: CustomerGiftCardList +// ==================================================== + +export interface CustomerGiftCardList_giftCards_edges_node { + __typename: "GiftCard"; + id: string; + displayCode: string; + expiryDate: any | null; + isActive: boolean; +} + +export interface CustomerGiftCardList_giftCards_edges { + __typename: "GiftCardCountableEdge"; + node: CustomerGiftCardList_giftCards_edges_node; +} + +export interface CustomerGiftCardList_giftCards { + __typename: "GiftCardCountableConnection"; + edges: CustomerGiftCardList_giftCards_edges[]; +} + +export interface CustomerGiftCardList { + giftCards: CustomerGiftCardList_giftCards | null; +} + +export interface CustomerGiftCardListVariables { + first?: number | null; + filter?: GiftCardFilterInput | null; +} diff --git a/src/giftCards/components/GiftCardCustomerCard/utils.ts b/src/giftCards/components/GiftCardCustomerCard/utils.ts new file mode 100644 index 00000000000..98f67729018 --- /dev/null +++ b/src/giftCards/components/GiftCardCustomerCard/utils.ts @@ -0,0 +1,7 @@ +import { ExtendedGiftCard } from "@saleor/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; + +import { CustomerGiftCardList_giftCards_edges_node } from "./types/CustomerGiftCardList"; + +export const getGiftCardDisplayCode = ( + giftCard: ExtendedGiftCard +) => giftCard.displayCode.slice(4, giftCard.displayCode.length); diff --git a/src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx b/src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx index de63ff846a1..32bc116c085 100644 --- a/src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx +++ b/src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx @@ -12,7 +12,15 @@ import GiftCardDeleteDialogContent, { import useGiftCardBulkDelete from "./useGiftCardBulkDelete"; import useGiftCardSingleDelete from "./useGiftCardSingleDelete"; -const GiftCardDeleteDialog: React.FC = ({ open, onClose }) => { +interface GiftCardDeleteDialogProps extends DialogProps { + refetchQueries?: string[]; +} + +const GiftCardDeleteDialog: React.FC = ({ + open, + onClose, + refetchQueries = [] +}) => { const giftCardBulkActionsProps = useGiftCardListBulkActions(); const { selectedItemsCount } = giftCardBulkActionsProps; @@ -25,7 +33,7 @@ const GiftCardDeleteDialog: React.FC = ({ open, onClose }) => { const { onDeleteGiftCard, deleteGiftCardOpts } = useGiftCardSingleDelete({ id, onClose, - refetchQueries: [GIFT_CARD_LIST_QUERY] + refetchQueries: [GIFT_CARD_LIST_QUERY, ...refetchQueries] }); const { @@ -33,7 +41,7 @@ const GiftCardDeleteDialog: React.FC = ({ open, onClose }) => { bulkDeleteGiftCardOpts } = useGiftCardBulkDelete({ onClose, - refetchQueries: [GIFT_CARD_LIST_QUERY] + refetchQueries: [GIFT_CARD_LIST_QUERY, ...refetchQueries] }); const dialogProps: Pick< diff --git a/src/giftCards/components/GiftCardStatusChip/GiftCardStatusChip.tsx b/src/giftCards/components/GiftCardStatusChip/GiftCardStatusChip.tsx new file mode 100644 index 00000000000..6443c4355be --- /dev/null +++ b/src/giftCards/components/GiftCardStatusChip/GiftCardStatusChip.tsx @@ -0,0 +1,50 @@ +import StatusChip from "@saleor/components/StatusChip"; +import { StatusType } from "@saleor/components/StatusChip/types"; +import { + ExtendedGiftCard, + GiftCardBase +} from "@saleor/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; +import React from "react"; +import { useIntl } from "react-intl"; + +import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages"; + +interface GiftCardStatusChipProps< + T extends ExtendedGiftCard +> { + giftCard: T; +} + +function GiftCardStatusChip< + T extends ExtendedGiftCard +>({ giftCard }: GiftCardStatusChipProps) { + const { isExpired, isActive } = giftCard; + const intl = useIntl(); + + const getGiftCardData = () => { + if (isExpired) { + return { + status: StatusType.NEUTRAL, + label: intl.formatMessage(giftCardStatusChipMessages.expiredStatusLabel) + }; + } + + if (!isActive) { + return { + status: StatusType.ERROR, + label: intl.formatMessage( + giftCardStatusChipMessages.disabledStatusLabel + ) + }; + } + + return { + status: null, + label: null + }; + }; + + return ; +} + +export default GiftCardStatusChip; diff --git a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx index a8eb8e53e3d..e4f3ef76369 100644 --- a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx +++ b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx @@ -8,6 +8,7 @@ import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; import Metadata, { MetadataFormData } from "@saleor/components/Metadata"; import PageHeader from "@saleor/components/PageHeader"; +import PageTitleWithStatusChip from "@saleor/components/PageTitleWithStatusChip"; import Savebar from "@saleor/components/Savebar"; import Skeleton from "@saleor/components/Skeleton"; import { SubmitPromise } from "@saleor/hooks/useForm"; @@ -35,8 +36,8 @@ import OrderFulfilledProductsCard from "../OrderFulfilledProductsCard"; import OrderHistory, { FormData as HistoryFormData } from "../OrderHistory"; import OrderInvoiceList from "../OrderInvoiceList"; import OrderPayment from "../OrderPayment/OrderPayment"; +import OrderStatusChip from "../OrderStatusChip/OrderStatusChip"; import OrderUnfulfilledProductsCard from "../OrderUnfulfilledProductsCard"; -import Title from "./Title"; import { filteredConditionalItems, hasAnyItemsReplaceable } from "./utils"; const useStyles = makeStyles( @@ -223,7 +224,11 @@ const OrderDetailsPage: React.FC = props => { } + title={ + + + + } > diff --git a/src/orders/components/OrderDetailsPage/Title.tsx b/src/orders/components/OrderStatusChip/OrderStatusChip.tsx similarity index 53% rename from src/orders/components/OrderDetailsPage/Title.tsx rename to src/orders/components/OrderStatusChip/OrderStatusChip.tsx index cd9d78f3d12..643f2787777 100644 --- a/src/orders/components/OrderDetailsPage/Title.tsx +++ b/src/orders/components/OrderStatusChip/OrderStatusChip.tsx @@ -1,14 +1,14 @@ -import PageTitleWithStatusChip from "@saleor/components/PageTitleWithStatusChip"; +import StatusChip from "@saleor/components/StatusChip"; import { transformOrderStatus } from "@saleor/misc"; import { OrderDetails_order } from "@saleor/orders/types/OrderDetails"; import React from "react"; import { useIntl } from "react-intl"; -interface TitleProps { +interface OrderStatusChipProps { order?: OrderDetails_order; } -const Title: React.FC = ({ order }) => { +const OrderStatusChip: React.FC = ({ order }) => { const intl = useIntl(); if (!order) { @@ -17,13 +17,7 @@ const Title: React.FC = ({ order }) => { const { localized, status } = transformOrderStatus(order.status, intl); - return ( - - ); + return ; }; -export default Title; +export default OrderStatusChip; diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 2136be5e65d..5046fab91d3 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -77292,6 +77292,57 @@ exports[`Storyshots Views / Customers / Customer details default 1`] = ` +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -78023,6 +78074,57 @@ exports[`Storyshots Views / Customers / Customer details different addresses 1`] +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -78729,6 +78831,57 @@ exports[`Storyshots Views / Customers / Customer details form errors 1`] = ` +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -79344,6 +79497,57 @@ exports[`Storyshots Views / Customers / Customer details loading 1`] = ` +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -80029,6 +80233,57 @@ exports[`Storyshots Views / Customers / Customer details never logged 1`] = ` +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -80714,6 +80969,57 @@ exports[`Storyshots Views / Customers / Customer details never placed order 1`] +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -81377,6 +81683,57 @@ exports[`Storyshots Views / Customers / Customer details no address at all 1`] = +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -82068,6 +82425,57 @@ exports[`Storyshots Views / Customers / Customer details no default billing addr +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -82759,6 +83167,57 @@ exports[`Storyshots Views / Customers / Customer details no default shipping add +
+
+
+ + Gift Cards + +
+
+
+ There are no gift cards assigned to this customer +
+
+
+ + ‌ + +
+ +
+
@@ -120653,10 +121112,10 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Cancelled
@@ -122599,10 +123058,10 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -124742,10 +125201,10 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Fulfilled
@@ -126876,7 +127335,15 @@ exports[`Storyshots Views / Orders / Order details loading 1`] = ` >
+ > +
+
+
+
@@ -127481,10 +127948,10 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -129624,10 +130091,10 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -131767,10 +132234,10 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -133902,10 +134369,10 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -136045,10 +136512,10 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -138188,10 +138655,10 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -140331,10 +140798,10 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -142474,10 +142941,10 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -144617,10 +145084,10 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Partially fulfilled
@@ -146760,10 +147227,10 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="HorizontalSpacer-container-id HorizontalSpacer-container-id" />
Unfulfilled
From 08ae0e5868bddf060603cf3c5b2c0a708e1f8b7e Mon Sep 17 00:00:00 2001 From: Wojciech Mista Date: Tue, 12 Oct 2021 13:23:44 +0300 Subject: [PATCH 02/17] Add loading button to CardMenu component (#1476) * WIP * WIP * Add gift card card to customer page * Fix status chip header * Fix naming scheme * Add currentOpts to act/deactivate gift cards hook * Remove unused lines of code * Revert style change * Tests and messages * Fix card list rendering * Type fix * Code review fixes * Review changes * Scripts * Add loading animation to card menu buttons * Added default messages * Change conditional prop checking to filtering --- locale/defaultMessages.json | 3 + src/components/CardMenu/CardMenu.tsx | 68 ++++++++++++++++--- .../PageTitleWithStatusChip.tsx | 1 - .../CustomerGiftCardsCardListItem.tsx | 14 +++- 4 files changed, 71 insertions(+), 15 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index e931d7279ec..121abb30e3a 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1971,6 +1971,9 @@ "context": "unassign multiple attributes from item", "string": "{counter,plural,one{Are you sure you want to unassign this attribute from {itemTypeName}?} other{Are you sure you want to unassign {attributeQuantity} attributes from {itemTypeName}?}}" }, + "src_dot_components_dot_CardMenu_dot_4078124637": { + "string": "working..." + }, "src_dot_components_dot_ChannelsAvailabilityCard_dot_3326160357": { "context": "section header", "string": "Availability" diff --git a/src/components/CardMenu/CardMenu.tsx b/src/components/CardMenu/CardMenu.tsx index 21b100a2d54..a45cde1d17c 100644 --- a/src/components/CardMenu/CardMenu.tsx +++ b/src/components/CardMenu/CardMenu.tsx @@ -1,23 +1,30 @@ import { + CircularProgress, ClickAwayListener, Grow, IconButton, MenuItem, MenuList, Paper, - Popper + Popper, + Typography } from "@material-ui/core"; import MoreVertIcon from "@material-ui/icons/MoreVert"; import { makeStyles } from "@saleor/macaw-ui"; -import React from "react"; +import classNames from "classnames"; +import * as React from "react"; +import { useEffect, useRef, useState } from "react"; +import { FormattedMessage } from "react-intl"; const ITEM_HEIGHT = 48; - export interface CardMenuItem { disabled?: boolean; label: string; testId?: string; onSelect: () => void; + loading?: boolean; + withLoading?: boolean; + hasError?: boolean; } export interface CardMenuProps { @@ -42,6 +49,14 @@ const useStyles = makeStyles( marginTop: theme.spacing(2), maxHeight: ITEM_HEIGHT * 4.5, overflowY: "scroll" + }, + loadingContent: { + width: "100%", + display: "grid", + gridTemplateColumns: "1fr 24px", + gap: "16px", + alignItems: "center", + justifyContent: "flex-end" } }), { name: "CardMenu" } @@ -51,8 +66,8 @@ const CardMenu: React.FC = props => { const { className, disabled, menuItems, ...rest } = props; const classes = useStyles(props); - const anchorRef = React.useRef(null); - const [open, setOpen] = React.useState(false); + const anchorRef = useRef(null); + const [open, setOpen] = useState(false); const handleToggle = () => setOpen(prevOpen => !prevOpen); @@ -74,8 +89,8 @@ const CardMenu: React.FC = props => { } }; - const prevOpen = React.useRef(open); - React.useEffect(() => { + const prevOpen = useRef(open); + useEffect(() => { if (prevOpen.current === true && open === false) { anchorRef.current!.focus(); } @@ -83,11 +98,27 @@ const CardMenu: React.FC = props => { prevOpen.current = open; }, [open]); + useEffect(() => { + const hasAnyItemsLoadingOrWithError = menuItems + ?.filter(({ withLoading }) => withLoading) + ?.some(({ loading, hasError }) => loading || hasError); + + if (!hasAnyItemsLoadingOrWithError) { + setOpen(false); + } + }, [menuItems]); + const handleMenuClick = (index: number) => { - menuItems[index].onSelect(); - setOpen(false); + const selectedItem = menuItems[index]; + selectedItem.onSelect(); + + if (!selectedItem.withLoading) { + setOpen(false); + } }; + const isWithLoading = menuItems.some(({ withLoading }) => withLoading); + return (
= props => { {menuItems.map((menuItem, menuItemIndex) => ( handleMenuClick(menuItemIndex)} key={menuItem.label} data-test={menuItem.testId} > - {menuItem.label} +
+ {menuItem.loading ? ( + <> + + + + + + ) : ( + menuItem.label + )} +
))} diff --git a/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx b/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx index e91ce223d3d..6aed1c87879 100644 --- a/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx +++ b/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx @@ -1,7 +1,6 @@ import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer"; import { makeStyles } from "@saleor/macaw-ui"; import React from "react"; - export interface PageTitleWithStatusChipProps { title: string; } diff --git a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx index bdc22fcdc61..23a65c630a1 100644 --- a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx +++ b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx @@ -35,7 +35,9 @@ const CustomerGiftCardsCardListItem: React.FC Date: Tue, 19 Oct 2021 11:39:32 +0200 Subject: [PATCH 03/17] Issue gift card in customer page (#1468) * WIP * WIP * Replace typed query with make query * Add customer details context to customer page * Add context to customer gift cards * Disable customer select when initial customer is present * Pass initial customer to create gift card form * Fixes after cherry-pick * Code cleanup * Remove getInitialData function * Remove unused package * Remove new line --- package-lock.json | 17 -- .../CustomerDetailsPage.tsx | 2 +- .../providers/CustomerDetailsProvider.tsx | 40 +++++ src/customers/queries.ts | 3 +- src/customers/views/CustomerDetails.tsx | 139 ++-------------- .../views/CustomerDetailsContent.tsx | 150 ++++++++++++++++++ .../GiftCardCreateDialog.tsx | 4 +- .../GiftCardCreateDialogForm.tsx | 15 +- .../GiftCardCustomerSelectField.tsx | 5 +- .../CustomerGiftCardsCard.tsx | 110 ++++++++----- .../GiftCardSendToCustomer.tsx | 6 +- src/utils/handlers/dialogActionHandlers.ts | 1 + 12 files changed, 299 insertions(+), 193 deletions(-) create mode 100644 src/customers/providers/CustomerDetailsProvider.tsx create mode 100644 src/customers/views/CustomerDetailsContent.tsx diff --git a/package-lock.json b/package-lock.json index e97e3266785..b2cd5ae7100 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6284,23 +6284,6 @@ "requires": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" - }, - "dependencies": { - "@types/react": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz", - "integrity": "sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "csstype": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", - "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" - } } }, "@types/html-minifier-terser": { diff --git a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx index fd1b5a0d5b6..f5412278e20 100644 --- a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx +++ b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx @@ -119,7 +119,7 @@ const CustomerDetailsPage: React.FC = ({ - +
(null); + +export const CustomerDetailsProvider: React.FC = ({ + children, + id +}) => { + const { data, loading } = useCustomerDetails({ + displayLoader: true, + variables: { + id + } + }); + + const providerValues: CustomerDetailsConsumerProps = { + customer: data, + loading + }; + + return ( + + {children} + + ); +}; diff --git a/src/customers/queries.ts b/src/customers/queries.ts index ea45c37a96f..803d430817d 100644 --- a/src/customers/queries.ts +++ b/src/customers/queries.ts @@ -90,7 +90,8 @@ const customerDetails = gql` } } `; -export const TypedCustomerDetailsQuery = TypedQuery< + +export const useCustomerDetails = makeQuery< CustomerDetails, CustomerDetailsVariables >(customerDetails); diff --git a/src/customers/views/CustomerDetails.tsx b/src/customers/views/CustomerDetails.tsx index 8ea5080c701..476d1cfcbb1 100644 --- a/src/customers/views/CustomerDetails.tsx +++ b/src/customers/views/CustomerDetails.tsx @@ -1,36 +1,18 @@ -import { DialogContentText } from "@material-ui/core"; -import ActionDialog from "@saleor/components/ActionDialog"; -import NotFoundPage from "@saleor/components/NotFoundPage"; -import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; -import { getStringOrPlaceholder } from "@saleor/misc"; -import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; -import { - useMetadataUpdate, - usePrivateMetadataUpdate -} from "@saleor/utils/metadata/updateMetadata"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { useIntl } from "react-intl"; -import { orderListUrl, orderUrl } from "../../orders/urls"; -import CustomerDetailsPage, { - CustomerDetailsPageFormData -} from "../components/CustomerDetailsPage/CustomerDetailsPage"; import { TypedRemoveCustomerMutation, TypedUpdateCustomerMutation } from "../mutations"; -import { TypedCustomerDetailsQuery } from "../queries"; +import { CustomerDetailsProvider } from "../providers/CustomerDetailsProvider"; import { RemoveCustomer } from "../types/RemoveCustomer"; import { UpdateCustomer } from "../types/UpdateCustomer"; -import { - customerAddressesUrl, - customerListUrl, - customerUrl, - CustomerUrlQueryParams -} from "../urls"; +import { customerListUrl, CustomerUrlQueryParams } from "../urls"; +import { CustomerDetailsContent } from "./CustomerDetailsContent"; interface CustomerDetailsViewProps { id: string; @@ -75,107 +57,18 @@ export const CustomerDetailsView: React.FC = ({ {(removeCustomer, removeCustomerOpts) => ( {(updateCustomer, updateCustomerOpts) => ( - - {customerDetails => { - const user = customerDetails.data?.user; - - if (user === null) { - return ; - } - - const [updateMetadata] = useMetadataUpdate({}); - const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); - - const updateData = async ( - data: CustomerDetailsPageFormData - ) => { - const result = await updateCustomer({ - variables: { - id, - input: { - email: data.email, - firstName: data.firstName, - isActive: data.isActive, - lastName: data.lastName, - note: data.note - } - } - }); - - return result.data.customerUpdate.errors; - }; - - const handleSubmit = createMetadataUpdateHandler( - user, - updateData, - variables => updateMetadata({ variables }), - variables => updatePrivateMetadata({ variables }) - ); - - return ( - <> - - - navigate(customerAddressesUrl(id)) - } - onBack={handleBack} - onRowClick={id => navigate(orderUrl(id))} - onSubmit={handleSubmit} - onDelete={() => - navigate( - customerUrl(id, { - action: "remove" - }) - ) - } - onViewAllOrdersClick={() => - navigate( - orderListUrl({ - customer: user?.email - }) - ) - } - /> - navigate(customerUrl(id), true)} - onConfirm={() => removeCustomer()} - title={intl.formatMessage({ - defaultMessage: "Delete Customer", - description: "dialog header" - })} - variant="delete" - open={params.action === "remove"} - > - - - {getStringOrPlaceholder(user?.email)} - - ) - }} - /> - - - - ); - }} - + + + )} )} diff --git a/src/customers/views/CustomerDetailsContent.tsx b/src/customers/views/CustomerDetailsContent.tsx new file mode 100644 index 00000000000..231b20ed578 --- /dev/null +++ b/src/customers/views/CustomerDetailsContent.tsx @@ -0,0 +1,150 @@ +import { DialogContentText } from "@material-ui/core"; +import ActionDialog from "@saleor/components/ActionDialog"; +import NotFoundPage from "@saleor/components/NotFoundPage"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import { UseNavigatorResult } from "@saleor/hooks/useNavigator"; +import { getStringOrPlaceholder } from "@saleor/misc"; +import { MutationResultAdditionalProps } from "@saleor/types"; +import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; +import { + useMetadataUpdate, + usePrivateMetadataUpdate +} from "@saleor/utils/metadata/updateMetadata"; +import React, { useContext } from "react"; +import { MutationFunction, MutationResult } from "react-apollo"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { orderListUrl, orderUrl } from "../../orders/urls"; +import CustomerDetailsPage, { + CustomerDetailsPageFormData +} from "../components/CustomerDetailsPage/CustomerDetailsPage"; +import { CustomerDetailsContext } from "../providers/CustomerDetailsProvider"; +import { + RemoveCustomer, + RemoveCustomerVariables +} from "../types/RemoveCustomer"; +import { + UpdateCustomer, + UpdateCustomerVariables +} from "../types/UpdateCustomer"; +import { + customerAddressesUrl, + customerUrl, + CustomerUrlQueryParams +} from "../urls"; + +export interface CustomerDetailsContentProps { + handleBack: () => void; + updateCustomer: MutationFunction; + removeCustomer: MutationFunction; + id: string; + updateCustomerOpts: MutationResult & + MutationResultAdditionalProps; + removeCustomerOpts: MutationResult & + MutationResultAdditionalProps; + navigate: UseNavigatorResult; + params: CustomerUrlQueryParams; +} + +export const CustomerDetailsContent: React.FC = ({ + handleBack, + updateCustomer, + id, + updateCustomerOpts, + removeCustomerOpts, + navigate, + removeCustomer, + params +}) => { + const customerDetails = useContext(CustomerDetailsContext); + const user = customerDetails?.customer?.user; + const customerDetailsLoading = customerDetails?.loading; + + const intl = useIntl(); + + if (user === null) { + return ; + } + + const [updateMetadata] = useMetadataUpdate({}); + const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); + + const updateData = async (data: CustomerDetailsPageFormData) => { + const result = await updateCustomer({ + variables: { + id, + input: { + email: data.email, + firstName: data.firstName, + isActive: data.isActive, + lastName: data.lastName, + note: data.note + } + } + }); + + return result.data.customerUpdate.errors; + }; + + const handleSubmit = createMetadataUpdateHandler( + user, + updateData, + variables => updateMetadata({ variables }), + variables => updatePrivateMetadata({ variables }) + ); + + return ( + <> + + navigate(customerAddressesUrl(id))} + onBack={handleBack} + onRowClick={id => navigate(orderUrl(id))} + onSubmit={handleSubmit} + onDelete={() => + navigate( + customerUrl(id, { + action: "remove" + }) + ) + } + onViewAllOrdersClick={() => + navigate( + orderListUrl({ + customer: user?.email + }) + ) + } + /> + navigate(customerUrl(id), true)} + onConfirm={() => removeCustomer()} + title={intl.formatMessage({ + defaultMessage: "Delete Customer", + description: "dialog header" + })} + variant="delete" + open={params.action === "remove"} + > + + {getStringOrPlaceholder(user?.email)} + }} + /> + + + + ); +}; diff --git a/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialog.tsx b/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialog.tsx index 6f591b0915c..309e34d7130 100644 --- a/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialog.tsx +++ b/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialog.tsx @@ -26,7 +26,8 @@ interface GiftCardCreateDialogProps extends DialogProps { const GiftCardCreateDialog: React.FC = ({ onClose, open, - refetchQueries + refetchQueries, + initialCustomer }) => { const intl = useIntl(); const notify = useNotifier(); @@ -121,6 +122,7 @@ const GiftCardCreateDialog: React.FC = ({ onClose={handleClose} apiErrors={createGiftCardOpts?.data?.giftCardCreate?.errors} onSubmit={handleSubmit} + initialCustomer={initialCustomer} /> ))} diff --git a/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialogForm.tsx b/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialogForm.tsx index 5fdc57fb36a..07b76984879 100644 --- a/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialogForm.tsx +++ b/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialogForm.tsx @@ -36,8 +36,6 @@ export interface GiftCardCreateFormData extends GiftCardCreateCommonFormData { channelSlug?: string; } -const initialCustomer = { email: "", name: "" }; - export const initialData: GiftCardCreateFormData = { tag: "", balanceAmount: 1, @@ -51,19 +49,22 @@ export const initialData: GiftCardCreateFormData = { expiryPeriodAmount: 12, requiresActivation: true }; - interface GiftCardCreateDialogFormProps { opts: { status: ConfirmButtonTransitionState }; apiErrors: GiftCardError[]; onSubmit: (data: GiftCardCreateFormData) => void; onClose: () => void; + initialCustomer?: GiftCardCreateFormCustomer | null; } +const defaultInitialCustomer = { email: "", name: "" }; + const GiftCardCreateDialogForm: React.FC = ({ onSubmit, opts, onClose, - apiErrors + apiErrors, + initialCustomer }) => { const intl = useIntl(); const classes = useStyles({}); @@ -75,7 +76,7 @@ const GiftCardCreateDialogForm: React.FC = ({ const [selectedCustomer, setSelectedCustomer] = useState< GiftCardCreateFormCustomer - >(initialCustomer); + >(initialCustomer || defaultInitialCustomer); const handleSubmit = (data: GiftCardCreateFormData) => onSubmit({ ...data, selectedCustomer }); @@ -103,7 +104,8 @@ const GiftCardCreateDialogForm: React.FC = ({ ...initialData, ...getInitialExpirySettingsData(), balanceCurrency: "", - channelSlug: "" + channelSlug: "", + sendToCustomerSelected: !!initialCustomer }, handleSubmit ); @@ -161,6 +163,7 @@ const GiftCardCreateDialogForm: React.FC = ({ sendToCustomerSelected={sendToCustomerSelected} selectedCustomer={selectedCustomer} setSelectedCustomer={setSelectedCustomer} + disabled={!!initialCustomer} /> diff --git a/src/giftCards/GiftCardCreateDialog/GiftCardCustomerSelectField.tsx b/src/giftCards/GiftCardCreateDialog/GiftCardCustomerSelectField.tsx index 69a91ba5bd7..8a0f2b7493e 100644 --- a/src/giftCards/GiftCardCreateDialog/GiftCardCustomerSelectField.tsx +++ b/src/giftCards/GiftCardCreateDialog/GiftCardCustomerSelectField.tsx @@ -13,11 +13,13 @@ import { GiftCardCreateFormCustomer } from "./types"; export interface GiftCardCustomerSelectFieldProps { selectedCustomer: GiftCardCreateFormCustomer; setSelectedCustomer: (customer: GiftCardCreateFormCustomer) => void; + disabled?: boolean; } const GiftCardCustomerSelectField: React.FC = ({ selectedCustomer, - setSelectedCustomer + setSelectedCustomer, + disabled = false }) => { const intl = useIntl(); @@ -54,6 +56,7 @@ const GiftCardCustomerSelectField: React.FC = fetchChoices={search} onChange={handleSelect} onFetchMore={loadMore} + disabled={disabled} /> ); }; diff --git a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx index 3b11703d0ff..20e714e6982 100644 --- a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx +++ b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx @@ -1,30 +1,38 @@ import { Button, Card, CardActions } from "@material-ui/core"; import VerticalSpacer from "@saleor/apps/components/VerticalSpacer"; import CardTitle from "@saleor/components/CardTitle"; +import { CustomerDetailsContext } from "@saleor/customers/providers/CustomerDetailsProvider"; +import GiftCardCreateDialog from "@saleor/giftCards/GiftCardCreateDialog/GiftCardCreateDialog"; +import { getFullName } from "@saleor/misc"; import { mapEdgesToItems } from "@saleor/utils/maps"; -import React from "react"; +import * as React from "react"; +import { useContext } from "react"; +import { useState } from "react"; import { FormattedMessage } from "react-intl"; import CustomerGiftCardsList from "./CustomerGiftCardsList"; import { giftCardCustomerCardMessages as messages } from "./messages"; -import { useCustomerGiftCardQuery } from "./queries"; +import { + CUSTOMER_GIFT_CARD_LIST_QUERY, + useCustomerGiftCardQuery +} from "./queries"; import { useCardActionsStyles } from "./styles"; -interface CustomerGiftCardsCardProps { - customerId?: string | null; -} +const CustomerGiftCardsCard: React.FC = () => { + const customerDetails = useContext(CustomerDetailsContext); + const customer = customerDetails?.customer?.user; + const id = customer?.id; -const CustomerGiftCardsCard: React.FC = ({ - customerId -}) => { const { data, loading } = useCustomerGiftCardQuery({ variables: { first: 5, filter: { - usedBy: [customerId] + usedBy: [id] } } }); + const [openCreateDialog, setOpenCreateDialog] = useState(false); + const closeCreateDialog = () => setOpenCreateDialog(false); const giftCards = mapEdgesToItems(data?.giftCards); @@ -37,43 +45,61 @@ const CustomerGiftCardsCard: React.FC = ({ }; const handleCreateNewCardButton = () => { - // oepn issue new card modal + setOpenCreateDialog(true); }; return ( - - - - - ) - } - > - - - - - - + ) + } > - - - - + + + + + + + + + + ); }; diff --git a/src/giftCards/components/GiftCardSendToCustomer/GiftCardSendToCustomer.tsx b/src/giftCards/components/GiftCardSendToCustomer/GiftCardSendToCustomer.tsx index d3844297d4f..b1441229334 100644 --- a/src/giftCards/components/GiftCardSendToCustomer/GiftCardSendToCustomer.tsx +++ b/src/giftCards/components/GiftCardSendToCustomer/GiftCardSendToCustomer.tsx @@ -18,6 +18,7 @@ interface GiftCardSendToCustomerProps { sendToCustomerSelected: boolean; selectedCustomer: GiftCardCreateFormCustomer; setSelectedCustomer: (customer: GiftCardCreateFormCustomer) => void; + disabled?: boolean; } const GiftCardSendToCustomer: React.FC = ({ @@ -25,7 +26,8 @@ const GiftCardSendToCustomer: React.FC = ({ sendToCustomerSelected, selectedChannelSlug, selectedCustomer, - setSelectedCustomer + setSelectedCustomer, + disabled = false }) => { const { channel, availableChannels } = useAppChannel(false); @@ -46,6 +48,7 @@ const GiftCardSendToCustomer: React.FC = ({ label={intl.formatMessage(messages.sendToCustomerSelectedLabel)} checked={sendToCustomerSelected} onChange={change} + disabled={disabled} /> {sendToCustomerSelected && ( <> @@ -53,6 +56,7 @@ const GiftCardSendToCustomer: React.FC = ({
diff --git a/src/components/CardMenu/messages.ts b/src/components/CardMenu/messages.ts new file mode 100644 index 00000000000..3fc26f439af --- /dev/null +++ b/src/components/CardMenu/messages.ts @@ -0,0 +1,8 @@ +import { defineMessages } from "react-intl"; + +export const cardMenuMessages = defineMessages({ + cardMenuItemLoading: { + defaultMessage: "working...", + description: "menu item loading" + } +}); diff --git a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx index 40f5d227771..8fbb2240e6d 100644 --- a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx +++ b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard.tsx @@ -1,16 +1,18 @@ import { Button, Card, CardActions } from "@material-ui/core"; import VerticalSpacer from "@saleor/apps/components/VerticalSpacer"; import CardTitle from "@saleor/components/CardTitle"; -import { CustomerDetailsContext } from "@saleor/customers/providers/CustomerDetailsProvider"; +import CollectionWithDividers from "@saleor/components/CollectionWithDividers"; +import Skeleton from "@saleor/components/Skeleton"; import GiftCardCreateDialog from "@saleor/giftCards/GiftCardCreateDialog/GiftCardCreateDialog"; +import { getExtendedGiftCard } from "@saleor/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/utils"; import { getFullName } from "@saleor/misc"; import { mapEdgesToItems } from "@saleor/utils/maps"; import * as React from "react"; -import { useContext } from "react"; import { useState } from "react"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; -import CustomerGiftCardsList from "./CustomerGiftCardsList"; +import CustomerGiftCardsCardListItem from "./CustomerGiftCardsCardListItem"; +import { useCustomerDetails } from "./hooks/useCustomerDetails"; import { giftCardCustomerCardMessages as messages } from "./messages"; import { CUSTOMER_GIFT_CARD_LIST_QUERY, @@ -19,7 +21,9 @@ import { import { useCardActionsStyles } from "./styles"; const CustomerGiftCardsCard: React.FC = () => { - const customerDetails = useContext(CustomerDetailsContext); + const intl = useIntl(); + const [openCreateDialog, setOpenCreateDialog] = useState(false); + const customerDetails = useCustomerDetails(); const customer = customerDetails?.customer?.user; const id = customer?.id; @@ -31,7 +35,7 @@ const CustomerGiftCardsCard: React.FC = () => { } } }); - const [openCreateDialog, setOpenCreateDialog] = useState(false); + const closeCreateDialog = () => setOpenCreateDialog(false); const giftCards = mapEdgesToItems(data?.giftCards); @@ -52,7 +56,7 @@ const CustomerGiftCardsCard: React.FC = () => { <> { /> - + + {!loading && giftCards && ( + ( + + )} + withOuterDividers + /> + )} +
@@ -175594,19 +175599,24 @@ exports[`Storyshots Views / Plugins / Plugin details form errors 1`] = `
-
+
- channel 1 +
+ channel 1 +
+
@@ -175919,19 +175929,24 @@ exports[`Storyshots Views / Plugins / Plugin details not configurable 1`] = `
-
+
- channel 1 +
+ channel 1 +
+
From b5848afe3ab39174bc67f269c8a516ab2cca182f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Mi=C5=9Bta?= Date: Mon, 25 Oct 2021 12:03:07 +0200 Subject: [PATCH 15/17] Make PageTitleWithStatusChip use ExtendedPageHeader --- .../ExtendedPageHeader/ExtendedPageHeader.tsx | 15 +++++++++++++-- .../PageTitleWithStatusChip.tsx | 16 +++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx b/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx index d601066dfdf..6c4dacfd44f 100644 --- a/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx +++ b/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx @@ -54,6 +54,7 @@ const useStyles = makeStyles( interface ExtendedPageHeaderProps { children?: React.ReactNode; className?: string; + childrenWrapperClassName?: string; inline?: boolean; underline?: boolean; title?: React.ReactNode; @@ -61,7 +62,15 @@ interface ExtendedPageHeaderProps { } const ExtendedPageHeader: React.FC = props => { - const { children, className, inline, underline, title, testId } = props; + const { + children, + className, + childrenWrapperClassName, + inline, + underline, + title, + testId + } = props; const classes = useStyles(props); @@ -75,7 +84,9 @@ const ExtendedPageHeader: React.FC = props => { })} > {title} -
{children}
+
+ {children} +
{underline && (
diff --git a/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx b/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx index 6aed1c87879..adbc0ac311d 100644 --- a/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx +++ b/src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx @@ -1,6 +1,8 @@ import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer"; import { makeStyles } from "@saleor/macaw-ui"; import React from "react"; + +import ExtendedPageHeader from "../ExtendedPageHeader"; export interface PageTitleWithStatusChipProps { title: string; } @@ -22,11 +24,15 @@ const PageTitleWithStatusChip: React.FC = ({ const classes = useStyles({}); return ( -
- {title} - - {children} -
+ + <> + + {children} + + ); }; From 10478b59f6430adf95c66814c774aec39c56222f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Mi=C5=9Bta?= Date: Mon, 25 Oct 2021 12:24:37 +0200 Subject: [PATCH 16/17] Update tests --- .../__snapshots__/Stories.test.ts.snap | 244 +++++++++++------- 1 file changed, 150 insertions(+), 94 deletions(-) diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 6f75a5974fc..f105acb212f 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -121105,19 +121105,23 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Cancelled +
+ Cancelled +
@@ -123051,19 +123055,23 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -125194,19 +125202,23 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Fulfilled +
+ Fulfilled +
@@ -127337,11 +127349,15 @@ exports[`Storyshots Views / Orders / Order details loading 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
+ class="ExtendedPageHeader-action-id OrderDetailsPageTitleWithStatusChip-container-id" + > +
+
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -130084,19 +130104,23 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -132227,19 +132251,23 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -134362,19 +134390,23 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -136505,19 +136537,23 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -138648,19 +138684,23 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -140791,19 +140831,23 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -142934,19 +142978,23 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -145077,19 +145125,23 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Partially fulfilled +
+ Partially fulfilled +
@@ -147220,19 +147272,23 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` class="MuiTypography-root-id PageHeader-title-id MuiTypography-h5-id" >
9
-
+
- Unfulfilled +
+ Unfulfilled +
From af2258cdc50944536139dc988ed0fde4d72513fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Mi=C5=9Bta?= Date: Thu, 28 Oct 2021 11:42:58 +0200 Subject: [PATCH 17/17] Update hook name --- .../GiftCardEnableDisableSection.tsx | 4 ++-- ...vationDeactivation.ts => useGiftCardActivateToggle.ts} | 8 ++++---- .../CustomerGiftCardsCardListItem.tsx | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/{useGiftCardActivationDeactivation.ts => useGiftCardActivateToggle.ts} (91%) diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/GiftCardEnableDisableSection.tsx b/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/GiftCardEnableDisableSection.tsx index 895219ad46d..fa35b5f059b 100644 --- a/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/GiftCardEnableDisableSection.tsx +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/GiftCardEnableDisableSection.tsx @@ -5,7 +5,7 @@ import { useIntl } from "react-intl"; import { bulkEnableDisableSectionMessages as buttonMessages } from "../../GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages"; import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails"; -import useGiftCardActivationDeactivation from "./hooks/useGiftCardActivationDeactivation"; +import useGiftCardActivateToggle from "./hooks/useGiftCardActivateToggle"; const GiftCardEnableDisableSection: React.FC = () => { const intl = useIntl(); @@ -22,7 +22,7 @@ const GiftCardEnableDisableSection: React.FC = () => { giftCardActivate, giftCardDeactivate, currentOpts - } = useGiftCardActivationDeactivation({ + } = useGiftCardActivateToggle({ isActive }); diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation.ts b/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivateToggle.ts similarity index 91% rename from src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation.ts rename to src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivateToggle.ts index fa9d22ade1f..8b395a27840 100644 --- a/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation.ts +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivateToggle.ts @@ -10,17 +10,17 @@ import { import { GiftCardActivate } from "../types/GiftCardActivate"; import { GiftCardDeactivate } from "../types/GiftCardDeactivate"; -interface UseGiftCardActivationDeactivationProps { +interface useGiftCardActivateToggleProps { onActivateActionComplete?: () => void | undefined; onDeactivateActionComplete?: () => void | undefined; isActive?: boolean; } -const useGiftCardActivationDeactivation = ({ +const useGiftCardActivateToggle = ({ onActivateActionComplete, onDeactivateActionComplete, isActive -}: UseGiftCardActivationDeactivationProps) => { +}: useGiftCardActivateToggleProps) => { const intl = useIntl(); const notify = useNotifier(); @@ -89,4 +89,4 @@ const useGiftCardActivationDeactivation = ({ }; }; -export default useGiftCardActivationDeactivation; +export default useGiftCardActivateToggle; diff --git a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx index c0d7c248ce5..f1d842877fd 100644 --- a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx +++ b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx @@ -1,7 +1,7 @@ import CardMenu, { CardMenuItem } from "@saleor/components/CardMenu"; import { bulkEnableDisableSectionMessages } from "@saleor/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages"; import { giftCardsListTableMessages } from "@saleor/giftCards/GiftCardsList/messages"; -import useGiftCardActivationDeactivation from "@saleor/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivationDeactivation"; +import useGiftCardActivateToggle from "@saleor/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivateToggle"; import { ExtendedGiftCard } from "@saleor/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; import * as React from "react"; import { useState } from "react"; @@ -35,7 +35,7 @@ const CustomerGiftCardsCardListItem: React.FC