generated from jphacks/JP_sample
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #73 from jphacks/feat/yama/create-logout-button
ログアウトボタンとメール通知の切り替えボタン&APIを追加
- Loading branch information
Showing
5 changed files
with
138 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" }, | ||
}, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<User>(); | ||
const [userData, setUserData] = useState<User | null>(null); | ||
const [myScore, setMyScore] = useState<MyScoreDetail[]>([]); | ||
const [userStatus, setUserStatus] = useState<experiencePoint>(); | ||
const [point, setPoint] = useState<ChangeStatus>(); | ||
const [isEditing, setIsEditing] = useState(false); | ||
const [isSubscribed, setIsSubscribed] = useState<boolean>(true); | ||
const [isOpen, setIsOpen] = useStatusChangeDialog(); | ||
const handleOpenDialog = () => setIsOpen(true); | ||
const [isLoading, setIsLoading] = useState<boolean>(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 <LoadingSpinner />; | ||
|
@@ -95,15 +133,11 @@ const UserPage = () => { | |
{isEditing ? ( | ||
<div className="flex flex-col gap-2"> | ||
<Input type="text" placeholder="新しいユーザー名を入力" /> | ||
<div> | ||
<Button variant={"default"} className="bg-[#333333]"> | ||
<div className="flex gap-2"> | ||
<Button variant="default" className="bg-[#333333]"> | ||
保存 | ||
</Button> | ||
<Button | ||
variant={"outline"} | ||
className="ml-2" | ||
onClick={() => setIsEditing(false)} | ||
> | ||
<Button variant="outline" onClick={() => setIsEditing(false)}> | ||
キャンセル | ||
</Button> | ||
</div> | ||
|
@@ -114,7 +148,7 @@ const UserPage = () => { | |
{userData.name || "[email protected]"} | ||
</span> | ||
<Button | ||
variant={"primary"} | ||
variant="primary" | ||
className="ml-2" | ||
onClick={() => setIsEditing(true)} | ||
> | ||
|
@@ -151,37 +185,51 @@ const UserPage = () => { | |
</button> | ||
<Card className="flex flex-col items-center border-none p-8"> | ||
<h2 className="text-2xl font-bold mb-4">過去のチャレンジ</h2> | ||
{myScore.length === 0 ? ( | ||
<div className="text-gray-500 text-center py-8"> | ||
<p>まだチャレンジの記録がありません</p> | ||
<p className="text-sm mt-2"> | ||
新しいチャレンジに挑戦してみましょう! | ||
</p> | ||
</div> | ||
) : ( | ||
myScore.map((score) => ( | ||
<div | ||
key={score.id} | ||
className="flex w-full items-center mb-2 border rounded-md" | ||
> | ||
<img | ||
src={score.imageUrl || "https://placehold.jp/150x150.png"} | ||
alt="チャレンジ画像" | ||
className="w-1/4 h-auto rounded-l-md" | ||
/> | ||
<div className="flex flex-col items-start justify-center w-1/2 text-xs"> | ||
<div className="pl-4 flex flex-col gap-1"> | ||
<p className="font-bold">{score.assignment}</p> | ||
<div className="flex items-center gap-1"> | ||
<LuClock /> | ||
<p className="pb-0.5">{score.answerTime}</p> | ||
<div className="w-full overflow-y-auto"> | ||
{myScore.length === 0 ? ( | ||
<div className="text-gray-500 text-center py-8"> | ||
<p>まだチャレンジの記録がありません</p> | ||
<p className="text-sm mt-2"> | ||
新しいチャレンジに挑戦してみましょう! | ||
</p> | ||
</div> | ||
) : ( | ||
myScore.map((score) => ( | ||
<div | ||
key={score.id} | ||
className="flex w-full items-center mb-2 border rounded-md" | ||
> | ||
<img | ||
src={score.imageUrl || "https://placehold.jp/150x150.png"} | ||
alt="チャレンジ画像" | ||
className="w-1/4 h-auto rounded-l-md" | ||
/> | ||
<div className="flex flex-col items-start justify-center w-1/2 text-xs"> | ||
<div className="pl-4 flex flex-col gap-1"> | ||
<p className="font-bold">{score.assignment}</p> | ||
<div className="flex items-center gap-1"> | ||
<LuClock /> | ||
<p className="pb-0.5">{score.answerTime}</p> | ||
</div> | ||
</div> | ||
</div> | ||
<p className="w-1/4 text-lg font-bold">{score.point}点</p> | ||
</div> | ||
<p className="w-1/4 text-lg font-bold">{score.point}点</p> | ||
</div> | ||
)) | ||
)} | ||
)) | ||
)} | ||
</div> | ||
</Card> | ||
<Card className="flex justify-center gap-2 w-[21rem] p-8"> | ||
<Button | ||
variant="default" | ||
onClick={signOut} | ||
className="px-6 bg-[#333333]" | ||
> | ||
ログアウト | ||
</Button> | ||
<Button variant="outline" onClick={handleToggleEmailSubscription}> | ||
{isSubscribed ? "メール通知を停止" : "メール通知を再開"} | ||
</Button> | ||
</Card> | ||
</div> | ||
); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters