Skip to content

Commit a4cb73b

Browse files
committed
feat: 결제 완료 후 특정 페이지로 넘어가도록 수정
1 parent de0d74f commit a4cb73b

File tree

2 files changed

+139
-120
lines changed

2 files changed

+139
-120
lines changed

packages/shop/src/components/features/cart.tsx

Lines changed: 112 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ErrorBoundary, Suspense } from "@suspensive/react";
44
import { useQueryClient } from "@tanstack/react-query";
55
import { enqueueSnackbar, OptionsObject } from "notistack";
66
import * as React from "react";
7+
import { useNavigate } from "react-router-dom";
78
import * as R from "remeda";
89

910
import ShopHooks from "../../hooks";
@@ -65,130 +66,124 @@ type CartStatusStateType = {
6566
openBackdrop: boolean;
6667
};
6768

68-
export const CartStatus: React.FC<{ onPaymentCompleted?: () => void }> = Suspense.with(
69-
{ fallback: <CircularProgress /> },
70-
({ onPaymentCompleted }) => {
71-
const queryClient = useQueryClient();
72-
const { language, shopImpAccountId } = ShopHooks.useShopContext();
73-
const shopAPIClient = ShopHooks.useShopClient();
74-
const cartOrderStartMutation = ShopHooks.usePrepareCartOrderMutation(shopAPIClient);
75-
const removeItemFromCartMutation = ShopHooks.useRemoveItemFromCartMutation(shopAPIClient);
76-
const [state, setState] = React.useState<CartStatusStateType>({
77-
openDialog: false,
78-
openBackdrop: false,
79-
});
69+
export const CartStatus: React.FC = Suspense.with({ fallback: <CircularProgress /> }, () => {
70+
const queryClient = useQueryClient();
71+
const navigate = useNavigate();
72+
const { language, shopImpAccountId } = ShopHooks.useShopContext();
73+
const shopAPIClient = ShopHooks.useShopClient();
74+
const cartOrderStartMutation = ShopHooks.usePrepareCartOrderMutation(shopAPIClient);
75+
const removeItemFromCartMutation = ShopHooks.useRemoveItemFromCartMutation(shopAPIClient);
76+
const [state, setState] = React.useState<CartStatusStateType>({
77+
openDialog: false,
78+
openBackdrop: false,
79+
});
8080

81-
const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) =>
82-
enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } });
81+
const addSnackbar = (c: string | React.ReactNode, variant: OptionsObject["variant"]) =>
82+
enqueueSnackbar(c, { variant, anchorOrigin: { vertical: "bottom", horizontal: "center" } });
8383

84-
const cartIsEmptyStr = language === "ko" ? "장바구니가 비어있어요!" : "Your cart is empty!";
85-
const totalPriceStr = language === "ko" ? "총 결제 금액" : "Total Payment Amount";
86-
const orderCartStr = language === "ko" ? "장바구니에 담긴 상품 결제" : "Pay for Items in Cart";
87-
const succeededToRemoveItemFromCartStr =
88-
language === "ko" ? "장바구니에서 상품을 삭제했습니다." : "Item has been removed from the cart.";
89-
const errorWhileLoadingCartStr =
90-
language === "ko"
91-
? "장바구니 정보를 불러오는 중 문제가 발생했습니다."
92-
: "An error occurred while loading the cart information.";
93-
const errorWhilePreparingOrderStr =
94-
language === "ko"
95-
? "장바구니 결제 준비 중 문제가 발생했습니다.\n잠시 후 다시 시도해주세요."
96-
: "An error occurred while preparing the cart order.\nPlease try again later.";
97-
const failedToOrderStr =
98-
language === "ko"
99-
? "장바구니 결제에 실패했습니다.\n잠시 후 다시 시도해주세요.\n"
100-
: "Failed to complete the cart order.\nPlease try again later.\n";
84+
const cartIsEmptyStr = language === "ko" ? "장바구니가 비어있어요!" : "Your cart is empty!";
85+
const totalPriceStr = language === "ko" ? "총 결제 금액" : "Total Payment Amount";
86+
const orderCartStr = language === "ko" ? "장바구니에 담긴 상품 결제" : "Pay for Items in Cart";
87+
const succeededToRemoveItemFromCartStr =
88+
language === "ko" ? "장바구니에서 상품을 삭제했습니다." : "Item has been removed from the cart.";
89+
const errorWhileLoadingCartStr =
90+
language === "ko"
91+
? "장바구니 정보를 불러오는 중 문제가 발생했습니다."
92+
: "An error occurred while loading the cart information.";
93+
const errorWhilePreparingOrderStr =
94+
language === "ko"
95+
? "장바구니 결제 준비 중 문제가 발생했습니다.\n잠시 후 다시 시도해주세요."
96+
: "An error occurred while preparing the cart order.\nPlease try again later.";
97+
const failedToOrderStr =
98+
language === "ko"
99+
? "장바구니 결제에 실패했습니다.\n잠시 후 다시 시도해주세요.\n"
100+
: "Failed to complete the cart order.\nPlease try again later.\n";
101101

