@@ -18,7 +18,8 @@ const updateDeveloperLevels = async (codevId?: string) => {
1818
1919 const { data : pointsData , error : pointsError } = await supabase
2020 . from ( "codev_points" )
21- . select ( "skill_category_id, points" ) ;
21+ . select ( "skill_category_id, points" )
22+ . eq ( "codev_id" , codevId ) ;
2223
2324 if ( pointsError ) {
2425 console . error ( "Error fetching points:" , pointsError ) ;
@@ -27,33 +28,43 @@ const updateDeveloperLevels = async (codevId?: string) => {
2728
2829 const levels : Record < string , number > = { } ;
2930
30- for ( const pointRecord of pointsData ) {
31+ // Fetch all levels in parallel instead of sequential
32+ const levelPromises = pointsData . map ( async ( pointRecord ) => {
3133 const { data : levelData , error : levelError } = await supabase
3234 . from ( "levels" )
3335 . select ( "*" )
3436 . eq ( "skill_category_id" , pointRecord . skill_category_id )
3537 . lte ( "min_points" , pointRecord . points )
3638 . order ( "level" , { ascending : false } )
3739 . limit ( 1 )
38- . single ( ) ;
40+ . maybeSingle ( ) ;
3941
40- if ( levelError ) {
41- console . error ( "Error finding level:" , levelError ) ;
42- continue ;
42+ if ( ! levelError && levelData ) {
43+ return {
44+ skillCategoryId : pointRecord . skill_category_id ,
45+ level : levelData . level
46+ } ;
4347 }
48+ return null ;
49+ } ) ;
4450
45- if ( levelData ) {
46- levels [ pointRecord . skill_category_id ] = levelData . level ;
51+ const levelResults = await Promise . all ( levelPromises ) ;
52+
53+ levelResults . forEach ( ( result ) => {
54+ if ( result ) {
55+ levels [ result . skillCategoryId ] = result . level ;
4756 }
48- }
57+ } ) ;
4958
50- const { error : updateError } = await supabase
51- . from ( "codev" )
52- . update ( { level : levels } )
53- . eq ( "id" , codevId ) ;
59+ if ( Object . keys ( levels ) . length > 0 ) {
60+ const { error : updateError } = await supabase
61+ . from ( "codev" )
62+ . update ( { level : levels } )
63+ . eq ( "id" , codevId ) ;
5464
55- if ( updateError ) {
56- console . error ( "Error updating levels:" , updateError ) ;
65+ if ( updateError ) {
66+ console . error ( "Error updating levels:" , updateError ) ;
67+ }
5768 }
5869} ;
5970
@@ -460,54 +471,40 @@ export const updateColumnName = async (
460471 }
461472} ;
462473
463- // FIXED : Complete task with proper field extraction and validation
474+ // OPTIMIZED : Complete task with parallel operations to prevent timeouts
464475export const completeTask = async (
465476 task : Task ,
466477) : Promise < { success : boolean ; error ?: string } > => {
467478 const supabase = await createClientServerComponent ( ) ;
468479
469480 try {
470- // CRITICAL FIX: Extract IDs safely from both flat and nested structures
481+ // Extract IDs safely from both flat and nested structures
471482 const primaryAssigneeId = task . codev ?. id || task . codev_id ;
472483 const skillCategoryId = task . skill_category ?. id || task . skill_category_id ;
473484 const taskPoints = task . points ;
474485
475- // Validate required fields before proceeding
486+ // Quick validation
476487 if ( ! task . id ) {
477- console . error ( "Task completion failed: Missing task ID" ) ;
478488 return { success : false , error : "Task ID is required" } ;
479489 }
480490
481491 if ( ! task . pr_link || task . pr_link . trim ( ) === "" ) {
482- console . error ( "Task completion failed: Missing PR link for task" , task . id ) ;
483492 return { success : false , error : "PR Link is required to complete task" } ;
484493 }
485494
486495 if ( ! primaryAssigneeId ) {
487- console . error ( "Task completion failed: No assignee for task" , task . id ) ;
488496 return { success : false , error : "Task must be assigned to complete" } ;
489497 }
490498
491499 if ( ! skillCategoryId ) {
492- console . error ( "Task completion failed: No skill category for task" , task . id ) ;
493500 return { success : false , error : "Skill category is required" } ;
494501 }
495502
496503 if ( ! taskPoints || taskPoints <= 0 ) {
497- console . error ( "Task completion failed: Invalid points for task" , task . id ) ;
498504 return { success : false , error : "Task must have points to award" } ;
499505 }
500506
501- console . log ( "Completing task:" , {
502- taskId : task . id ,
503- title : task . title ,
504- assigneeId : primaryAssigneeId ,
505- skillCategoryId,
506- points : taskPoints ,
507- hasSidekicks : task . sidekick_ids ?. length || 0
508- } ) ;
509-
510- // Archive the task FIRST
507+ // Archive the task immediately
511508 const { error : archiveError } = await supabase
512509 . from ( "tasks" )
513510 . update ( {
@@ -521,132 +518,72 @@ export const completeTask = async (
521518 return { success : false , error : `Failed to archive task: ${ archiveError . message } ` } ;
522519 }
523520
524- console . log ( "Task archived successfully:" , task . id ) ;
525-
526- // Award points to primary assignee
527- const { data : existingPoints , error : fetchPointsError } = await supabase
528- . from ( "codev_points" )
529- . select ( "*" )
530- . eq ( "codev_id" , primaryAssigneeId )
531- . eq ( "skill_category_id" , skillCategoryId )
532- . single ( ) ;
533-
534- if ( fetchPointsError && fetchPointsError . code !== 'PGRST116' ) {
535- console . error ( "Error fetching existing points:" , fetchPointsError ) ;
536- // Continue anyway - we'll try to insert
537- }
538-
539- if ( existingPoints ) {
540- const { error : updateError } = await supabase
541- . from ( "codev_points" )
542- . update ( {
543- points : existingPoints . points + taskPoints ,
544- updated_at : new Date ( ) . toISOString ( ) ,
545- } )
546- . eq ( "id" , existingPoints . id ) ;
547-
548- if ( updateError ) {
549- console . error ( "Error updating points for primary assignee:" , updateError ) ;
550- return { success : false , error : `Failed to award points: ${ updateError . message } ` } ;
551- }
521+ // OPTIMIZATION: Prepare all member IDs and points
522+ const sidekickPoints = Math . floor ( taskPoints * 0.5 ) ;
523+ const allMemberIds = [
524+ primaryAssigneeId ,
525+ ...( task . sidekick_ids || [ ] )
526+ ] ;
552527
553- console . log ( `Updated points for assignee ${ primaryAssigneeId } : + ${ taskPoints } (total: ${ existingPoints . points + taskPoints } )` ) ;
554- } else {
555- const { error : insertError } = await supabase
528+ // Fetch all existing points records in parallel
529+ const pointsPromises = allMemberIds . map ( memberId =>
530+ supabase
556531 . from ( "codev_points" )
557- . insert ( {
558- codev_id : primaryAssigneeId ,
559- skill_category_id : skillCategoryId ,
560- points : taskPoints ,
561- } ) ;
562-
563- if ( insertError ) {
564- console . error ( "Error inserting new points for primary assignee:" , insertError ) ;
565- return { success : false , error : `Failed to award points: ${ insertError . message } ` } ;
566- }
567-
568- console . log ( `Created new points record for assignee ${ primaryAssigneeId } : ${ taskPoints } ` ) ;
569- }
570-
571- // Award 50% points to sidekicks
572- if ( task . sidekick_ids ?. length ) {
573- const sidekickPoints = Math . floor ( taskPoints * 0.5 ) ;
574- console . log ( `Awarding ${ sidekickPoints } points to ${ task . sidekick_ids . length } sidekick(s)` ) ;
575-
576- for ( const sidekickId of task . sidekick_ids ) {
577- const { data : sidekickExisting } = await supabase
532+ . select ( "*" )
533+ . eq ( "codev_id" , memberId )
534+ . eq ( "skill_category_id" , skillCategoryId )
535+ . maybeSingle ( )
536+ ) ;
537+
538+ const pointsResults = await Promise . all ( pointsPromises ) ;
539+
540+ // Prepare batch updates/inserts
541+ const pointsOperations = allMemberIds . map ( ( memberId , index ) => {
542+ const pointsResult = pointsResults [ index ] ;
543+ const existingPoints = pointsResult ?. data ;
544+ const pointsToAward = memberId === primaryAssigneeId ? taskPoints : sidekickPoints ;
545+
546+ if ( existingPoints ) {
547+ // Update existing record
548+ return supabase
578549 . from ( "codev_points" )
579- . select ( "*" )
580- . eq ( "codev_id" , sidekickId )
581- . eq ( "skill_category_id" , skillCategoryId )
582- . single ( ) ;
583-
584- if ( sidekickExisting ) {
585- await supabase
586- . from ( "codev_points" )
587- . update ( {
588- points : sidekickExisting . points + sidekickPoints ,
589- updated_at : new Date ( ) . toISOString ( ) ,
590- } )
591- . eq ( "id" , sidekickExisting . id ) ;
592-
593- console . log ( `Updated points for sidekick ${ sidekickId } : +${ sidekickPoints } ` ) ;
594- } else {
595- await supabase
596- . from ( "codev_points" )
597- . insert ( {
598- codev_id : sidekickId ,
599- skill_category_id : skillCategoryId ,
600- points : sidekickPoints ,
601- } ) ;
602-
603- console . log ( `Created new points record for sidekick ${ sidekickId } : ${ sidekickPoints } ` ) ;
604- }
550+ . update ( {
551+ points : existingPoints . points + pointsToAward ,
552+ updated_at : new Date ( ) . toISOString ( ) ,
553+ } )
554+ . eq ( "id" , existingPoints . id ) ;
555+ } else {
556+ // Insert new record
557+ return supabase
558+ . from ( "codev_points" )
559+ . insert ( {
560+ codev_id : memberId ,
561+ skill_category_id : skillCategoryId ,
562+ points : pointsToAward ,
563+ } ) ;
605564 }
606- }
565+ } ) ;
607566
608- // Update developer levels for primary assignee
609- console . log ( "Updating developer levels..." ) ;
610- await updateDeveloperLevels ( primaryAssigneeId ) ;
611-
612- // Update developer levels for sidekicks
613- if ( task . sidekick_ids ?. length ) {
614- for ( const sidekickId of task . sidekick_ids ) {
615- await updateDeveloperLevels ( sidekickId ) ;
616- }
567+ // Execute all points updates in parallel
568+ const pointsUpdateResults = await Promise . all ( pointsOperations ) ;
569+
570+ // Check for critical errors
571+ const criticalErrors = pointsUpdateResults . filter ( result => result . error ) ;
572+ if ( criticalErrors . length > 0 ) {
573+ console . error ( "Errors awarding points:" , criticalErrors ) ;
574+ // Task is already archived, so we continue
617575 }
618576
619- console . log ( "Developer levels updated successfully" ) ;
577+ // Update levels for all members in parallel
578+ const levelUpdatePromises = allMemberIds . map ( memberId =>
579+ updateDeveloperLevels ( memberId )
580+ ) ;
620581
621- // Revalidate pages
622- const { data : taskData } = await supabase
623- . from ( "tasks" )
624- . select ( "kanban_column_id" )
625- . eq ( "id" , task . id )
626- . single ( ) ;
582+ await Promise . all ( levelUpdatePromises ) ;
627583
628- if ( taskData ?. kanban_column_id ) {
629- const { data : columnData } = await supabase
630- . from ( "kanban_columns" )
631- . select ( "board_id" )
632- . eq ( "id" , taskData . kanban_column_id )
633- . single ( ) ;
634-
635- if ( columnData ?. board_id ) {
636- const { data : boardData } = await supabase
637- . from ( "kanban_boards" )
638- . select ( "project_id" )
639- . eq ( "id" , columnData . board_id )
640- . single ( ) ;
641-
642- if ( boardData ?. project_id ) {
643- revalidatePath ( `/home/kanban/${ boardData . project_id } /${ columnData . board_id } ` ) ;
644- revalidatePath ( "/home/kanban" ) ;
645- }
646- }
647- }
584+ // Single revalidation at the end
585+ revalidatePath ( "/home/kanban" ) ;
648586
649- console . log ( "Task completion successful:" , task . id ) ;
650587 return { success : true } ;
651588 } catch ( error ) {
652589 console . error ( "Error completing task:" , error ) ;
0 commit comments