Skip to content

Commit

Permalink
Order/Invoice/CreditMemo and Shipment views
Browse files Browse the repository at this point in the history
  • Loading branch information
paales committed Feb 21, 2025
1 parent f70d5ff commit dac9084
Show file tree
Hide file tree
Showing 33 changed files with 1,069 additions and 548 deletions.
7 changes: 7 additions & 0 deletions .changeset/tender-waves-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@graphcommerce/magento-customer': patch
'@graphcommerce/magento-product-bundle': patch
'@graphcommerce/magento-product-downloadable': patch
---

Order/Invoice/CreditMemo and Shipment views
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fragment CreditMemoItem on CreditMemoItemInterface {
__typename
id
product_name
product_sku
Expand Down
139 changes: 95 additions & 44 deletions packages/magento-customer/components/CreditMemo/CreditMemoItem.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,107 @@
import { Money } from '@graphcommerce/magento-store'
import { extendableComponent } from '@graphcommerce/next-ui'
import { Trans } from '@lingui/macro'
import { Box, Typography } from '@mui/material'
import { Money, PriceModifiersTable, type PriceModifier } from '@graphcommerce/magento-store'
import { ActionCard, type ActionCardProps } from '@graphcommerce/next-ui'
import { Trans } from '@lingui/react'
import { Box } from '@mui/material'
import type { CreditMemoItemFragment } from './CreditMemoItem.gql'

export type CreditMemoItemProps = {
export type CreditMemoItemProps = Omit<ActionCardProps, 'value' | 'image' | 'title'> & {
item: CreditMemoItemFragment
additionalInfo?: React.ReactNode
priceModifiers?: PriceModifier[]
}

const componentName = 'CreditMemoItem'
const parts = ['root', 'itemInfo', 'additionalInfo', 'skuInfo', 'priceInfo'] as const
const { classes } = extendableComponent(componentName, parts)

export function CreditMemoItem(props: CreditMemoItemProps) {
const { item, additionalInfo } = props
const { product_name, product_sku, quantity_refunded, product_sale_price, discounts } = item
const { item, priceModifiers, size = 'responsive', ...rest } = props
const { product_name, product_sku, quantity_refunded, product_sale_price, discounts, id } = item

return (
<Box
className={classes.root}
<ActionCard
{...rest}
value={id}
sx={(theme) => ({
display: 'grid',
gridTemplateColumns: '1fr auto auto',
gap: theme.spacings.sm,
padding: `${theme.spacings.xs} 0`,
borderBottom: `1px solid ${theme.palette.divider}`,
'&:last-child': {
borderBottom: 'none',
},
alignItems: 'center',
'&.ActionCard-root': {
px: 0,
py: theme.spacings.xs,
},
'& .ActionCard-rootInner': {
justifyContent: 'space-between',
alignItems: 'stretch',
},
'&.sizeSmall': {
px: 0,
},
'&.sizeResponsive': {
[theme.breakpoints.down('md')]: { px: 0 },
},
'& .ActionCard-end': {
justifyContent: 'space-between',
},
'& .ActionCard-action': {
pr: 0,
},
'& .ActionCard-image': {
alignSelf: 'flex-start',
},
'& .ActionCard-secondaryAction': {
display: 'grid',
rowGap: theme.spacings.xs,
justifyItems: 'start',
},
'& .ActionCard-price': {
pr: 0,
mb: { xs: 0.5, sm: 0 },
},
})}
>
<Box className={classes.itemInfo}>
<Typography variant='subtitle1'>
{quantity_refunded}{product_name}
</Typography>
{discounts?.map((discount) => (
<Typography key={discount?.label} variant='body2' color='textSecondary'>
{discount?.label}: <Money {...discount?.amount} />
</Typography>
))}
</Box>
<Box className={classes.additionalInfo}>
<Typography className={classes.skuInfo} variant='body2' color='textSecondary'>
<Trans>SKU: {product_sku}</Trans>
</Typography>
{additionalInfo}
</Box>
<Box className={classes.priceInfo} sx={{ textAlign: 'right' }}>
<Money {...product_sale_price} />
</Box>
</Box>
size={size}
title={product_name}
price={
<Money
currency={product_sale_price.currency}
value={(product_sale_price.value ?? 0) * (quantity_refunded ?? 1)}
/>
}
secondaryAction={
<>
<Box
sx={{
display: 'flex',
alignItems: 'center',
color: 'text.secondary',
mt: 1,
gap: '10px',
justifyContent: 'start',
}}
>
{quantity_refunded}
{' ⨉ '}
<Money {...product_sale_price} />
</Box>

<Box sx={{ mt: 1, color: 'text.secondary' }}>
<Trans>SKU: {product_sku}</Trans>
</Box>

{discounts?.map((discount) => (
<Box key={discount?.label} sx={{ color: 'text.secondary' }}>
{discount?.label}: <Money {...discount?.amount} />
</Box>
))}

{rest.secondaryAction}
</>
}
details={
<>
{priceModifiers && priceModifiers.length > 0 && (
<PriceModifiersTable
label={<Trans id='Base Price'>Base price</Trans>}
modifiers={[...priceModifiers]}
total={product_sale_price.value ?? 0}
currency={product_sale_price.currency}
/>
)}
{rest.details}
</>
}
/>
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fragment InvoiceItem on InvoiceItemInterface {
__typename
id
discounts {
amount {
Expand All @@ -12,4 +13,7 @@ fragment InvoiceItem on InvoiceItemInterface {
}
product_sku
quantity_invoiced
order_item {
...OrderItem
}
}
176 changes: 132 additions & 44 deletions packages/magento-customer/components/Invoice/InvoiceItem.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,144 @@
import { Money } from '@graphcommerce/magento-store'
import { extendableComponent } from '@graphcommerce/next-ui'
import { Trans } from '@lingui/macro'
import { Box, Typography } from '@mui/material'
import { Image } from '@graphcommerce/image'
import { Money, PriceModifiersTable, type PriceModifier } from '@graphcommerce/magento-store'
import {
ActionCard,
actionCardImageSizes,
filterNonNullableKeys,
type ActionCardProps,
} from '@graphcommerce/next-ui'
import { Trans } from '@lingui/react'
import { Box } from '@mui/material'
import type { InvoiceItemFragment } from './InvoiceItem.gql'

export type InvoiceItemProps = {
export type InvoiceItemProps = Omit<ActionCardProps, 'value' | 'image' | 'title'> & {
item: InvoiceItemFragment
additionalInfo?: React.ReactNode
priceModifiers?: PriceModifier[]
}

const componentName = 'InvoiceItem'
const parts = ['root', 'itemInfo', 'additionalInfo', 'skuInfo', 'priceInfo'] as const
const { classes } = extendableComponent(componentName, parts)

export function InvoiceItem(props: InvoiceItemProps) {
const { item, additionalInfo } = props
const { product_name, product_sku, quantity_invoiced, product_sale_price, discounts } = item
const { item, priceModifiers: incomingPriceModifiers, size = 'responsive', ...rest } = props
const {
product_name,
product_sku,
quantity_invoiced,
product_sale_price,
discounts,
order_item,
id,
} = item

const priceModifiers: PriceModifier[] = [
...(incomingPriceModifiers ?? []),
...filterNonNullableKeys(order_item?.selected_options).map((option) => ({
key: option.label,
label: option.label,
items: [{ key: option.value, label: option.value }],
})),
...filterNonNullableKeys(order_item?.entered_options).map((option) => ({
key: option.label,
label: option.label,
items: [{ key: option.value, label: option.value }],
})),
]

return (
<Box
className={classes.root}
<ActionCard
{...rest}
value={id}
sx={(theme) => ({
display: 'grid',
gridTemplateColumns: '1fr auto auto',
gap: theme.spacings.sm,
padding: `${theme.spacings.xs} 0`,
borderBottom: `1px solid ${theme.palette.divider}`,
'&:last-child': {
borderBottom: 'none',
},
alignItems: 'center',
'&.ActionCard-root': {
px: 0,
py: theme.spacings.xs,
},
'& .ActionCard-rootInner': {
justifyContent: 'space-between',
alignItems: 'stretch',
},
'&.sizeSmall': {
px: 0,
},
'&.sizeResponsive': {
[theme.breakpoints.down('md')]: { px: 0 },
},
'& .ActionCard-end': {
justifyContent: 'space-between',
},
'& .ActionCard-action': {
pr: 0,
},
'& .ActionCard-image': {
alignSelf: 'flex-start',
},
'& .ActionCard-secondaryAction': {
display: 'grid',
rowGap: theme.spacings.xs,
justifyItems: 'start',
},
'& .ActionCard-price': {
pr: 0,
mb: { xs: 0.5, sm: 0 },
},
})}
>
<Box className={classes.itemInfo}>
<Typography variant='subtitle1'>
{quantity_invoiced}{product_name}
</Typography>
{discounts?.map((discount) => (
<Typography key={discount?.label} variant='body2' color='textSecondary'>
{discount?.label}: <Money {...discount?.amount} />
</Typography>
))}
</Box>
<Box className={classes.additionalInfo}>
<Typography className={classes.skuInfo} variant='body2' color='textSecondary'>
<Trans>SKU: {product_sku}</Trans>
</Typography>
{additionalInfo}
</Box>
<Box className={classes.priceInfo} sx={{ textAlign: 'right' }}>
<Money {...product_sale_price} />
</Box>
</Box>
size={size}
image={
order_item?.product?.thumbnail?.url && (
<Image
layout='fill'
src={order_item.product.thumbnail.url}
alt={order_item?.product.thumbnail?.label ?? ''}
sx={{
width: actionCardImageSizes[size],
height: actionCardImageSizes[size],
display: 'block',
borderRadius: 1,
objectFit: 'contain',
backgroundColor: 'background.image',
}}
sizes={actionCardImageSizes[size]}
/>
)
}
title={product_name}
action={<>&nbsp;</>}
price={
<Money
currency={product_sale_price.currency}
value={(product_sale_price.value ?? 0) * (quantity_invoiced ?? 1)}
/>
}
secondaryAction={
<>
<Box
sx={{
display: 'flex',
alignItems: 'center',
color: 'text.secondary',
mt: 1,
gap: '10px',
justifyContent: 'start',
}}
>
{quantity_invoiced}
{' ⨉ '}
<Money {...product_sale_price} />
</Box>

{rest.secondaryAction}
</>
}
details={
<>
{priceModifiers && priceModifiers.length > 0 && (
<PriceModifiersTable
label={<Trans id='Base Price'>Base price</Trans>}
modifiers={[...priceModifiers]}
total={product_sale_price.value ?? 0}
currency={product_sale_price.currency}
/>
)}
{rest.details}
</>
}
/>
)
}
Loading

0 comments on commit dac9084

Please sign in to comment.