Skip to content

Commit

Permalink
Gift card customer page (#1520)
Browse files Browse the repository at this point in the history
* 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

* 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

* 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

* Post-rebase fixes

* Code cleanup & extract messages

* Remove unused code

* Create customer details hook

* Minor fixes

* Update default messages

* Update gift card types

* Type fixes

* Change directory of useCustomerDetails hook

* CR Fixes

* Update tests

* Make PageTitleWithStatusChip use ExtendedPageHeader

* Update tests

* Update hook name
  • Loading branch information
Cloud11PL committed Jan 13, 2022
1 parent f24c443 commit 69f9329
Show file tree
Hide file tree
Showing 37 changed files with 1,737 additions and 520 deletions.
28 changes: 28 additions & 0 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,10 @@
"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_cardMenuItemLoading": {
"context": "menu item loading",
"string": "working..."
},
"src_dot_components_dot_ChannelsAvailabilityCard_dot_3326160357": {
"context": "section header",
"string": "Availability"
Expand Down Expand Up @@ -3820,6 +3824,10 @@
"context": "used by filter label",
"string": "Used by"
},
"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"
Expand Down Expand Up @@ -3924,6 +3932,26 @@
"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_customerGiftCardsCardTitle": {
"context": "title",
"string": "Gift Cards"
},
"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}}"
Expand Down
17 changes: 0 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 61 additions & 11 deletions src/components/CardMenu/CardMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
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;
import { cardMenuMessages as messages } from "./messages";

const ITEM_HEIGHT = 48;
export interface CardMenuItem {
disabled?: boolean;
label: string;
testId?: string;
onSelect: () => void;
loading?: boolean;
withLoading?: boolean;
hasError?: boolean;
}

export interface CardMenuProps {
Expand All @@ -42,6 +51,14 @@ const useStyles = makeStyles(
marginTop: theme.spacing(2),
maxHeight: ITEM_HEIGHT * 4.5,
overflowY: "scroll"
},
loadingContent: {
width: "100%",
display: "grid",
gridTemplateColumns: "1fr 24px",
gap: theme.spacing(2),
alignItems: "center",
justifyContent: "flex-end"
}
}),
{ name: "CardMenu" }
Expand All @@ -51,8 +68,8 @@ const CardMenu: React.FC<CardMenuProps> = props => {
const { className, disabled, menuItems, ...rest } = props;
const classes = useStyles(props);

const anchorRef = React.useRef<HTMLButtonElement | null>(null);
const [open, setOpen] = React.useState(false);
const anchorRef = useRef<HTMLButtonElement | null>(null);
const [open, setOpen] = useState(false);

const handleToggle = () => setOpen(prevOpen => !prevOpen);

Expand All @@ -74,20 +91,36 @@ const CardMenu: React.FC<CardMenuProps> = props => {
}
};