102-
const removeItemFromCart = (cartProductId: string) =>
103-
removeItemFromCartMutation.mutate(
104-
{ cartProductId },
105-
{
106-
onSuccess: () => addSnackbar(succeededToRemoveItemFromCartStr, "success"),
107-
onError: (error) => addSnackbar(error.message || errorWhilePreparingOrderStr, "error"),
108-
}
109-
);
102+
const removeItemFromCart = (cartProductId: string) =>
103+
removeItemFromCartMutation.mutate(
104+
{ cartProductId },
105+
{
106+
onSuccess: () => addSnackbar(succeededToRemoveItemFromCartStr, "success"),
107+
onError: (error) => addSnackbar(error.message || errorWhilePreparingOrderStr, "error"),
108+
}
109+
);
110110

111-
const openDialog = () => setState((ps) => ({ ...ps, openDialog: true }));
112-
const closeDialog = () => setState((ps) => ({ ...ps, openDialog: false }));
113-
const openBackdrop = () => setState((ps) => ({ ...ps, openBackdrop: true }));
114-
const closeBackdrop = () => setState((ps) => ({ ...ps, openBackdrop: false }));
111+
const openDialog = () => setState((ps) => ({ ...ps, openDialog: true }));
112+
const closeDialog = () => setState((ps) => ({ ...ps, openDialog: false }));
113+
const openBackdrop = () => setState((ps) => ({ ...ps, openBackdrop: true }));
114+
const closeBackdrop = () => setState((ps) => ({ ...ps, openBackdrop: false }));
115115

116-
const onFormSubmit = (formData: ShopSchemas.CustomerInfo) => {
117-
closeDialog();
118-
openBackdrop();
119-
cartOrderStartMutation.mutate(formData, {
120-
onSuccess: (order: ShopSchemas.Order) => {
121-
ShopUtils.startPortOnePurchase(
122-
shopImpAccountId,
123-
order,
124-
() => {
125-
queryClient.invalidateQueries();
126-
queryClient.resetQueries();
127-
onPaymentCompleted?.();
128-
},
129-
(response) => alert(failedToOrderStr + response.error_msg),
130-
closeBackdrop
131-
);
132-
},
133-
onError: (error) => alert(error.message || errorWhilePreparingOrderStr),
134-
});
135-
};
116+
const onFormSubmit = (formData: ShopSchemas.CustomerInfo) => {
117+
closeDialog();
118+
openBackdrop();
119+
cartOrderStartMutation.mutate(formData, {
120+
onSuccess: (order: ShopSchemas.Order) => {
121+
ShopUtils.startPortOnePurchase(
122+
shopImpAccountId,
123+
order,
124+
() => {
125+
queryClient.invalidateQueries();
126+
queryClient.resetQueries();
127+
navigate("/store/thank-you-for-your-purchase");
128+
},
129+
(response) => alert(failedToOrderStr + response.error_msg),
130+
closeBackdrop
131+
);
132+
},
133+
onError: (error) => alert(error.message || errorWhilePreparingOrderStr),
134+
});
135+
};
136136

