@@ -21,12 +21,12 @@ export interface KanbanBoardProps {
2121 initialColleagues ?: Colleague [ ]
2222 // Task handlers
2323 onAddTask ?: ( newTask : Omit < Task , 'id' | 'createdAt' | 'updatedAt' > ) => void
24- onUpdateTask ?: ( taskId : string , updates : Partial < Task > ) => Promise < Task >
24+ onUpdateTask ?: ( taskId : string , updates : Partial < Task > ) => void
2525 onDeleteTask ?: ( taskId : string ) => void
2626 onTaskClick ?: ( task : Task ) => void
2727 // Epic handlers
2828 onAddEpic ?: ( newEpic : Omit < Epic , 'id' | 'updatedAt' | 'createdAt' > ) => void
29- onAddComment ?: ( { content, taskId } : { taskId : string ; content : string } ) => Promise < Task >
29+ onAddComment ?: ( { content, taskId } : { taskId : string ; content : string } ) => void
3030}
3131
3232export const KanbanBoardView : React . FC < KanbanBoardProps > = ( {
@@ -45,39 +45,77 @@ export const KanbanBoardView: React.FC<KanbanBoardProps> = ({
4545 onAddEpic,
4646 onAddComment,
4747} ) => {
48- const [ tasks , setTasks ] = useState < Task [ ] > ( initialTasks )
49- const [ epics , setEpics ] = useState < Epic [ ] > ( initialEpics )
50- const [ sprints , setSprints ] = useState < ( Sprint & { isSelected : boolean } ) [ ] > ( initialSprints )
48+ const [ tasks , setTasks ] = useState < Task [ ] > ( [ ] )
49+ const [ epics , setEpics ] = useState < Epic [ ] > ( [ ] )
50+ const [ sprints , setSprints ] = useState < ( Sprint & { isSelected : boolean } ) [ ] > ( [ ] )
5151 const [ isAddTaskModalOpen , setIsAddTaskModalOpen ] = useState ( false )
5252 const [ isAddEpicModalOpen , setIsAddEpicModalOpen ] = useState ( false )
5353 const [ selectedTask , setSelectedTask ] = useState < Task | null > ( null )
5454 const [ draggedTask , setDraggedTask ] = useState < Task | null > ( null )
55- const [ users , setUsers ] = useState < User [ ] > ( initialUsers )
56- const [ colleagues , setColleagues ] = useState < Colleague [ ] > ( initialColleagues )
55+ const [ users , setUsers ] = useState < User [ ] > ( [ ] )
56+ const [ colleagues , setColleagues ] = useState < Colleague [ ] > ( [ ] )
5757 const [ heroHeight , setHeroHeight ] = useState ( 0 )
5858
5959 const heroRef = useRef < HTMLDivElement > ( null )
6060 const containerRef = useRef < HTMLDivElement > ( null )
61+ const selectedEpicIds = React . useMemo (
62+ ( ) => [ ...epics . map ( ( e ) => e . id ) , 0 ] , // include “no-epic” sentinel id 0
63+ [ epics ] ,
64+ )
6165
62- const selectedEpics = epics . filter ( ( epic ) => true ) . map ( ( epic ) => epic . id )
63- const selectedSprint = sprints . find ( ( sprint ) => sprint . isSelected )
66+ const prevTaskHash = useRef < string > ( '' )
67+ const prevEpicHash = useRef < string > ( '' )
68+ const prevSprintHash = useRef < string > ( '' )
69+ const prevColleagueHash = useRef < string > ( '' )
6470
65- selectedEpics . push ( 0 )
71+ const getHash = ( list : { id : string | number ; createdAt : string ; updatedAt : string } [ ] ) => {
72+ return list
73+ . map (
74+ ( item ) =>
75+ `${ item . id } -${ new Date ( item . createdAt ) . getTime ( ) } -${ new Date ( item . updatedAt ) . getTime ( ) } ` ,
76+ )
77+ . join ( '-' )
78+ }
6679
6780 useEffect ( ( ) => {
68- setTasks ( initialTasks )
81+ const next = getHash ( initialTasks )
82+ if ( next !== prevTaskHash . current ) {
83+ setTasks ( initialTasks )
84+ if ( selectedTask ) {
85+ setSelectedTask ( initialTasks . find ( ( task ) => task . id === selectedTask . id ) || null )
86+ }
87+ prevTaskHash . current = next
88+ }
6989 } , [ initialTasks ] )
7090
7191 useEffect ( ( ) => {
72- setEpics ( initialEpics )
92+ const next = getHash ( initialEpics )
93+ if ( next !== prevEpicHash . current ) {
94+ setEpics ( initialEpics )
95+ prevEpicHash . current = next
96+ }
7397 } , [ initialEpics ] )
7498
7599 useEffect ( ( ) => {
76- setSprints ( initialSprints )
100+ const next = getHash ( initialSprints )
101+ if ( next !== prevSprintHash . current ) {
102+ setSprints ( initialSprints )
103+ prevSprintHash . current = next
104+ }
77105 } , [ initialSprints ] )
78106
79107 useEffect ( ( ) => {
80- setColleagues ( initialColleagues )
108+ const next = getHash (
109+ initialColleagues . map ( ( el ) => ( {
110+ id : el . id ,
111+ updatedAt : el . updatedAt ,
112+ createdAt : el . createdAt ,
113+ } ) ) ,
114+ )
115+ if ( next !== prevColleagueHash . current ) {
116+ setColleagues ( initialColleagues )
117+ prevColleagueHash . current = next
118+ }
81119 } , [ initialColleagues ] )
82120
83121 // Measure hero height and adjust when it changes
@@ -90,6 +128,7 @@ export const KanbanBoardView: React.FC<KanbanBoardProps> = ({
90128 }
91129
92130 // Initial measurement
131+
93132 measureHeroHeight ( )
94133
95134 // Set up ResizeObserver to watch for changes in hero height
@@ -170,18 +209,17 @@ export const KanbanBoardView: React.FC<KanbanBoardProps> = ({
170209 onAddTask ?.( newTask )
171210 }
172211
173- const handleUpdateTask = async ( taskId : string , updates : Partial < Task > ) : Promise < Task > => {
212+ const handleUpdateTask = async ( taskId : string , updates : Partial < Task > ) : Promise < void > => {
174213 setTasks ( ( prev ) =>
175214 prev . map ( ( task ) => ( task . id === Number ( taskId ) ? { ...task , ...updates } : task ) ) ,
176215 )
177216 if ( onUpdateTask ) {
178- return await onUpdateTask ( taskId , updates )
217+ await onUpdateTask ( taskId , updates )
179218 }
180- const task = tasks . find ( ( task ) => task . id === Number ( taskId ) )
181- if ( ! task ) {
182- throw new Error ( 'Task not found' )
219+
220+ if ( selectedTask ?. id === Number ( taskId ) ) {
221+ setSelectedTask ( ( prev ) => ( { ... prev , ... updates } as Task ) )
183222 }
184- return { ...task , ...updates }
185223 }
186224
187225 const handleDeleteTask = async ( taskId : string ) : Promise < void > => {
@@ -265,6 +303,7 @@ export const KanbanBoardView: React.FC<KanbanBoardProps> = ({
265303 { tasksByEpic [ 'no-epic' ] . map ( ( task ) => {
266304 return (
267305 < TaskCard
306+ key = { task . id }
268307 epic = { null }
269308 task = { task }
270309 onDragStart = { handleDragStart }
@@ -273,7 +312,7 @@ export const KanbanBoardView: React.FC<KanbanBoardProps> = ({
273312 />
274313 )
275314 } ) }
276- { selectedEpics . map ( ( epicId ) => {
315+ { selectedEpicIds . map ( ( epicId ) => {
277316 const epic = epics . find ( ( e ) => e . id === epicId )
278317 const epicTasks = tasksByEpic [ epicId ] || [ ]
279318
0 commit comments