From cec8a1f8181a235b1214f702bdebce517142d07f Mon Sep 17 00:00:00 2001 From: Gabriel Menezes Date: Thu, 22 Oct 2026 09:47:23 -0400 Subject: [PATCH] feat(tasks): add scope toggle bar for individual vs team tasks --- frontend/src/pages/TasksPage.tsx | 137 ++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 29 deletions(-) diff --git a/frontend/src/pages/TasksPage.tsx b/frontend/src/pages/TasksPage.tsx index 44518ff..3f96cb0 100644 --- a/frontend/src/pages/TasksPage.tsx +++ b/frontend/src/pages/TasksPage.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react' +import { useSearchParams } from 'react-router-dom' import { LayoutGrid, List as ListIcon, @@ -7,8 +8,8 @@ import { Filter, RefreshCw, Folder, - UserCheck, Users, + User, CheckCircle2, Clock, Sparkles, @@ -58,7 +59,29 @@ export const TasksPage: React.FC = () => { const [searchQuery, setSearchQuery] = useState('') const [selectedStatus, setSelectedStatus] = useState('ALL') const [selectedProjectId, setSelectedProjectId] = useState('ALL') - const [onlyMyTasks, setOnlyMyTasks] = useState(false) + + // Scope Toggle State: 'mine' (Individual) vs 'team' (Equipe) + const [searchParams, setSearchParams] = useSearchParams() + const initialScope = + (searchParams.get('scope') as 'mine' | 'team') || + (localStorage.getItem('beetask_tasks_scope') as 'mine' | 'team') || + 'mine' + const [viewScope, setViewScope] = useState<'mine' | 'team'>(initialScope) + + const handleScopeChange = (newScope: 'mine' | 'team') => { + setViewScope(newScope) + try { + localStorage.setItem('beetask_tasks_scope', newScope) + } catch (_) {} + setSearchParams( + (prev) => { + const next = new URLSearchParams(prev) + next.set('scope', newScope) + return next + }, + { replace: true } + ) + } // Modal state const [isModalOpen, setIsModalOpen] = useState(false) @@ -132,9 +155,7 @@ export const TasksPage: React.FC = () => { try { setRefreshing(true) const [tasksRes, projsRes] = await Promise.all([ - onlyMyTasks - ? taskService.listMyTasks({ organizationId: activeOrgId }) - : taskService.listTasks({ organizationId: activeOrgId }), + taskService.listTasks({ organizationId: activeOrgId }), projectService.listProjects(activeOrgId).catch(() => []), ]) @@ -148,15 +169,29 @@ export const TasksPage: React.FC = () => { setLoading(false) setRefreshing(false) } - }, [activeOrgId, onlyMyTasks]) + }, [activeOrgId]) useEffect(() => { loadData() }, [loadData]) + // User's own tasks + const myTasks = useMemo(() => { + return tasks.filter((t) => { + if (!user?.email) return false + return Boolean( + t.user?.email && + t.user.email.toLowerCase() === user.email.toLowerCase() + ) + }) + }, [tasks, user]) + + // Scope tasks based on viewScope + const scopeTasks = viewScope === 'mine' ? myTasks : tasks + // Filter tasks client-side for ultra-responsive search const filteredTasks = useMemo(() => { - return tasks.filter((task) => { + return scopeTasks.filter((task) => { // Status filter if (selectedStatus !== 'ALL' && task.status !== selectedStatus) { return false @@ -178,7 +213,7 @@ export const TasksPage: React.FC = () => { } return true }) - }, [tasks, selectedStatus, selectedProjectId, searchQuery]) + }, [scopeTasks, selectedStatus, selectedProjectId, searchQuery]) // Timer play/pause handler const handleToggleTimer = async (task: Task) => { @@ -305,11 +340,11 @@ export const TasksPage: React.FC = () => { [activeTask, stopTimer, editingTask] ) - // Summary counts - const notStartedCount = tasks.filter((t) => t.status === 'NOT_STARTED').length - const inProgressCount = tasks.filter((t) => t.status === 'IN_PROGRESS').length - const completedCount = tasks.filter((t) => t.status === 'COMPLETED').length - const canceledCount = tasks.filter((t) => t.status === 'CANCELED').length + // Summary counts based on active scope + const notStartedCount = scopeTasks.filter((t) => t.status === 'NOT_STARTED').length + const inProgressCount = scopeTasks.filter((t) => t.status === 'IN_PROGRESS').length + const completedCount = scopeTasks.filter((t) => t.status === 'COMPLETED').length + const canceledCount = scopeTasks.filter((t) => t.status === 'CANCELED').length return (
@@ -511,6 +546,64 @@ export const TasksPage: React.FC = () => {
)} + {/* Scope Selector: Individual (Minhas Tarefas) vs Equipe (Workspace) */} +
+
+ + Modo de Visualização: + +
+ + +
+
+ +
+ + + {viewScope === 'mine' + ? 'Exibindo tarefas atribuídas diretamente a você' + : 'Exibindo tarefas de todos os integrantes do workspace'} + +
+
+ {/* Filter and Search Bar with embedded status stats */}
{/* Left side: Search Input & Quick Status Counts */} @@ -542,7 +635,7 @@ export const TasksPage: React.FC = () => { {/* A Fazer (NOT_STARTED) */} @@ -617,20 +710,6 @@ export const TasksPage: React.FC = () => { {/* Filters Group */}
- {/* Mine vs All toggle */} - - {/* Project Filter */} {projects.length > 0 && (