Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/oreumi-dreamer/Dreamer into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
chochohee committed Dec 20, 2024
2 parents f7e79b3 + 7a31f36 commit 3efa26c
Show file tree
Hide file tree
Showing 32 changed files with 2,087 additions and 475 deletions.
77 changes: 77 additions & 0 deletions src/app/account/Account.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
.header {
margin-bottom: 3rem;
}

.title {
font-size: var(--h2-font-size);
font-weight: 700;
}

.section {
background-color: var(--background-white);
border-radius: 8px;
padding: 2rem;
filter: drop-shadow(6px 6px 9.4px rgba(0, 0, 0, 0.15));
margin-bottom: 2rem;
display: flex;
flex-direction: column;
}

.section-title {
font-size: var(--h3-font-size);
font-weight: 600;
color: #333;
margin-bottom: 1.5rem;
}

.info-list {
display: grid;
grid-template-columns: 120px 1fr;
row-gap: 0.75rem;
}

.info-item {
display: flex;
padding: 0.75rem 0;
border-bottom: 1px solid #eee;
}

.info-item:last-child {
border-bottom: none;
}

.info-label {
font-weight: 500;
color: #666;
padding: 0.75rem 0;
border-bottom: 1px solid #eee;
}

.info-value {
margin: 0;
color: #333;
padding: 0.75rem 0;
border-bottom: 1px solid #eee;
}

.info-label:nth-last-child(2),
.info-value:last-child {
border-bottom: none;
}

.button-group {
display: flex;
justify-content: space-around;
gap: 1rem;
margin-bottom: 2rem;
}

.button {
flex: 1;
}

.delete-button {
width: fit-content;
color: var(--active-btn-color);
margin-left: auto;
}
55 changes: 55 additions & 0 deletions src/app/account/modify-email/ModifyEmail.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.page-title {
font-size: var(--h2-font-size);
font-weight: 600;
margin-bottom: 2rem;
color: #333;
}

.email-form {
background: var(--background-white);
padding: 2rem;
border-radius: 1rem;
filter: drop-shadow(6px 6px 9.4px rgba(0, 0, 0, 0.15));
}

.form-fieldset {
border: none;
padding: 0;
}

.form-fieldset legend {
font-size: var(--h3-font-size);
}

.input-group {
margin-bottom: 1.5rem;
display: grid;
grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
grid-template-areas: "label label" "input submit";
gap: 0.5rem;
}

.input-label {
display: block;
margin-bottom: 0.5rem;
grid-area: label;
}

.form-button {
width: 100%;
}

.submit-button {
width: 100%;
color: white;
}

.error {
background-color: #fee2e2;
border: 1px solid #ef4444;
color: #dc2626;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
191 changes: 191 additions & 0 deletions src/app/account/modify-email/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
"use client";

import { useState } from "react";
import { Button, Input } from "@/components/Controls";
import styles from "./ModifyEmail.module.css";
import { verifyPassword } from "@/utils/auth/verifyPassword";
import { updateEmail } from "@/utils/auth/updateEmail";
import Loading from "@/components/Loading";

export default function ModifyEmail() {
const [password, setPassword] = useState("");
const [isVerifyingPassword, setIsVerifyingPassword] = useState(false);
const [newEmail, setNewEmail] = useState("");
const [verificationCode, setVerificationCode] = useState("");
const [isPasswordVerified, setIsPasswordVerified] = useState(false);
const [isEmailSent, setIsEmailSent] = useState(false);
const [isEmailSentBtnClicked, setIsEmailSentBtnClicked] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState("");

const handlePasswordVerify = async (e) => {
e.preventDefault();
setIsVerifyingPassword(true);
try {
await verifyPassword(password);
setIsPasswordVerified(true);
setError("");
} catch (err) {
setError("비밀번호가 일치하지 않습니다.");
} finally {
setIsVerifyingPassword(false);
}
};

const handleSendVerification = async (e) => {
e.preventDefault();
setIsEmailSentBtnClicked(true);
try {
const checkEmail = await fetch("/api/auth/check-email", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email: newEmail }),
});

if (checkEmail.exists || checkEmail.error) {
setError("이메일 확인에 실패했습니다. 다시 시도해주세요.");
return;
} else {
const sendEmail = await fetch("/api/auth/send-email-verification", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email: newEmail }),
});

if (!sendEmail.ok) {
setError("인증 코드 발송에 실패했습니다. 다시 시도해주세요.");
return;
} else {
setIsEmailSent(true);
setError("");
}
}
} catch (err) {
setError("인증 코드 발송에 실패했습니다. 다시 시도해주세요.");
} finally {
setIsEmailSentBtnClicked(false);
}
};

const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
try {
await updateEmail(password, newEmail, verificationCode);

// 이메일 변경 성공 시, 초기화
setError("");

alert("이메일이 성공적으로 변경되었습니다.\n다시 로그인해주세요.");
location.href = "/";
} catch (err) {
setError("이메일 변경에 실패했습니다. 다시 시도해주세요.");
}
};

return (
<main className={styles["email-container"]}>
<h1 className={styles["page-title"]}>이메일 변경</h1>
{error && (
<p className={styles.error} role="alert">
{error}
</p>
)}

<form onSubmit={handleSubmit} className={styles["email-form"]}>
{!isPasswordVerified ? (
<fieldset className={styles["form-fieldset"]}>
<div className={styles["input-group"]}>
<label htmlFor="password" className={styles["input-label"]}>
먼저, 현재 비밀번호를 입력해주세요.
</label>
<Input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handlePasswordVerify(e)}
required
className={styles["form-input"]}
/>
<Button
type="button"
highlight={true}
onClick={handlePasswordVerify}
className={styles["form-button"]}
>
{isVerifyingPassword ? <Loading type="miniCircle" /> : "확인"}
</Button>
</div>
</fieldset>
) : (
<fieldset className={styles["form-fieldset"]}>
<div className={styles["input-group"]}>
<label htmlFor="newEmail" className={styles["input-label"]}>
변경할 이메일 주소를 입력해주세요.
</label>
<Input
type="email"
id="newEmail"
value={newEmail}
onChange={(e) => setNewEmail(e.target.value)}
onKeyDown={(e) =>
e.key === "Enter" && handleSendVerification(e)
}
required
className={styles["form-input"]}
/>
<Button
type="button"
onClick={handleSendVerification}
disabled={isEmailSent}
className={styles["form-button"]}
>
{isEmailSentBtnClicked ? (
<Loading type="miniCircle" />
) : isEmailSent ? (
"전송 완료"
) : (
"인증 코드 전송"
)}
</Button>
</div>

{isEmailSent && (
<div className={styles["input-group"]}>
<label
htmlFor="verificationCode"
className={styles["input-label"]}
>
인증 코드
</label>
<Input
type="text"
id="verificationCode"
value={verificationCode}
highlight={!isEmailSent}
onChange={(e) => setVerificationCode(e.target.value)}
required
maxLength={6}
className={styles["form-input"]}
/>
<Button
type="submit"
highlight={true}
className={styles["submit-button"]}
disabled={!verificationCode}
>
{isSubmitting ? <Loading type="miniCircle" /> : "변경하기"}
</Button>
</div>
)}
</fieldset>
)}
</form>
</main>
);
}
Loading

0 comments on commit 3efa26c

Please sign in to comment.