Skip to content

Commit bd2f60f

Browse files
Merge pull request #487 from CodebilityDev/fixes-task-approve-success-message-errors
Fixed Task Completion: Errors in Success Message
2 parents 7358846 + 6dea63a commit bd2f60f

2 files changed

Lines changed: 87 additions & 150 deletions

File tree

‎apps/codebility/app/home/kanban/[projectId]/[id]/_components/tasks/TaskViewModal.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { createClientClientComponent } from "@/utils/supabase/client";
2727
import { Ellipsis, Loader2Icon } from "lucide-react";
2828
import toast from "react-hot-toast";
2929

30-
import {
30+
import {
3131
DropdownMenu,
3232
DropdownMenuContent,
3333
DropdownMenuItem,

‎apps/codebility/app/home/kanban/[projectId]/[id]/actions.ts‎

Lines changed: 86 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -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
464475
export 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

Comments
 (0)