Skip to content

Commit 9f8dd91

Browse files
author
raineer
committed
deadline taskaddmodal
1 parent a015d06 commit 9f8dd91

3 files changed

Lines changed: 156 additions & 6 deletions

File tree

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ const TaskAddModal = () => {
314314
const [isPending, startTransition] = useTransition();
315315
const [description, setDescription] = useState("");
316316
const [selectedDifficulty, setSelectedDifficulty] = useState<string>("");
317+
const [deadline, setDeadline] = useState<string>(""); // NEW: Deadline state
317318
const user = useUserStore((state) => state.user);
318319
const [supabase, setSupabase] = useState<any>(null);
319320

@@ -338,6 +339,7 @@ const TaskAddModal = () => {
338339
setDescription("");
339340
setMainAssignee("");
340341
setSidekicks([]);
342+
setDeadline(""); // NEW: Reset deadline
341343
onClose();
342344
};
343345

@@ -382,6 +384,13 @@ const TaskAddModal = () => {
382384
const calculatedPoints = getPointsFromDifficulty(selectedDifficulty);
383385
formData.set("points", calculatedPoints.toString());
384386

387+
// NEW: Add deadline to formData if set
388+
if (deadline) {
389+
// Convert to ISO string for database
390+
const deadlineISO = new Date(deadline).toISOString();
391+
formData.append("deadline", deadlineISO);
392+
}
393+
385394
const image = formData.get("image");
386395
if (image && image instanceof File) {
387396
formData.append("image", image);
@@ -476,6 +485,7 @@ const TaskAddModal = () => {
476485
readOnly
477486
/>
478487
</div>
488+
479489
<div className="space-y-2">
480490
<Label className="text-sm font-medium">Priority</Label>
481491
<Select name="priority">
@@ -575,6 +585,28 @@ const TaskAddModal = () => {
575585
</div>
576586
</div>
577587

588+
{/* NEW: Deadline Field - Full width for better UX */}
589+
<div className="space-y-2">
590+
<Label htmlFor="deadline" className="text-sm font-medium">
591+
Deadline{" "}
592+
<span className="ml-2 text-xs font-normal text-gray-500">
593+
(Optional - Set a specific date and time)
594+
</span>
595+
</Label>
596+
<Input
597+
id="deadline"
598+
name="deadline"
599+
type="datetime-local"
600+
value={deadline}
601+
onChange={(e) => setDeadline(e.target.value)}
602+
className="bg-light-900 dark:bg-dark-200 dark:text-light-900 focus:border-customBlue-500 border border-gray-300"
603+
placeholder="Select deadline"
604+
/>
605+
<p className="text-xs text-gray-500 dark:text-gray-400">
606+
Set a hard deadline with specific time for this task
607+
</p>
608+
</div>
609+
578610
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
579611
<div className="space-y-2">
580612
<Label className="text-sm font-medium">

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

Lines changed: 123 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,25 @@ import { revalidatePath } from "next/cache";
44
import { Task } from "@/types/home/codev";
55
import { createClientServerComponent } from "@/utils/supabase/server";
66

7+
// =============================================================================
8+
// TYPE DEFINITIONS
9+
// =============================================================================
10+
711
interface CodevMember {
812
id: string;
913
first_name: string;
1014
last_name: string;
1115
image_url?: string | null;
1216
}
1317

18+
// =============================================================================
19+
// TASK OPERATIONS
20+
// =============================================================================
21+
22+
/**
23+
* Updates a task's column assignment (for drag-and-drop)
24+
* Line 18-37
25+
*/
1426
export const updateTaskColumnId = async (
1527
taskId: string,
1628
newColumnId: string,
@@ -35,6 +47,11 @@ export const updateTaskColumnId = async (
3547
}
3648
};
3749

50+
/**
51+
* Fetches available team members for task assignment
52+
* Prioritizes team leader, then sorts members alphabetically
53+
* Line 39-107
54+
*/
3855
export const fetchAvailableMembers = async (
3956
projectId: string,
4057
): Promise<CodevMember[]> => {
@@ -95,12 +112,19 @@ export const fetchAvailableMembers = async (
95112
return members.sort((a, b) => a.first_name.localeCompare(b.first_name));
96113
};
97114

115+
/**
116+
* Creates a new task with all fields including deadline
117+
* ENHANCED: Added deadline field support
118+
* created_at is auto-generated by database default
119+
* Line 109-178
120+
*/
98121
export const createNewTask = async (
99122
formData: FormData,
100123
): Promise<{ success: boolean; error?: string }> => {
101124
const supabase = await createClientServerComponent();
102125

103126
try {
127+
// Extract all form fields
104128
const title = formData.get("title")?.toString();
105129
const description = formData.get("description")?.toString();
106130
const priority = formData.get("priority")?.toString();
@@ -117,11 +141,23 @@ export const createNewTask = async (
117141
.filter(Boolean);
118142
const skill_category_id = formData.get("skill_category_id")?.toString();
119143
const created_by = formData.get("created_by")?.toString();
120-
144+
145+
// ENHANCED: Extract deadline field (nullable)
146+
const rawDeadline = formData.get("deadline")?.toString();
147+
const deadline = rawDeadline && rawDeadline !== "" && rawDeadline !== "null"
148+
? new Date(rawDeadline).toISOString()
149+
: null;
150+
151+
// Validate required fields
121152
if (!title || !kanban_column_id || !skill_category_id) {
122-
return { success: false, error: "Required fields are missing (title, column, and skill category)" };
153+
return {
154+
success: false,
155+
error: "Required fields are missing (title, column, and skill category)"
156+
};
123157
}
124158

159+
// Insert task with deadline support
160+
// Note: created_at is auto-generated by database default (now())
125161
const { error } = await supabase.from("tasks").insert([
126162
{
127163
title,
@@ -136,6 +172,7 @@ export const createNewTask = async (
136172
sidekick_ids,
137173
skill_category_id,
138174
created_by,
175+
deadline, // ENHANCED: Include deadline field
139176
},
140177
]);
141178

@@ -173,20 +210,34 @@ export const createNewTask = async (
173210
}
174211
};
175212

213+
/**
214+
* Updates an existing task with all fields including deadline
215+
* ENHANCED: Added deadline field support
216+
* Line 180-252
217+
*/
176218
export const updateTask = async (
177219
formData: FormData,
178220
taskId: string,
179221
): Promise<{ success: boolean; error?: string }> => {
180222
const supabase = await createClientServerComponent();
181223

182224
try {
183-
// Handle BOTH UUID fields properly
225+
// Handle UUID fields properly (codev_id, skill_category_id)
184226
const rawCodevId = formData.get("codev_id")?.toString();
185227
const codev_id = rawCodevId === "" || rawCodevId === "null" ? null : rawCodevId;
186228

187229
const rawSkillCategoryId = formData.get("skill_category_id")?.toString();
188-
const skill_category_id = rawSkillCategoryId === "" || rawSkillCategoryId === "null" ? null : rawSkillCategoryId;
230+
const skill_category_id = rawSkillCategoryId === "" || rawSkillCategoryId === "null"
231+
? null
232+
: rawSkillCategoryId;
233+
234+
// ENHANCED: Handle deadline field properly
235+
const rawDeadline = formData.get("deadline")?.toString();
236+
const deadline = rawDeadline && rawDeadline !== "" && rawDeadline !== "null"
237+
? new Date(rawDeadline).toISOString()
238+
: null;
189239

240+
// Prepare update data
190241
const updateData = {
191242
title: formData.get("title")?.toString(),
192243
description: formData.get("description")?.toString(),
@@ -200,9 +251,11 @@ export const updateTask = async (
200251
: [],
201252
skill_category_id: skill_category_id,
202253
codev_id: codev_id,
254+
deadline: deadline, // ENHANCED: Include deadline in updates
203255
updated_at: new Date().toISOString(),
204256
};
205257

258+
// Validate task exists before updating
206259
const { data: existingTask, error: fetchError } = await supabase
207260
.from("tasks")
208261
.select("*")
@@ -214,6 +267,7 @@ export const updateTask = async (
214267
return { success: false, error: "Could not find task to update" };
215268
}
216269

270+
// Perform update
217271
const { error: updateError } = await supabase
218272
.from("tasks")
219273
.update({
@@ -237,6 +291,10 @@ export const updateTask = async (
237291
}
238292
};
239293

294+
/**
295+
* Deletes a task and revalidates the kanban board path
296+
* Line 254-295
297+
*/
240298
export const deleteTask = async (
241299
taskId: string,
242300
): Promise<{ success: boolean; error?: string }> => {
@@ -288,6 +346,14 @@ export const deleteTask = async (
288346
}
289347
};
290348

349+
// =============================================================================
350+
// COLUMN OPERATIONS
351+
// =============================================================================
352+
353+
/**
354+
* Creates a new kanban column with auto-incremented position
355+
* Line 297-333
356+
*/
291357
export const createNewColumn = async (
292358
columnName: string,
293359
boardId: string,
@@ -332,6 +398,10 @@ export const createNewColumn = async (
332398
}
333399
};
334400

401+
/**
402+
* Updates column position (for drag-and-drop column reordering)
403+
* Line 335-361
404+
*/
335405
export const updateColumnPosition = async (
336406
columnId: string,
337407
newPosition: number,
@@ -361,6 +431,10 @@ export const updateColumnPosition = async (
361431
}
362432
};
363433

434+
/**
435+
* Deletes a kanban column
436+
* Line 363-382
437+
*/
364438
export const deleteColumn = async (
365439
columnId: string,
366440
): Promise<{ success: boolean; error?: string }> => {
@@ -384,6 +458,10 @@ export const deleteColumn = async (
384458
}
385459
};
386460

461+
/**
462+
* Updates column name
463+
* Line 384-408
464+
*/
387465
export const updateColumnName = async (
388466
columnId: string,
389467
newName: string,
@@ -412,14 +490,24 @@ export const updateColumnName = async (
412490
}
413491
};
414492

493+
// =============================================================================
494+
// POINTS & LEVELING SYSTEM
495+
// =============================================================================
496+
497+
/**
498+
* Internal function to update developer levels based on points
499+
* Calculates level for each skill category based on accumulated points
500+
* Line 410-458
501+
*/
415502
const updateDeveloperLevels = async (codevId?: string) => {
416503
if (!codevId) return;
417504

418505
const supabase = await createClientServerComponent();
419506

420507
const { data: pointsData, error: pointsError } = await supabase
421508
.from("codev_points")
422-
.select("skill_category_id, points");
509+
.select("skill_category_id, points")
510+
.eq("codev_id", codevId);
423511

424512
if (pointsError) {
425513
console.error("Error fetching points:", pointsError);
@@ -458,7 +546,15 @@ const updateDeveloperLevels = async (codevId?: string) => {
458546
}
459547
};
460548

461-
// RESOLVED: Comprehensive completeTask implementation with RPC fallback
549+
/**
550+
* Completes a task with comprehensive point distribution
551+
* - Archives task instead of deleting for data preservation
552+
* - Awards 100% points to primary assignee
553+
* - Awards 50% points to sidekicks
554+
* - Updates developer levels for all participants
555+
* - Falls back to manual implementation if RPC function unavailable
556+
* Line 460-631
557+
*/
462558
export const completeTask = async (
463559
task: Task,
464560
): Promise<{ success: boolean; error?: string }> => {
@@ -637,6 +733,15 @@ export const completeTask = async (
637733
}
638734
};
639735

736+
// =============================================================================
737+
// BATCH OPERATIONS
738+
// =============================================================================
739+
740+
/**
741+
* Batch updates multiple tasks' column assignments
742+
* Used for optimizing drag-and-drop performance
743+
* Line 633-657
744+
*/
640745
export async function batchUpdateTasks(
641746
updates: Array<{ taskId: string; newColumnId: string }>,
642747
) {
@@ -666,6 +771,14 @@ export async function batchUpdateTasks(
666771
}
667772
}
668773

774+
// =============================================================================
775+
// TASK SPECIFIC OPERATIONS
776+
// =============================================================================
777+
778+
/**
779+
* Updates a task's PR link
780+
* Line 659-700
781+
*/
669782
export async function updateTaskPRLink(taskId: string, prLink: string) {
670783
const supabase = await createClientServerComponent();
671784
try {
@@ -716,6 +829,10 @@ export async function updateTaskPRLink(taskId: string, prLink: string) {
716829
}
717830
}
718831

832+
/**
833+
* Unarchives a previously archived task
834+
* Line 702-743
835+
*/
719836
export async function unarchiveTask(taskId: string) {
720837
const supabase = await createClientServerComponent();
721838
try {

‎apps/codebility/types/home/codev.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ export interface Task {
302302
difficulty?: string;
303303
type?: string;
304304
due_date?: string;
305+
deadline?: string;
305306
kanban_column_id?: string; // UUID
306307
codev_id?: string; // UUID (primary assignee)
307308
created_by?: string; // UUID

0 commit comments

Comments
 (0)