-
+
handleNavigation(NEW_CAR_PAGE_ROUTE)}
+ >
아반떼 N
-
-
+
+
handleNavigation(PICK_EVENT_PAGE_ROUTE)}
+ >
내 아반떼 N 뽑기
-
-
+
+
handleNavigation(N_QUIZ_EVENT_PAGE_ROUTE)}
+ >
퀴즈
-
-
+
+
handleNavigation(PARTS_COLLECTION_PAGE_ROUTE)}
+ >
내 컬렉션
-
-
+
+
handleNavigation("#")}>
응모 내역 확인
-
+
);
};
diff --git a/packages/service/src/components/partsCollection/CustomCard/CustomCard.css.ts b/packages/service/src/components/partsCollection/CustomCard/CustomCard.css.ts
index 552b4643..d1328e1f 100644
--- a/packages/service/src/components/partsCollection/CustomCard/CustomCard.css.ts
+++ b/packages/service/src/components/partsCollection/CustomCard/CustomCard.css.ts
@@ -20,6 +20,8 @@ export const bgImg = css`
top: 0;
left: 0;
z-index: 1;
+ border-radius: 20px;
+ height: 100%;
${mobile(css`
width: 400px;
diff --git a/packages/service/src/components/partsCollection/CustomCard/CustomCard.tsx b/packages/service/src/components/partsCollection/CustomCard/CustomCard.tsx
index 9254c222..9ff05337 100644
--- a/packages/service/src/components/partsCollection/CustomCard/CustomCard.tsx
+++ b/packages/service/src/components/partsCollection/CustomCard/CustomCard.tsx
@@ -17,7 +17,7 @@ export const CustomCard = ({
-
+ {bgImg &&
}
diff --git a/packages/service/src/components/share/CustomCard/CustomCard.css.ts b/packages/service/src/components/share/CustomCard/CustomCard.css.ts
new file mode 100644
index 00000000..d1328e1f
--- /dev/null
+++ b/packages/service/src/components/share/CustomCard/CustomCard.css.ts
@@ -0,0 +1,86 @@
+import { css } from "@emotion/react";
+import { mobile } from "@service/common/responsive/responsive";
+
+export const card = css`
+ width: 700px;
+ aspect-ratio: 16 / 9;
+ border-radius: 20px;
+ background-color: white;
+ margin: 0 auto;
+ position: relative;
+
+ ${mobile(css`
+ width: 400px;
+ `)}
+`;
+
+export const bgImg = css`
+ position: absolute;
+ width: 700px;
+ top: 0;
+ left: 0;
+ z-index: 1;
+ border-radius: 20px;
+ height: 100%;
+
+ ${mobile(css`
+ width: 400px;
+ `)}
+`;
+
+export const carImg = css`
+ width: 700px;
+ z-index: 2;
+ position: absolute;
+
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+
+ ${mobile(css`
+ width: 450px;
+ `)}
+`;
+
+export const colorImg = css`
+ width: 700px;
+ z-index: 2;
+ position: absolute;
+
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+
+ ${mobile(css`
+ width: 450px;
+ `)}
+`;
+
+export const wheelImg = css`
+ z-index: 3;
+
+ position: absolute;
+ width: 245px;
+ bottom: 95px;
+ left: 305px;
+
+ ${mobile(css`
+ bottom: 45px;
+ left: 170px;
+ width: 161px;
+ `)}
+`;
+
+export const spoilerImg = css`
+ z-index: 3;
+ position: absolute;
+ width: 50px;
+ top: 144px;
+ right: 118px;
+
+ ${mobile(css`
+ width: 30px;
+ top: 78px;
+ right: 53px;
+ `)}
+`;
diff --git a/packages/service/src/components/share/CustomCard/CustomCard.tsx b/packages/service/src/components/share/CustomCard/CustomCard.tsx
new file mode 100644
index 00000000..51518151
--- /dev/null
+++ b/packages/service/src/components/share/CustomCard/CustomCard.tsx
@@ -0,0 +1,26 @@
+import * as style from "./CustomCard.css";
+
+export interface ICustomCardProps {
+ bgImg?: string;
+ spoilerImg?: string;
+ wheelImg?: string;
+ colorImg?: string;
+}
+
+export const CustomCard = ({
+ bgImg,
+ spoilerImg,
+ wheelImg,
+ colorImg,
+}: ICustomCardProps) => {
+ return (
+
+
+
+ {bgImg &&
}
+
+
+
+
+ );
+};
diff --git a/packages/service/src/components/share/CustomCard/index.ts b/packages/service/src/components/share/CustomCard/index.ts
new file mode 100644
index 00000000..f085d78f
--- /dev/null
+++ b/packages/service/src/components/share/CustomCard/index.ts
@@ -0,0 +1 @@
+export { CustomCard } from "./CustomCard";
diff --git a/packages/service/src/components/share/PartsTab/PartsCard/PartsCard.css.ts b/packages/service/src/components/share/PartsTab/PartsCard/PartsCard.css.ts
new file mode 100644
index 00000000..92507ac5
--- /dev/null
+++ b/packages/service/src/components/share/PartsTab/PartsCard/PartsCard.css.ts
@@ -0,0 +1,37 @@
+import { css } from "@emotion/react";
+import { mobile } from "@service/common/responsive/responsive";
+import { theme } from "@watermelon-clap/core/src/theme";
+
+export const container = css`
+ ${theme.flex.column}
+ width : 20%;
+ flex-shrink: 1;
+
+ ${mobile(css`
+ width: 30%;
+ margin: 0 4%;
+ `)}
+`;
+
+export const card = css`
+ border-radius: 20px;
+ aspect-ratio: 1 / 1;
+ overflow: hidden;
+ position: relative;
+ background-color: ${theme.color.white};
+ ${theme.flex.center};
+`;
+
+export const img = (cate: string) => css`
+ width: 100%;
+ width: ${cate === "REAR" && "80%"};
+ ${cate === "DRIVE_MODE" &&
+ "height : 80%; width : 80%; border-radius : 10px; object-fit : cover"};
+`;
+
+export const name = css`
+ ${theme.font.preB};
+ font-size: clamp(0px, calc(10px + 1vw), 24px);
+ color: ${theme.color.white};
+ margin-top: 20px;
+`;
diff --git a/packages/service/src/components/share/PartsTab/PartsCard/PartsCard.tsx b/packages/service/src/components/share/PartsTab/PartsCard/PartsCard.tsx
new file mode 100644
index 00000000..d6d1cc46
--- /dev/null
+++ b/packages/service/src/components/share/PartsTab/PartsCard/PartsCard.tsx
@@ -0,0 +1,20 @@
+import * as style from "./PartsCard.css";
+import { IParts } from "@watermelon-clap/core/src/types";
+
+interface IPartsCardProps {
+ partsData: IParts;
+}
+
+export const PartsCard = ({ partsData }: IPartsCardProps) => {
+ return (
+
+
+
+
+
{partsData.name}
+
+ );
+};
diff --git a/packages/service/src/components/share/PartsTab/PartsCard/index.ts b/packages/service/src/components/share/PartsTab/PartsCard/index.ts
new file mode 100644
index 00000000..396dab49
--- /dev/null
+++ b/packages/service/src/components/share/PartsTab/PartsCard/index.ts
@@ -0,0 +1 @@
+export { PartsCard } from "./PartsCard";
diff --git a/packages/service/src/components/share/PartsTab/PartsWrap.css.ts b/packages/service/src/components/share/PartsTab/PartsWrap.css.ts
new file mode 100644
index 00000000..090d7be5
--- /dev/null
+++ b/packages/service/src/components/share/PartsTab/PartsWrap.css.ts
@@ -0,0 +1,54 @@
+import { css } from "@emotion/react";
+import { mobile } from "@service/common/responsive/responsive";
+import { theme } from "@watermelon-clap/core/src/theme";
+
+export const container = css`
+ ${theme.flex.column}
+ margin: 0 auto;
+`;
+export const tabWrap = css`
+ display: flex;
+ justify-content: space-between;
+ margin: 0 auto;
+ margin-top: 80px;
+ width: 700px;
+
+ ${mobile(css`
+ width: 100%;
+ flex-wrap: wrap;
+
+ justify-content: space-around;
+ `)}
+`;
+
+export const tabBtn = (isSelected: boolean) => css`
+ border: ${isSelected ? `2px solid ${theme.color.white}` : `none`};
+ ${theme.font.preB};
+ font-size: 20px;
+ border-radius: 100px;
+ width: 130px;
+ height: 48px;
+
+ cursor: pointer;
+ background: none;
+ color: ${isSelected ? theme.color.white : theme.color.gray300};
+ outline: none;
+
+ ${mobile(css`
+ margin: 10px 8%;
+ `)}
+`;
+
+export const partsCardWrap = css`
+ ${theme.flex.between}
+ gap: 80px 20px;
+ flex-wrap: wrap;
+ width: 90%;
+ max-width: 1000px;
+ margin-top: 100px;
+
+ ${mobile(css`
+ justify-content: center;
+ gap: 20px;
+ `)}
+`;
diff --git a/packages/service/src/components/share/PartsTab/PartsWrap.tsx b/packages/service/src/components/share/PartsTab/PartsWrap.tsx
new file mode 100644
index 00000000..ddb2354d
--- /dev/null
+++ b/packages/service/src/components/share/PartsTab/PartsWrap.tsx
@@ -0,0 +1,19 @@
+import * as style from "./PartsWrap.css";
+import { PartsCard } from "./PartsCard";
+import { IParts } from "@watermelon-clap/core/src/types";
+
+export interface IPartsWrapProps {
+ equippedPartsData?: IParts[];
+}
+
+export const PartsWrap = ({ equippedPartsData }: IPartsWrapProps) => {
+ return (
+
+
+ {equippedPartsData?.map((partsData, idx) => (
+
+ ))}
+
+
+ );
+};
diff --git a/packages/service/src/components/share/PartsTab/index.ts b/packages/service/src/components/share/PartsTab/index.ts
new file mode 100644
index 00000000..f35538c0
--- /dev/null
+++ b/packages/service/src/components/share/PartsTab/index.ts
@@ -0,0 +1 @@
+export { PartsWrap } from "./PartsWrap";
diff --git a/packages/service/src/components/share/index.ts b/packages/service/src/components/share/index.ts
new file mode 100644
index 00000000..54b53a41
--- /dev/null
+++ b/packages/service/src/components/share/index.ts
@@ -0,0 +1 @@
+export * from "./CustomCard";
diff --git a/packages/service/src/constants/routes.ts b/packages/service/src/constants/routes.ts
index 14ce8a65..e316236d 100644
--- a/packages/service/src/constants/routes.ts
+++ b/packages/service/src/constants/routes.ts
@@ -13,5 +13,6 @@ export const NEW_CAR_PAGE_ROUTE = "/new-car" as const;
export const N_PARTS_PICK_PAGE_ROUTE = "/parts-pick" as const;
export const PICK_EVENT_PAGE_ROUTE = "/pick-event" as const;
export const PARTS_COLLECTION_PAGE_ROUTE = "/parts-collection" as const;
+export const SHARE_PAGE_ROUTE = "/share/:linkKey" as const;
export const N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE =
"/quiz-event-apply" as const;
diff --git a/packages/service/src/pages/Error/Error.tsx b/packages/service/src/pages/Error/Error.tsx
index 202560c0..28d7586e 100644
--- a/packages/service/src/pages/Error/Error.tsx
+++ b/packages/service/src/pages/Error/Error.tsx
@@ -3,9 +3,12 @@ import { errorContainerStyle, errorMessageStyle } from "./Error.css";
import { Button } from "@service/common/components/Button";
import { theme } from "@watermelon-clap/core/src/theme";
import { useNavigate } from "react-router-dom";
+import { useErrorBoundary } from "react-error-boundary";
+import { GlobalNavigationBar } from "@service/common/components/GlobalNavigationBar";
export const Error = ({ error, resetErrorBoundary }: FallbackProps) => {
const navigate = useNavigate();
+ const { resetBoundary } = useErrorBoundary();
let errorMessage;
switch (error.message) {
@@ -29,16 +32,19 @@ export const Error = ({ error, resetErrorBoundary }: FallbackProps) => {
const handleHomeRedirect = () => {
navigate("/");
- window.location.reload();
+ resetBoundary();
};
return (
-
-
{errorMessage}
-
-
-
+ <>
+
+
+
{errorMessage}
+
+
+
+
-
+ >
);
};
diff --git a/packages/service/src/pages/PartsCollection/PartsCollection.css.ts b/packages/service/src/pages/PartsCollection/PartsCollection.css.ts
index 8de7dd51..cb6cb1ec 100644
--- a/packages/service/src/pages/PartsCollection/PartsCollection.css.ts
+++ b/packages/service/src/pages/PartsCollection/PartsCollection.css.ts
@@ -3,7 +3,7 @@ import { mobile } from "@service/common/responsive/responsive";
import { theme } from "@watermelon-clap/core/src/theme";
export const mainBg = css`
- background-image: url("images/common/main-bg.webp");
+ background-image: url("/images/common/main-bg.webp");
background-size: cover;
padding-bottom: 200px;
`;
diff --git a/packages/service/src/pages/Share/Share.css.ts b/packages/service/src/pages/Share/Share.css.ts
new file mode 100644
index 00000000..dea7fb8c
--- /dev/null
+++ b/packages/service/src/pages/Share/Share.css.ts
@@ -0,0 +1,30 @@
+import { css } from "@emotion/react";
+import { mobile } from "@service/common/responsive/responsive";
+import { theme } from "@watermelon-clap/core/src/theme";
+
+export const mainBg = css`
+ background-image: url("/images/common/main-bg.webp");
+ background-size: cover;
+ padding-bottom: 200px;
+`;
+
+export const pageTitle = css`
+ text-align: center;
+ ${theme.font.pcpB}
+ font-size : calc(50px + 2vw);
+ padding: 100px;
+ color: ${theme.color.white};
+
+ ${mobile(css`
+ font-size: calc(20px + 2vw);
+ padding: 100px 0 50px 0;
+ `)}
+`;
+
+export const btn = css`
+ margin: 0 auto;
+ ${mobile(css`
+ width: fit-content;
+ padding: 10px 20px;
+ `)}
+`;
diff --git a/packages/service/src/pages/Share/Share.tsx b/packages/service/src/pages/Share/Share.tsx
new file mode 100644
index 00000000..7bfe8557
--- /dev/null
+++ b/packages/service/src/pages/Share/Share.tsx
@@ -0,0 +1,82 @@
+import { useSuspenseQuery } from "@tanstack/react-query";
+import { IMyParts, IParts } from "@watermelon-clap/core/src/types";
+import { getAccessToken } from "@watermelon-clap/core/src/utils";
+import { useState, useEffect } from "react";
+import * as style from "./Share.css";
+import { PartsWrap } from "@service/components/share/PartsTab";
+import {
+ CustomCard,
+ ICustomCardProps,
+} from "@service/components/share/CustomCard/CustomCard";
+import { useNavigate, useParams } from "react-router-dom";
+import { apiGetSharedParts } from "@service/apis/partsEvent/apiGetSharedParts";
+import { Button, ButtonVariant } from "@service/common/components/Button";
+import { Space } from "@service/common/styles/Space";
+import { MAIN_PAGE_ROUTE } from "@service/constants/routes";
+
+export const Share = () => {
+ const navigator = useNavigate();
+
+ const [equippedPartsImg, setEquippedPartsImg] = useState
();
+ const [equippedPartsData, setEquippedPartsData] = useState();
+
+ const { linkKey } = useParams();
+
+ const { data: partsDatas } = useSuspenseQuery({
+ queryKey: ["myParts", getAccessToken()],
+ queryFn: () => apiGetSharedParts(linkKey),
+ });
+
+ useEffect(() => {
+ setEquippedPartsImg(getEquippedParts(partsDatas)._equippedPartsImg);
+ setEquippedPartsData(getEquippedParts(partsDatas)._equippedPartsData);
+ }, [partsDatas]);
+
+ return (
+
+
아반떼 N 파츠 컬렉션
+
+
+
+
+
+ );
+};
+
+const getEquippedParts = (partsDatas?: IMyParts[]) => {
+ const _equippedPartsImg: ICustomCardProps = {};
+ const _equippedPartsData: IParts[] = [];
+
+ partsDatas?.map((cate) =>
+ cate.parts.map((parts) => {
+ if (parts.equipped) {
+ switch (parts.category) {
+ case "DRIVE_MODE":
+ _equippedPartsImg.bgImg = parts.imgSrc;
+ break;
+ case "COLOR":
+ _equippedPartsImg.colorImg = parts.imgSrc;
+ break;
+ case "REAR":
+ _equippedPartsImg.spoilerImg = parts.imgSrc;
+ break;
+ case "WHEEL":
+ _equippedPartsImg.wheelImg = parts.imgSrc;
+ break;
+ }
+ _equippedPartsData.push(parts);
+ }
+ }),
+ );
+
+ return {
+ _equippedPartsImg: _equippedPartsImg,
+ _equippedPartsData: _equippedPartsData,
+ };
+};
diff --git a/packages/service/src/pages/Share/index.ts b/packages/service/src/pages/Share/index.ts
new file mode 100644
index 00000000..ebca8e78
--- /dev/null
+++ b/packages/service/src/pages/Share/index.ts
@@ -0,0 +1 @@
+export { Share } from "./Share";
diff --git a/packages/service/src/pages/index.ts b/packages/service/src/pages/index.ts
index 0230fa7c..1621efdf 100644
--- a/packages/service/src/pages/index.ts
+++ b/packages/service/src/pages/index.ts
@@ -4,4 +4,5 @@ export { NewCar } from "./NewCar";
export { PartsCollection } from "./PartsCollection";
export { PartsPick } from "./PartsPick";
export { PickEvent } from "./PickEvent";
+export { Share } from "./Share";
export { NQuizEventWinnerApply } from "./NQuizEventWinnerApply";
diff --git a/packages/service/src/router.tsx b/packages/service/src/router.tsx
index c15845d8..58206498 100644
--- a/packages/service/src/router.tsx
+++ b/packages/service/src/router.tsx
@@ -14,6 +14,7 @@ import {
PICK_EVENT_PAGE_ROUTE,
N_PARTS_PICK_PAGE_ROUTE,
PARTS_COLLECTION_PAGE_ROUTE,
+ SHARE_PAGE_ROUTE,
N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE,
} from "./constants/routes";
import { RotateDemoPage } from "./Demo/pages/RotateDemoPage";
@@ -32,6 +33,7 @@ import {
NewCar,
PartsPick,
PartsCollection,
+ Share,
NQuizEventWinnerApply,
} from "./pages";
@@ -46,6 +48,7 @@ export const router = createBrowserRouter([
{ path: NEW_CAR_PAGE_ROUTE, element: },
{ path: N_PARTS_PICK_PAGE_ROUTE, element: },
{ path: PARTS_COLLECTION_PAGE_ROUTE, element: },
+ { path: SHARE_PAGE_ROUTE, element: },
{
path: N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE,
element: ,