diff --git a/app/prisma/schema.prisma b/app/prisma/schema.prisma index 75887b1..514b292 100755 --- a/app/prisma/schema.prisma +++ b/app/prisma/schema.prisma @@ -22,7 +22,6 @@ model User { experiencePoint ExperiencePoint? @relation() rateId Int @default(1) ratePoint Int - // todo メール受信のフラグを追記。APIは別で作成する isReceivedMail Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt diff --git a/app/src/app/api/mailer/route.ts b/app/src/app/api/mailer/route.ts index 98fd12e..8328ac6 100644 --- a/app/src/app/api/mailer/route.ts +++ b/app/src/app/api/mailer/route.ts @@ -25,6 +25,7 @@ export async function POST() { try { const usersEmails = ( await prisma.user.findMany({ + where: { isReceivedMail: true }, select: { email: true, }, diff --git a/app/src/app/api/user/updateReceivedMail/route.ts b/app/src/app/api/user/updateReceivedMail/route.ts new file mode 100644 index 0000000..9012347 --- /dev/null +++ b/app/src/app/api/user/updateReceivedMail/route.ts @@ -0,0 +1,27 @@ +import { prisma } from "@lib/prisma"; +import type { NextRequest } from "next/server"; + +export async function PUT(req: NextRequest) { + try { + const { uid, isReceivedMail } = await req.json(); + + await prisma.user.update({ + where: { uid }, + data: { isReceivedMail }, + }); + + return new Response(JSON.stringify({ message: "設定を更新しました" }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + console.error("設定更新エラー:", error); + return new Response( + JSON.stringify({ error: "設定の更新に失敗しました。" }), + { + status: 500, + headers: { "Content-Type": "application/json" }, + }, + ); + } +} diff --git a/app/src/app/user/page.tsx b/app/src/app/user/page.tsx index c0b6a52..74a4e96 100644 --- a/app/src/app/user/page.tsx +++ b/app/src/app/user/page.tsx @@ -8,6 +8,7 @@ import PlayerRankCard from "@/components/view/user/PlayerRankCard"; import { StatusChangeDialog } from "@/components/view/user/StatusChangeDialog"; import { StatusList } from "@/components/view/user/StatusList"; import { useStatusChangeDialog } from "@/lib/atom"; +import { signOut } from "@/lib/signOut"; import type { ChangeStatus, MyScoreDetail, @@ -20,32 +21,43 @@ import { LuClock, LuFlame, LuTrophy } from "react-icons/lu"; import { VscAccount } from "react-icons/vsc"; const UserPage = () => { - const [userData, setUserData] = useState(); + const [userData, setUserData] = useState(null); const [myScore, setMyScore] = useState([]); const [userStatus, setUserStatus] = useState(); const [point, setPoint] = useState(); const [isEditing, setIsEditing] = useState(false); + const [isSubscribed, setIsSubscribed] = useState(true); const [isOpen, setIsOpen] = useStatusChangeDialog(); const handleOpenDialog = () => setIsOpen(true); const [isLoading, setIsLoading] = useState(true); useEffect(() => { - const fetchData = async () => { - const userIdString = localStorage.getItem("userID"); - if (!userIdString) { - window.location.href = "/login"; - return; - } + const userIdString = localStorage.getItem("userID"); + if (!userIdString) { + window.location.href = "/login"; + return; + } + + const userData: User = JSON.parse(userIdString); + setUserData(userData); - const userData = JSON.parse(userIdString); - setUserData(userData); + const fetchUserData = async () => { try { - const response = await fetch(`/api/score/me/${userData.uid}?all=true`); - if (!response.ok) { - throw new Error("データの取得に失敗しました"); + const [userResponse, scoreResponse] = await Promise.all([ + fetch(`/api/user?uid=${userData.uid}`), + fetch(`/api/score/me/${userData.uid}?all=true`), + ]); + + if (!userResponse.ok) { + throw new Error("ユーザー情報の取得に失敗しました"); } + const userDetails = await userResponse.json(); + setIsSubscribed(userDetails.isReceivedMail); - const data = await response.json(); + if (!scoreResponse.ok) { + throw new Error("データの取得に失敗しました"); + } + const data = await scoreResponse.json(); setMyScore(data); } catch (error) { console.error("エラーが発生しました:", error); @@ -70,9 +82,35 @@ const UserPage = () => { } setIsLoading(false); }; - fetchData(); + + fetchUserData(); }, []); + const handleToggleEmailSubscription = async () => { + if (!userData) return; + + try { + const response = await fetch("/api/user/updateReceivedMail", { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + uid: userData.uid, + isReceivedMail: !isSubscribed, + }), + }); + + if (!response.ok) { + throw new Error("設定の更新に失敗しました"); + } + + setIsSubscribed((prev) => !prev); + } catch (error) { + console.error("エラーが発生しました:", error); + } + } + if (!userData) return null; if (isLoading) { return ; @@ -95,15 +133,11 @@ const UserPage = () => { {isEditing ? (
-
- -
@@ -114,7 +148,7 @@ const UserPage = () => { {userData.name || "user@example.com"}

過去のチャレンジ

- {myScore.length === 0 ? ( -
-

まだチャレンジの記録がありません

-

- 新しいチャレンジに挑戦してみましょう! -

-
- ) : ( - myScore.map((score) => ( -
- チャレンジ画像 -
-
-

{score.assignment}

-
- -

{score.answerTime}

+
+ {myScore.length === 0 ? ( +
+

まだチャレンジの記録がありません

+

+ 新しいチャレンジに挑戦してみましょう! +

+
+ ) : ( + myScore.map((score) => ( +
+ チャレンジ画像 +
+
+

{score.assignment}

+
+ +

{score.answerTime}

+
+

{score.point}点

-

{score.point}点

-
- )) - )} + )) + )} +
+ + + +
); diff --git a/app/src/lib/signInAndUp.ts b/app/src/lib/signInAndUp.ts index 56b7dcd..6d60edb 100644 --- a/app/src/lib/signInAndUp.ts +++ b/app/src/lib/signInAndUp.ts @@ -14,18 +14,22 @@ export const signInOrUp = async (firebaseUser: FirebaseUser) => { }, }); - const userData = await res.json(); + if (res.status === 200) { + const userData = await res.json(); - const user: DBUser = { ...userData }; + const user: DBUser = { ...userData }; - if (userData) { - storeStorageUser(user); - if (!userData.experiencePoint) { - await createExp(userData.id); + if (userData) { + storeStorageUser(user); + if (!userData.experiencePoint) { + await createExp(userData.id); + } + toRoot(); } - toRoot(); - } else { + } else if (res.status === 404) { await signUp(firebaseUser); + } else { + console.error(`Unexpected status code: ${res.status}`); } } catch (error) { console.error("エラーが発生しました:", error);