Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ログアウトボタンとメール通知の切り替えボタン&APIを追加 #73

Merged
merged 10 commits into from
Nov 16, 2024
1 change: 0 additions & 1 deletion app/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions app/src/app/api/mailer/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export async function POST() {
try {
const usersEmails = (
await prisma.user.findMany({
where: { isReceivedMail: true },
select: {
email: true,
},
Expand Down
27 changes: 27 additions & 0 deletions app/src/app/api/user/updateReceivedMail/route.ts
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" },
},
);
}
}
148 changes: 98 additions & 50 deletions app/src/app/user/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand All @@ -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 />;
Expand All @@ -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>
Expand All @@ -114,7 +148,7 @@ const UserPage = () => {
{userData.name || "[email protected]"}
</span>
<Button
variant={"primary"}
variant="primary"
className="ml-2"
onClick={() => setIsEditing(true)}
>
Expand Down Expand Up @@ -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>
);
Expand Down
20 changes: 12 additions & 8 deletions app/src/lib/signInAndUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading