Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gift card customer page #1520

Merged
merged 17 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1971,6 +1971,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 @@ -3765,6 +3769,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"
Expand Down Expand Up @@ -3869,6 +3877,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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined is also falsy, so unless we're going to silence some strict null checking errors (which is not the case) I think it's safe to omit this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather keep 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> = ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it can more things than StatusChip, so I think the component name should be changed to something more appropriate

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a component ExtendedPageHeader somewhere in the project, doesn't it meet your needs anyway?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it can more things than StatusChip, so I think the component name should be changed to something more appropriate

Technically yes, practically it's styled only to be used with StatusChip. I think it's better to have one component that does one thing without having to change styles every time I want to use it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dominik-zeglen Maybe we can make a PageTitleWithStatusChip precisely styled for status chip but using extended page header underneath? @Cloud11PL check if it's possible

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bmigirl @dominik-zeglen It is, I pushed the changes

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