11"use client" ;
22
3- import { Suspense , useCallback , useEffect , useMemo , useRef , useState } from "react" ;
3+ import { Suspense , useCallback , useEffect , useRef , useState } from "react" ;
44
55import AddTopicDialog from "@/components/(with-side-bar)/layout/add-topic-dialog" ;
66import AddStickerDialog from "@/components/topic/add-sticker-dialog" ;
77import ContentList from "@/components/topic/content-list" ;
8- import CustomNode from "@/components/topic/custom-node" ;
98import EditTopicSidebar from "@/components/topic/edit-sticker-sidebar" ;
9+ import FlowCanvas from "@/components/topic/flow-canvas" ;
1010import RemoveContentButton from "@/components/topic/remove-content-button" ;
1111import SummarizeDialog from "@/components/topic/summarize-dialog" ;
1212import { Button } from "@/components/ui/button" ;
1313import { Input } from "@/components/ui/input" ;
1414import type { ContentTypeOptions } from "@/constants/content" ;
1515import { useCreateConnection , useRemoveConnection } from "@/lib/tanstack/mutation/connection" ;
16- import { useCreateContent } from "@/lib/tanstack/mutation/topic-content" ;
1716import { useGetTopicById } from "@/lib/tanstack/query/topic" ;
1817import { useMobileMenuStore } from "@/lib/zustand/mobile-menu-store" ;
19- import type { CategoryContentDTO } from "@/models/content" ;
2018import { infoToast } from "@/utils/toast" ;
2119import {
2220 addEdge ,
23- Background ,
2421 Connection ,
2522 Edge ,
2623 Node ,
27- NodeProps ,
28- NodeTypes ,
29- ReactFlow ,
24+ ReactFlowProvider ,
3025 useEdgesState ,
3126 useNodesState ,
3227} from "@xyflow/react" ;
3328
34- import { AlertTriangle , Lightbulb , Loader2 , Menu , Plus , Search } from "lucide-react" ;
29+ import { Menu , Plus , Search } from "lucide-react" ;
3530
3631interface TopicBoardPageProps {
3732 id : string ;
@@ -40,24 +35,14 @@ interface TopicBoardPageProps {
4035
4136const initialNodes : Node [ ] = [ ] ;
4237
43- const connectionLineStyle = {
44- stroke : "#b1b1b7" ,
45- strokeWidth : 3 ,
46- } ;
47-
48- const defaultEdgeOptions = {
49- style : {
50- strokeWidth : 3 ,
51- } ,
52- } ;
53-
5438export default function TopicBoardPage ( { id, type } : TopicBoardPageProps ) {
5539 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
5640 const [ contentPanelWidth , setContentPanelWidth ] = useState ( 300 ) ; // Content Panel 기본 너비
5741 const [ isResizing , setIsResizing ] = useState ( false ) ;
5842 const [ selectedNodeIds , setSelectedNodeIds ] = useState < string [ ] > ( [ ] ) ;
5943
6044 const contentPanelRef = useRef < HTMLDivElement | null > ( null ) ;
45+ const reactFlowWrapper = useRef < HTMLDivElement | null > ( null ) ;
6146
6247 const { toggle } = useMobileMenuStore ( ) ;
6348
@@ -70,7 +55,6 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
7055 } = useGetTopicById ( id ) ;
7156 const { mutateAsync : createConnection } = useCreateConnection ( ) ;
7257 const { mutateAsync : removeConnection } = useRemoveConnection ( ) ;
73- const { mutateAsync : createContent } = useCreateContent ( id ) ;
7458
7559 const [ nodes , setNodes , onNodesChange ] = useNodesState < Node > ( initialNodes ) ;
7660 const [ edges , setEdges , onEdgesChange ] = useEdgesState < Edge > ( [ ] ) ;
@@ -82,19 +66,6 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
8266 } ;
8367
8468 const isNotFoundError = ! isLoading && error ?. message . includes ( "404" ) ;
85- const nodeTypes : NodeTypes = useMemo (
86- ( ) => ( {
87- custom : ( props : NodeProps ) => (
88- < CustomNode
89- { ...props }
90- topicId = { id }
91- isSelected = { selectedNodeIds . includes ( props . id ) }
92- onSelect = { onNodeSelect }
93- />
94- ) ,
95- } ) ,
96- [ selectedNodeIds , id ]
97- ) ;
9869
9970 const onConnect = useCallback (
10071 async ( params : Connection ) => {
@@ -148,18 +119,6 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
148119 e . dataTransfer . dropEffect = "copy" ;
149120 } ;
150121
151- const onDrop = async ( e : React . DragEvent ) => {
152- e . preventDefault ( ) ;
153-
154- const contentData : CategoryContentDTO = JSON . parse ( e . dataTransfer . getData ( "application/json" ) ) ;
155-
156- // 서버에 콘텐츠 추가
157- await createContent ( {
158- topicId : id ,
159- contentId : contentData . id ,
160- } ) ;
161- } ;
162-
163122 const onSearchChange = ( event : React . ChangeEvent < HTMLInputElement > ) => {
164123 setSearchQuery ( event . target . value ) ;
165124 } ;
@@ -283,52 +242,24 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
283242 </ div >
284243
285244 { /* Canvas */ }
286- < div
287- className = "relative flex-1 overflow-hidden rounded-r-lg border border-l-0"
288- onDragOver = { onDragOver }
289- onDrop = { onDrop }
290- >
291- { isLoading ? (
292- < div className = "flex h-full flex-col items-center justify-center gap-2" >
293- < Loader2 className = "text-muted-foreground size-16 animate-spin" />
294- < p className = "text-muted-foreground text-xl font-semibold" > 토픽을 불러오고 있어요</ p >
295- </ div >
296- ) : isTopicError ? (
297- < div className = "flex h-full flex-col items-center justify-center gap-2" >
298- < AlertTriangle className = "text-destructive size-16" />
299- < p className = "text-muted-foreground text-xl font-semibold" >
300- { isNotFoundError ? "토픽을 찾을 수 없어요." : "토픽을 불러오는데 실패했어요." }
301- </ p >
302- </ div >
303- ) : id ? (
304- < ReactFlow
305- nodes = { nodes }
306- edges = { edges }
307- onNodesChange = { onNodesChange }
308- onEdgesChange = { onEdgesChange }
309- onConnect = { onConnect }
310- onEdgeClick = { onEdgeClick }
311- nodeTypes = { nodeTypes }
312- connectionLineStyle = { connectionLineStyle }
313- defaultEdgeOptions = { defaultEdgeOptions }
314- >
315- < Background />
316- </ ReactFlow >
317- ) : (
318- < div className = "bg-background absolute inset-0 z-10 flex items-center justify-center" >
319- < div className = "text-center" >
320- < Lightbulb size = { 64 } className = "text-muted-foreground mx-auto mb-6 opacity-50" />
321- < h3 className = "mb-4 text-xl font-semibold" > 선택된 토픽이 없습니다</ h3 >
322- < p className = "text-muted-foreground mb-6" >
323- 토픽을 선택하거나 새 토픽을 생성해보세요
324- </ p >
325- < AddTopicDialog >
326- < Plus size = { 16 } /> 새 토픽 생성
327- </ AddTopicDialog >
328- </ div >
329- </ div >
330- ) }
331- </ div >
245+ < ReactFlowProvider >
246+ < FlowCanvas
247+ ref = { reactFlowWrapper }
248+ isLoading = { isLoading }
249+ isTopicError = { isTopicError }
250+ isNotFoundError = { isNotFoundError || false }
251+ id = { id }
252+ nodes = { nodes }
253+ edges = { edges }
254+ onNodesChange = { onNodesChange }
255+ onEdgesChange = { onEdgesChange }
256+ onConnect = { onConnect }
257+ onEdgeClick = { onEdgeClick }
258+ onDragOver = { onDragOver }
259+ selectedNodeIds = { selectedNodeIds }
260+ onNodeSelect = { onNodeSelect }
261+ />
262+ </ ReactFlowProvider >
332263 </ div >
333264
334265 { /* Edit Topic Sidebar */ }
0 commit comments