const prevOpen = React.useRef(open);
React.useEffect(() => {
const prevOpen = useRef(open);
useEffect(() => {
if (prevOpen.current === true && open === false) {
anchorRef.current!.focus();
}

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 (
<div className={className} {...rest}>
<IconButton
Expand Down Expand Up @@ -128,12 +161,29 @@ const CardMenu: React.FC<CardMenuProps> = props => {
{menuItems.map((menuItem, menuItemIndex) => (
<MenuItem
data-test-id={menuItem.testId}
disabled={menuItem.disabled}
disabled={menuItem.loading || menuItem.disabled}
onClick={() => handleMenuClick(menuItemIndex)}
key={menuItem.label}
data-test={menuItem.testId}
>
{menuItem.label}
<div
className={classNames(className, {
[classes.loadingContent]: isWithLoading
})}
>
{menuItem.loading ? (
<>
<Typography variant="subtitle1">
<FormattedMessage
{...messages.cardMenuItemLoading}
/>
</Typography>
<CircularProgress size={24} />
</>
) : (
<Typography>{menuItem.label}</Typography>
)}
</div>
</MenuItem>
))}
</MenuList>
Expand Down
8 changes: 8 additions & 0 deletions src/components/CardMenu/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineMessages } from "react-intl";

export const cardMenuMessages = defineMessages({
cardMenuItemLoading: {
defaultMessage: "working...",
description: "menu item loading"
}
});
5 changes: 5 additions & 0 deletions src/components/CardTitle/CardTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const useStyles = makeStyles(
fontWeight: 500,
lineHeight: 1
},
subtitle: {
fontWeight: 400
},
toolbar: {
marginRight: theme.spacing(-1)
}
Expand All @@ -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<any>) => void;
}
Expand All @@ -49,6 +53,7 @@ const CardTitle: React.FC<CardTitleProps> = props => {
children,
height,
title,
subtitle,
toolbar,
onClick,
...rest
Expand Down
43 changes: 33 additions & 10 deletions src/components/CollectionWithDividers/CollectionWithDividers.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Divider } from "@material-ui/core";
import initial from "lodash/initial";
import React from "react";

interface CollectionWithDividersProps<T> {
DividerComponent?: React.FunctionComponent;
renderEmpty?: (collection: T[]) => any;
withOuterDividers?: boolean;
collection: T[];
renderItem: (
item: T | undefined,
Expand All @@ -13,7 +13,25 @@ interface CollectionWithDividersProps<T> {
) => any;
}

const Wrapper: React.FC<{
withOuterDividers?: boolean;
SelectedDivider?: React.FunctionComponent;
}> = ({ withOuterDividers, SelectedDivider, children }) => (
<div>
{withOuterDividers && SelectedDivider ? (
<>
<SelectedDivider />
{children}
<SelectedDivider />
</>
) : (
<>{children}</>
)}
</div>
);

function CollectionWithDividers<T>({
withOuterDividers = false,
collection,
renderItem,
DividerComponent,
Expand All @@ -31,15 +49,20 @@ function CollectionWithDividers<T>({

const SelectedDividerComponent = DividerComponent || Divider;

return initial(
collection.reduce(
(result, item, index) => [
...result,
renderItem(item, index, collection),
<SelectedDividerComponent />
],
[]
)
return (
<Wrapper
withOuterDividers={withOuterDividers}
SelectedDivider={SelectedDividerComponent}
>
<>
{collection.map((item, index) => (
<>
{renderItem(item, index, collection)}
<SelectedDividerComponent />
</>
))}
</>
</Wrapper>
);
}

Expand Down
15 changes: 13 additions & 2 deletions src/components/ExtendedPageHeader/ExtendedPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,23 @@ const useStyles = makeStyles(
interface ExtendedPageHeaderProps {
children?: React.ReactNode;
className?: string;
childrenWrapperClassName?: string;
inline?: boolean;
underline?: boolean;
title?: React.ReactNode;
testId?: string;
}

const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => {
const { children, className, inline, underline, title, testId } = props;
const {
children,
className,
childrenWrapperClassName,
inline,
underline,
title,
testId
} = props;

const classes = useStyles(props);

Expand All @@ -75,7 +84,9 @@ const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => {
})}
>
{title}
<div className={classes.action}>{children}</div>
<div className={classNames(classes.action, childrenWrapperClassName)}>
{children}
</div>
</div>
{underline && (
<div className={classes.underline}>
Expand Down
22 changes: 11 additions & 11 deletions src/components/PageTitleWithStatusChip/PageTitleWithStatusChip.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
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";

import ExtendedPageHeader from "../ExtendedPageHeader";
export interface PageTitleWithStatusChipProps {
title: string;
statusLabel: string;
statusType: StatusType;
}

const useStyles = makeStyles(
Expand All @@ -22,17 +19,20 @@ const useStyles = makeStyles(

const PageTitleWithStatusChip: React.FC<PageTitleWithStatusChipProps> = ({
title,
statusLabel,
statusType
children
}) => {
const classes = useStyles({});

return (
<div className={classes.container}>
{title}
<HorizontalSpacer spacing={2} />
<StatusChip label={statusLabel} status={statusType} />
</div>
<ExtendedPageHeader
title={title}
childrenWrapperClassName={classes.container}
>
<>
<HorizontalSpacer spacing={2} />
{children}
</>
</ExtendedPageHeader>
);
};

Expand Down
Loading

0 comments on commit 69f9329

Please sign in to comment.