-
-
-
+ <>
+
+
+
+
+
+
-
-
이솝
-
픽업마스터
-
cotton@gmail.com
+
+ {isLoading ? (
+ <>
+
-
+
-
+
-
+ >
+ ) : (
+ <>
+
{memberName ?? "-"}
+
{nickname ?? "-"}
+
{email ?? "-"}
+ >
+ )}
+
+
+
+
-
-
-
+
+ >
);
};
\ No newline at end of file
diff --git a/apps/customer/src/app/(tabs)/mypage/_components/RewardQrModal.tsx b/apps/customer/src/app/(tabs)/mypage/_components/RewardQrModal.tsx
new file mode 100644
index 0000000..7312dfa
--- /dev/null
+++ b/apps/customer/src/app/(tabs)/mypage/_components/RewardQrModal.tsx
@@ -0,0 +1,96 @@
+"use client";
+
+import { useEffect, useMemo, useState } from "react";
+import { Icon } from "@compasser/design-system";
+import { useRewardQrQuery } from "@/shared/queries/query/member/useRewardQrQuery";
+
+interface RewardQrModalProps {
+ open: boolean;
+ onClose: () => void;
+}
+
+export const RewardQrModal = ({ open, onClose }: RewardQrModalProps) => {
+ const { data, isLoading, isFetching, dataUpdatedAt } = useRewardQrQuery({
+ enabled: open,
+ });
+
+ const [secondsLeft, setSecondsLeft] = useState(60);
+
+ const qrImageUrl = useMemo(() => {
+ if (!data) return null;
+ return URL.createObjectURL(data);
+ }, [data]);
+
+ useEffect(() => {
+ return () => {
+ if (qrImageUrl) {
+ URL.revokeObjectURL(qrImageUrl);
+ }
+ };
+ }, [qrImageUrl]);
+
+ useEffect(() => {
+ if (!open) return;
+
+ setSecondsLeft(60);
+
+ const interval = window.setInterval(() => {
+ const elapsed = Math.floor((Date.now() - dataUpdatedAt) / 1000);
+ const remain = Math.max(60 - elapsed, 0);
+ setSecondsLeft(remain);
+ }, 1000);
+
+ return () => {
+ window.clearInterval(interval);
+ };
+ }, [open, dataUpdatedAt]);
+
+ if (!open) return null;
+
+ return (
+
+
e.stopPropagation()}
+ >
+
+
+
+
+ 사장님께 QR을 보여주세요.
+
+
+
{secondsLeft}초
+
+
+ {isLoading || isFetching ? (
+
+ ) : qrImageUrl ? (
+

+ ) : (
+
+ )}
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/apps/customer/src/app/(tabs)/mypage/_components/StatsCard.tsx b/apps/customer/src/app/(tabs)/mypage/_components/StatsCard.tsx
index 39ad842..26eb765 100644
--- a/apps/customer/src/app/(tabs)/mypage/_components/StatsCard.tsx
+++ b/apps/customer/src/app/(tabs)/mypage/_components/StatsCard.tsx
@@ -2,15 +2,32 @@
import { useRouter } from "next/navigation";
import { Card, Icon } from "@compasser/design-system";
-import { stats } from "../_constants/stats";
-export const StatsCard = () => {
+interface StatsCardProps {
+ totalStampCount?: number;
+ totalUnboxingCount?: number;
+ totalCouponCount?: number;
+ isLoading?: boolean;
+}
+
+export const StatsCard = ({
+ totalStampCount = 0,
+ totalUnboxingCount = 0,
+ totalCouponCount = 0,
+ isLoading = false,
+}: StatsCardProps) => {
const router = useRouter();
const handleMoveDetailPage = () => {
router.push("/mypage/detail");
};
+ const stats = [
+ { label: "총 스탬프", value: totalStampCount },
+ { label: "총 언박싱", value: totalUnboxingCount },
+ { label: "총 쿠폰", value: totalCouponCount },
+ ];
+
return (
@@ -43,7 +60,7 @@ export const StatsCard = () => {
{item.label}
- {item.value}
+ {isLoading ? "-" : item.value}
))}
diff --git a/apps/customer/src/app/(tabs)/mypage/page.tsx b/apps/customer/src/app/(tabs)/mypage/page.tsx
index 1ca90a6..c791319 100644
--- a/apps/customer/src/app/(tabs)/mypage/page.tsx
+++ b/apps/customer/src/app/(tabs)/mypage/page.tsx
@@ -3,16 +3,54 @@
import { AccountCard } from "./_components/AccountCard";
import { ProfileSection } from "./_components/ProfileSection";
import { StatsCard } from "./_components/StatsCard";
+import { useMyPageQuery } from "@/shared/queries/query/member/useMyPageQuery";
export default function MyPage() {
+ const { data, isLoading, isError } = useMyPageQuery();
+
+ if (isLoading) {
+ return (
+
+
+
+
+ );
+ }
+
+ if (isError || !data) {
+ return (
+
+
+
+ 마이페이지 정보를 불러오지 못했습니다.
+
+
+
+ );
+ }
+
return (
-
+
diff --git a/apps/customer/src/shared/api/api.ts b/apps/customer/src/shared/api/api.ts
index ee646dd..21cbacd 100644
--- a/apps/customer/src/shared/api/api.ts
+++ b/apps/customer/src/shared/api/api.ts
@@ -6,6 +6,7 @@ import {
createStoreModule,
type TokenPair,
type TokenStore,
+ createMemberModule,
} from "@compasser/api";
const tokenStore: TokenStore = {
@@ -38,4 +39,5 @@ export const compasserApi = createCompasserApi({
});
export const authModule = createAuthModule(compasserApi);
+export const memberModule = createMemberModule(compasserApi);
export const storeModule = createStoreModule(compasserApi);
\ No newline at end of file
diff --git a/apps/customer/src/shared/queries/mutation/auth/useLogoutMutation.ts b/apps/customer/src/shared/queries/mutation/auth/useLogoutMutation.ts
new file mode 100644
index 0000000..a3b4d02
--- /dev/null
+++ b/apps/customer/src/shared/queries/mutation/auth/useLogoutMutation.ts
@@ -0,0 +1,10 @@
+"use client";
+
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { authModule } from "@/shared/api/api";
+
+export const useLogoutMutation = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation(authModule.mutations.logout(queryClient));
+};
\ No newline at end of file
diff --git a/apps/customer/src/shared/queries/query/member/useMyPageQuery.ts b/apps/customer/src/shared/queries/query/member/useMyPageQuery.ts
new file mode 100644
index 0000000..566919d
--- /dev/null
+++ b/apps/customer/src/shared/queries/query/member/useMyPageQuery.ts
@@ -0,0 +1,8 @@
+"use client";
+
+import { useQuery } from "@tanstack/react-query";
+import { memberModule } from "@/shared/api/api";
+
+export const useMyPageQuery = () => {
+ return useQuery(memberModule.queries.myPage());
+};
\ No newline at end of file
diff --git a/apps/customer/src/shared/queries/query/member/useRewardQrQuery.ts b/apps/customer/src/shared/queries/query/member/useRewardQrQuery.ts
new file mode 100644
index 0000000..92a270c
--- /dev/null
+++ b/apps/customer/src/shared/queries/query/member/useRewardQrQuery.ts
@@ -0,0 +1,16 @@
+"use client";
+
+import { useQuery } from "@tanstack/react-query";
+import { memberModule } from "@/shared/api/api";
+
+interface UseRewardQrQueryParams {
+ enabled: boolean;
+}
+
+export const useRewardQrQuery = ({ enabled }: UseRewardQrQueryParams) => {
+ return useQuery({
+ ...memberModule.queries.qrTest(),
+ enabled,
+ refetchInterval: enabled ? 60000 : false,
+ });
+};
\ No newline at end of file