diff --git a/src/api/productionInfo/productionInfo.ts b/src/api/productionInfo/productionInfo.ts index 1b0784a..457cc7f 100644 --- a/src/api/productionInfo/productionInfo.ts +++ b/src/api/productionInfo/productionInfo.ts @@ -33,7 +33,7 @@ export const createProductionInfo = async ( export const updateProductionInfo = async ( body: GoodProductionInfo, -): Promise => { +): Promise> => { const { data: productionInfo } = await axiosClient.put( "/storage/good/production_info/", body, diff --git a/src/api/storage/storage.ts b/src/api/storage/storage.ts index 65c5087..ea9d3c8 100644 --- a/src/api/storage/storage.ts +++ b/src/api/storage/storage.ts @@ -12,6 +12,10 @@ export const moveGoodsToStorage = async (body: DeliveryToStorage[]) => { await axiosClient.post("/storage/move_goods_from_delivery/", body) } +export const revertGoodsToDelivery = async (deliveryId: number) => { + await axiosClient.delete(`/storage/revert_goods_to_delivery/${deliveryId}`) +} + export const getStorageActualInfoByGoodId = async ( storageGoodId: number, ): Promise => { diff --git a/src/component/orderProcessing/EngravingChatAndButtons.tsx b/src/component/orderProcessing/EngravingChatAndButtons.tsx index ed6b27d..e415b4f 100644 --- a/src/component/orderProcessing/EngravingChatAndButtons.tsx +++ b/src/component/orderProcessing/EngravingChatAndButtons.tsx @@ -17,25 +17,29 @@ export const EngravingChatAndButtons: FC = ( const { canReadTicketMessages } = useUserPermissions() - if (isViewOnlyMode && !isOrderHasTicket) { - return <> - } - return ( - {!isViewOnlyMode && } + {isOrderHasTicket && canReadTicketMessages && } ) } -export const Buttons: FC<{ isOrderHasTicket?: boolean }> = (props) => { - const { isOrderHasTicket } = props +interface ButtonsProps { + isOrderHasTicket?: boolean + isViewOnlyMode?: boolean +} + +export const Buttons: FC = (props) => { + const { isOrderHasTicket, isViewOnlyMode } = props return ( - - + + ) } diff --git a/src/component/orderProcessing/ProcessingOrderButtons.tsx b/src/component/orderProcessing/ProcessingOrderButtons.tsx index 3dcfaaa..06ba0de 100644 --- a/src/component/orderProcessing/ProcessingOrderButtons.tsx +++ b/src/component/orderProcessing/ProcessingOrderButtons.tsx @@ -1,4 +1,5 @@ import { Flex } from "@chakra-ui/react" +import { BackBtn } from "component/orderProcessing/buttons/BackBtn" import { FinishBtn } from "component/orderProcessing/buttons/FinishBtn" import { PauseBtn } from "component/orderProcessing/buttons/PauseBtn" import { TakeBreakBtn } from "component/orderProcessing/buttons/TakeBreakBtn" @@ -10,12 +11,14 @@ import { useProcessingOrderStatusUpdateMutation } from "service/engraver/process import { ProcessingOrderStatusUpdate } from "type/engraver/processingOrder" import { notify } from "util/toasts" -interface ProcessingOrderButtonsProps {} +interface ProcessingOrderButtonsProps { + isViewOnlyMode?: boolean +} export const ProcessingOrderButtons: FC = ( props, ) => { - const {} = props + const { isViewOnlyMode } = props const location = useLocation() const navigate = useNavigate() @@ -68,11 +71,20 @@ export const ProcessingOrderButtons: FC = ( alignItems="center" gap={5} > - - - - - + {isViewOnlyMode ? ( + + ) : ( + <> + + + + + + + )} ) diff --git a/src/component/orderProcessing/buttons/BackBtn.tsx b/src/component/orderProcessing/buttons/BackBtn.tsx new file mode 100644 index 0000000..0cf2ac5 --- /dev/null +++ b/src/component/orderProcessing/buttons/BackBtn.tsx @@ -0,0 +1,29 @@ +import { Button } from "@chakra-ui/react" +import { FC } from "react" + +interface BackBtnProps { + onClick: () => void + isLoading: boolean +} + +export const BackBtn: FC = (props) => { + const { onClick, isLoading } = props + + const handleClick = async () => { + onClick() + } + + return ( + + ) +} diff --git a/src/component/productionInfo/ProductionInfoModal.tsx b/src/component/productionInfo/ProductionInfoModal.tsx index 108524f..4ca1359 100644 --- a/src/component/productionInfo/ProductionInfoModal.tsx +++ b/src/component/productionInfo/ProductionInfoModal.tsx @@ -14,6 +14,7 @@ import { import { ModalBackgroundBlur } from "component/ModalBackgroundBlur" import { SKUBadge } from "component/badge/SKUBadge" import { ProductionInfoModalInput } from "component/productionInfo/ProductionInfoModalInput" +import { useCommentInput } from "hook/useCommentInput" import { FC, useEffect, useState } from "react" import { useProductionInfoUpdateMutation } from "service/productionInfo/productionInfo" import { ModalProps } from "type/modalProps" @@ -28,7 +29,7 @@ import { notify } from "util/toasts" interface ProductionInfoModalProps extends ModalProps { good: WithId - prevProductionInfo?: ProductionInfo + prevProductionInfo?: WithId accessibleColumns: (TableColumn | null)[] } @@ -41,16 +42,26 @@ export const ProductionInfoModal: FC = (props) => { const accessibleParams = accessibleColumns.map((column) => column?.param) const [productionInfo, setProductionInfo] = useState< - ProductionInfo | undefined + WithId | undefined >(prevProductionInfo) + const productionInfoId = productionInfo?.id + + const { CommentInputComponent, onCommentSubmit, handleCommentClear } = + useCommentInput({ + objectName: "production_info", + objectId: productionInfoId, + }) const productionInfoUpdateMutation = useProductionInfoUpdateMutation() const isLoading = productionInfoUpdateMutation.isLoading - const handleChange = (param: string, value: number | string) => { + const handleChange = ( + param: K, + value: ProductionInfo[K], + ) => { setProductionInfo((prevProductionInfo) => ({ - ...prevProductionInfo, + ...(prevProductionInfo || ({} as WithId)), [param]: value, })) } @@ -61,7 +72,10 @@ export const ProductionInfoModal: FC = (props) => { good_id: goodId!, } - await productionInfoUpdateMutation.mutateAsync(body) + const { id: updatedProductionInfoId } = + await productionInfoUpdateMutation.mutateAsync(body) + + onCommentSubmit(updatedProductionInfoId) notify(`Storage Good #${goodId} Production updated successfully`, "success") @@ -106,6 +120,7 @@ export const ProductionInfoModal: FC = (props) => { + {/* Engraving Params */} {/* Power */} {isFieldAvailable("power") && ( @@ -263,6 +278,9 @@ export const ProductionInfoModal: FC = (props) => { /> )} + + {/* Comment */} + {CommentInputComponent} diff --git a/src/component/productionInfo/ProductionInfoTableRow.tsx b/src/component/productionInfo/ProductionInfoTableRow.tsx index 8566545..49c1d10 100644 --- a/src/component/productionInfo/ProductionInfoTableRow.tsx +++ b/src/component/productionInfo/ProductionInfoTableRow.tsx @@ -6,6 +6,7 @@ import { TableTdSkeleton } from "component/customTable/TableTdSkeleton" import { ProductionInfoModal } from "component/productionInfo/ProductionInfoModal" import { ProductionInfoTableRowMenu } from "component/productionInfo/ProductionInfoTableRowMenu" import { useUserContext } from "context/user" +import { useCommentInput } from "hook/useCommentInput" import { useUserTableAccess } from "hook/useUserTableAccess" import { FC, useEffect } from "react" import { useQuery } from "react-query" @@ -32,6 +33,7 @@ export const ProductionInfoTableRow: FC = ( const good = goodWithProductionInfo const productionInfo = good.production_info + const productionInfoId = productionInfo?.id const goodTotalQuantity = good.quantity const goodsShelvesList = good?.shelf @@ -68,6 +70,12 @@ export const ProductionInfoTableRow: FC = ( const goodPrimeCost = storageActualInfo?.prime_cost + const { CommentComponent } = useCommentInput({ + objectName: "production_info", + objectId: productionInfoId, + isAsTooltip: true, + }) + useEffect(() => { refetch() }, [refetch, selectedShopId]) @@ -127,11 +135,17 @@ export const ProductionInfoTableRow: FC = ( ) })} - {/* Menu Btns */} + {/* Comment & Menu Btns */} - + + {/* Comment */} + {CommentComponent} + + {/* Menu Btns */} + + diff --git a/src/component/storage/RevertDeliveryModal.tsx b/src/component/storage/RevertDeliveryModal.tsx new file mode 100644 index 0000000..355b86d --- /dev/null +++ b/src/component/storage/RevertDeliveryModal.tsx @@ -0,0 +1,79 @@ +import { + Button, + Flex, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + Text, +} from "@chakra-ui/react" +import { ModalBackgroundBlur } from "component/ModalBackgroundBlur" +import { FC } from "react" +import { useRevertDeliveryMutation } from "service/storage/storage" +import { ModalProps } from "type/modalProps" +import { notify } from "util/toasts" + +interface RevertDeliveryModalProps extends ModalProps { + deliveryId: number +} + +export const RevertDeliveryModal: FC = (props) => { + const { deliveryId, isOpen, onClose } = props + + const deliveryRevertMutation = useRevertDeliveryMutation() + + const isLoading = deliveryRevertMutation.isLoading + + const onRevertConfirm = async () => { + await deliveryRevertMutation.mutateAsync(deliveryId) + + notify(`Delivery #${deliveryId} was successfully reverted`, "success") + onClose() + } + + return ( + + + + + Revert Delivery + + + + + + Are you sure you want to revert{" "} + + Delivery #{deliveryId} + + + + + All Storage Goods from the Delivery will be reverted too, do not + forget to move the Delivery to Storage again correctly + + + + + + + + + + + + + + ) +} diff --git a/src/component/storage/StorageCard.tsx b/src/component/storage/StorageCard.tsx index 8f14de0..72cc38f 100644 --- a/src/component/storage/StorageCard.tsx +++ b/src/component/storage/StorageCard.tsx @@ -9,6 +9,7 @@ import { Text, useDisclosure, } from "@chakra-ui/react" +import { RevertDeliveryModal } from "component/storage/RevertDeliveryModal" import { StorageCardMenu } from "component/storage/StorageCardMenu" import { StorageDeleteModal } from "component/storage/StorageDeleteModal" import { StorageModal } from "component/storage/StorageModal" @@ -41,12 +42,21 @@ export const StorageCard: FC = (props) => { const isPrimeCostExists = storage.prime_cost !== undefined const isItemPriceExists = storage.cost_per_item !== undefined + // * Revert To Delivery Modal + const { + isOpen: isRevertDeliveryModalOpen, + onOpen: onRevertDeliveryModalOpen, + onClose: onRevertDeliveryModalClose, + } = useDisclosure() + + // * Update Modal const { isOpen: isStorageUpdateModalOpen, onOpen: onStorageUpdateModalOpen, onClose: onStorageUpdateModalClose, } = useDisclosure() + // * Delete Modal const { isOpen: isStorageDeleteModalOpen, onOpen: onStorageDeleteModalOpen, @@ -70,6 +80,8 @@ export const StorageCard: FC = (props) => { @@ -150,6 +162,14 @@ export const StorageCard: FC = (props) => { {/* Modals */} <> + {isFromDelivery && ( + + )} + void onEdit: () => void onDelete: () => void } export const StorageCardMenu: FC = (props) => { - const { onEdit, onDelete } = props + const { isFromDelivery, onRevertDelivery, onEdit, onDelete } = props - const { canEditStorage } = useUserPermissions() + const { canEditStorage, canEditPurchases } = useUserPermissions() return ( @@ -31,6 +33,16 @@ export const StorageCardMenu: FC = (props) => { /> + {isFromDelivery && ( + } + onClick={onRevertDelivery} + isDisabled={!canEditStorage || !canEditPurchases} + > + Revert full Delivery + + )} + } onClick={onEdit} diff --git a/src/hook/useCommentInput.tsx b/src/hook/useCommentInput.tsx index b568816..ba082f5 100644 --- a/src/hook/useCommentInput.tsx +++ b/src/hook/useCommentInput.tsx @@ -1,6 +1,10 @@ +import { Flex, Text } from "@chakra-ui/react" import { getCommentByObjNameAndId } from "api/comment/comment" +import { CommentInput } from "component/comment/Comment" +import { CommentTooltip } from "component/comment/CommentTooltip" import { useUserContext } from "context/user" import { ChangeEvent, useEffect, useState } from "react" +import { FiMessageSquare } from "react-icons/fi" import { useQuery } from "react-query" import { useCommentCreateMutation, @@ -13,10 +17,11 @@ import { WithId } from "type/withId" interface useCommentInputProps { objectId?: number objectName: string + isAsTooltip?: boolean } export const useCommentInput = (props: useCommentInputProps) => { - const { objectId, objectName } = props + const { objectId, objectName, isAsTooltip } = props const { currentUser } = useUserContext() const userId = currentUser?.id @@ -41,6 +46,7 @@ export const useCommentInput = (props: useCommentInputProps) => { commentDeleteMutation.isLoading const isPrevCommentExists = !!prevComment + const isCommentExists = !!comment.trim() const isCommentEmpty = isPrevCommentExists && !comment.trim() const isCommentChanged = prevComment?.comment?.trim() !== comment.trim() @@ -89,10 +95,40 @@ export const useCommentInput = (props: useCommentInputProps) => { setComment(prevComment?.comment || "") }, [prevComment]) + const CommentComponent = isCommentExists ? ( + <> + {isAsTooltip ? ( + + ) : ( + + + + + {comment} + + + )} + + ) : ( + <> + ) + + const CommentInputComponent = ( + + ) + return { + CommentComponent, + CommentInputComponent, comment, handleCommentChange, onCommentSubmit, + handleCommentClear, isCommentLoading, + isCommentExists, } } diff --git a/src/service/storage/storage.ts b/src/service/storage/storage.ts index 1db04fb..cc67584 100644 --- a/src/service/storage/storage.ts +++ b/src/service/storage/storage.ts @@ -3,6 +3,7 @@ import { createStorage, deleteStorage, moveGoodsToStorage, + revertGoodsToDelivery, updateStorage, } from "api/storage/storage" import { useMutation } from "react-query" @@ -12,7 +13,16 @@ export const useMoveGoodsToStorageMutation = () => { onSuccess: () => { queryClient.invalidateQueries("purchaseDeliveriesList") queryClient.invalidateQueries("storageGoodsList") - queryClient.invalidateQueries("storageGoodsCount") + }, + }) +} + +export const useRevertDeliveryMutation = () => { + return useMutation(revertGoodsToDelivery, { + onSuccess: () => { + queryClient.invalidateQueries("purchaseDeliveriesList") + queryClient.invalidateQueries("storageGoodsList") + queryClient.invalidateQueries("goodWithStorages") }, }) } diff --git a/src/type/storage/storageGood.ts b/src/type/storage/storageGood.ts index d62da3a..267bb62 100644 --- a/src/type/storage/storageGood.ts +++ b/src/type/storage/storageGood.ts @@ -42,7 +42,7 @@ export type StorageGoodUpdate = { export type StorageGoodSearchFilter = WithId export type FullStorageGoodWithProductionInfo = FullStorageGood & { - production_info?: ProductionInfo + production_info?: WithId } export type StorageGoodWithProductionInfo = StorageGood & {