137-
const disabled = removeItemFromCartMutation.isPending || cartOrderStartMutation.isPending;
137+
const disabled = removeItemFromCartMutation.isPending || cartOrderStartMutation.isPending;
138138

139-
const WrappedShopCartList: React.FC = () => {
140-
const { data } = ShopHooks.useCart(shopAPIClient);
139+
const WrappedShopCartList: React.FC = () => {
140+
const { data } = ShopHooks.useCart(shopAPIClient);
141141

142-
return !R.isArray(data.products) || data.products.length === 0 ? (
143-
<Typography variant="body1" color="error">
144-
{cartIsEmptyStr}
142+
return !R.isArray(data.products) || data.products.length === 0 ? (
143+
<Typography variant="body1" color="error">
144+
{cartIsEmptyStr}
145+
</Typography>
146+
) : (
147+
<>
148+
<Backdrop sx={(theme) => ({ zIndex: theme.zIndex.drawer + 1 })} open={state.openBackdrop} onClick={() => {}} />
149+
<CommonComponents.CustomerInfoFormDialog
150+
open={state.openDialog}
151+
closeFunc={closeDialog}
152+
onSubmit={onFormSubmit}
153+
defaultValue={data.customer_info}
154+
/>
155+
<Stack spacing={2}>
156+
<Common.Components.MDX.OneDetailsOpener>
157+
{data.products.map((prodRel) => (
158+
<CartItem
159+
language={language}
160+
key={prodRel.id}
161+
cartProdRel={prodRel}
162+
disabled={disabled}
163+
removeItemFromCartFunc={removeItemFromCart}
164+
/>
165+
))}
166+
</Common.Components.MDX.OneDetailsOpener>
167+
</Stack>
168+
<br />
169+
<Divider />
170+
<Typography variant="h6" sx={{ textAlign: "end" }}>
171+
{totalPriceStr}: <CommonComponents.PriceDisplay price={data.first_paid_price} />
145172
</Typography>
146-
) : (
147-
<>
148-
<Backdrop
149-
sx={(theme) => ({ zIndex: theme.zIndex.drawer + 1 })}
150-
open={state.openBackdrop}
151-
onClick={() => {}}
152-
/>
153-
<CommonComponents.CustomerInfoFormDialog
154-
open={state.openDialog}
155-
closeFunc={closeDialog}
156-
onSubmit={onFormSubmit}
157-
defaultValue={data.customer_info}
158-
/>
159-
<Stack spacing={2}>
160-
<Common.Components.MDX.OneDetailsOpener>
161-
{data.products.map((prodRel) => (
162-
<CartItem
163-
language={language}
164-
key={prodRel.id}
165-
cartProdRel={prodRel}
166-
disabled={disabled}
167-
removeItemFromCartFunc={removeItemFromCart}
168-
/>
169-
))}
170-
</Common.Components.MDX.OneDetailsOpener>
171-
</Stack>
172-
<br />
173-
<Divider />
174-
<Typography variant="h6" sx={{ textAlign: "end" }}>
175-
{totalPriceStr}: <CommonComponents.PriceDisplay price={data.first_paid_price} />
176-
</Typography>
177-
<Button variant="contained" color="secondary" onClick={openDialog} disabled={disabled}>
178-
{orderCartStr}
179-
</Button>
180-
</>
181-
);
182-
};
183-
184-
return (
185-
<CommonComponents.SignInGuard>
186-
<ErrorBoundary fallback={errorWhileLoadingCartStr}>
187-
<Suspense fallback={<CircularProgress />}>
188-
<WrappedShopCartList />
189-
</Suspense>
190-
</ErrorBoundary>
191-
</CommonComponents.SignInGuard>
173+
<Button variant="contained" color="secondary" onClick={openDialog} disabled={disabled}>
174+
{orderCartStr}
175+
</Button>
176+
</>
192177
);
193-
}
194-
);
178+
};
179+
180+
return (
181+
<CommonComponents.SignInGuard>
182+
<ErrorBoundary fallback={errorWhileLoadingCartStr}>
183+
<Suspense fallback={<CircularProgress />}>
184+
<WrappedShopCartList />
185+
</Suspense>
186+
</ErrorBoundary>
187+
</CommonComponents.SignInGuard>
188+
);
189+
});

