@@ -158,11 +158,9 @@ function AssigneeSelector({
158158 . includes ( searchQuery . toLowerCase ( ) ) ,
159159 ) ;
160160
161- // Restore proper removal functionality for AssigneeSelector
162161 const handleRemove = ( ) => {
163162 setLocalAssignee ( null ) ;
164- onAssigneeChange ( [ ] ) ; // Pass empty array to indicate no assignee
165- setTimeout ( ( ) => setLocalAssignee ( null ) , 0 ) ; // Ensure UI updates
163+ onAssigneeChange ( [ ] ) ;
166164 } ;
167165
168166 const handleSelect = ( memberId : string ) => {
@@ -304,7 +302,6 @@ const TaskViewModal = ({
304302} ) => {
305303 const { isOpen, onOpen, onClose, type, data } = useModal ( ) ;
306304 const user = useUserStore ( ( state ) => state . user ) ;
307- // Include removeTaskOptimistic in the destructured store methods
308305 const { fetchBoardData, removeTaskOptimistic } = useKanbanStore ( ) ;
309306
310307 const [ isLoading , setIsLoading ] = useState ( false ) ;
@@ -325,7 +322,6 @@ const TaskViewModal = ({
325322 const [ createdBy , setCreatedBy ] = useState < CodevMember | null > ( null ) ;
326323 const [ forceRefreshKey , setForceRefreshKey ] = useState < string > ( "" ) ;
327324
328- // Enhanced state management for assignee changes
329325 const [ manualSaveChanges , setManualSaveChanges ] = useState ( false ) ;
330326 const [ isSavingChanges , setIsSavingChanges ] = useState ( false ) ;
331327 const [ pendingAssigneeId , setPendingAssigneeId ] = useState <
@@ -378,35 +374,30 @@ const TaskViewModal = ({
378374 fetchBoardId ( ) ;
379375 } , [ task ?. kanban_column_id , supabase ] ) ;
380376
377+ // FIXED: Consolidated state reset logic with single execution flow
381378 useEffect ( ( ) => {
382- if ( task ?. id ) {
383- const taskPrLink = task ?. pr_link || "" ;
384- setPrLink ( taskPrLink ) ;
385- setOriginalPrLink ( taskPrLink ) ;
386- setPrimaryAssignee ( null ) ; // Reset to null first
387- setManualSaveChanges ( false ) ;
388- setPendingAssigneeId ( undefined ) ;
389- setForceRefreshKey ( Date . now ( ) . toString ( ) ) ;
390-
391- // Force reset the AssigneeSelector local state by resetting primaryAssignee
392- setTimeout ( ( ) => {
393- // Fetch actual assignee from task after reset
394- if ( task . codev_id || task ?. codev ?. id ) {
395- const assigneeId = task . codev_id || task . codev ?. id || "" ;
396- if ( supabase && assigneeId ) {
397- supabase
398- . from ( "codev" )
399- . select ( "id, first_name, last_name, image_url" )
400- . eq ( "id" , assigneeId )
401- . single ( )
402- . then ( ( { data, error } ) => {
403- if ( ! error && data ) {
404- setPrimaryAssignee ( data as CodevMember ) ;
405- }
406- } ) ;
407- }
408- }
409- } , 0 ) ;
379+ if ( ! task ?. id || ! supabase ) return ;
380+
381+ const taskPrLink = task ?. pr_link || "" ;
382+ setPrLink ( taskPrLink ) ;
383+ setOriginalPrLink ( taskPrLink ) ;
384+ setManualSaveChanges ( false ) ;
385+ setPendingAssigneeId ( undefined ) ;
386+ setForceRefreshKey ( Date . now ( ) . toString ( ) ) ;
387+
388+ // Fetch and set assignee in one go
389+ const assigneeId = task . codev_id || task ?. codev ?. id ;
390+ if ( assigneeId ) {
391+ supabase
392+ . from ( "codev" )
393+ . select ( "id, first_name, last_name, image_url" )
394+ . eq ( "id" , assigneeId )
395+ . single ( )
396+ . then ( ( { data, error } : { data : any ; error : any } ) => {
397+ setPrimaryAssignee ( ! error && data ? ( data as CodevMember ) : null ) ;
398+ } ) ;
399+ } else {
400+ setPrimaryAssignee ( null ) ;
410401 }
411402 } , [ task ?. id , supabase ] ) ;
412403
@@ -441,37 +432,6 @@ const TaskViewModal = ({
441432 fetchSidekickDetails ( ) ;
442433 } , [ task ?. sidekick_ids , supabase ] ) ;
443434
444- useEffect ( ( ) => {
445- if ( ! supabase || ! task ) return ;
446-
447- const fetchPrimaryAssignee = async ( ) => {
448- const assigneeId = task ?. codev_id || task ?. codev ?. id ;
449-
450- setPrimaryAssignee ( null ) ;
451-
452- if ( assigneeId ) {
453- const { data, error } = await supabase
454- . from ( "codev" )
455- . select ( "id, first_name, last_name, image_url" )
456- . eq ( "id" , assigneeId )
457- . single ( ) ;
458-
459- if ( ! error && data ) {
460- setPrimaryAssignee ( data as CodevMember ) ;
461- }
462- } else if ( task ?. codev ) {
463- setPrimaryAssignee ( {
464- id : task . codev . id ,
465- first_name : task . codev . first_name ,
466- last_name : task . codev . last_name ,
467- image_url : task . codev . image_url ,
468- } ) ;
469- }
470- } ;
471-
472- fetchPrimaryAssignee ( ) ;
473- } , [ task , supabase ] ) ;
474-
475435 useEffect ( ( ) => {
476436 if ( ! supabase || ! task ?. created_by ) return ;
477437
@@ -512,47 +472,62 @@ const TaskViewModal = ({
512472 setUpdateLoading ( false ) ;
513473 } ;
514474
515- // Optimistic UI approach for task completion
475+ // FIXED: Improved task completion with better error handling and data consistency
516476 const handleMarkAsDone = async ( ) => {
517477 if ( ! task ) return ;
518478
479+ // Validate required fields before attempting completion
480+ if ( ! task . id ) {
481+ toast . error ( "Task ID is missing" ) ;
482+ return ;
483+ }
484+
485+ if ( ! task . pr_link ) {
486+ toast . error ( "PR Link is required to complete the task" ) ;
487+ return ;
488+ }
489+
519490 setIsLoading ( true ) ;
520491
521- // Optimistically remove the task from UI immediately
492+ console . log ( "Attempting to complete task with ID:" , task . id ) ;
493+
494+ // Optimistically remove the task from UI
522495 removeTaskOptimistic ( task . id ) ;
523496
524- // Close modal immediately for better UX
497+ // Close modal for better UX
525498 onClose ( ) ;
526499
527500 // Show optimistic success message
528501 toast . success ( "Completing task..." ) ;
529502
530503 try {
504+ // Pass the original task object - let the server action handle field extraction
531505 const result = await completeTask ( task ) ;
532506
507+ console . log ( "Task completion result:" , result ) ;
508+
533509 if ( result . success ) {
534- // Update success message
535510 toast . success ( "Task completed and points awarded!" ) ;
536511
537- // Call onComplete callback if provided
538512 if ( onComplete ) {
539513 onComplete ( task . id ) ;
540514 }
541515
542- // Optional: Fetch fresh data in background after a delay
543- // This ensures data consistency without blocking the UI
516+ // Refresh data in background
544517 setTimeout ( ( ) => {
545518 fetchBoardData ( ) ;
546519 } , 1000 ) ;
547520 } else {
548- // Revert optimistic update by refetching data
549- toast . error ( result . error || "Failed to complete task" ) ;
521+ // Revert optimistic update
522+ const errorMessage = result . error || "Failed to complete task" ;
523+ console . error ( "Task completion failed:" , errorMessage , result ) ;
524+ toast . error ( errorMessage ) ;
550525 await fetchBoardData ( ) ;
551526 }
552527 } catch ( error ) {
553528 console . error ( "Error completing task:" , error ) ;
554- toast . error ( "Failed to complete task" ) ;
555- // Revert optimistic update by refetching data
529+ const errorMsg = error instanceof Error ? error . message : "Failed to complete task" ;
530+ toast . error ( errorMsg ) ;
556531 await fetchBoardData ( ) ;
557532 } finally {
558533 setIsLoading ( false ) ;
@@ -564,12 +539,10 @@ const TaskViewModal = ({
564539
565540 const newAssigneeId = memberIds . length > 0 ? memberIds [ 0 ] : undefined ;
566541
567- // Set manual save state to show Save Changes button
568542 setManualSaveChanges ( true ) ;
569543 setPendingAssigneeId ( newAssigneeId ) ;
570544
571545 if ( newAssigneeId ) {
572- // Fetch assignee data for UI display only
573546 const { data : assigneeData } = await supabase
574547 . from ( "codev" )
575548 . select ( "id, first_name, last_name, image_url" )
@@ -580,73 +553,58 @@ const TaskViewModal = ({
580553 setPrimaryAssignee ( assigneeData ) ;
581554 }
582555 } else {
583- // Handle removal case
584556 setPrimaryAssignee ( null ) ;
585557 }
586558
587559 setForceRefreshKey ( `${ Date . now ( ) } -${ Math . random ( ) } ` ) ;
588560 } ;
589561
590- // Reset state when modal closes without saving
562+ // FIXED: Simplified close handler with cleaner state reset
591563 const handleClose = ( ) => {
592564 if ( hasUnsavedChanges ) {
593- // Reset all unsaved changes
594565 setManualSaveChanges ( false ) ;
595566 setPendingAssigneeId ( undefined ) ;
596567 setPrLink ( originalPrLink ) ;
597568
598- // Force complete reset by setting primary assignee to null first
599- setPrimaryAssignee ( null ) ;
600-
601- // Then set a unique force refresh key to reset AssigneeSelector
602- setForceRefreshKey ( `close-reset-${ Date . now ( ) } -${ Math . random ( ) } ` ) ;
603-
604- // Reset to original task assignee after a brief delay
605- setTimeout ( ( ) => {
606- if ( task ?. codev_id || task ?. codev ?. id ) {
607- const assigneeId = task . codev_id || task . codev ?. id || "" ;
608- if ( supabase && assigneeId ) {
609- supabase
610- . from ( "codev" )
611- . select ( "id, first_name, last_name, image_url" )
612- . eq ( "id" , assigneeId )
613- . single ( )
614- . then ( ( { data, error } ) => {
615- if ( ! error && data ) {
616- setPrimaryAssignee ( data as CodevMember ) ;
617- }
618- } ) ;
619- }
620- } else {
621- // Ensure it stays null for unassigned tasks
622- setPrimaryAssignee ( null ) ;
569+ // Reset assignee to original state
570+ if ( task ?. codev_id || task ?. codev ?. id ) {
571+ const assigneeId = task . codev_id || task . codev ?. id || "" ;
572+ if ( supabase && assigneeId ) {
573+ supabase
574+ . from ( "codev" )
575+ . select ( "id, first_name, last_name, image_url" )
576+ . eq ( "id" , assigneeId )
577+ . single ( )
578+ . then ( ( { data, error } : { data : any ; error : any } ) => {
579+ setPrimaryAssignee ( ! error && data ? ( data as CodevMember ) : null ) ;
580+ setForceRefreshKey ( `reset-${ Date . now ( ) } ` ) ;
581+ } ) ;
623582 }
624- } , 50 ) ;
583+ } else {
584+ setPrimaryAssignee ( null ) ;
585+ setForceRefreshKey ( `reset-${ Date . now ( ) } ` ) ;
586+ }
625587 }
626588 onClose ( ) ;
627589 } ;
628590
629- // Enhanced handleSaveChanges with assignee removal support
630591 const handleSaveChanges = async ( ) => {
631592 if ( ! task || ! supabase ) return ;
632593
633594 setIsSavingChanges ( true ) ;
634595
635596 try {
636- // Save PR Link changes
637597 if ( hasPrLinkChanges ) {
638598 await handleUpdate ( ) ;
639599 }
640600
641- // Save assignee changes (including removal)
642601 if ( manualSaveChanges ) {
643602 const { error } = await supabase
644603 . from ( "tasks" )
645- . update ( { codev_id : pendingAssigneeId || null } ) // Convert undefined to null for database
604+ . update ( { codev_id : pendingAssigneeId || null } )
646605 . eq ( "id" , task . id ) ;
647606
648607 if ( ! error ) {
649- // Update task object after successful save
650608 if ( task ) {
651609 task . codev_id = pendingAssigneeId ;
652610 if ( pendingAssigneeId && primaryAssignee ) {
@@ -836,18 +794,17 @@ const TaskViewModal = ({
836794 ) }
837795 </ div >
838796
839- { /* deadline */ }
840797 < div className = "space-y-2" >
841798 < Label className = "text-sm font-medium" > Deadline</ Label >
842799 < div className = "rounded-md bg-blue-50 p-2 text-sm font-medium text-blue-700 dark:bg-blue-900/20 dark:text-blue-300" >
843800 { task ?. deadline ? new Date ( task . deadline ) . toLocaleString ( ) : "Not Set" }
844801 </ div >
845802 </ div >
846803
804+ { /* FIXED: Primary Assignee - Now allows modification even when assigned */ }
847805 < div className = "space-y-2" >
848806 < Label className = "text-sm font-medium" > Primary Assignee</ Label >
849- { canModifyTask && ! primaryAssignee ? (
850- // Only show AssigneeSelector for UNASSIGNED tasks
807+ { canModifyTask ? (
851808 < div className = "space-y-2" >
852809 { boardId ? (
853810 < AssigneeSelector
@@ -865,7 +822,6 @@ const TaskViewModal = ({
865822 ) }
866823 </ div >
867824 ) : primaryAssignee ? (
868- // Read-only display for ASSIGNED tasks
869825 < div className = "flex items-center gap-2" >
870826 { primaryAssignee . image_url ? (
871827 < Image
@@ -883,7 +839,6 @@ const TaskViewModal = ({
883839 </ span >
884840 </ div >
885841 ) : (
886- // Read-only display for users without modify permissions
887842 < div className = "flex items-center gap-2 text-gray-500" >
888843 < DefaultAvatar size = { 32 } />
889844 < span > Unassigned</ span >
@@ -937,7 +892,6 @@ const TaskViewModal = ({
937892 />
938893 </ div >
939894
940- { /* created by - created at */ }
941895 < div className = "grid grid-cols-1 gap-6 md:grid-cols-2" >
942896 < div className = "space-y-2" >
943897 < Label className = "text-sm font-medium" > Created By</ Label >
@@ -972,16 +926,14 @@ const TaskViewModal = ({
972926 </ div >
973927
974928 < DialogFooter className = "mt-4 flex justify-end" >
975- { /*Previous style: flex gap-6 sm:justify-end */ }
976- { /* Copy URL button */ }
977929 < Button
978930 onClick = { ( ) => {
979931 if ( ! task ?. id ) {
980932 toast . error ( "Cannot copy URL: Task ID is missing" ) ;
981933 return ;
982934 }
983935
984- const baseUrl = window . location . origin ; // gets the website domain
936+ const baseUrl = window . location . origin ;
985937 const url = task . ticket_code
986938 ? `${ baseUrl } /home/kanban/ticket/${ task . ticket_code } `
987939 : new URL ( window . location . href ) . toString ( ) ;
@@ -1003,7 +955,6 @@ const TaskViewModal = ({
1003955 Copy URL
1004956 </ Button >
1005957
1006- { /* VIEWING MODE: Show Close when just viewing (no changes) */ }
1007958 { ! hasUnsavedChanges && (
1008959 < Button
1009960 onClick = { onClose }
@@ -1022,7 +973,6 @@ const TaskViewModal = ({
1022973 </ Button >
1023974 ) }
1024975
1025- { /* EDITING MODE: Show Cancel + Save Changes when has changes */ }
1026976 { hasUnsavedChanges && (
1027977 < >
1028978 < Button
@@ -1063,7 +1013,6 @@ const TaskViewModal = ({
10631013 </ >
10641014 ) }
10651015
1066- { /* Mark as Done button - separate logic */ }
10671016 { canMarkAsDone && task ?. pr_link && (
10681017 < Button
10691018 onClick = { handleMarkAsDone }
@@ -1092,4 +1041,4 @@ const TaskViewModal = ({
10921041 ) ;
10931042} ;
10941043
1095- export default TaskViewModal ;
1044+ export default TaskViewModal ;
0 commit comments