Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
38fcb47
πŸ”§ config: gitignore μˆ˜μ • #66
Head-ddy Feb 7, 2025
ff2391f
Merge branch 'develop' of https://github.com/UMC-Master/frontend into…
Head-ddy Feb 7, 2025
a115218
✨ feat: 전체 μ΄μš©μ•½κ΄€ μ²΄ν¬λ°•μŠ€ 였λ₯˜ μˆ˜μ • #66
Head-ddy Feb 7, 2025
176ec5d
✏️ Typo: ν•„μš”μ—†λŠ” μ½”λ“œ μ‚­μ œ #66
Head-ddy Feb 7, 2025
8d0df32
✨ feat: λΉ„λ°€λ²ˆν˜Έ μž…λ ₯ μƒνƒœ 검증 및 μ—λŸ¬λ©”μ‹œμ§€ μΆ”κ°€ #66
Head-ddy Feb 7, 2025
1efefeb
✨ feat: λ‹‰λ„€μž„ μƒνƒœ 검증 및 μ—λŸ¬λ©”μ‹œμ§€ μΆ”κ°€ #66
Head-ddy Feb 7, 2025
beb3851
Merge branch 'develop' of https://github.com/UMC-Master/frontend into…
Head-ddy Feb 20, 2025
ff2cef5
✨ feat: λ‹‰λ„€μž„ μ—λŸ¬λ©”μ„Έμ§€ μΆ”κ°€ #66
Head-ddy Feb 20, 2025
681e320
πŸ’„ UI: νšŒμ›κ°€μž… 단계 UI μˆ˜μ • #66
Head-ddy Feb 20, 2025
d711b42
✨ feat: λΉ„λ°€λ²ˆν˜Έ μ—λŸ¬λ©”μ„Έμ§€ μΆ”κ°€ #66
Head-ddy Feb 20, 2025
89099ca
✨ feat: νšŒμ›κ°€μž… 성곡! #66
Head-ddy Feb 20, 2025
f21df71
✨ feat: 이메일 쀑볡 확인 및 λ²„νŠΌ μ• λ‹ˆλ©”μ΄μ…˜ μΆ”κ°€ #66
Head-ddy Feb 20, 2025
8a5096c
✨ feat: api μˆ˜μ • #66
Head-ddy Feb 20, 2025
53f51b0
✨ feat: νšŒμ›κ°€μž… ν›„ 둜그인 νŽ˜μ΄μ§€λ‘œ #66
Head-ddy Feb 20, 2025
578f97e
✨ feat: ν”„λ‘œν•„ 이미지 μ—…λ‘œλ“œ #66
Head-ddy Feb 20, 2025
d981e47
✨ feat: 이메일 쀑볡 확인 μˆ˜μ • #66
Head-ddy Feb 20, 2025
dc5822d
πŸ’„ UI: νšŒμ›κ°€μž… 성곡 alert μΆ”κ°€ #66
Head-ddy Feb 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 15 additions & 35 deletions umc-master/src/apis/authApi.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,18 @@
// import axiosInstance from '@apis/axios-instance';
import axiosInstance from '@apis/axios-instance';

// interface UserSignup {
// email: string;
// password: string;
// nickname: string;
// hashtags: string[];
// }
interface UserSignup {
email: string;
password: string;
nickname: string;
hashtags: string[];
}

// export const postSignup = async ({ email, password, nickname, hashtags } : UserSignup) => {
// const { data } = await axiosInstance.post(`/signup`, {
// email,
// password,
// nickname,
// hashtags,
// });
// return data;
// };

import axios from 'axios';