packages/shop/src/components/features/product.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import * as Common from "@frontend/common";
22
import { AccordionProps, Button, ButtonProps, CircularProgress, Divider, Stack, Typography } from "@mui/material";
33
import { ErrorBoundary, Suspense } from "@suspensive/react";
4+
import { useQueryClient } from "@tanstack/react-query";
45
import { enqueueSnackbar, OptionsObject } from "notistack";
56
import * as React from "react";
7+
import { useNavigate } from "react-router-dom";
68
import * as R from "remeda";
79

810
import ShopHooks from "../../hooks";
@@ -82,6 +84,7 @@ const ProductItem: React.FC<ProductItemPropType> = ({
8284
startPurchaseProcess,
8385
...props
8486
}) => {
87+
const navigate = useNavigate();
8588
const optionFormRef = React.useRef<HTMLFormElement>(null);
8689
const shopAPIClient = ShopHooks.useShopClient();
8790
const addItemToCartMutation = ShopHooks.useAddItemToCartMutation(shopAPIClient);
@@ -101,6 +104,7 @@ const ProductItem: React.FC<ProductItemPropType> = ({
101104
language === "ko"
102105
? "장바구니에 상품을 담는 중 문제가 발생했어요,\n잠시 후 다시 시도해주세요."
103106
: "An error occurred while adding the product to the cart,\nplease try again later.";
107+
const gotoCartPageStr = language === "ko" ? "장바구니로 이동" : "Go to Cart";
104108

105109
const formOnSubmit: React.FormEventHandler = (e) => {
106110
e.preventDefault();
@@ -113,8 +117,22 @@ const ProductItem: React.FC<ProductItemPropType> = ({
113117

114118
const addItemToCart = () =>
115119
addItemToCartMutation.mutate(getCartAppendRequestPayload(product, optionFormRef), {
116-
onSuccess: () => addSnackbar(succeededToAddOneItemToCartStr, "success"),
117-
onError: () => addSnackbar(failedToAddOneItemToCartStr, "error"),
120+
onSuccess: () =>
121+
addSnackbar(
122+
<Stack spacing={2} justifyContent="center" alignItems="center" sx={{ width: "100%", flexGrow: 1 }}>
123+
<div>{succeededToAddOneItemToCartStr}</div>
124+
<Button
125+
variant="outlined"
126+
color="primary"
127+
onClick={() => navigate("/store/cart")}
128+
children={gotoCartPageStr}
129+
sx={{ backgroundColor: "white" }}
130+
fullWidth
131+
/>
132+
</Stack>,
133+
"success"
134+
),
135+
onError: () => alert(failedToAddOneItemToCartStr),
118136
});
119137
const onOrderOneItemButtonClick = () => startPurchaseProcess(getCartAppendRequestPayload(product, optionFormRef));
120138
const actionButton = R.isNullish(notPurchasableReason) && (
@@ -170,6 +188,8 @@ type ProductStateType = {
170188

171189
export const ProductList: React.FC<ShopSchemas.ProductListQueryParams> = (qs) => {
172190
const WrappedProductList: React.FC = () => {
191+
const queryClient = useQueryClient();
192+
const navigate = useNavigate();
173193
const { language, shopImpAccountId } = ShopHooks.useShopContext();
174194
const shopAPIClient = ShopHooks.useShopClient();
175195
const oneItemOrderStartMutation = ShopHooks.usePrepareOneItemOrderMutation(shopAPIClient);
@@ -210,7 +230,11 @@ export const ProductList: React.FC<ShopSchemas.ProductListQueryParams> = (qs) =>
210230
ShopUtils.startPortOnePurchase(
211231
shopImpAccountId,
212232
order,
213-
() => {},
233+
() => {
234+
queryClient.invalidateQueries();
235+
queryClient.resetQueries();
236+
navigate("/store/thank-you-for-your-purchase");
237+
},
214238
(response) => alert(failedToOrderStr + response.error_msg),
215239
closeBackdrop
216240
);

0 commit comments

Comments
 (0)