프로토타입 사용자 관리 및 로그인 시스템 개선

- 8명의 샘플 사용자 데이터 추가 (SAMPLE_USERS)
- 8가지 명확한 아바타 색상 스타일 추가 (red, blue, green, yellow, purple, cyan, magenta, orange)
- 로그인 시스템 개선: 사번 입력으로 SAMPLE_USERS에서 검색하여 로그인
- 대시보드 사용자 정보 동적 렌더링 (사이드바, 헤더)
- 회의예약 참석자 추가 모달에 전체 사용자 목록 표시
- 참석자 검색 기능 개선 (이름/이메일 검색)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
yabo0812 2025-10-24 00:51:43 +09:00
parent e71b5c64d0
commit d2443f90ad
5 changed files with 82 additions and 37 deletions

View File

@ -235,20 +235,20 @@
// 실제 환경에서는 API 호출 // 실제 환경에서는 API 호출
setTimeout(() => { setTimeout(() => {
// 데모용 로그인 검증 // 데모용 로그인 검증 - SAMPLE_USERS에서 사용자 찾기
if (formData.employeeId === 'user-001' || formData.employeeId === 'demo') { const user = SAMPLE_USERS.find(u => u.id === formData.employeeId);
if (user) {
// 로그인 성공 - 사용자 정보 저장 // 로그인 성공 - 사용자 정보 저장
const user = { const userToSave = {
id: 'user-001', ...user,
name: '김민준',
email: 'minjun.kim@example.com',
employeeId: formData.employeeId employeeId: formData.employeeId
}; };
saveToStorage('currentUser', user); saveToStorage('currentUser', userToSave);
saveToStorage('isLoggedIn', true); saveToStorage('isLoggedIn', true);
// 대시보드로 이동 // 대시보드로 이동
showToast('로그인 성공!', 'success'); showToast(`${user.name}님 로그인 성공!`, 'success');
setTimeout(() => { setTimeout(() => {
navigateTo('02-대시보드.html'); navigateTo('02-대시보드.html');
}, 500); }, 500);
@ -308,7 +308,10 @@
} }
console.log('01-로그인 화면 초기화 완료'); console.log('01-로그인 화면 초기화 완료');
console.log('데모 계정: user-001 또는 demo (비밀번호: 아무거나 8자 이상)'); console.log('데모 계정 목록 (비밀번호: 아무거나 8자 이상):');
SAMPLE_USERS.forEach(user => {
console.log(`- ${user.id}: ${user.name} (${user.email})`);
});
</script> </script>
</body> </body>
</html> </html>

View File

@ -525,12 +525,8 @@
</nav> </nav>
<!-- 사용자 정보 영역 (Desktop) --> <!-- 사용자 정보 영역 (Desktop) -->
<div class="sidebar-user"> <div class="sidebar-user" id="sidebar-user">
<div class="avatar avatar-green"></div> <!-- 동적으로 생성 -->
<div class="sidebar-user-info">
<div class="sidebar-user-name">김민준</div>
<div class="sidebar-user-email">minjun.kim@company.com</div>
</div>
</div> </div>
<button class="btn btn-ghost" style="width: calc(100% - 32px); margin: 0 16px 16px;" onclick="logout()">로그아웃</button> <button class="btn btn-ghost" style="width: calc(100% - 32px); margin: 0 16px 16px;" onclick="logout()">로그아웃</button>
</aside> </aside>
@ -538,8 +534,8 @@
<!-- 헤더 --> <!-- 헤더 -->
<header class="header"> <header class="header">
<div class="header-left"> <div class="header-left">
<h1 class="header-title">김민준님</h1> <h1 class="header-title" id="header-title"><!-- 동적으로 생성 --></h1>
<p class="header-subtitle">오늘 2건의 회의가 예정되어 있어요</p> <p class="header-subtitle" id="header-subtitle"><!-- 동적으로 생성 --></p>
</div> </div>
<!-- Mobile 프로필 아이콘 --> <!-- Mobile 프로필 아이콘 -->
<button class="mobile-profile-btn" onclick="toggleProfileMenu()" title="프로필"> <button class="mobile-profile-btn" onclick="toggleProfileMenu()" title="프로필">
@ -652,6 +648,37 @@
const currentUser = getFromStorage('currentUser') || CURRENT_USER; const currentUser = getFromStorage('currentUser') || CURRENT_USER;
/**
* 사이드바 사용자 정보 렌더링
*/
function renderSidebarUser() {
const sidebarUser = $('#sidebar-user');
sidebarUser.innerHTML = `
${createAvatar(currentUser, 'md')}
<div class="sidebar-user-info">
<div class="sidebar-user-name">${currentUser.name}</div>
<div class="sidebar-user-email">${currentUser.email}</div>
</div>
`;
}
/**
* 헤더 정보 렌더링
*/
function renderHeader() {
const today = new Date().toISOString().split('T')[0];
const todayMeetings = SAMPLE_MEETINGS.filter(m =>
m.date === today &&
(m.status === 'scheduled' || m.status === 'ongoing') &&
m.participants.some(p => p.id === currentUser.id)
).length;
$('#header-title').textContent = `${currentUser.name}님`;
$('#header-subtitle').textContent = todayMeetings > 0
? `오늘 ${todayMeetings}건의 회의가 예정되어 있어요`
: '오늘 예정된 회의가 없습니다';
}
/** /**
* 최근 회의 렌더링 (회의록 미생성 먼저, 빠른 일시 순으로 3개) * 최근 회의 렌더링 (회의록 미생성 먼저, 빠른 일시 순으로 3개)
*/ */
@ -877,6 +904,8 @@
* 초기화 * 초기화
*/ */
function init() { function init() {
renderSidebarUser();
renderHeader();
updateStats(); updateStats();
renderRecentMeetings(); renderRecentMeetings();
renderMyTodos(); renderMyTodos();

View File

@ -498,7 +498,12 @@
} }
// 현재 사용자 정보 // 현재 사용자 정보
const currentUser = getFromStorage('currentUser') || CURRENT_USER; const storedUser = getFromStorage('currentUser');
const currentUser = storedUser ? {
...storedUser,
avatar: storedUser.avatar || CURRENT_USER.avatar,
avatarColor: storedUser.avatarColor || CURRENT_USER.avatarColor
} : CURRENT_USER;
// 참석자 목록 (현재 사용자는 기본 포함) // 참석자 목록 (현재 사용자는 기본 포함)
let participants = [currentUser]; let participants = [currentUser];
@ -693,17 +698,17 @@
* 참석자 검색 결과 렌더링 * 참석자 검색 결과 렌더링
*/ */
function renderSearchResults(query) { function renderSearchResults(query) {
// 샘플 사용자 목록 (실제로는 API 호출) // 샘플 사용자 목록에서 이미 추가된 참석자 제외
const sampleUsers = SAMPLE_MEETINGS[0].participants.filter(p => const availableUsers = SAMPLE_USERS.filter(u =>
!participants.some(participant => participant.id === p.id) !participants.some(participant => participant.id === u.id)
); );
const results = query const results = query
? sampleUsers.filter(u => ? availableUsers.filter(u =>
u.name.toLowerCase().includes(query.toLowerCase()) || u.name.toLowerCase().includes(query.toLowerCase()) ||
(u.email && u.email.toLowerCase().includes(query.toLowerCase())) (u.email && u.email.toLowerCase().includes(query.toLowerCase()))
) )
: sampleUsers; : availableUsers;
if (results.length === 0) { if (results.length === 0) {
participantSearchResults.innerHTML = ` participantSearchResults.innerHTML = `

View File

@ -764,12 +764,14 @@ input[type="date"]::-webkit-calendar-picker-indicator {
} }
/* Avatar Color Variants */ /* Avatar Color Variants */
.avatar-green { background: #4DD5A7; } .avatar-red { background: #EF5350; color: var(--white); }
.avatar-blue { background: #64B5F6; } .avatar-blue { background: #42A5F5; color: var(--white); }
.avatar-yellow { background: #FFB74D; } .avatar-green { background: #66BB6A; color: var(--white); }
.avatar-pink { background: #F06292; } .avatar-yellow { background: #FFEE58; color: var(--gray-900); }
.avatar-purple { background: #9575CD; } .avatar-purple { background: #AB47BC; color: var(--white); }
.avatar-orange { background: #FF9800; } .avatar-cyan { background: #26C6DA; color: var(--white); }
.avatar-magenta { background: #EC407A; color: var(--white); }
.avatar-orange { background: #FF7043; color: var(--white); }
/* ======================================== /* ========================================
12. Lists 12. Lists

View File

@ -7,14 +7,20 @@
// 1. Global State & Sample Data // 1. Global State & Sample Data
// ======================================== // ========================================
// 현재 사용자 정보 // 샘플 사용자 목록
const CURRENT_USER = { const SAMPLE_USERS = [
id: 'user-001', { id: "user-001", name: "김민준", email: "minjun.kim@example.com", avatar: "김", avatarColor: "red" },
name: '김민준', { id: "user-002", name: "박서연", email: "seoyeon.park@example.com", avatar: "박", avatarColor: "blue" },
email: 'minjun.kim@example.com', { id: "user-003", name: "이준호", email: "junho.lee@example.com", avatar: "이", avatarColor: "green" },
avatar: '김', { id: "user-004", name: "최유진", email: "yujin.choi@example.com", avatar: "최", avatarColor: "yellow" },
avatarColor: 'green' { id: "user-005", name: "정도현", email: "dohyun.jung@example.com", avatar: "정", avatarColor: "purple" },
}; { id: "user-006", name: "강지수", email: "jisoo.kang@example.com", avatar: "강", avatarColor: "cyan" },
{ id: "user-007", name: "송주영", email: "jooyoung.song@example.com", avatar: "송", avatarColor: "magenta" },
{ id: "user-008", name: "백현정", email: "hyunjung.baek@example.com", avatar: "백", avatarColor: "orange" }
];
// 현재 사용자 정보 (기본값)
const CURRENT_USER = SAMPLE_USERS[0];
// 샘플 회의 데이터 // 샘플 회의 데이터
const SAMPLE_MEETINGS = [ const SAMPLE_MEETINGS = [