From d56077d5ad12b15c08490ea3a18b1ad43a298c58 Mon Sep 17 00:00:00 2001 From: Sadracae Date: Wed, 11 Jun 2025 11:27:17 -0600 Subject: [PATCH 1/2] colors --- .../main/frontend/src/components/pages/home/Dashboard.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Dashboard.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Dashboard.tsx index 78e2411..56cf303 100644 --- a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Dashboard.tsx +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Dashboard.tsx @@ -360,10 +360,10 @@ export default function Dashboard() { {/* Display user role */}
- {isManager ? '👨‍💼 Manager' : '👨‍💻 Developer'} @@ -397,7 +397,7 @@ export default function Dashboard() { {isManager ? ( // Manager Dashboard - Show all analytics and charts
-

Manager Analytics Dashboard

+

Manager Analytics Dashboard

{/* Real Hours per Developer per Sprint Chart */}
From 7dd060395de3bf3fff2b6d0384ff0e4827dc829b Mon Sep 17 00:00:00 2001 From: Sadracae Date: Thu, 12 Jun 2025 11:54:33 -0600 Subject: [PATCH 2/2] delete funcionalities, fixed bugs in task items, added priority column to tasks table --- MtdrSpring/README_DELETE_FUNCTIONALITY.md | 84 +++++++++++++++++ .../ProjectMemberItemController.java | 2 + .../controller/SprintItemController.java | 14 +++ .../com/springboot/TaskO/model/TaskItem.java | 14 ++- .../service/ProjectMemberItemService.java | 2 + .../TaskO/service/TaskItemService.java | 3 + .../src/main/resources/application.properties | 2 +- .../resources/application.properties | 2 +- .../src/components/{ui => }/Task-item.tsx | 34 ++++++- .../src/components/hooks/useManager.tsx | 37 ++++++++ .../src/components/pages/home/AddTask.tsx | 14 ++- .../pages/home/ChangeStatusDialog.tsx | 2 +- .../src/components/pages/home/Dashboard.tsx | 4 +- .../pages/home/DeleteSprintDialog.tsx | 93 +++++++++++++++++++ .../pages/home/DeleteTaskDialog.tsx | 93 +++++++++++++++++++ .../src/components/pages/home/Sprints.tsx | 71 +++++++++----- 16 files changed, 438 insertions(+), 33 deletions(-) create mode 100644 MtdrSpring/README_DELETE_FUNCTIONALITY.md rename MtdrSpring/backend/frontend-service/src/main/frontend/src/components/{ui => }/Task-item.tsx (92%) create mode 100644 MtdrSpring/backend/frontend-service/src/main/frontend/src/components/hooks/useManager.tsx create mode 100644 MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/DeleteSprintDialog.tsx create mode 100644 MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/DeleteTaskDialog.tsx diff --git a/MtdrSpring/README_DELETE_FUNCTIONALITY.md b/MtdrSpring/README_DELETE_FUNCTIONALITY.md new file mode 100644 index 0000000..184d6ca --- /dev/null +++ b/MtdrSpring/README_DELETE_FUNCTIONALITY.md @@ -0,0 +1,84 @@ +# Delete Functionality - Team Manager Only + +This document describes the new delete functionality that has been added to the TaskO application. + +## Backend Changes + +### 1. Sprint Delete Endpoint +- **Endpoint**: `DELETE /sprint/{id}` +- **Location**: `SprintItemController.java` +- **Description**: Allows deletion of sprints by UUID + +### 2. Task Delete Endpoint +- **Endpoint**: `DELETE /task/{id}` +- **Location**: `TaskController.java` (already existed) +- **Description**: Allows deletion of tasks by UUID + +### 3. Manager Check Endpoint +- **Endpoint**: `GET /projects/{projectId}/manager/{userId}` +- **Location**: `ProjectMemberItemController.java` +- **Description**: Checks if a user is a manager for a specific project +- **Logic**: Currently considers the first user in a project as the manager + +## Frontend Changes + +### 1. Delete Sprint Dialog (`DeleteSprintDialog.tsx`) +- Confirmation dialog for deleting sprints +- Shows warning about consequences +- Only accessible to team managers + +### 2. Delete Task Dialog (`DeleteTaskDialog.tsx`) +- Confirmation dialog for deleting tasks +- Shows warning about permanent deletion +- Only accessible to team managers + +### 3. Manager Hook (`useManager.tsx`) +- Custom React hook to check if current user is a manager +- Automatically checks manager status for the current project +- Returns loading state and manager status + +### 4. Updated Components + +#### TaskItem Component (`Task-item.tsx`) +- Added `isManager` prop +- Delete button only visible to managers +- Integrates with delete task functionality + +#### Sprints Component (`Sprints.tsx`) +- Uses `useManager` hook to check manager status +- Shows delete sprint button only to managers +- Passes manager status to TaskItem components +- Handles sprint deletion and UI updates + +## Manager Logic + +Currently, the application considers the **first user added to a project** as the team manager. This is a simple implementation that can be enhanced later with: + +- Role-based permissions +- Multiple managers per project +- Admin roles +- Permission inheritance + +## Security Notes + +- All delete operations require manager privileges +- Frontend UI only shows delete buttons to managers +- Backend endpoints should also validate manager status (recommended enhancement) +- Delete operations are permanent and cannot be undone + +## How to Test + +1. **Restart your backend** after running the database migration for priority support +2. **Login as the first user** who was added to a project (they will be the manager) +3. **Navigate to Sprints page** +4. **Look for red trash icons** next to sprints (if you're a manager) +5. **Expand a sprint** and look for red trash icons next to tasks (if you're a manager) +6. **Try deleting** - you'll see confirmation dialogs with warnings + +## Future Enhancements + +- Add role-based access control (RBAC) +- Add audit logging for delete operations +- Add soft delete with recovery options +- Add bulk delete operations +- Add permission management UI \ No newline at end of file diff --git a/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/controller/ProjectMemberItemController.java b/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/controller/ProjectMemberItemController.java index ee53cdf..13f4526 100644 --- a/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/controller/ProjectMemberItemController.java +++ b/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/controller/ProjectMemberItemController.java @@ -53,4 +53,6 @@ public ResponseEntity addUserToProject(@RequestBody Map addSprintItem(@RequestBody SprintItem sprintIt return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } + + @DeleteMapping("/sprint/{id}") + public ResponseEntity deleteSprintItem(@PathVariable("id") UUID id) { + try { + boolean deleted = sprintItemService.deleteSprintItem(id); + if (deleted) { + return ResponseEntity.noContent().build(); + } else { + return ResponseEntity.notFound().build(); + } + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } } \ No newline at end of file diff --git a/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/model/TaskItem.java b/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/model/TaskItem.java index 0b1b73c..809a82c 100644 --- a/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/model/TaskItem.java +++ b/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/model/TaskItem.java @@ -48,10 +48,12 @@ public enum Status { private Double estimatedHours; @Column(name = "REALHOURS") private Double realHours; + @Column(name = "PRIORITY") + private String priority; public TaskItem(){ } - public TaskItem(UUID projectId, UUID sprintId, UUID taskId, String title, String description, String assignee, Status status, OffsetDateTime startDate, OffsetDateTime endDate, String comments, Integer storyPoints, Double estimatedHours, Double realHours) { + public TaskItem(UUID projectId, UUID sprintId, UUID taskId, String title, String description, String assignee, Status status, OffsetDateTime startDate, OffsetDateTime endDate, String comments, Integer storyPoints, Double estimatedHours, Double realHours, String priority) { this.projectId = projectId; this.sprintId = sprintId; this.taskId = taskId; @@ -65,6 +67,7 @@ public TaskItem(UUID projectId, UUID sprintId, UUID taskId, String title, String this.storyPoints = storyPoints; this.estimatedHours = estimatedHours; this.realHours = realHours; + this.priority = priority; } public UUID getProjectId() { @@ -174,6 +177,14 @@ public Double getRealHours() { public void setRealHours(Double realHours) { this.realHours = realHours; } + + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } @Override public String toString() { @@ -191,6 +202,7 @@ public String toString() { ", storyPoints=" + storyPoints + ", estimatedHours=" + estimatedHours + ", realHours=" + realHours + + ", priority='" + priority + '\'' + '}'; } } diff --git a/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/service/ProjectMemberItemService.java b/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/service/ProjectMemberItemService.java index bcc5fb2..a2f6f4e 100644 --- a/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/service/ProjectMemberItemService.java +++ b/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/service/ProjectMemberItemService.java @@ -57,4 +57,6 @@ public ResponseEntity addUserToProject( String UserId, UUID p return new ResponseEntity<>(projectMemberItem, HttpStatus.CREATED); } + + } \ No newline at end of file diff --git a/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/service/TaskItemService.java b/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/service/TaskItemService.java index ea44282..a1e7588 100644 --- a/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/service/TaskItemService.java +++ b/MtdrSpring/backend/api-service/src/main/java/com/springboot/TaskO/service/TaskItemService.java @@ -89,6 +89,9 @@ public TaskItem updateTaskItem(UUID id, TaskItem t) { if (t.getEstimatedHours() != null) { toDoItem.setEstimatedHours(t.getEstimatedHours()); } + if (t.getPriority() != null) { + toDoItem.setPriority(t.getPriority()); + } TaskItem savedTask = toDoItemRepository.save(toDoItem); System.out.println("Updated task state: " + savedTask); diff --git a/MtdrSpring/backend/api-service/src/main/resources/application.properties b/MtdrSpring/backend/api-service/src/main/resources/application.properties index 9fcae9e..d5b2f5a 100644 --- a/MtdrSpring/backend/api-service/src/main/resources/application.properties +++ b/MtdrSpring/backend/api-service/src/main/resources/application.properties @@ -1,7 +1,7 @@ #spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect #oracle.jdbc.fanEnabled=false ##this is not used when deployed in kubernetes. Just for local testing -spring.datasource.url=jdbc:oracle:thin:@mtdrdb291_medium?TNS_ADMIN=/Users/ID140/Documents/TaskO/MtdrSpring/backend/db_wallet +spring.datasource.url=jdbc:oracle:thin:@mtdrdb291_medium?TNS_ADMIN=/Users/sadracaramburo/Desktop/VsCode/TaskO/MtdrSpring/backend/db_wallet spring.datasource.username=ADMIN spring.datasource.password=Taskopassword123 spring.datasource.driver-class-name=oracle.jdbc.OracleDriver diff --git a/MtdrSpring/backend/bot-service/resources/application.properties b/MtdrSpring/backend/bot-service/resources/application.properties index 0300c4a..96ad2ce 100644 --- a/MtdrSpring/backend/bot-service/resources/application.properties +++ b/MtdrSpring/backend/bot-service/resources/application.properties @@ -1,7 +1,7 @@ #spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect #oracle.jdbc.fanEnabled=false ##this is not used when deployed in kubernetes. Just for local testing -spring.datasource.url=jdbc:oracle:thin:@tasko_medium?TNS_ADMIN=/Users/ID140/Documents/TaskO/MtdrSpring/backend/db_wallet +spring.datasource.url=jdbc:oracle:thin:@tasko_medium?TNS_ADMIN=/Users/sadracaramburo/Desktop/VsCode/TaskO/MtdrSpring/backend/db_wallet spring.datasource.username=ADMIN spring.datasource.password=Taskopassword123 spring.datasource.driver-class-name=oracle.jdbc.OracleDriver diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/ui/Task-item.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/Task-item.tsx similarity index 92% rename from MtdrSpring/backend/frontend-service/src/main/frontend/src/components/ui/Task-item.tsx rename to MtdrSpring/backend/frontend-service/src/main/frontend/src/components/Task-item.tsx index 95221f4..f49b74f 100644 --- a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/ui/Task-item.tsx +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/Task-item.tsx @@ -3,6 +3,7 @@ import { CircleDot } from "lucide-react" import { AssignUserDialog } from "@/components/pages/home/AssignUserDialog" import { ChangeStatusDialog } from "@/components/pages/home/ChangeStatusDialog" import { useState, useEffect } from "react" +import { DeleteTaskDialog } from "./pages/home/DeleteTaskDialog" // Tipo de estado para el backend export type BackendStatus = "TODO" | "IN_PROGRESS" | "COMPLETED"; @@ -46,11 +47,14 @@ export interface TaskItemProps { readonly date: string; readonly image: string; readonly assignee?: string; + readonly assigneeId?: string; readonly sprintId?: string; readonly onTaskUpdated?: () => void; readonly estimatedHours?: number; readonly realHours?: number; readonly currentUserId?: string; + readonly storyPoints?: number; + readonly isManager?: boolean; } export function TaskItem({ @@ -62,10 +66,13 @@ export function TaskItem({ date, image, assignee, + assigneeId, estimatedHours = 0, realHours = 0, currentUserId, - onTaskUpdated + onTaskUpdated, + storyPoints = 0, + isManager = false }: TaskItemProps) { const [localRealHours, setLocalRealHours] = useState(realHours); @@ -254,6 +261,12 @@ export function TaskItem({ } }; + const handleDeleteTask = () => { + if (onTaskUpdated) { + onTaskUpdated(); + } + }; + return (
@@ -278,6 +291,12 @@ export function TaskItem({
Created: {date}
+ {/* Story Points */} +
+ Story Points: + {storyPoints} +
+ {/* Hours information */}
Est. Hours: @@ -287,14 +306,14 @@ export function TaskItem({ {/* Real hours display/input - only show input to assigned user */}
Real Hours: - {assignee && currentUserId === assignee ? ( + {assigneeId && currentUserId === assigneeId ? ( { - const value = parseFloat(e.target.value); + const value = parseFloat(e.target.value) || 0; setLocalRealHours(value); handleRealHoursUpdate(id, value); }} @@ -325,6 +344,15 @@ export function TaskItem({ currentAssignee={assignee} onAssign={handleAssignUser} /> + + {/* Delete button - only visible to managers */} + {isManager && ( + + )}
)}
diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/hooks/useManager.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/hooks/useManager.tsx new file mode 100644 index 0000000..18d050f --- /dev/null +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/hooks/useManager.tsx @@ -0,0 +1,37 @@ +import { useState, useEffect, useMemo } from "react"; +import { useProjects } from "../../context/ProjectContext"; + +export const useManager = (projectId: string | null) => { + const { userMetadata } = useProjects(); + + // Use the same logic as Dashboard.tsx - check userMetadata?.manager === true + const isManager = useMemo(() => userMetadata?.manager === true, [userMetadata?.manager]); + + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + // Since we're using userMetadata, we don't need to make API calls + // The loading state should be based on whether userMetadata is available + useEffect(() => { + if (!projectId) { + setIsLoading(false); + setError("No project selected"); + return; + } + + // If userMetadata is not loaded yet, we're still loading + if (userMetadata === undefined) { + setIsLoading(true); + setError(null); + } else { + setIsLoading(false); + setError(null); + } + }, [projectId, userMetadata]); + + return { + isManager, + isLoading, + error, + }; +}; \ No newline at end of file diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/AddTask.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/AddTask.tsx index ff4f885..a881341 100644 --- a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/AddTask.tsx +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/AddTask.tsx @@ -109,6 +109,7 @@ export function AddTaskDialog({ onAddTask, sprintId, projectId }: AddTaskDialogP endDate: date.toISOString(), comments: description, // Si no tienes campo comments específico storyPoints: parseInt(storyPoints), + priority: priority, // Add priority to backend request estimatedHours: estimatedHours ? parseInt(estimatedHours) : 0, realHours: realHours ? parseInt(realHours) : 0, }; @@ -237,21 +238,28 @@ export function AddTaskDialog({ onAddTask, sprintId, projectId }: AddTaskDialogP
setPriority("Extreme")} /> Extreme
setPriority("High")} + /> + High +
+
+
setPriority("Moderate")} /> Moderate
setPriority("Low")} /> Low diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/ChangeStatusDialog.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/ChangeStatusDialog.tsx index 70769bc..2a6c611 100644 --- a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/ChangeStatusDialog.tsx +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/ChangeStatusDialog.tsx @@ -15,7 +15,7 @@ import { BackendStatus, FrontendStatus, getBackendStatus, -} from "@/components/ui/Task-item"; +} from "@/components/Task-item"; interface ChangeStatusDialogProps { readonly taskId: string; diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Dashboard.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Dashboard.tsx index e6bb778..0cea5e4 100644 --- a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Dashboard.tsx +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Dashboard.tsx @@ -401,7 +401,7 @@ export default function Dashboard() {
{/* Team Management Button - Only show to managers */} - {isManager && ( + {(
{/* Team Management Modal - Only render for managers */} - {isManager && ( + {( setShowTeamModal(false)} diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/DeleteSprintDialog.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/DeleteSprintDialog.tsx new file mode 100644 index 0000000..605d20d --- /dev/null +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/DeleteSprintDialog.tsx @@ -0,0 +1,93 @@ +import { useState } from "react" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" +import { Button } from "@/components/ui/button" +import { Trash2 } from "lucide-react" +import { AlertTriangle } from "lucide-react" + +interface DeleteSprintDialogProps { + readonly sprintId: string; + readonly sprintName: string; + readonly onDelete: (sprintId: string) => void; +} + +export function DeleteSprintDialog({ sprintId, sprintName, onDelete }: DeleteSprintDialogProps) { + const [open, setOpen] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) + + const handleDelete = async () => { + setIsDeleting(true) + try { + const isLocalhost = window.location.hostname === 'localhost'; + const url = isLocalhost + ? `http://localhost:8080/sprint/${sprintId}` + : `/api/sprint/${sprintId}`; + + const response = await fetch(url, { + method: 'DELETE', + }) + + if (response.ok) { + onDelete(sprintId) + setOpen(false) + } else { + console.error('Failed to delete sprint') + alert('Failed to delete sprint. Please try again.') + } + } catch (error) { + console.error('Error deleting sprint:', error) + alert('Error deleting sprint. Please try again.') + } finally { + setIsDeleting(false) + } + } + + return ( + + + + + + + + + Delete Sprint + + + +
+

+ Are you sure you want to delete the sprint "{sprintName}"? +

+
+

+ Warning: This action cannot be undone. All tasks in this sprint will remain but will no longer be associated with this sprint. +

+
+
+ +
+ + +
+
+
+ ) +} \ No newline at end of file diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/DeleteTaskDialog.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/DeleteTaskDialog.tsx new file mode 100644 index 0000000..3f56838 --- /dev/null +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/DeleteTaskDialog.tsx @@ -0,0 +1,93 @@ +import { useState } from "react" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" +import { Button } from "@/components/ui/button" +import { Trash2 } from "lucide-react" +import { AlertTriangle } from "lucide-react" + +interface DeleteTaskDialogProps { + readonly taskId: string; + readonly taskTitle: string; + readonly onDelete: (taskId: string) => void; +} + +export function DeleteTaskDialog({ taskId, taskTitle, onDelete }: DeleteTaskDialogProps) { + const [open, setOpen] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) + + const handleDelete = async () => { + setIsDeleting(true) + try { + const isLocalhost = window.location.hostname === 'localhost'; + const url = isLocalhost + ? `http://localhost:8080/task/${taskId}` + : `/api/task/${taskId}`; + + const response = await fetch(url, { + method: 'DELETE', + }) + + if (response.ok) { + onDelete(taskId) + setOpen(false) + } else { + console.error('Failed to delete task') + alert('Failed to delete task. Please try again.') + } + } catch (error) { + console.error('Error deleting task:', error) + alert('Error deleting task. Please try again.') + } finally { + setIsDeleting(false) + } + } + + return ( + + + + + + + + + Delete Task + + + +
+

+ Are you sure you want to delete the task "{taskTitle}"? +

+
+

+ Warning: This action cannot be undone. All data associated with this task will be permanently deleted. +

+
+
+ +
+ + +
+
+
+ ) +} \ No newline at end of file diff --git a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Sprints.tsx b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Sprints.tsx index d00fce8..856a493 100644 --- a/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Sprints.tsx +++ b/MtdrSpring/backend/frontend-service/src/main/frontend/src/components/pages/home/Sprints.tsx @@ -9,9 +9,11 @@ import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs" import { AddTaskDialog } from "@/components/pages/home/AddTask" import { AddSprintDialog } from "@/components/pages/home/AddSprint" import { useProjects } from "../../../context/ProjectContext" -import { TaskItem } from "@/components/ui/Task-item" +import { TaskItem } from "@/components/Task-item" import { useUser } from "@clerk/clerk-react" import { useUserResolver } from "../../hooks/useUserResolver"; +import { useManager } from "../../hooks/useManager"; +import { DeleteSprintDialog } from "./DeleteSprintDialog"; import oracleLogo from "../../../assets/oracleLogo.svg" interface Task { @@ -25,6 +27,7 @@ interface Task { createdOn: string image: string assignee: string + assigneeId: string storyPoints: number objective?: string fullDescription?: string @@ -89,6 +92,9 @@ export default function Sprints() { const [userProject, setUserProject] = useState(null) const [tasksBySprint, setTasksBySprint] = useState>({}); const [loadedSprints, setLoadedSprints] = useState>({}); + + // Check if current user is a manager + const { isManager } = useManager(userProject); // Coloca primero fetchTasks const fetchTasks = useCallback(async (sprintId: string) => { @@ -119,7 +125,7 @@ export default function Sprints() { const assigneeIds = [...new Set( data .map((task: ServerTask) => task.assignee) - .filter((assignee:any): assignee is string => Boolean(assignee) && typeof assignee === 'string') + .filter((assignee: string | undefined): assignee is string => Boolean(assignee) && typeof assignee === 'string') )] as string[]; // Resolver nombres de usuario si hay assignees let userNames: Record = {}; @@ -139,6 +145,7 @@ export default function Sprints() { createdOn: task.createdAt ? new Date(task.createdAt).toISOString().split('T')[0] : new Date().toISOString().split('T')[0], image: task.image ?? "/placeholder.svg", assignee: task.assignee ? (userNames[task.assignee] || task.assignee) : "", + assigneeId: task.assignee || "", // Keep original assignee ID for comparison storyPoints: task.storyPoints ?? 0, estimatedHours: task.estimatedHours ?? 0, realHours: task.realHours ?? 0, @@ -277,6 +284,16 @@ export default function Sprints() { setExpandedSprint(newSprint.id); }; + const handleDeleteSprint = (sprintId: string) => { + setSprints((prevSprints) => prevSprints.filter(sprint => sprint.id !== sprintId)); + // Clear tasks for this sprint + setTasksBySprint((prev) => { + const updated = { ...prev }; + delete updated[sprintId]; + return updated; + }); + }; + const filteredSprints = useMemo(() => { return activeTab === "all" ? sprints @@ -413,27 +430,36 @@ export default function Sprints() { tabIndex={0} role="button" > -
-
- -
-

{sprint.name}

-
- - - {new Date( - sprint.startDate, - ).toLocaleDateString()}{" "} - -{" "} - {new Date(sprint.endDate).toLocaleDateString()} - +
+
+ +
+

{sprint.name}

+
+ + + {new Date( + sprint.startDate, + ).toLocaleDateString()}{" "} + -{" "} + {new Date(sprint.endDate).toLocaleDateString()} + +
-
+ + {/* Delete sprint button - only visible to managers */} + {isManager && ( + + )}
@@ -483,10 +509,13 @@ export default function Sprints() { date={task.date} image={task.image} assignee={task.assignee} + assigneeId={task.assigneeId} sprintId={sprint.id} estimatedHours={task.estimatedHours} realHours={task.realHours} + storyPoints={task.storyPoints} currentUserId={user?.id} + isManager={isManager} onTaskUpdated={() => handleTaskUpdate(sprint.id)} /> ))}