🔔 회원가입 승인 알람 기능 추가
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled

- 상단 헤더에 🔔 알람 버튼 추가 (관리자 전용)
- 승인 대기 중인 사용자 수를 빨간 뱃지로 표시
- 클릭 시 사용자 관리 페이지로 이동
- 30초마다 자동 갱신
- 노란색 배경 + pulse 애니메이션으로 눈에 잘 띄게
- 마우스 오버 시 확대 효과

기능:
- GET /auth/signup-requests로 대기 수 조회
- 관리자(admin, system)만 표시
- 실시간 업데이트
This commit is contained in:
Hyungi Ahn
2025-10-14 07:51:16 +09:00
parent e468663386
commit 521446d56b
2 changed files with 94 additions and 6 deletions

View File

@@ -71,6 +71,17 @@ body {
100% { transform: rotate(360deg); }
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.8;
transform: scale(1.05);
}
}
/* 접근 거부 페이지 */
.access-denied-container {
display: flex;

View File

@@ -27,6 +27,19 @@ function App() {
const [newProjectCode, setNewProjectCode] = useState('');
const [newProjectName, setNewProjectName] = useState('');
const [newClientName, setNewClientName] = useState('');
const [pendingSignupCount, setPendingSignupCount] = useState(0);
// 승인 대기 중인 회원가입 수 조회
const loadPendingSignups = async () => {
try {
const response = await api.get('/auth/signup-requests');
if (response.data && response.data.requests) {
setPendingSignupCount(response.data.count || 0);
}
} catch (error) {
// 에러 무시 (관리자가 아니면 403)
}
};
// 프로젝트 목록 로드
const loadProjects = async () => {
@@ -104,8 +117,18 @@ function App() {
if (token && userData) {
setIsAuthenticated(true);
setUser(JSON.parse(userData));
const userObj = JSON.parse(userData);
setUser(userObj);
loadProjects(); // 프로젝트 목록 로드
// 관리자인 경우 승인 대기 수 조회
if (userObj.role === 'admin' || userObj.role === 'system') {
loadPendingSignups();
// 30초마다 갱신
const interval = setInterval(loadPendingSignups, 30000);
return () => clearInterval(interval);
}
}
setIsLoading(false);
@@ -247,11 +270,65 @@ function App() {
</p>
</div>
{/* 사용자 메뉴 */}
<div className="user-menu-container" style={{ position: 'relative' }}>
<button
onClick={() => setShowUserMenu(!showUserMenu)}
style={{
{/* 알람 및 사용자 메뉴 */}
<div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
{/* 회원가입 승인 알람 (관리자 전용) */}
{(user?.role === 'admin' || user?.role === 'system') && pendingSignupCount > 0 && (
<button
onClick={() => navigateToPage('user-management')}
style={{
position: 'relative',
width: '44px',
height: '44px',
background: '#fef3c7',
border: '2px solid #f59e0b',
borderRadius: '50%',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '20px',
transition: 'all 0.2s ease',
animation: 'pulse 2s ease-in-out infinite'
}}
title={`회원가입 승인 대기: ${pendingSignupCount}`}
onMouseEnter={(e) => {
e.target.style.background = '#fde68a';
e.target.style.transform = 'scale(1.1)';
}}
onMouseLeave={(e) => {
e.target.style.background = '#fef3c7';
e.target.style.transform = 'scale(1)';
}}
>
🔔
<div style={{
position: 'absolute',
top: '-4px',
right: '-4px',
background: '#ef4444',
color: 'white',
borderRadius: '50%',
width: '22px',
height: '22px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '11px',
fontWeight: '700',
border: '2px solid white',
boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
}}>
{pendingSignupCount}
</div>
</button>
)}
{/* 사용자 메뉴 */}
<div className="user-menu-container" style={{ position: 'relative' }}>
<button
onClick={() => setShowUserMenu(!showUserMenu)}
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',