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: = session()->get('ISLGIN') ?>, auth: = json_encode(session()->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'"으로 처리됨