forked from Sitecore/Sitecore.Commerce.Headstart.ReactAdmin
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ability to create order returns on order detail Sitecore#179
- Loading branch information
Crhistian
committed
Jul 18, 2023
1 parent
70417c6
commit 4f84bac
Showing
6 changed files
with
537 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
src/components/orders/detail/order-returns/return-modal/OrderReturnActionMenu.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import {DeleteIcon, EditIcon} from "@chakra-ui/icons" | ||
import { | ||
AlertDialog, | ||
AlertDialogBody, | ||
AlertDialogContent, | ||
AlertDialogFooter, | ||
AlertDialogHeader, | ||
AlertDialogOverlay, | ||
Button, | ||
HStack, | ||
IconButton, | ||
Menu, | ||
MenuButton, | ||
MenuDivider, | ||
MenuItem, | ||
MenuList, | ||
Text, | ||
useDisclosure | ||
} from "@chakra-ui/react" | ||
import {TbDotsVertical} from "react-icons/tb" | ||
import {ILineItem} from "types/ordercloud/ILineItem" | ||
import {IOrder} from "types/ordercloud/IOrder" | ||
import {useRef} from "react" | ||
import {IOrderReturn} from "types/ordercloud/IOrderReturn" | ||
import {ReturnModal} from "./ReturnModal" | ||
|
||
interface ReturnActionMenuProps { | ||
orderReturn: IOrderReturn | ||
allOrderReturns: IOrderReturn[] | ||
order: IOrder | ||
lineItems: ILineItem[] | ||
onUpdate: () => void | ||
onDelete: () => void | ||
} | ||
|
||
export function ReturnActionMenu({ | ||
orderReturn, | ||
allOrderReturns, | ||
order, | ||
lineItems, | ||
onUpdate, | ||
onDelete | ||
}: ReturnActionMenuProps) { | ||
const {isOpen: isDeleteDialogOpen, onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog} = useDisclosure() | ||
const cancelRef = useRef() | ||
const handleCancelDeleteReturn = () => { | ||
onDelete() | ||
onCloseDeleteDialog() | ||
} | ||
return ( | ||
<> | ||
<Menu> | ||
<MenuButton | ||
as={IconButton} | ||
icon={<TbDotsVertical />} | ||
aria-label={`Return action menu for ${orderReturn.ID}`} | ||
variant="ghost" | ||
></MenuButton> | ||
<MenuList> | ||
<ReturnModal | ||
order={order} | ||
orderReturn={orderReturn} | ||
allOrderReturns={allOrderReturns} | ||
lineItems={lineItems} | ||
onUpdate={onUpdate} | ||
as="menuitem" | ||
> | ||
<HStack width="full" justifyContent="space-between"> | ||
<Text>Edit</Text> <EditIcon /> | ||
</HStack> | ||
</ReturnModal> | ||
<MenuDivider /> | ||
<MenuItem justifyContent="space-between" color="red.500" onClick={onOpenDeleteDialog}> | ||
Delete <DeleteIcon /> | ||
</MenuItem> | ||
</MenuList> | ||
</Menu> | ||
|
||
<AlertDialog isOpen={isDeleteDialogOpen} leastDestructiveRef={cancelRef} onClose={onCloseDeleteDialog}> | ||
<AlertDialogOverlay> | ||
<AlertDialogContent> | ||
<AlertDialogHeader>Delete Return</AlertDialogHeader> | ||
<AlertDialogBody>Are you sure? You can't undo this action afterwards.</AlertDialogBody> | ||
<AlertDialogFooter> | ||
<Button ref={cancelRef} onClick={onCloseDeleteDialog}> | ||
Cancel | ||
</Button> | ||
<Button colorScheme="red" onClick={handleCancelDeleteReturn} ml={3}> | ||
Delete | ||
</Button> | ||
</AlertDialogFooter> | ||
</AlertDialogContent> | ||
</AlertDialogOverlay> | ||
</AlertDialog> | ||
</> | ||
) | ||
} |
146 changes: 146 additions & 0 deletions
146
src/components/orders/detail/order-returns/return-modal/ReturnItemsTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import {InputControl, SelectControl} from "@/components/react-hook-form" | ||
import { | ||
Flex, | ||
HStack, | ||
FormControl, | ||
Hide, | ||
Stack, | ||
Table, | ||
TableContainer, | ||
Tbody, | ||
Td, | ||
Thead, | ||
Tr, | ||
VStack, | ||
Text, | ||
useMediaQuery, | ||
theme, | ||
FormLabel, | ||
Divider | ||
} from "@chakra-ui/react" | ||
import {flatten} from "lodash" | ||
import {Control, FieldValues} from "react-hook-form" | ||
import {ILineItem} from "types/ordercloud/ILineItem" | ||
import {IOrderReturn} from "types/ordercloud/IOrderReturn" | ||
import {ProductThumbnail} from "../../order-products/ProductThumbnail" | ||
|
||
interface ReturnItemsTableProps { | ||
control: Control<FieldValues, any> | ||
lineItems: ILineItem[] | ||
allOrderReturns: IOrderReturn[] | ||
existingReturn: IOrderReturn | ||
} | ||
|
||
export function ReturnItemsTable({control, lineItems, allOrderReturns, existingReturn}: ReturnItemsTableProps) { | ||
const [isMobile] = useMediaQuery(`(max-width: ${theme.breakpoints["md"]})`, { | ||
ssr: true, | ||
fallback: false // return false on the server, and re-evaluate on the client side | ||
}) | ||
const maxReturnQuantity = (lineItem: ILineItem) => { | ||
const otherReturnItems = flatten( | ||
allOrderReturns | ||
.filter((orderReturn) => orderReturn.ID !== existingReturn?.ID) | ||
.filter((orderReturn) => orderReturn.ItemsToReturn.some((returnItem) => returnItem.LineItemID === lineItem.ID)) | ||
.map((orderReturn) => orderReturn.ItemsToReturn.filter((returnItem) => returnItem.LineItemID === lineItem.ID)) | ||
) | ||
const quantityAlreadyReturned = otherReturnItems.reduce((acc, returnItem) => acc + returnItem.Quantity || 0, 0) | ||
return lineItem.Quantity - quantityAlreadyReturned | ||
} | ||
|
||
const buildQuantityOptions = (lineItem: ILineItem) => { | ||
const max = maxReturnQuantity(lineItem) | ||
const numberArray = Array(max) | ||
.fill("") | ||
.map((_, i) => { | ||
const value = i + 1 | ||
return { | ||
label: value, | ||
value | ||
} | ||
}) | ||
.reverse() | ||
return [...numberArray, {label: "Do not return", value: ""}] | ||
} | ||
|
||
const tableCellPadding = 2 | ||
|
||
return ( | ||
// without overflow visible, the dropdown for Quantity may not show fully | ||
<TableContainer overflowX="visible" overflowY="visible" w="100%" maxW="container.xl"> | ||
<Table variant={{base: "unstyled", sm: "simple"}}> | ||
<Hide below="md"> | ||
<Thead> | ||
<Tr> | ||
<Td>Lineitem</Td> | ||
<Td>Quantity</Td> | ||
<Td>Refund Amount</Td> | ||
<Td>Comments</Td> | ||
</Tr> | ||
</Thead> | ||
</Hide> | ||
<Tbody> | ||
{lineItems.map((lineItem, index) => ( | ||
<Tr key={lineItem.ID} as={isMobile && VStack} alignItems={{base: "flex-start", sm: "initial"}}> | ||
<Td padding={tableCellPadding}> | ||
<Stack | ||
direction={["row"]} | ||
alignItems="center" | ||
gap={4} | ||
width={{base: "full", sm: "initial"}} | ||
flex="1" | ||
w="max-content" | ||
> | ||
<ProductThumbnail imageProps={{boxSize: {base: 75, sm: 50}}} product={lineItem.Product} /> | ||
<VStack justifyContent="space-between"> | ||
<Text fontSize={{base: "md", sm: "sm"}}>{lineItem.Product.Name}</Text> | ||
<Text fontSize="xs" color="gray.400"> | ||
SKU: {lineItem.Product.ID} | ||
</Text> | ||
</VStack> | ||
</Stack> | ||
</Td> | ||
<Td padding={tableCellPadding} w={{base: "100%", sm: "initial"}}> | ||
<FormControl justifyContent="space-between"> | ||
<Hide above="sm"> | ||
<FormLabel fontSize="sm">Quantity</FormLabel> | ||
</Hide> | ||
<SelectControl | ||
control={control} | ||
name={`ItemsToReturn.${index}.Quantity`} | ||
selectProps={{ | ||
options: buildQuantityOptions(lineItem) | ||
}} | ||
/> | ||
</FormControl> | ||
</Td> | ||
<Td padding={tableCellPadding} w={{base: "100%", sm: "initial"}}> | ||
<FormControl justifyContent="space-between"> | ||
<Hide above="sm"> | ||
<FormLabel fontSize="sm">Refund Amount</FormLabel> | ||
</Hide> | ||
<InputControl | ||
name={`ItemsToReturn.${index}.RefundAmount`} | ||
control={control} | ||
inputProps={{type: "number"}} | ||
leftAddon="$" | ||
/> | ||
</FormControl> | ||
</Td> | ||
<Td padding={tableCellPadding} w={{base: "100%", sm: "initial"}}> | ||
<FormControl> | ||
<Hide above="sm"> | ||
<FormLabel fontSize="sm">Comments</FormLabel> | ||
</Hide> | ||
<InputControl name={`ItemsToReturn.${index}.Comments`} control={control} /> | ||
</FormControl> | ||
<Hide above="sm"> | ||
<Divider mt={8} /> | ||
</Hide> | ||
</Td> | ||
</Tr> | ||
))} | ||
</Tbody> | ||
</Table> | ||
</TableContainer> | ||
) | ||
} |
Oops, something went wrong.