const socket = io(); // UI Elements const chatMessages = document.getElementById('chat-messages'); const messageInput = document.getElementById('message-input'); const sendBtn = document.getElementById('send-btn'); const authToggleBtn = document.getElementById('auth-toggle-btn'); const loginArea = document.getElementById('login-area'); const chatInputArea = document.getElementById('chat-input-area'); const loginSubmitBtn = document.getElementById('login-submit-btn'); const loginCancelBtn = document.getElementById('login-cancel-btn'); const imageBtn = document.getElementById('image-btn'); const imageInput = document.getElementById('image-input'); const replyInfo = document.getElementById('reply-info'); const sidebar = document.getElementById('sidebar'); const roomListContainer = document.getElementById('room-list'); const roomSearchInput = document.getElementById('room-search'); const headerAvatar = document.getElementById('header-avatar'); const targetNameLabel = document.getElementById('target-name'); const targetStatusLabel = document.getElementById('target-status'); const exitReplyBtn = document.getElementById('exit-reply-btn'); const signupShowBtn = document.getElementById('signup-show-btn'); // Unique Anonymous ID generation/recovery function getAnonymousId() { let id = localStorage.getItem('chat_anon_id'); if (!id) { id = 'anon_' + Math.random().toString(36).substr(2, 9); localStorage.setItem('chat_anon_id', id); } return id; } const isAdminPath = window.location.pathname.includes('/admin'); // Load User from LocalStorage or create Anonymous let savedUser = localStorage.getItem('chat_user'); let currentUser; // CI4 Session Bridge: CI4 뷰에서 넘겨준 세션 정보가 있는지 확인 // CI4 PHP 예시: window.chatConfig = { isLog-in: get('ISLGIN') ?>, auth: get('AUTH')) ?> }; if (window.chatConfig && window.chatConfig.ISLGIN) { const auth = window.chatConfig.AUTH; currentUser = { id: auth.id || auth.uid, username: auth.name || auth.id, role: auth.role || 'user' }; // 세션 정보가 있으면 로컬 스토리지도 동기화 (선택 사항) localStorage.setItem('chat_user', JSON.stringify(currentUser)); } else if (savedUser) { currentUser = JSON.parse(savedUser); } else { currentUser = { id: getAnonymousId(), username: '익명', role: 'guest' }; } if (currentUser && currentUser.role !== 'guest') { if (authToggleBtn) authToggleBtn.textContent = '로그아웃 (' + currentUser.username + ')'; } let currentRoom = (isAdminPath && isConsultantRole(currentUser.role)) ? 'consultants_group' : 'room_' + currentUser.id; let currentConsultant = null; // Initialize if (isAdminPath) { const headerTitle = document.querySelector('#chat-header span'); if (headerTitle) headerTitle.innerText = 'IDC 상담원 관리 시스템'; // 상담원 채널 접속 시 로그인 영역 자동 표시 및 사이드바 노출 준비 if (currentUser.role === 'guest') { loginArea.style.display = 'flex'; } else if (isConsultantRole(currentUser.role)) { if (sidebar) sidebar.style.display = 'flex'; } } else { // 일반 채팅 경로(/chat) authToggleBtn.innerText = '상담원 로그인'; if (sidebar) sidebar.style.display = 'none'; if (replyInfo) replyInfo.style.display = 'none'; // 일반 사용자용 화면은 너비를 컴팩트하게 조정 (기존 1000px의 70% 수준인 700px) const chatWidget = document.getElementById('chat-widget'); if (chatWidget) chatWidget.style.width = '700px'; } socket.emit('join_room', { room: currentRoom, user_id: currentUser.id, role: currentUser.role }); // 룸 목록 업데이트 수신 (상담원 전용) socket.on('update_room_list', (roomList) => { if (!roomListContainer) return; allRoomsData = roomList; // 검색 필터링을 위해 저장 renderRoomList(allRoomsData); }); let allRoomsData = []; function getAvatarColor(name) { const colors = ['#0078d4', '#107c10', '#d13438', '#008575', '#8764b8', '#004e8c']; let hash = 0; for (let i = 0; i < name.length; i++) { hash = name.charCodeAt(i) + ((hash << 5) - hash); } return colors[Math.abs(hash) % colors.length]; } function renderRoomList(rooms) { // 기본 모니터링 룸 추가 let listHtml = `
📢
전체 모니터링 모든 실시간 대화 감시
`; rooms.forEach(roomInfo => { const isActive = currentRoom === roomInfo.room; const initial = roomInfo.username.charAt(0); const avatarColor = getAvatarColor(roomInfo.username); listHtml += `
${initial}
${roomInfo.username} 최근 메시지 확인 중...
`; }); roomListContainer.innerHTML = listHtml; } // 룸 검색 필터링 제거 (사용자 요청) // if (roomSearchInput) { ... } // Send Message async function sendMessage(text = null, imageUrl = null) { const message = text || messageInput.value.trim(); if (message || imageUrl) { const data = { room: currentRoom, sender: currentUser.username, user_code: currentUser.id, consultant_code: currentConsultant, message: imageUrl || message, msg_type: imageUrl ? 'image' : 'text', role: currentUser.role, // Add role to metadata timestamp: new Date().toLocaleTimeString() }; socket.emit('send_message', data); if (!imageUrl) messageInput.value = ''; } } sendBtn.addEventListener('click', () => sendMessage()); messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') sendMessage(); }); // Image Upload Handling imageBtn.addEventListener('click', () => imageInput.click()); imageInput.addEventListener('change', async (e) => { const file = e.target.files[0]; if (file) await uploadAndSendImage(file); imageInput.value = ''; }); // Clipboard Paste Handling (Screen Capture) messageInput.addEventListener('paste', async (e) => { const clipboardData = e.clipboardData || window.clipboardData; if (!clipboardData) return; const items = clipboardData.items; let imageFound = false; for (let i = 0; i < items.length; i++) { if (items[i].type.indexOf('image') !== -1) { const file = items[i].getAsFile(); if (file) { imageFound = true; console.log('이미지 붙여넣기 감지됨:', file.name); await uploadAndSendImage(file); } } } // 이미지를 전송했다면 텍스트박스에 텍스트가 중복으로 들어가는 것을 방지하고 싶을 때 사용 (선택 사항) // if (imageFound) e.preventDefault(); }); async function uploadAndSendImage(file) { const formData = new FormData(); formData.append('image', file); console.log('이미지 업로드 시도 중:', file.name); try { const response = await fetch('/api/upload', { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`서버 응답 오류: ${response.status}`); } const result = await response.json(); if (result.success) { console.log('이미지 업로드 성공:', result.imageUrl); await sendMessage(null, result.imageUrl); } else { alert('이미지 업로드 실패: ' + result.message); } } catch (err) { console.error('이미지 업로드 프로세스 오류:', err); alert('이미지 전송 중 오류가 발생했습니다. 서버 로그를 확인하거나 다시 시도해 주세요.'); } } // Helper: 상담원 역할 확인 function isConsultantRole(role) { if (!role) return false; const consultantKeywords = ['manage', 'manager', 'cloudflare', 'security', 'director', 'master', 'admin', 'consultant']; const roles = String(role).toLowerCase(); return consultantKeywords.some(keyword => roles.includes(keyword)); } // Receive Message socket.on('receive_message', (data) => { if (!chatMessages) return; const msgDiv = document.createElement('div'); msgDiv.classList.add('message'); // Align messages: Consultant right, Guest left (Skype style is often left-dominant, but we follow chat standards) const isConsultant = isConsultantRole(data.role); const isMe = (data.user_code === currentUser.id); msgDiv.classList.add('message'); msgDiv.classList.add(isMe ? 'right' : 'left'); let contentHtml = ''; if (data.msg_type === 'image') { contentHtml += ``; } else { contentHtml += `${data.message}`; } msgDiv.innerHTML = contentHtml; chatMessages.appendChild(msgDiv); chatMessages.scrollTop = chatMessages.scrollHeight; // 만약 상담원이고 전체 모니터링 모드라면 룸 목록의 미리보기 텍스트 업데이트 if (isAdminPath && isConsultantRole(currentUser.role) && currentRoom === 'consultants_group') { const roomItem = document.querySelector(`.room-item[onclick*="${data.room}"] .preview`); if (roomItem) { roomItem.innerText = data.msg_type === 'image' ? '(이미지 메시지)' : data.message; } } }); // 상담원이 특정 유저의 대화방에 참여하는 함수 (답장 기능) function joinUserRoom(roomName, userName) { if (currentRoom === roomName) return; // 이미 해당 방이면 무시 // 기존 대화 내용 비우기 chatMessages.innerHTML = ''; currentRoom = roomName; socket.emit('join_room', { room: currentRoom, user_id: currentUser.id, role: currentUser.role }); // UI 업데이트 updateSidebarActive(); if (targetNameLabel) targetNameLabel.innerText = userName; if (targetStatusLabel) targetStatusLabel.innerText = '상담 중'; if (headerAvatar) { headerAvatar.innerText = userName.charAt(0); headerAvatar.style.background = getAvatarColor(userName); } if (exitReplyBtn) exitReplyBtn.style.display = 'block'; const sysMsg = document.createElement('div'); sysMsg.style.cssText = "text-align:center; margin:20px 0; font-size:12px; color:var(--text-sub); width:100%;"; sysMsg.innerHTML = `--- ${userName} 님과의 대화가 시작되었습니다 ---`; chatMessages.appendChild(sysMsg); chatMessages.scrollTop = chatMessages.scrollHeight; } function updateSidebarActive() { const items = document.querySelectorAll('.room-item'); items.forEach(item => { // onclick 속성에서 룸 분석 (간결한 구현을 위해 속성 매칭) if (item.getAttribute('onclick').includes(currentRoom)) { item.classList.add('active'); } else { item.classList.remove('active'); } }); } // 답장 모드 종료 (전체 모니터링으로 복귀) function exitReplyMode() { if (currentRoom === 'consultants_group') return; chatMessages.innerHTML = ''; currentRoom = 'consultants_group'; socket.emit('join_room', { room: currentRoom, user_id: currentUser.id, role: currentUser.role }); updateSidebarActive(); if (targetNameLabel) targetNameLabel.innerText = '전체 모니터링'; if (targetStatusLabel) targetStatusLabel.innerText = '실시간 대기 중'; if (headerAvatar) { headerAvatar.innerText = '📢'; headerAvatar.style.background = '#605e5c'; } if (exitReplyBtn) exitReplyBtn.style.display = 'none'; const sysMsg = document.createElement('div'); sysMsg.style.cssText = "text-align:center; margin:20px 0; font-size:12px; color:var(--text-sub); width:100%;"; sysMsg.innerHTML = `--- 실시간 전체 대화 모니터링 중입니다 ---`; chatMessages.appendChild(sysMsg); chatMessages.scrollTop = chatMessages.scrollHeight; } if (exitReplyBtn) { exitReplyBtn.addEventListener('click', exitReplyMode); } // Auth Toggle authToggleBtn.addEventListener('click', () => { if (currentUser.role === 'guest') { loginArea.style.display = loginArea.style.display === 'flex' ? 'none' : 'flex'; } else { // 로그아웃 처리: 세션 삭제 후 새로고침 localStorage.removeItem('chat_user'); location.reload(); } }); // Handle Login Cancellation loginCancelBtn.addEventListener('click', () => { if (isAdminPath) { location.href = '/chat'; } else { loginArea.style.display = 'none'; } }); // Handle Login Submit loginSubmitBtn.addEventListener('click', async () => { const username = document.getElementById('username').value; const password = document.getElementById('password').value; try { const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const result = await response.json(); if (result.success) { // 로그인 정보 저장 및 새로고침 localStorage.setItem('chat_user', JSON.stringify(result.user)); alert(`${result.user.username}님 환영합니다!`); location.reload(); } else { alert('로그인 실패: ' + result.message); } } catch (err) { console.error('Login error:', err); alert('로그인 처리 중 오류 발생'); } }); // Signup Show(Page Redirect)는 HTML의 onclick="location.href='/register'"으로 처리됨