Files
vexplor_dev/frontend/components/messenger/MessengerModal.tsx
syc0123 f558073ef8 [RAPID] feat: 메신저 기능 구현 (Socket.IO 실시간 채팅)
- DB: messenger_rooms/participants/messages/reactions/files 테이블 생성
- Backend: REST API 9개 엔드포인트 + Socket.IO 실시간 핸들러
- Frontend: Gmail 스타일 FAB + 모달, 채팅방 목록, 채팅 패널
- 기능: DM/그룹/채널, 파일 첨부, 이모지 리액션, 멘션, 스레드
- 알림: 토스트 on/off 토글, FAB 읽지 않은 배지

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

[RAPID-fix] 메신저 API snake_case→camelCase 변환 및 Socket.IO URL 수정

- useRooms/useMessages/useCompanyUsers 훅에서 DB 응답 camelCase 변환
- Socket.IO 기본 연결 URL 3001 → 8080 수정
- runMigration.ts 마이그레이션 파일 경로 수정 (../../ → ../../../)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

[RAPID-fix] 방 생성 API camelCase/snake_case 호환 처리

- createRoom 컨트롤러에서 participantIds/type/name (camelCase) fallback 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

[RAPID-fix] 메시지 전송 API 추가 (sendMessage 라우트/컨트롤러 누락)

- POST /api/messenger/rooms/:roomId/messages 라우트 등록
- MessengerController.sendMessage 메서드 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 12:20:40 +09:00

67 lines
2.2 KiB
TypeScript

"use client";
import { useState } from "react";
import { X, Settings } from "lucide-react";
import { useMessengerContext } from "@/contexts/MessengerContext";
import { useRooms } from "@/hooks/useMessenger";
import { RoomList } from "./RoomList";
import { ChatPanel } from "./ChatPanel";
import { MessengerSettings } from "./MessengerSettings";
export function MessengerModal() {
const { isOpen, closeMessenger, selectedRoomId } = useMessengerContext();
const { data: rooms = [] } = useRooms();
const [showSettings, setShowSettings] = useState(false);
if (!isOpen) return null;
const selectedRoom = rooms.find((r) => r.id === selectedRoomId) || null;
return (
<div
className="fixed bottom-6 right-6 z-[9999] flex flex-col bg-background border rounded-lg shadow-2xl overflow-hidden"
style={{ width: 720, height: 500 }}
>
{/* Header */}
<div className="flex items-center justify-between px-4 py-2 border-b bg-muted/30">
<h2 className="text-sm font-semibold"></h2>
<div className="flex items-center gap-1">
<button
onClick={() => setShowSettings((p) => !p)}
className="p-1 hover:bg-muted rounded"
aria-label="설정"
>
<Settings className="h-4 w-4 text-muted-foreground" />
</button>
<button
onClick={closeMessenger}
className="p-1 hover:bg-muted rounded"
aria-label="닫기"
>
<X className="h-4 w-4 text-muted-foreground" />
</button>
</div>
</div>
{/* Body */}
<div className="flex flex-1 min-h-0 relative">
<RoomList />
<ChatPanel room={selectedRoom} />
{/* Settings slide panel */}
{showSettings && (
<div className="absolute inset-0 bg-background z-10 flex flex-col">
<div className="flex items-center justify-between px-4 py-2 border-b">
<span className="text-sm font-semibold"></span>
<button onClick={() => setShowSettings(false)} className="p-1 hover:bg-muted rounded">
<X className="h-4 w-4" />
</button>
</div>
<MessengerSettings />
</div>
)}
</div>
</div>
);
}