export const postSignup = async () => {
try {
const response = await axios.post('https://api.hmaster.shop/api/v1/signup', {
email: 'ekos555@naver.com',
password: 'asfa1234!@',
nickname: 'rael',
hashtags: ['λ΄„', 'νŒ¨μ…˜', 'μ²­μ†Œ', 'μš”λ¦¬', 'μž¬ν™œμš©', '주택']
}, {
headers: {
'Content-Type': 'application/json'
}
});
console.log('νšŒμ›κ°€μž… 성곡:', response.data);
} catch (error) {
console.error('νšŒμ›κ°€μž… 였λ₯˜:', error);
}
export const postSignup = async ({ email, password, nickname, hashtags }: UserSignup) => {
const { data } = await axiosInstance.post(`/signup`, {
email,
password,
nickname,
hashtags,
});
return data;
};
8 changes: 5 additions & 3 deletions umc-master/src/pages/auth/SignUpPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,17 @@ const handleNicknameChange = (nickname: string) => {
};

const handleHashtagChange = (hashtags: string[]) => {
setHashtag(hashtags);
const hashtagStrings = hashtags.map(hashtag => hashtag.toString());
setHashtag(hashtagStrings);
};

const handleSignUpComplete = async () => {
try {
const userSignupData = { email, password, nickname, hashtags };
console.log("νšŒμ›κ°€μž… 확인:", userSignupData)
await postSignup();
navigate("/main");
await postSignup(userSignupData);
alert("νšŒμ›κ°€μž… 성곡!");
navigate("/login");
} catch (error) {
console.error("νšŒμ›κ°€μž… 였λ₯˜:", error);
alert("νšŒμ›κ°€μž…μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”.");
Expand Down
25 changes: 10 additions & 15 deletions umc-master/src/pages/auth/Signup_components/AgreementForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Typography from "@components/common/typography";
import styled, { useTheme } from "styled-components";
import ImgAdd from "@assets/add.svg";
import ImgRemove from "@assets/remove.svg";
import { useEffect, useState } from "react";
import { useState } from "react";

interface AgreementItemProps {
isRequired: boolean; // ν•„μˆ˜ μ—¬λΆ€
Expand Down Expand Up @@ -67,10 +67,14 @@ const AgreementForm: React.FC<Section1Props> = ({ onCheckRequired }) => {
});

// 전체 λ™μ˜ μ²΄ν¬λ°•μŠ€
const handleAllAgreeChange = (checked: boolean) => {
setIsAllAgreed(checked);
const handleAllAgreeChange = () => {
const newCheckedState = !isAllAgreed;
setIsAllAgreed(newCheckedState);
const updatedItems = {
terms: checked, privacy: checked, thirdinfo: checked, marketing: checked,
terms: newCheckedState,
privacy: newCheckedState,
thirdinfo: newCheckedState,
marketing: newCheckedState,
};
setCheckedItems(updatedItems);
onCheckRequired(updatedItems.terms && updatedItems.privacy);
Expand All @@ -79,22 +83,13 @@ const AgreementForm: React.FC<Section1Props> = ({ onCheckRequired }) => {
const handleCheckboxChange = (key: string, checked: boolean) => {
setCheckedItems((prevState) => {
const updatedItems = { ...prevState, [key]: checked };
const allChecked = Object.values(updatedItems).every(Boolean);
setIsAllAgreed(allChecked);
onCheckRequired(updatedItems.terms && updatedItems.privacy);
return updatedItems;
});
};

useEffect(() => {
if (isAllAgreed) {
setCheckedItems({
terms: true, privacy: true, thirdinfo: true, marketing: true,
});
onCheckRequired(true);
} else {
onCheckRequired(checkedItems.terms && checkedItems.privacy);
}
}, [isAllAgreed, checkedItems, onCheckRequired]);

return (
<Container>
<AgreementItem
Expand Down
43 changes: 34 additions & 9 deletions umc-master/src/pages/auth/Signup_components/EmailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const EmailForm: React.FC<{
const [emailSent, setEmailSent] = useState<boolean>(false);
const [verified, setVerified] = useState<boolean>(false);
const [timer, setTimer] = useState<number>(180);
const [retryEnabled, setRetryEnabled] = useState<boolean>(false);
const [isEmailDuplicate, setIsEmailDuplicate] = useState<boolean>(false);

const fullEmail = localPart && domain ? `${localPart}@${domain}` : "";

Expand All @@ -49,9 +49,7 @@ const EmailForm: React.FC<{
timerInterval = setInterval(() => {
setTimer((prev) => prev - 1);
}, 1000);
} else if (timer === 0) {
setRetryEnabled(true);
}
}
return () => {
if (timerInterval) {
clearInterval(timerInterval);
Expand Down Expand Up @@ -103,6 +101,8 @@ const EmailForm: React.FC<{
onEmailChange(updatedFullEmail);

console.log("도메인 λ³€κ²½:", updatedFullEmail);

setIsEmailDuplicate(false); // 쀑볡 확인 λ²„νŠΌμ„ λ‹€μ‹œ ν™œμ„±ν™”
};

const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -113,8 +113,26 @@ const EmailForm: React.FC<{
onEmailChange(updatedFullEmail);

console.log("이메일 μž…λ ₯:", updatedFullEmail);

setIsEmailDuplicate(false); // 쀑볡 확인 λ²„νŠΌμ„ λ‹€μ‹œ ν™œμ„±ν™”
};

// 이메일 쀑볡 체크
const checkEmailDuplicate = async () => {
if (!fullEmail) {
alert("이메일을 μž…λ ₯ν•΄μ£Όμ„Έμš”.");
return;
}
try {
const response = await axiosInstance.post("/check-email", { email: fullEmail });
if (response.data.isSuccess) {
setIsEmailDuplicate(true);
alert("μ‚¬μš© κ°€λŠ₯ν•œ μ΄λ©”μΌμž…λ‹ˆλ‹€.");
}
} catch (error) {
alert("이메일이 이미 μ‘΄μž¬ν•©λ‹ˆλ‹€.");
}
};

const handleRetry = () => {
setLocalPart("");
Expand All @@ -123,7 +141,6 @@ const EmailForm: React.FC<{
setEmailSent(false);
setVerified(false);
setTimer(180);
setRetryEnabled(false);
};

return (
Expand All @@ -147,16 +164,24 @@ const EmailForm: React.FC<{
<EmailSelect
value={domain}
onChange={handleDomainChange}
disabled={emailSent}
disabled={emailSent || verified}
>
{emails.map((email) => (
<option key={email.value} value={email.value}>
{email.label}
</option>
))}
</EmailSelect>
<Button variant="emailCheck" onClick={handleEmailRequest} disabled={emailSent}>
이메일 인증
<Button
variant="emailCheck"
onClick={isEmailDuplicate === false ? checkEmailDuplicate : handleEmailRequest}
disabled={emailSent || verified}
style={{
backgroundColor: isEmailDuplicate === false ? theme.colors.primary[500] : theme.colors.primary[900],
transition: 'background-color 0.5s ease'
}}
>
{isEmailDuplicate === false && !emailSent ? "쀑볡 확인" : "이메일 인증"}
</Button>
</Email>
<Email>
Expand All @@ -175,7 +200,7 @@ const EmailForm: React.FC<{
variant="titleXxxSmall"
style={{color: theme.colors.red[500]}}
>남은 μ‹œκ°„: {Math.floor(timer / 60)}λΆ„ {timer % 60}초</Typography>
<Button variant="emailCheck" onClick={handleRetry} disabled={retryEnabled} >
<Button variant="emailCheck" onClick={handleRetry} >
μž¬μ‹œλ„
</Button>
</Timer>
Expand Down
43 changes: 37 additions & 6 deletions umc-master/src/pages/auth/Signup_components/PasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable react/prop-types */
import Typography from "@components/common/typography";
import Input from "@components/Input/Input";
import useInput from "@hooks/useInput";
import { validatePasswordFormat } from "@utils/validation";
import { useEffect, useState } from "react";
import { styled, useTheme } from "styled-components";

Expand All @@ -13,11 +15,40 @@ const PasswordForm: React.FC<{

const theme = useTheme();

const [password, setPassword] = useState<string>('');
const [confirmPassword, setConfirmPassword] = useState<string>('');
const [passwordErrorMessage, setPasswordErrorMessage] = useState<string>('');
const [confirmPasswordErrorMessage, setConfirmPasswordErrorMessage] = useState<string>('');
const [isPasswordValid, setIsPasswordValid] = useState<boolean>(false);

// λΉ„λ°€λ²ˆν˜Έ μƒνƒœ 검증 및 μ—λŸ¬λ©”μ„Έμ§€
const {
input: password,
errorMessage: passwordInputErrorMessage,
changeHandler: passwordChangeHandler,
} = useInput({
initialValue: "",
validate: async (value) => {
const error = validatePasswordFormat(value);
setIsPasswordValid(!error); // ν˜•μ‹μ΄ 맞으면 true, μ•„λ‹ˆλ©΄ false
return error;
}
});

// λΉ„λ°€λ²ˆν˜Έ 확인 였λ₯˜ λ©”μ‹œμ§€ μ‹€μ‹œκ°„ 변동
useEffect(() => {
if (confirmPassword && password !== confirmPassword) {
setConfirmPasswordErrorMessage("λΉ„λ°€λ²ˆν˜Έκ°€ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.");
} else {
setConfirmPasswordErrorMessage("");
}
}, [confirmPassword, password]);

useEffect(() => {
setPasswordErrorMessage(passwordInputErrorMessage || "");
}, [passwordInputErrorMessage]);

useEffect(() => {
if (password && confirmPassword && password === confirmPassword) {
if (isPasswordValid && confirmPassword && password === confirmPassword) {
onCheckRequired(true);
} else {
onCheckRequired(false);
Expand All @@ -26,8 +57,8 @@ const PasswordForm: React.FC<{

const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newPassword = e.target.value;
setPassword(newPassword);
onPasswordChange(newPassword); // μƒμœ„ μ»΄ν¬λ„ŒνŠΈμ— 전달
passwordChangeHandler(e);
console.log("λΉ„λ°€λ²ˆν˜Έ: ", newPassword);
};

Expand All @@ -38,14 +69,14 @@ const PasswordForm: React.FC<{
style={{color: theme.colors.primary[700]}}
>λΉ„λ°€λ²ˆν˜Έ μž…λ ₯ (ν•„μˆ˜) *</Typography>
<Input
errorMessage=""
errorMessage={passwordErrorMessage}
type={'password'}
placeholder={'λΉ„λ°€λ²ˆν˜Έ μž…λ ₯ (숫자, 영문자, 문자 포함 8~15자 이내)'}
value={password}
onChange={handlePasswordChange}
/>
<Input
errorMessage=""
errorMessage={confirmPasswordErrorMessage}
type={'password'}
placeholder={'λΉ„λ°€λ²ˆν˜Έ 확인'}
value={confirmPassword}
Expand All @@ -57,7 +88,7 @@ const PasswordForm: React.FC<{

export default PasswordForm;

const Container = styled.div`
const Container = styled.form`
display: flex;
flex-direction: column;
align-items: flex-start;
Expand Down
Loading