- {changeDialog.type === 'role' ? '권한 변경' : '상태 변경'}
+ {changeDialog.type === 'role'
+ ? '권한 변경'
+ : changeDialog.type === 'status'
+ ? '상태 변경'
+ : '신분 변경'}
{changeDialog.member.name}님의
- {changeDialog.type === 'role' ? ' 권한' : ' 상태'}을 선택하세요.
+ {changeDialog.type === 'role'
+ ? ' 권한'
+ : changeDialog.type === 'status'
+ ? ' 상태'
+ : ' 신분'}을 선택하세요.
);
diff --git a/frontend/src/components/Board/Modal.module.css b/frontend/src/components/Board/Modal.module.css
index 44cd9284..1a6e9e4d 100644
--- a/frontend/src/components/Board/Modal.module.css
+++ b/frontend/src/components/Board/Modal.module.css
@@ -8,7 +8,7 @@
display: flex;
align-items: center;
justify-content: center;
- z-index: 1000;
+ z-index: 1200;
}
.modal {
@@ -370,3 +370,20 @@
cursor: not-allowed;
transform: none;
}
+
+.actionRow {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 12px;
+ flex-shrink: 0;
+}
+
+.anonymousOption {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 14px;
+ color: rgba(80, 80, 80, 1);
+ user-select: none;
+}
diff --git a/frontend/src/components/Board/PostDetail/CommentSection.jsx b/frontend/src/components/Board/PostDetail/CommentSection.jsx
index faf03e2a..dfeac8dd 100644
--- a/frontend/src/components/Board/PostDetail/CommentSection.jsx
+++ b/frontend/src/components/Board/PostDetail/CommentSection.jsx
@@ -27,13 +27,18 @@ const CommentItem = ({
const date = comment.createdDate || comment.createdAt || comment.date;
const [localReplyText, setLocalReplyText] = useState('');
+ const [isAnonymousReply, setIsAnonymousReply] = useState(false);
React.useEffect(() => {
- if (!isReplying) setLocalReplyText('');
+ if (!isReplying) {
+ setLocalReplyText('');
+ setIsAnonymousReply(false);
+ }
}, [isReplying]);
const handleLocalSubmit = () => {
- onSubmitReply(commentId, localReplyText);
+ onSubmitReply(commentId, localReplyText, isAnonymousReply);
+ setIsAnonymousReply(false);
};
return (
@@ -97,14 +102,24 @@ const CommentItem = ({
value={localReplyText}
onChange={(e) => setLocalReplyText(e.target.value)}
/>
-
+
+
+
+
)}
@@ -146,10 +161,12 @@ const CommentSection = ({
setShowCommentMenu,
}) => {
const [commentText, setCommentText] = useState('');
+ const [isAnonymousComment, setIsAnonymousComment] = useState(false);
const handleAdd = () => {
- onAddComment(commentText);
+ onAddComment(commentText, isAnonymousComment);
setCommentText('');
+ setIsAnonymousComment(false);
};
const handleKeyDown = (e) => {
@@ -195,13 +212,23 @@ const CommentSection = ({
onKeyDown={handleKeyDown}
/>
-
{comments.length > 0 ? (
diff --git a/frontend/src/components/Board/PostDetail/PostView.jsx b/frontend/src/components/Board/PostDetail/PostView.jsx
index 017a3b6b..ec876844 100644
--- a/frontend/src/components/Board/PostDetail/PostView.jsx
+++ b/frontend/src/components/Board/PostDetail/PostView.jsx
@@ -9,6 +9,7 @@ import FileAttachmentList from './FileAttachmentList';
const PostView = ({
post,
boardName,
+ canManagePost,
showMenu,
setShowMenu,
onEdit,
@@ -37,30 +38,32 @@ const PostView = ({
{post.title}
-
-
- {showMenu && (
-
-
-
-
- )}
-
+ {canManagePost && (
+
+
+ {showMenu && (
+
+
+
+
+ )}
+
+ )}
diff --git a/frontend/src/components/mypage/ProfileCard.jsx b/frontend/src/components/mypage/ProfileCard.jsx
index 619f7ecc..26aef11e 100644
--- a/frontend/src/components/mypage/ProfileCard.jsx
+++ b/frontend/src/components/mypage/ProfileCard.jsx
@@ -7,11 +7,12 @@ import { toast } from 'react-toastify';
import AccountSecurity from './AccountSecurity';
const roleMap = {
- SYSTEM_ADMIN: '시스템관리자',
+ SYSTEM_ADMIN: '관리자',
PRESIDENT: '회장',
VICE_PRESIDENT: '부회장',
TEAM_LEADER: '팀장',
- TEAM_MEMBER: '부원',
+ TEAM_MEMBER: '일반',
+ PENDING_MEMBER: '대기회원',
};
const ProfileCard = () => {
@@ -29,7 +30,7 @@ const ProfileCard = () => {
setUserName(name || '사용자');
setUserPoint(point || 0);
- setUserRole(roleMap[role] || '일반');
+ setUserRole(roleMap[role] || '-');
} catch (error) {
console.error('사용자 정보 불러오기 실패:', error);
toast.error('사용자 정보를 불러올 수 없습니다.');
diff --git a/frontend/src/components/signup/SignUpForm.jsx b/frontend/src/components/signup/SignUpForm.jsx
index b6717935..b8b80ee9 100644
--- a/frontend/src/components/signup/SignUpForm.jsx
+++ b/frontend/src/components/signup/SignUpForm.jsx
@@ -37,10 +37,14 @@ const SignUpForm = () => {
);
const [isSending, setIsSending] = useState(false);
+ const [isCheckingVerification, setIsCheckingVerification] = useState(false);
+ const [isSigningUp, setIsSigningUp] = useState(false);
const [isVerificationSent, setVerificationSent] = useState(false);
const [isVerificationChecked, setVerificationChecked] = useState(false);
- const abortRef = useRef(null);
+ const abortRefSend = useRef(null);
+ const abortRefCheck = useRef(null);
+ const abortRefSignUp = useRef(null);
const nav = useNavigate();
const handlePasswordChange = (e) => {
@@ -118,13 +122,13 @@ const SignUpForm = () => {
const handleSendVerificationNumber = async (e) => {
e.preventDefault();
- abortRef.current?.abort();
- abortRef.current = new AbortController();
+ abortRefSend.current?.abort();
+ abortRefSend.current = new AbortController();
setIsSending(true);
try {
- await sendVerificationNumber({ email }, abortRef.current.signal);
+ await sendVerificationNumber({ email }, abortRefSend.current.signal);
setVerificationSent(true);
toast.success('인증번호가 발송되었습니다.');
} catch (error) {
@@ -136,27 +140,41 @@ const SignUpForm = () => {
};
const handleCheckVerificationNumber = async () => {
- abortRef.current?.abort();
- abortRef.current = new AbortController();
+ if (isCheckingVerification) {
+ return;
+ }
+
+ abortRefCheck.current?.abort();
+ abortRefCheck.current = new AbortController();
+
+ setIsCheckingVerification(true);
try {
await checkVerificationNumber(
{ email, verificationNumber },
- abortRef.current.signal
+ abortRefCheck.current.signal
);
setVerificationChecked(true);
toast.success('인증되었습니다.');
} catch (error) {
console.log(error);
toast.error('인증에 실패했습니다.');
+ } finally {
+ setIsCheckingVerification(false);
}
};
const handleSignUp = async (e) => {
e.preventDefault();
- abortRef.current?.abort();
- abortRef.current = new AbortController();
+ if (isSigningUp) {
+ return;
+ }
+
+ abortRefSignUp.current?.abort();
+ abortRefSignUp.current = new AbortController();
+
+ setIsSigningUp(true);
try {
await signUp(
@@ -173,13 +191,15 @@ const SignUpForm = () => {
teamName,
remark,
},
- abortRef.current.signal
+ abortRefSignUp.current.signal
);
toast.success('회원가입이 완료되었습니다.');
nav('/login');
} catch (error) {
console.log(error);
toast.error('회원가입에 실패하였습니다.');
+ } finally {
+ setIsSigningUp(false);
}
};
@@ -288,9 +308,9 @@ const SignUpForm = () => {
type="button"
className={styles.verifyButton}
onClick={handleCheckVerificationNumber}
- disabled={!isVerificationSent}
+ disabled={!isVerificationSent || isCheckingVerification}
>
- 인증번호 확인
+ {isCheckingVerification ? '확인 중...' : '인증번호 확인'}
@@ -382,9 +402,9 @@ const SignUpForm = () => {