Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions archon-ui-main/src/features/projects/tasks/components/TaskCard.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Tag } from "lucide-react";
import type React from "react";
import { useCallback, useState } from "react";
import { useCallback } from "react";
import { useDrag, useDrop } from "react-dnd";
import { useTaskActions } from "../hooks";
import type { Assignee, Task } from "../types";
import type { Assignee, Task, TaskPriority } from "../types";
import { getOrderColor, getOrderGlow, ItemTypes } from "../utils/task-styles";
import { TaskAssignee } from "./TaskAssignee";
import { TaskCardActions } from "./TaskCardActions";
import { type Priority, TaskPriority } from "./TaskPriority";
import { TaskPriorityComponent } from ".";

export interface TaskCardProps {
task: Task;
Expand All @@ -34,12 +34,8 @@ export const TaskCard: React.FC<TaskCardProps> = ({
selectedTasks,
onTaskSelect,
}) => {
// Local state for frontend-only priority
// NOTE: Priority is display-only and doesn't sync with backend yet
const [localPriority, setLocalPriority] = useState<Priority>("medium");

// Use business logic hook
const { changeAssignee, isUpdating } = useTaskActions(projectId);
// Use business logic hook with changePriority
const { changeAssignee, changePriority, isUpdating } = useTaskActions(projectId);

// Handlers - now just call hook methods
const handleEdit = useCallback(() => {
Expand All @@ -59,10 +55,12 @@ export const TaskCard: React.FC<TaskCardProps> = ({
}
}, [onDelete, task]);

const handlePriorityChange = useCallback((priority: Priority) => {
// Frontend-only priority change
setLocalPriority(priority);
}, []);
const handlePriorityChange = useCallback(
(priority: TaskPriority) => {
changePriority(task.id, priority);
},
[changePriority, task.id],
);

const handleAssigneeChange = useCallback(
(newAssignee: Assignee) => {
Expand Down Expand Up @@ -218,8 +216,12 @@ export const TaskCard: React.FC<TaskCardProps> = ({
<div className="flex items-center justify-between mt-auto pt-2 pl-1.5 pr-3">
<TaskAssignee assignee={task.assignee} onAssigneeChange={handleAssigneeChange} isLoading={isUpdating} />

{/* Priority display (frontend-only for now) */}
<TaskPriority priority={localPriority} onPriorityChange={handlePriorityChange} isLoading={false} />
{/* Priority display connected to database */}
<TaskPriorityComponent
priority={task.priority}
onPriorityChange={handlePriorityChange}
isLoading={isUpdating}
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ import {
TextArea,
} from "../../../ui/primitives";
import { useTaskEditor } from "../hooks";
import type { Assignee, Task } from "../types";
import type { Assignee, Task, TaskPriority } from "../types";
import { FeatureSelect } from "./FeatureSelect";
import type { Priority } from "./TaskPriority";

interface TaskEditModalProps {
isModalOpen: boolean;
Expand Down Expand Up @@ -52,7 +51,7 @@ export const TaskEditModal = memo(
status: "todo",
assignee: "User" as Assignee,
feature: "",
priority: "medium" as Priority, // Frontend-only priority
priority: "medium" as TaskPriority, // Direct priority field
});
}
}, [editingTask]);
Expand Down Expand Up @@ -133,9 +132,9 @@ export const TaskEditModal = memo(
<FormField>
<Label>Priority</Label>
<Select
value={(localTask as Task & { priority?: Priority })?.priority || "medium"}
value={localTask?.priority || "medium"}
onValueChange={(value) =>
setLocalTask((prev) => (prev ? { ...prev, priority: value as Priority } : null))
setLocalTask((prev) => (prev ? { ...prev, priority: value as TaskPriority } : null))
}
>
<SelectTrigger className="w-full">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,17 @@ import { AlertCircle } from "lucide-react";
import type React from "react";
import { Select, SelectContent, SelectItem, SelectTrigger } from "../../../ui/primitives/select";
import { cn, glassmorphism } from "../../../ui/primitives/styles";

export type Priority = "critical" | "high" | "medium" | "low";
import type { TaskPriority } from "../types";

interface TaskPriorityProps {
priority?: Priority;
onPriorityChange?: (priority: Priority) => void;
priority?: TaskPriority;
onPriorityChange?: (priority: TaskPriority) => void;
isLoading?: boolean;
}

// Priority options for the dropdown
const PRIORITY_OPTIONS: Array<{
value: Priority;
value: TaskPriority;
label: string;
color: string;
}> = [
Expand All @@ -32,13 +31,13 @@ const PRIORITY_OPTIONS: Array<{
{ value: "low", label: "Low", color: "text-gray-600" },
];

export const TaskPriority: React.FC<TaskPriorityProps> = ({
export const TaskPriorityComponent: React.FC<TaskPriorityProps> = ({
priority = "medium",
onPriorityChange,
isLoading = false,
}) => {
// Get priority-specific styling with Tron glow
const getPriorityStyles = (priorityValue: Priority) => {
const getPriorityStyles = (priorityValue: TaskPriority) => {
switch (priorityValue) {
case "critical":
return {
Expand Down Expand Up @@ -101,7 +100,7 @@ export const TaskPriority: React.FC<TaskPriorityProps> = ({
}

return (
<Select value={priority} onValueChange={(value) => onPriorityChange(value as Priority)}>
<Select value={priority} onValueChange={(value) => onPriorityChange(value as TaskPriority)}>
<SelectTrigger
disabled={isLoading}
className={cn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ export type { TaskCardProps } from "./TaskCard";
export { TaskCard } from "./TaskCard";
export { TaskCardActions } from "./TaskCardActions";
export { TaskEditModal } from "./TaskEditModal";
export { TaskPriority as TaskPriorityComponent } from "./TaskPriority";
export { TaskPriorityComponent } from "./TaskPriorityComponent";
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe("useTaskQueries", () => {
status: "todo",
assignee: "User",
task_order: 100,
priority: "medium",
created_at: "2024-01-01T00:00:00Z",
updated_at: "2024-01-01T00:00:00Z",
},
Expand Down Expand Up @@ -120,6 +121,7 @@ describe("useTaskQueries", () => {
status: "todo",
assignee: "User",
task_order: 100,
priority: "medium",
created_at: "2024-01-01T00:00:00Z",
updated_at: "2024-01-01T00:00:00Z",
};
Expand Down Expand Up @@ -159,6 +161,7 @@ describe("useTaskQueries", () => {
status: "todo",
assignee: "User",
task_order: 100,
priority: "medium",
created_at: "2024-01-01T00:00:00Z",
updated_at: "2024-01-01T00:00:00Z",
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useState } from "react";
import type { Assignee, Task, UseTaskActionsReturn } from "../types";
import type { Assignee, Task, TaskPriority, UseTaskActionsReturn } from "../types";
import { useDeleteTask, useUpdateTask } from "./useTaskQueries";

export const useTaskActions = (projectId: string): UseTaskActionsReturn => {
Expand All @@ -21,6 +21,17 @@ export const useTaskActions = (projectId: string): UseTaskActionsReturn => {
[updateTaskMutation],
);

// Priority change handler
const changePriority = useCallback(
(taskId: string, newPriority: TaskPriority) => {
updateTaskMutation.mutate({
taskId,
updates: { priority: newPriority },
});
},
[updateTaskMutation],
);

// Delete task handler with confirmation flow - now accepts full task object
const initiateDelete = useCallback((task: Task) => {
setTaskToDelete(task);
Expand Down Expand Up @@ -54,6 +65,7 @@ export const useTaskActions = (projectId: string): UseTaskActionsReturn => {
return {
// Actions
changeAssignee,
changePriority,
initiateDelete,
confirmDelete,
cancelDelete,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const useTaskEditor = (projectId: string): UseTaskEditorReturn => {
if (localTask.status !== editingTask.status) updates.status = localTask.status;
if (localTask.assignee !== editingTask.assignee) updates.assignee = localTask.assignee || "User";
if (localTask.task_order !== editingTask.task_order) updates.task_order = localTask.task_order;
if (localTask.priority !== editingTask.priority) updates.priority = localTask.priority;
if (localTask.feature !== editingTask.feature) updates.feature = localTask.feature || "";
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return updates;
Expand All @@ -51,6 +52,7 @@ export const useTaskEditor = (projectId: string): UseTaskEditorReturn => {
description: localTask.description || "",
status: (localTask.status as Task["status"]) || "todo",
assignee: (localTask.assignee as Assignee) || "User",
priority: localTask.priority || "medium",
feature: localTask.feature || "",
task_order: localTask.task_order || getDefaultTaskOrder((localTask.status as Task["status"]) || "todo"),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ export const taskService = {
});

// Invalidate related caches
// Note: We don't know the project_id here, so TanStack Query will handle invalidation
// Invalidate the specific project's tasks using the returned task data
if (task.project_id) {
invalidateETagCache(`/api/projects/${task.project_id}/tasks`);
}
invalidateETagCache("/api/tasks/counts");

return task;
Expand Down
3 changes: 2 additions & 1 deletion archon-ui-main/src/features/projects/tasks/types/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
* Type definitions for task-related hooks
*/

import type { Task } from "./task";
import type { Task, TaskPriority } from "./task";

/**
* Return type for useTaskActions hook
*/
export interface UseTaskActionsReturn {
// Actions
changeAssignee: (taskId: string, newAssignee: string) => void;
changePriority: (taskId: string, newPriority: TaskPriority) => void;
initiateDelete: (task: Task) => void;
confirmDelete: () => void;
cancelDelete: () => void;
Expand Down
31 changes: 14 additions & 17 deletions archon-ui-main/src/features/projects/tasks/types/priority.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
/**
* Priority System Types
*
* Defines user-facing priority levels separate from task_order (which handles drag-and-drop positioning).
* Priority is for display and user understanding, not for ordering logic.
* Defines priority levels independent from task_order (which handles drag-and-drop positioning).
* Priority represents semantic importance and is stored directly in the database.
*/

export type TaskPriority = "critical" | "high" | "medium" | "low";

export interface TaskPriorityOption {
value: number; // Maps to task_order values for backwards compatibility
value: TaskPriority; // Direct priority values from database enum
label: string;
color: string;
}

export const TASK_PRIORITY_OPTIONS: readonly TaskPriorityOption[] = [
{ value: 1, label: "Critical", color: "text-red-600" },
{ value: 25, label: "High", color: "text-orange-600" },
{ value: 50, label: "Medium", color: "text-blue-600" },
{ value: 100, label: "Low", color: "text-gray-600" },
{ value: "critical", label: "Critical", color: "text-red-600" },
{ value: "high", label: "High", color: "text-orange-600" },
{ value: "medium", label: "Medium", color: "text-blue-600" },
{ value: "low", label: "Low", color: "text-gray-600" },
] as const;

/**
* Convert task_order value to TaskPriority enum
* Get task priority display properties from priority value
*/
export function getTaskPriorityFromTaskOrder(taskOrder: number): TaskPriority {
if (taskOrder <= 1) return "critical";
if (taskOrder <= 25) return "high";
if (taskOrder <= 50) return "medium";
return "low";
export function getTaskPriorityOption(priority: TaskPriority): TaskPriorityOption {
const priorityOption = TASK_PRIORITY_OPTIONS.find((p) => p.value === priority);
return priorityOption || TASK_PRIORITY_OPTIONS[2]; // Default to 'Medium'
}

/**
* Get task priority display properties from task_order
* Validate priority value against allowed enum values
*/
export function getTaskPriorityOption(taskOrder: number): TaskPriorityOption {
const priority = TASK_PRIORITY_OPTIONS.find((p) => p.value >= taskOrder);
return priority || TASK_PRIORITY_OPTIONS[TASK_PRIORITY_OPTIONS.length - 1]; // Default to 'Low'
export function isValidTaskPriority(priority: string): priority is TaskPriority {
return ["critical", "high", "medium", "low"].includes(priority);
}
4 changes: 3 additions & 1 deletion archon-ui-main/src/features/projects/tasks/types/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ export interface Task {
archived_at?: string;
archived_by?: string;

// Priority field (required database field)
priority: TaskPriority;

// Extended UI properties
featureColor?: string;
priority?: TaskPriority;
}

// Request types
Expand Down
Loading