From d3a6ce770bdd7a16be610a96a498aae5b90ba790 Mon Sep 17 00:00:00 2001 From: aniketceminds Date: Sat, 28 Sep 2024 14:37:47 +0530 Subject: [PATCH] fix(p2p-energy): added summary, billing, payment, orderconfirmarion page with integrtaion --- apps/p2p-energy/pages/cart.tsx | 114 ++++++++ apps/p2p-energy/pages/checkout.tsx | 245 ++++++++++++++++++ apps/p2p-energy/pages/orderConfirmation.tsx | 97 +++++++ apps/p2p-energy/pages/product.tsx | 55 +++- apps/p2p-energy/public/images/cartIcon.svg | 19 ++ apps/p2p-energy/public/images/emptyCard.svg | 4 + .../public/images/orderConfirmmark.svg | 28 ++ apps/p2p-energy/store/index.ts | 4 +- .../src/components/checkout/checkout.tsx | 4 +- .../src/components/checkout/checkout.types.ts | 2 +- packages/common/src/utils/payload.ts | 30 ++- 11 files changed, 573 insertions(+), 29 deletions(-) create mode 100644 apps/p2p-energy/pages/cart.tsx create mode 100644 apps/p2p-energy/pages/checkout.tsx create mode 100644 apps/p2p-energy/pages/orderConfirmation.tsx create mode 100644 apps/p2p-energy/public/images/cartIcon.svg create mode 100644 apps/p2p-energy/public/images/emptyCard.svg create mode 100644 apps/p2p-energy/public/images/orderConfirmmark.svg diff --git a/apps/p2p-energy/pages/cart.tsx b/apps/p2p-energy/pages/cart.tsx new file mode 100644 index 000000000..3bbee5891 --- /dev/null +++ b/apps/p2p-energy/pages/cart.tsx @@ -0,0 +1,114 @@ +import { useRouter } from 'next/router' + +import React from 'react' +import { useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { useLanguage } from '@hooks/useLanguage' +import { Cart as BecknCart } from '@beckn-ui/becknified-components' + +import { Box, useToast } from '@chakra-ui/react' + +import { CartItemProps } from '@beckn-ui/becknified-components/src/components/cart/cart.types' +import { getSelectPayload } from '@beckn-ui/common/src/utils' +import { DiscoveryRootState, ICartRootState } from '@beckn-ui/common/lib/types' +import { cartActions } from '@beckn-ui/common/src/store/cart-slice' +import { DOMAIN } from '@lib/config' +import { useSelectMutation } from '@beckn-ui/common/src/services/select' +import { testIds } from '@shared/dataTestIds' + +const Cart = () => { + const [fetchQuotes, { isLoading, data, isError }] = useSelectMutation() + const dispatch = useDispatch() + const toast = useToast() + + const router = useRouter() + const { t } = useLanguage() + + const { items, totalQuantity } = useSelector((state: ICartRootState) => state.cart) + const totalAmount = useSelector((state: ICartRootState) => state.cart.totalAmount) + const { transactionId, productList } = useSelector((state: DiscoveryRootState) => state.discovery) + + useEffect(() => { + if (items.length > 0) { + fetchQuotes(getSelectPayload(items, transactionId, DOMAIN)) + } + }, [totalQuantity]) + + const handleShopButton = () => { + router.push('/') + } + + const onOrderClick = () => { + router.push('/checkout') + } + + return ( + + + ({ + id: singleItem.id, + quantity: singleItem.quantity, + name: singleItem.name, + image: singleItem.images?.[0].url, + price: Number(singleItem.price.value), + symbol: singleItem.price.currency, + totalAmountText: t.totalAmount, + handleIncrement: id => { + const selectedItem = productList.find(singleItem => singleItem.item.id === id) + if (selectedItem) { + dispatch(cartActions.addItemToCart({ product: selectedItem, quantity: 1 })) + } + }, + handleDecrement: id => { + dispatch(cartActions.removeItemFromCart(id)) + } + }) as CartItemProps + ), + loader: { text: t.quoteRequestLoader, dataTest: testIds.loadingIndicator }, + orderSummary: { + totalAmount: { + price: totalAmount, + currencyType: items[0]?.price.currency + }, + totalQuantity: { + text: totalQuantity.toString(), + variant: 'subTitleSemibold' + }, + pageCTA: { + text: t.procced, + handleClick: onOrderClick + }, + orderSummaryText: t.orderSummary, + totalQuantityText: t.totalQuantity, + totalAmountText: t.totalAmount, + dataTestTotalQuantity: testIds.cartpage_totalQuantityText, + dataTestTotalAmount: testIds.cartpage_totalAmountText, + dataTestCta: testIds.cartpage_cartOrderButton + }, + emptyCard: { + image: '/images/emptyCard.svg', + heading: t.emptyCardHeading, + subHeading: t.emptyCardSubHeading, + buttonText: t.shop, + buttonHanler: handleShopButton, + dataTestImage: testIds.cartpage_emptyImage, + dataTestHeading: testIds.cartpage_emptyheading, + dataTestSubHeading: testIds.cartpage_emptySubHeading, + dataTestCta: testIds.cartpage_emptyButton + } + }} + /> + + ) +} + +export default Cart diff --git a/apps/p2p-energy/pages/checkout.tsx b/apps/p2p-energy/pages/checkout.tsx new file mode 100644 index 000000000..98169a851 --- /dev/null +++ b/apps/p2p-energy/pages/checkout.tsx @@ -0,0 +1,245 @@ +import React, { useEffect, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { Box, useToast, useTheme } from '@chakra-ui/react' +import { DOMAIN } from '@lib/config' +import { useLanguage } from '../hooks/useLanguage' +import { + areShippingAndBillingDetailsSame, + getInitPayload, + getSubTotalAndDeliveryCharges +} from '@beckn-ui/common/src/utils' +import { Checkout } from '@beckn-ui/becknified-components' +import { useRouter } from 'next/router' +import { ShippingFormInitialValuesType } from '@beckn-ui/becknified-components' +import { isEmpty } from '@beckn-ui/common/src/utils' +import { FormField } from '@beckn-ui/molecules' +import { checkoutActions, CheckoutRootState } from '@beckn-ui/common/src/store/checkout-slice' +import { useInitMutation } from '@beckn-ui/common/src/services/init' +import { DiscoveryRootState, ICartRootState, PaymentBreakDownModel } from '@beckn-ui/common' +import { cartActions } from '@beckn-ui/common/src/store/cart-slice' +import { testIds } from '@shared/dataTestIds' + +export type ShippingFormData = { + name: string + mobileNumber: string + email: string + address: string + zipCode: string +} + +const CheckoutPage = () => { + const cartItems = useSelector((state: ICartRootState) => state.cart.items) + + const theme = useTheme() + const bgColorOfSecondary = theme.colors.secondary['100'] + const toast = useToast() + + const [shippingFormData, setShippingFormData] = useState({ + name: 'Anand', + mobileNumber: '9886098860', + email: 'nobody@nomail.com', + address: 'B005 aspire heights, Jurong East, SGP', + pinCode: '680230' + }) + + const router = useRouter() + const dispatch = useDispatch() + const [initialize, { isLoading, isError }] = useInitMutation() + const { t, locale } = useLanguage() + const initResponse = useSelector((state: CheckoutRootState) => state.checkout.initResponse) + const selectResponse = useSelector((state: CheckoutRootState) => state.checkout.selectResponse) + const isBillingSameRedux = useSelector((state: CheckoutRootState) => state.checkout.isBillingSame) + const { transactionId, productList } = useSelector((state: DiscoveryRootState) => state.discovery) + + ////////// For field Data /////////// + const formFieldConfig: FormField[] = [ + { + name: 'name', + label: t.formName, + type: 'text', + validate: (value: string) => { + if (!value.trim()) return t.errorName + return undefined + } + }, + { + name: 'mobileNumber', + label: t.formNumber, + type: 'number', + validate: (value: string) => { + if (!value.trim()) return t.errorNumber + if (!/^\d{10}$/.test(value)) return t.errorNumber2 + return undefined + } + }, + { + name: 'email', + label: t.formEmail, + type: 'email', + validate: (value: string) => { + if (!value.trim()) return t.requiredEmail + if (!/\S+@\S+\.\S+/.test(value)) return t.invalidEmail + return undefined + } + }, + { + name: 'address', + label: t.formAddress, + type: 'text', + validate: (value: string) => { + if (!value.trim()) return t.errorAddress + return undefined + } + }, + { + name: 'pinCode', + label: t.formZipCode, + type: 'text', + validate: (value: string) => { + if (!value.trim()) return t.errorZipcode + if (!/^\d{5,6}$/.test(value)) return t.errorZipcode2 + return undefined + } + } + ] + + useEffect(() => { + if (localStorage) { + if (localStorage.getItem('userPhone')) { + const copiedFormData = structuredClone(shippingFormData) + + copiedFormData.mobileNumber = localStorage.getItem('userPhone') as string + + setShippingFormData(copiedFormData) + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + useEffect(() => { + if (typeof window !== 'undefined') { + if (localStorage.getItem('shippingAdress')) { + setShippingFormData(JSON.parse(localStorage.getItem('shippingAdress') as string)) + } + } + }, []) + + useEffect(() => { + const shippingAddressComplete = Object.values(shippingFormData).every(value => value.length > 0) + if (shippingAddressComplete && typeof window !== 'undefined') { + localStorage.setItem('shippingAdress', JSON.stringify(shippingFormData)) + } + }, [shippingFormData]) + + const formSubmitHandler = (data: any) => { + if (data) { + const { id, type } = + selectResponse[0].message.order.fulfillments?.[0] || + (selectResponse[0].message.order.provider as any)?.fulfillments[0] + getInitPayload(shippingFormData, {}, cartItems, transactionId, DOMAIN, { id, type }).then(res => { + return initialize(res) + }) + } + } + + const isInitResultPresent = () => { + return !!initResponse && initResponse.length > 0 + } + + const createPaymentBreakdownMap = () => { + const paymentBreakdownMap: PaymentBreakDownModel = {} + let totalPayment: number = 0 + if (isInitResultPresent()) { + initResponse[0].message.order.quote.breakup.forEach(breakup => { + let price = breakup.price.value + const totalAmount = cartItems?.[0]?.totalPrice + if (breakup.title === 'base-price') { + price = (totalAmount - totalAmount * (12 / 100)).toString() + } + if (breakup.title === 'taxes') { + price = (totalAmount * (12 / 100)).toString() + } + paymentBreakdownMap[breakup.title] = { + value: price, + currency: breakup.price.currency + } + totalPayment = Number(totalPayment) + Number(price) + }) + } + return { paymentBreakdownMap, totalPayment } + } + + return ( + + {/* start Item Details */} + ({ + title: singleItem.name, + description: singleItem.short_desc, + quantity: singleItem.quantity, + // priceWithSymbol: `${currencyMap[singleItem.price.currency]}${singleItem.totalPrice}`, + price: singleItem.totalPrice, + currency: singleItem.price.currency, + image: singleItem.images?.[0].url + })) + }, + shipping: { + triggerFormTitle: t.change, + showDetails: isInitResultPresent(), + color: bgColorOfSecondary, + shippingDetails: { + name: shippingFormData.name, + location: shippingFormData.address!, + number: shippingFormData.mobileNumber, + title: t.shipping + }, + shippingForm: { + formFieldConfig: formFieldConfig, + onSubmit: formSubmitHandler, + submitButton: { text: t.saveShippingDetails }, + values: shippingFormData, + onChange: data => setShippingFormData(data) + }, + sectionTitle: t.shipping, + formTitle: t.addShippingDetails, + sectionSubtitle: t.addShippingDetails, + dataTest: testIds.checkoutpage_shippingDetails + }, + payment: { + title: t.payment, + paymentDetails: { + hasBoxShadow: false, + paymentBreakDown: createPaymentBreakdownMap().paymentBreakdownMap, + totalText: t.total, + totalValueWithCurrency: { + value: createPaymentBreakdownMap().totalPayment.toString(), + currency: getSubTotalAndDeliveryCharges(initResponse).currencySymbol! + } + } + }, + loader: { + text: t.initializingOrderLoader + }, + pageCTA: { + text: t.proceedToCheckout, + dataTest: testIds.checkoutpage_proceedToCheckout, + handleClick: () => { + dispatch(cartActions.clearCart()) + router.push('/paymentMode') + } + } + }} + isLoading={isLoading} + hasInitResult={isInitResultPresent()} + /> + + ) +} +export default CheckoutPage diff --git a/apps/p2p-energy/pages/orderConfirmation.tsx b/apps/p2p-energy/pages/orderConfirmation.tsx new file mode 100644 index 000000000..c990c6bff --- /dev/null +++ b/apps/p2p-energy/pages/orderConfirmation.tsx @@ -0,0 +1,97 @@ +import React, { useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import orderConfirmmark from '../public/images/orderConfirmmark.svg' +import { useSelector, useDispatch } from 'react-redux' +import { useLanguage } from '../hooks/useLanguage' +import { ConfirmationPage } from '@beckn-ui/becknified-components' +import axios from '@services/axios' +import { Box } from '@chakra-ui/react' +import Cookies from 'js-cookie' +import { utilGenerateEllipsedText } from '@beckn-ui/molecules' +import LoaderWithMessage from '@components/loader/LoaderWithMessage' +import { ConfirmResponseModel } from '@beckn-ui/common/lib/types' +import { checkoutActions, CheckoutRootState } from '@beckn-ui/common/src/store/checkout-slice' +import { orderActions } from '@beckn-ui/common/src/store/order-slice' +import { getPayloadForConfirm, getPayloadForOrderHistoryPost } from '@beckn-ui/common/src/utils' +import { useConfirmMutation } from '@beckn-ui/common/src/services/confirm' +import { testIds } from '@shared/dataTestIds' +import { ORDER_CATEGORY_ID } from '../lib/config' + +const OrderConfirmation = () => { + const { t } = useLanguage() + const router = useRouter() + const [confirmData, setConfirmData] = useState([]) + const [confirm, { isLoading, data }] = useConfirmMutation() + const dispatch = useDispatch() + const [orderId, setOrderId] = useState() + + const initResponse = useSelector((state: CheckoutRootState) => state.checkout.initResponse) + const confirmResponse = useSelector((state: CheckoutRootState) => state.checkout.confirmResponse) + const apiUrl = process.env.NEXT_PUBLIC_API_URL + const strapiUrl = process.env.NEXT_PUBLIC_STRAPI_URL + + const bearerToken = Cookies.get('authToken') + const axiosConfig = { + headers: { + Authorization: `Bearer ${bearerToken}`, + 'Content-Type': 'application/json' // You can set the content type as needed + } + } + + useEffect(() => { + if (confirmResponse && confirmResponse.length > 0) { + setOrderId(confirmResponse[0].message.orderId.slice(0, 8)) + } + }, [confirmResponse]) + + useEffect(() => { + if (initResponse && initResponse.length > 0) { + const payLoad = getPayloadForConfirm(initResponse) + confirm(payLoad) + } + }, []) + + if (isLoading || !confirmResponse) { + return ( + + + + ) + } + + return ( + + { + router.push('/') + dispatch(checkoutActions.clearState()) + }, + disabled: false, + variant: 'outline', + colorScheme: 'primary', + dataTest: testIds.orderConfirmation_goBackToHome + } + ] + }} + /> + + ) +} + +export default OrderConfirmation diff --git a/apps/p2p-energy/pages/product.tsx b/apps/p2p-energy/pages/product.tsx index ec9606acd..af07fe5fb 100644 --- a/apps/p2p-energy/pages/product.tsx +++ b/apps/p2p-energy/pages/product.tsx @@ -1,19 +1,21 @@ import React, { useState } from 'react' import { ProductDetailPage, ProductPrice } from '@beckn-ui/becknified-components' import { useDispatch, useSelector } from 'react-redux' -import { Box, Flex } from '@chakra-ui/react' +import { Box, Flex, Input } from '@chakra-ui/react' import { useLanguage } from '@hooks/useLanguage' import { DiscoveryRootState, ParsedItemModel } from '@beckn-ui/common/lib/types' import { cartActions } from '@beckn-ui/common/src/store/cart-slice' import { feedbackActions } from '@beckn-ui/common/src/store/ui-feedback-slice' import { testIds } from '@shared/dataTestIds' import { Button, Typography } from '@beckn-ui/molecules' +import { useRouter } from 'next/router' const Product = () => { const { t } = useLanguage() const selectedProduct: ParsedItemModel = useSelector((state: DiscoveryRootState) => state.discovery.selectedProduct) - // const dispatch = useDispatch() - // const [counter, setCounter] = useState(1) + const dispatch = useDispatch() + const router = useRouter() + const [counter, setCounter] = useState(1000) // const [totalPrice, setTotalPrice] = useState(Number(selectedProduct.item.price.value)) // const increment = () => { @@ -32,6 +34,10 @@ const Product = () => { // } // } + const handleOnInput = (e: any) => { + setCounter(Number(e.target.value)) + } + if (!selectedProduct) { return <> } @@ -79,10 +85,34 @@ const Product = () => { rateLabel={selectedProduct.item.price.rateLabel} /> - {/*