diff --git a/frontend/src/components/TaskFilterBar.tsx b/frontend/src/components/TaskFilterBar.tsx
index 7684be2..e02ab44 100644
--- a/frontend/src/components/TaskFilterBar.tsx
+++ b/frontend/src/components/TaskFilterBar.tsx
@@ -22,6 +22,7 @@ interface TaskFilterBarProps {
projects: Project[]
categories: Category[]
assignees: TaskAssignee[]
+ currentUserId?: number
isLoading?: boolean
onRefresh?: () => void
}
@@ -32,6 +33,7 @@ export function TaskFilterBar({
projects,
categories,
assignees,
+ currentUserId,
isLoading,
onRefresh,
}: TaskFilterBarProps) {
@@ -98,7 +100,22 @@ export function TaskFilterBar({
{assignees.length > 0 && (
-
+
+ {currentUserId !== undefined && (
+
+ )}
Promise
+ handleFiltersChange: (newFilters: TaskFilters) => void
+}
+
+function mergeProjects(existing: Project[], tasks: TaskResponse[]) {
+ const byId = new Map(existing.map((project) => [project.id, project]))
+
+ tasks.forEach((task) => {
+ if (task.project) {
+ byId.set(task.project.id, {
+ id: task.project.id,
+ name: task.project.name,
+ color: task.project.color,
+ icon: task.project.icon,
+ })
+ }
+ })
+
+ return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
+}
+
+function extractAssignees(tasks: TaskResponse[]) {
+ const byId = new Map()
+
+ tasks.forEach((task) => {
+ if (task.user) {
+ byId.set(task.user.id, task.user)
+ }
+ })
+
+ return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
+}
+
+export function useTaskPage({ fetchProjects = false }: UseTaskPageOptions = {}): UseTaskPageResult {
+ const [tasks, setTasks] = useState([])
+ const [isLoading, setIsLoading] = useState(true)
+ const [projects, setProjects] = useState([])
+ const [categories, setCategories] = useState([])
+ const [assignees, setAssignees] = useState([])
+ const [filters, setFilters] = useState(DEFAULT_TASK_FILTERS)
+ const [currentUserAssigneeId, setCurrentUserAssigneeId] = useState(null)
+ const { activeOrg, user } = useAuth()
+ const debounceRef = useRef | null>(null)
+
+ const loadTasks = async (currentFilters: TaskFilters, silent = false) => {
+ if (!activeOrg) return
+ if (!silent) setIsLoading(true)
+ try {
+ const response = await taskService.getTasks({
+ organizationId: activeOrg.id,
+ text: currentFilters.searchQuery || undefined,
+ projectIds: currentFilters.projectIds.length > 0 ? currentFilters.projectIds : undefined,
+ statuses: currentFilters.statuses.length > 0 ? currentFilters.statuses : undefined,
+ categoryIds: currentFilters.categoryIds.length > 0 ? currentFilters.categoryIds : undefined,
+ userIds: currentFilters.userIds.length > 0 ? currentFilters.userIds : undefined,
+ })
+ setTasks(response)
+ setProjects((current) => mergeProjects(current, response))
+ setAssignees((current) => {
+ const byId = new Map(current.map((assignee) => [assignee.id, assignee]))
+ extractAssignees(response).forEach((assignee) => byId.set(assignee.id, assignee))
+ return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
+ })
+ } catch (error) {
+ console.error('Erro ao carregar tarefas', error)
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ useEffect(() => {
+ if (!activeOrg) return
+
+ const init = async () => {
+ setFilters(DEFAULT_TASK_FILTERS)
+ setCategories([])
+
+ const promises: Promise[] = [organizationService.listMembers(activeOrg.id)]
+
+ promises.push(categoryService.listByOrganization(activeOrg.id).then(setCategories).catch(() => {}))
+
+ if (fetchProjects) {
+ promises.push(projectService.listByOrganization(activeOrg.id).then(setProjects).catch(() => {}))
+ }
+
+ const [members] = await Promise.all(promises)
+
+ const assigneesData = members.map((m: any) => ({
+ id: m.userId,
+ name: m.userName,
+ email: m.userEmail,
+ photo: m.userPhoto ?? null,
+ }))
+ setAssignees(assigneesData)
+
+ const currentMember = members.find((m: any) => m.userEmail === user?.email)
+ const userId = currentMember?.userId ?? null
+ setCurrentUserAssigneeId(userId)
+
+ const initialFilters = userId
+ ? { ...DEFAULT_TASK_FILTERS, userIds: [userId] }
+ : DEFAULT_TASK_FILTERS
+ setFilters(initialFilters)
+ void loadTasks(initialFilters)
+ }
+
+ void init()
+ }, [activeOrg, user?.email, fetchProjects])
+
+ const handleFiltersChange = (newFilters: TaskFilters) => {
+ setFilters(newFilters)
+ if (debounceRef.current) clearTimeout(debounceRef.current)
+ debounceRef.current = setTimeout(() => {
+ void loadTasks(newFilters)
+ }, 300)
+ }
+
+ return {
+ tasks,
+ isLoading,
+ projects,
+ categories,
+ assignees,
+ filters,
+ currentUserAssigneeId,
+ loadTasks,
+ handleFiltersChange,
+ }
+}
diff --git a/frontend/src/pages/TaskBoardPage.tsx b/frontend/src/pages/TaskBoardPage.tsx
index 4bf0d80..05ec2b7 100644
--- a/frontend/src/pages/TaskBoardPage.tsx
+++ b/frontend/src/pages/TaskBoardPage.tsx
@@ -10,20 +10,16 @@ import {
Plus,
XCircle,
} from 'lucide-react'
-import { useEffect, useRef, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { taskService } from '../services/taskService'
-import type { Project } from '../services/projectService'
-import { categoryService } from '../services/categoryService'
-import type { Category } from '../types/category'
+import type { TaskStatus } from '../types/task'
import { cn } from '../lib/utils'
-import type { TaskAssignee, TaskResponse, TaskStatus } from '../types/task'
import { TaskTimer } from '../components/TaskTimer'
import { TaskFilterBar } from '../components/TaskFilterBar'
-import { DEFAULT_TASK_FILTERS, type TaskFilters } from '../components/taskFilters'
import { CategoryBadge } from '../components/CategoryBadge'
import { useAuth } from '../contexts/AuthContext'
+import { useTaskPage } from '../hooks/useTaskPage'
const COLUMNS: { id: TaskStatus; label: string; icon: LucideIcon; color: string }[] = [
{ id: 'NOT_STARTED', label: 'Pendente', icon: AlertCircle, color: 'text-text-muted' },
@@ -32,84 +28,21 @@ const COLUMNS: { id: TaskStatus; label: string; icon: LucideIcon; color: string
{ id: 'CANCELED', label: 'Cancelada', icon: XCircle, color: 'text-danger' },
]
-function mergeProjects(existing: Project[], tasks: TaskResponse[]) {
- const byId = new Map(existing.map((project) => [project.id, project]))
-
- tasks.forEach((task) => {
- if (task.project) {
- byId.set(task.project.id, { id: task.project.id, name: task.project.name })
- }
- })
-
- return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
-}
-
-function extractAssignees(tasks: TaskResponse[]) {
- const byId = new Map()
-
- tasks.forEach((task) => {
- if (task.user) {
- byId.set(task.user.id, task.user)
- }
- })
-
- return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
-}
export function TaskBoardPage() {
- const [tasks, setTasks] = useState([])
- const [isLoading, setIsLoading] = useState(true)
- const [projects, setProjects] = useState([])
- const [categories, setCategories] = useState([])
- const [assignees, setAssignees] = useState([])
- const [filters, setFilters] = useState(DEFAULT_TASK_FILTERS)
const navigate = useNavigate()
- const { activeOrg, user } = useAuth()
- const debounceRef = useRef | null>(null)
-
- const loadTasks = async (currentFilters: TaskFilters, silent = false) => {
- if (!activeOrg) return
- if (!silent) setIsLoading(true)
- try {
- const response = await taskService.getTasks({
- organizationId: activeOrg.id,
- text: currentFilters.searchQuery || undefined,
- projectIds: currentFilters.projectIds.length > 0 ? currentFilters.projectIds : undefined,
- statuses: currentFilters.statuses.length > 0 ? currentFilters.statuses : undefined,
- categoryIds: currentFilters.categoryIds.length > 0 ? currentFilters.categoryIds : undefined,
- userIds: currentFilters.userIds.length > 0 ? currentFilters.userIds : undefined,
- })
- setTasks(response)
- setProjects((current) => mergeProjects(current, response))
- setAssignees((current) => {
- const byId = new Map(current.map((assignee) => [assignee.id, assignee]))
- extractAssignees(response).forEach((assignee) => byId.set(assignee.id, assignee))
- return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
- })
- } catch (error) {
- console.error('Erro ao carregar tarefas', error)
- } finally {
- setIsLoading(false)
- }
- }
-
- useEffect(() => {
- if (!activeOrg) return
- setFilters(DEFAULT_TASK_FILTERS)
- setProjects([])
- setAssignees([])
- setCategories([])
- void loadTasks(DEFAULT_TASK_FILTERS)
- categoryService.listByOrganization(activeOrg.id).then(setCategories).catch(() => {})
- }, [activeOrg])
-
- const handleFiltersChange = (newFilters: TaskFilters) => {
- setFilters(newFilters)
- if (debounceRef.current) clearTimeout(debounceRef.current)
- debounceRef.current = setTimeout(() => {
- void loadTasks(newFilters)
- }, 300)
- }
+ const { user } = useAuth()
+ const {
+ tasks,
+ isLoading,
+ projects,
+ categories,
+ assignees,
+ filters,
+ currentUserAssigneeId,
+ loadTasks,
+ handleFiltersChange,
+ } = useTaskPage()
const onDragEnd = async (result: DropResult) => {
const { destination, source, draggableId } = result
@@ -125,13 +58,9 @@ export function TaskBoardPage() {
const taskId = Number.parseInt(draggableId)
const newStatus = destination.droppableId as TaskStatus
- const updatedTasks = tasks.map((t) =>
- t.id === taskId ? { ...t, status: newStatus } : t,
- )
- setTasks(updatedTasks)
-
try {
await taskService.updateTaskStatus(taskId, newStatus)
+ await loadTasks(filters, true)
} catch (error) {
console.error('Erro ao atualizar status da tarefa', error)
void loadTasks(filters, true)
@@ -182,6 +111,7 @@ export function TaskBoardPage() {
projects={projects}
categories={categories}
assignees={assignees}
+ currentUserId={currentUserAssigneeId ?? undefined}
isLoading={isLoading}
onRefresh={() => void loadTasks(filters)}
/>
diff --git a/frontend/src/pages/TaskListPage.tsx b/frontend/src/pages/TaskListPage.tsx
index c50c812..d2cba88 100644
--- a/frontend/src/pages/TaskListPage.tsx
+++ b/frontend/src/pages/TaskListPage.tsx
@@ -11,23 +11,17 @@ import {
Tag,
XCircle,
} from 'lucide-react'
-import { useEffect, useRef, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
-import { taskService } from '../services/taskService'
-import type { Project } from '../services/projectService'
-import { projectService } from '../services/projectService'
-import { categoryService } from '../services/categoryService'
-import { organizationService } from '../services/organizationService'
-import type { Category } from '../types/category'
+import type { TaskResponse } from '../types/task'
import { cn } from '../lib/utils'
-import type { TaskAssignee, TaskResponse } from '../types/task'
import { TaskTimer } from '../components/TaskTimer'
import { TaskFilterBar } from '../components/TaskFilterBar'
-import { DEFAULT_TASK_FILTERS, type TaskFilters } from '../components/taskFilters'
+import { DEFAULT_TASK_FILTERS } from '../components/taskFilters'
import { CategoryBadge } from '../components/CategoryBadge'
import { UserAvatar } from '../components/UserAvatar'
import { useAuth } from '../contexts/AuthContext'
+import { useTaskPage } from '../hooks/useTaskPage'
function getStatusConfig(status: TaskResponse['status']) {
switch (status) {
@@ -58,99 +52,20 @@ function getStatusConfig(status: TaskResponse['status']) {
}
}
-function mergeProjects(existing: Project[], tasks: TaskResponse[]) {
- const byId = new Map(existing.map((project) => [project.id, project]))
-
- tasks.forEach((task) => {
- if (task.project) {
- byId.set(task.project.id, {
- id: task.project.id,
- name: task.project.name,
- color: task.project.color,
- icon: task.project.icon,
- })
- }
- })
-
- return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
-}
-
-function extractAssignees(tasks: TaskResponse[]) {
- const byId = new Map()
-
- tasks.forEach((task) => {
- if (task.user) {
- byId.set(task.user.id, task.user)
- }
- })
-
- return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
-}
-
export function TaskListPage() {
- const [tasks, setTasks] = useState([])
- const [isLoading, setIsLoading] = useState(true)
- const [projects, setProjects] = useState([])
- const [categories, setCategories] = useState([])
- const [assignees, setAssignees] = useState([])
- const [filters, setFilters] = useState(DEFAULT_TASK_FILTERS)
const navigate = useNavigate()
- const { activeOrg, user } = useAuth()
- const debounceRef = useRef | null>(null)
-
- const loadTasks = async (currentFilters: TaskFilters, silent = false) => {
- if (!activeOrg) return
- if (!silent) setIsLoading(true)
- try {
- const response = await taskService.getTasks({
- organizationId: activeOrg.id,
- text: currentFilters.searchQuery || undefined,
- projectIds: currentFilters.projectIds.length > 0 ? currentFilters.projectIds : undefined,
- statuses: currentFilters.statuses.length > 0 ? currentFilters.statuses : undefined,
- categoryIds: currentFilters.categoryIds.length > 0 ? currentFilters.categoryIds : undefined,
- userIds: currentFilters.userIds.length > 0 ? currentFilters.userIds : undefined,
- })
- setTasks(response)
- setProjects((current) => mergeProjects(current, response))
- setAssignees((current) => {
- const byId = new Map(current.map((assignee) => [assignee.id, assignee]))
- extractAssignees(response).forEach((assignee) => byId.set(assignee.id, assignee))
- return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
- })
- } catch (error) {
- console.error('Erro ao carregar tarefas', error)
- } finally {
- setIsLoading(false)
- }
- }
-
- useEffect(() => {
- if (!activeOrg) return
- setFilters(DEFAULT_TASK_FILTERS)
- setCategories([])
- void loadTasks(DEFAULT_TASK_FILTERS)
-
- categoryService.listByOrganization(activeOrg.id).then(setCategories).catch(() => {})
-
- projectService.listByOrganization(activeOrg.id).then(setProjects).catch(() => {})
-
- organizationService.listMembers(activeOrg.id).then((members) => {
- setAssignees(members.map((m) => ({
- id: m.userId,
- name: m.userName,
- email: m.userEmail,
- photo: m.userPhoto ?? null,
- })))
- }).catch(() => {})
- }, [activeOrg])
-
- const handleFiltersChange = (newFilters: TaskFilters) => {
- setFilters(newFilters)
- if (debounceRef.current) clearTimeout(debounceRef.current)
- debounceRef.current = setTimeout(() => {
- void loadTasks(newFilters)
- }, 300)
- }
+ const { user } = useAuth()
+ const {
+ tasks,
+ isLoading,
+ projects,
+ categories,
+ assignees,
+ filters,
+ currentUserAssigneeId,
+ loadTasks,
+ handleFiltersChange,
+ } = useTaskPage({ fetchProjects: true })
return (
@@ -193,6 +108,7 @@ export function TaskListPage() {
projects={projects}
categories={categories}
assignees={assignees}
+ currentUserId={currentUserAssigneeId ?? undefined}
isLoading={isLoading}
onRefresh={() => void loadTasks(filters)}
/>