diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 095046499..4dc3d753d 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -1132,7 +1132,7 @@ "task-boards": { "name": "Name", "board": "Board", - "progress": "Progress", + "progress": "Progress", "tasks": "Tasks", "status": "Status", "last_updated": "Last Updated", diff --git a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/kanban.tsx b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/kanban.tsx index 0b4a0388a..98bf26868 100644 --- a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/kanban.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/kanban.tsx @@ -315,7 +315,8 @@ export function KanbanBoard({ boardId, tasks, isLoading }: Props) { const { transform } = args; // Get viewport bounds (safely handle SSR) - const viewportWidth = typeof window !== 'undefined' ? window.innerWidth : 1920; + const viewportWidth = + typeof window !== 'undefined' ? window.innerWidth : 1920; const maxX = viewportWidth - 350; // Account for card width return { diff --git a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/task.tsx b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/task.tsx index 6e86cb1b2..8d863d224 100644 --- a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/task.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/[boardId]/task.tsx @@ -268,9 +268,9 @@ export function TaskCard({ variant: 'destructive', }); } finally { - setIsLoading(false); - setMenuOpen(false); - } + setIsLoading(false); + setMenuOpen(false); + } } async function handleDelete() { diff --git a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/columns.tsx b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/columns.tsx index a56fc6d6d..51c8860df 100644 --- a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/columns.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/columns.tsx @@ -1,13 +1,13 @@ 'use client'; import { ProjectRowActions } from './row-action'; -import { ColumnDef } from '@tanstack/react-table'; import { EnhancedBoard } from './types'; -import { DataTableColumnHeader } from '@tuturuuu/ui/custom/tables/data-table-column-header'; +import { ColumnDef } from '@tanstack/react-table'; import { Badge } from '@tuturuuu/ui/badge'; -import { Progress } from '@tuturuuu/ui/progress'; import { Button } from '@tuturuuu/ui/button'; +import { DataTableColumnHeader } from '@tuturuuu/ui/custom/tables/data-table-column-header'; import { ExternalLink } from '@tuturuuu/ui/icons'; +import { Progress } from '@tuturuuu/ui/progress'; import moment from 'moment'; import Link from 'next/link'; @@ -29,16 +29,16 @@ export const projectColumns = ( return (
{/* Department/Group Color Indicator */} -
+
{board.name} {board.groupId && ( -

+

Group: {board.groupId}

)} @@ -59,9 +59,9 @@ export const projectColumns = ( cell: ({ row }) => { const completionRate = row.original.stats.completionRate; return ( -
- - +
+ + {completionRate}%
@@ -106,20 +106,28 @@ export const projectColumns = ( )} {stats.hasMultipleOverdue && ( - + Overdue )} {stats.hasWorkloadImbalance && ( - + Imbalanced )} - {!stats.hasUrgentTasks && !stats.hasMultipleOverdue && !stats.hasWorkloadImbalance && ( - - Normal - - )} + {!stats.hasUrgentTasks && + !stats.hasMultipleOverdue && + !stats.hasWorkloadImbalance && ( + + Normal + + )}
); }, @@ -156,7 +164,11 @@ export const projectColumns = ( return (
- diff --git a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/board-card.tsx b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/board-card.tsx index 0519ecba6..e69de29bb 100644 --- a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/board-card.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/board-card.tsx @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/board-list.tsx b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/board-list.tsx index d4b9d4b57..bbfa3e78a 100644 --- a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/board-list.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/board-list.tsx @@ -1,12 +1,14 @@ 'use client'; -import { EnhancedBoard, ViewSettings, BoardGroup, GROUP_COLORS } from '../types'; +import { + BoardGroup, + EnhancedBoard, + GROUP_COLORS, + ViewSettings, +} from '../types'; import { Badge } from '@tuturuuu/ui/badge'; import { Button } from '@tuturuuu/ui/button'; import { Card, CardContent, CardHeader } from '@tuturuuu/ui/card'; -import { Progress } from '@tuturuuu/ui/progress'; -// import { Separator } from '@tuturuuu/ui/separator'; -import { Input } from '@tuturuuu/ui/input'; import { DropdownMenu, DropdownMenuContent, @@ -14,25 +16,28 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@tuturuuu/ui/dropdown-menu'; -import { +import { + AlertCircle, + Archive, + BarChart3, Calendar, + ChevronDown, + ChevronRight, Clock, - BarChart3, - Users, + Edit, + ExternalLink, // AlertTriangle, Flag, - TrendingUp, - ExternalLink, MoreHorizontal, - Edit, - Archive, - Trash2, - ChevronDown, - ChevronRight, Plus, + Trash2, + TrendingUp, + Users, X, - AlertCircle } from '@tuturuuu/ui/icons'; +// import { Separator } from '@tuturuuu/ui/separator'; +import { Input } from '@tuturuuu/ui/input'; +import { Progress } from '@tuturuuu/ui/progress'; import { cn } from '@tuturuuu/utils/format'; import Link from 'next/link'; import { useState } from 'react'; @@ -44,7 +49,11 @@ interface BoardListProps { onSettingsChange: (settings: ViewSettings) => void; } -export function BoardList({ boards, settings, onSettingsChange }: BoardListProps) { +export function BoardList({ + boards, + settings, + onSettingsChange, +}: BoardListProps) { // Smart filtering logic const getFilteredBoards = () => { if (settings.forceShowAll) { @@ -61,7 +70,7 @@ export function BoardList({ boards, settings, onSettingsChange }: BoardListProps const sortedBoards = [...filteredBoards].sort((a, b) => { switch (settings.sortBy) { case 'name': - return settings.sortOrder === 'asc' + return settings.sortOrder === 'asc' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name); case 'id': @@ -91,8 +100,8 @@ export function BoardList({ boards, settings, onSettingsChange }: BoardListProps if (settings.viewMode === 'groups') { return ( - @@ -105,8 +114,8 @@ export function BoardList({ boards, settings, onSettingsChange }: BoardListProps function EnhancedCardsView({ boards }: { boards: EnhancedBoard[] }) { return ( -
-
+
+
{boards.map((board) => ( ))} @@ -117,22 +126,28 @@ function EnhancedCardsView({ boards }: { boards: EnhancedBoard[] }) { function EnhancedBoardCard({ board }: { board: EnhancedBoard }) { const [isHovered, setIsHovered] = useState(false); - - const progressColor = board.stats.completionRate >= 75 ? 'from-emerald-400 to-emerald-600' : - board.stats.completionRate >= 50 ? 'from-blue-400 to-blue-600' : - board.stats.completionRate >= 25 ? 'from-yellow-400 to-yellow-600' : 'from-gray-400 to-gray-600'; - const groupColor = board.groupId && GROUP_COLORS[board.groupId as keyof typeof GROUP_COLORS] - ? GROUP_COLORS[board.groupId as keyof typeof GROUP_COLORS] - : GROUP_COLORS.Default; + const progressColor = + board.stats.completionRate >= 75 + ? 'from-emerald-400 to-emerald-600' + : board.stats.completionRate >= 50 + ? 'from-blue-400 to-blue-600' + : board.stats.completionRate >= 25 + ? 'from-yellow-400 to-yellow-600' + : 'from-gray-400 to-gray-600'; + + const groupColor = + board.groupId && GROUP_COLORS[board.groupId as keyof typeof GROUP_COLORS] + ? GROUP_COLORS[board.groupId as keyof typeof GROUP_COLORS] + : GROUP_COLORS.Default; return ( - setIsHovered(true)} @@ -141,29 +156,38 @@ function EnhancedBoardCard({ board }: { board: EnhancedBoard }) { {/* Alert Indicators */}
{board.stats.hasUrgentTasks && ( -
+
)} {board.stats.hasMultipleOverdue && ( -
+
)} {board.stats.hasWorkloadImbalance && ( -
+
)}
- {board.name} {board.groupId && ( -

- + {board.groupId} @@ -177,17 +201,22 @@ function EnhancedBoardCard({ board }: { board: EnhancedBoard }) {

Progress - {board.stats.completionRate}% + + {board.stats.completionRate}% +
-
-
+
{board.stats.completionRate > 80 && ( -
+
)}
@@ -196,19 +225,19 @@ function EnhancedBoardCard({ board }: { board: EnhancedBoard }) { {/* Enhanced Stats Grid */}
-
+
- {board.stats.totalTasks} - tasks + {board.stats.totalTasks} + tasks
-
+
- {board.stats.totalLists} - lists + {board.stats.totalLists} + lists
@@ -216,22 +245,30 @@ function EnhancedBoardCard({ board }: { board: EnhancedBoard }) { {/* Status Indicators */}
{board.stats.activeTasks > 0 && ( -
+
- Active tasks + + Active tasks +
- {board.stats.activeTasks} + + {board.stats.activeTasks} +
)} {board.stats.overdueTasks > 0 && ( -
+
- Overdue + + Overdue +
- {board.stats.overdueTasks} + + {board.stats.overdueTasks} +
)}
@@ -239,19 +276,28 @@ function EnhancedBoardCard({ board }: { board: EnhancedBoard }) { {/* Enhanced Priority Badges */}
{board.stats.priorityDistribution.urgent > 0 && ( - + {board.stats.priorityDistribution.urgent} urgent )} {board.stats.priorityDistribution.high > 0 && ( - + {board.stats.priorityDistribution.high} high )} {board.stats.completionRate > 75 && ( - + High progress @@ -259,19 +305,21 @@ function EnhancedBoardCard({ board }: { board: EnhancedBoard }) {
{/* Last Activity */} -
+
- Updated {new Date(board.stats.lastActivity).toLocaleDateString()} + + Updated {new Date(board.stats.lastActivity).toLocaleDateString()} +
{/* Enhanced Action Button */} - @@ -279,31 +327,43 @@ function EnhancedBoardCard({ board }: { board: EnhancedBoard }) { ); } -function GroupsView({ - boards, - settings, - onSettingsChange -}: { - boards: EnhancedBoard[]; +function GroupsView({ + boards, + settings, + onSettingsChange, +}: { + boards: EnhancedBoard[]; settings: ViewSettings; onSettingsChange: (settings: ViewSettings) => void; }) { const [groups, setGroups] = useState([ - { id: 'gaming', name: 'Gaming', boards: [], color: GROUP_COLORS.Gaming, order: 1 }, - { id: 'robotics', name: 'Robotics', boards: [], color: GROUP_COLORS.Robotics, order: 2 }, + { + id: 'gaming', + name: 'Gaming', + boards: [], + color: GROUP_COLORS.Gaming, + order: 1, + }, + { + id: 'robotics', + name: 'Robotics', + boards: [], + color: GROUP_COLORS.Robotics, + order: 2, + }, ]); const [newGroupName, setNewGroupName] = useState(''); const [isCreatingGroup, setIsCreatingGroup] = useState(false); // Organize boards into groups - const organizedGroups = groups.map(group => ({ + const organizedGroups = groups.map((group) => ({ ...group, - boards: boards.filter(board => board.groupId === group.id) + boards: boards.filter((board) => board.groupId === group.id), })); // Unassigned boards - const unassignedBoards = boards.filter(board => - !groups.find(group => group.id === board.groupId) + const unassignedBoards = boards.filter( + (board) => !groups.find((group) => group.id === board.groupId) ); const handleCreateGroup = () => { @@ -313,7 +373,7 @@ function GroupsView({ name: newGroupName, boards: [], color: GROUP_COLORS.Default, - order: groups.length + 1 + order: groups.length + 1, }; setGroups([...groups, newGroup]); setNewGroupName(''); @@ -322,28 +382,28 @@ function GroupsView({ }; const handleDeleteGroup = (groupId: string) => { - setGroups(groups.filter(g => g.id !== groupId)); + setGroups(groups.filter((g) => g.id !== groupId)); }; const toggleGroup = (groupId: string) => { const collapsed = settings.groupView?.collapsed?.includes(groupId) - ? settings.groupView.collapsed.filter(id => id !== groupId) + ? settings.groupView.collapsed.filter((id) => id !== groupId) : [...(settings.groupView?.collapsed || []), groupId]; - + onSettingsChange({ ...settings, - groupView: { - ...settings.groupView, - collapsed - } + groupView: { + ...settings.groupView, + collapsed, + }, }); }; return (
-
+
{/* Groups Management Header */} -
+

Board Groups

{isCreatingGroup ? ( @@ -361,13 +421,17 @@ function GroupsView({ -
) : ( )} @@ -376,27 +440,29 @@ function GroupsView({ {/* Scrollable Groups Container */}
-
+
{/* Existing Groups */} {organizedGroups.map((group) => ( - toggleGroup(group.id)} onDelete={() => handleDeleteGroup(group.id)} /> ))} - + {/* Unassigned Boards Column */} {unassignedBoards.length > 0 && ( - {}} @@ -411,26 +477,26 @@ function GroupsView({ ); } -function GroupColumn({ - group, - isCollapsed, - onToggle, +function GroupColumn({ + group, + isCollapsed, + onToggle, onDelete, - isUnassigned = false -}: { - group: BoardGroup; - isCollapsed: boolean; - onToggle: () => void; + isUnassigned = false, +}: { + group: BoardGroup; + isCollapsed: boolean; + onToggle: () => void; onDelete: () => void; isUnassigned?: boolean; }) { const [dragOver, setDragOver] = useState(false); return ( -
{ e.preventDefault(); @@ -443,15 +509,15 @@ function GroupColumn({ // Handle board drop logic here }} > -
+
{/* Group Header */} -
-
+
-

{group.name}

@@ -468,11 +534,11 @@ function GroupColumn({ )}
- + {!isUnassigned && ( - - + Edit Board - + Archive Board - + Delete Board ); -} \ No newline at end of file +} diff --git a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/view-settings.tsx b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/view-settings.tsx index b3dd668f9..069fb5b3c 100644 --- a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/view-settings.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/components/view-settings.tsx @@ -1,20 +1,20 @@ 'use client'; -import { useState } from 'react'; +import { SmartFilters, ViewSettings } from '../types'; +import { Badge } from '@tuturuuu/ui/badge'; import { Button } from '@tuturuuu/ui/button'; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, - DropdownMenuTrigger, DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuTrigger, } from '@tuturuuu/ui/dropdown-menu'; -import { Badge } from '@tuturuuu/ui/badge'; import { Columns3, RefreshCw, SortAsc, SortDesc } from '@tuturuuu/ui/icons'; -import { ViewSettings, SmartFilters } from '../types'; +import { useState } from 'react'; interface ViewSettingsPanelProps { settings: ViewSettings; @@ -62,17 +62,19 @@ export function ViewSettingsPanel({
- + - + {/* Sorting Section */} @@ -84,18 +86,28 @@ export function ViewSettingsPanel({ onSettingsChange({ ...settings, sortBy: value as any }) } > - Sort by Name - Sort by ID - Sort by Created Date - Sort by Progress - Sort by Task Count + + Sort by Name + + + Sort by ID + + + Sort by Created Date + + + Sort by Progress + + + Sort by Task Count + - +
); -} \ No newline at end of file +} diff --git a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/enhanced-content.tsx b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/enhanced-content.tsx index 976e4e125..c211cd136 100644 --- a/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/enhanced-content.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/[wsId]/tasks/boards/enhanced-content.tsx @@ -1,25 +1,31 @@ 'use client'; -import { EnhancedBoard, ViewSettings, DEFAULT_VIEW_SETTINGS, STORAGE_KEYS, SmartFilters } from './types'; +import { projectColumns } from './columns'; import { BoardList } from './components/board-list'; import { ViewSettingsPanel } from './components/view-settings'; -import { useState, useEffect } from 'react'; -import dynamic from 'next/dynamic'; -import { Button } from '@tuturuuu/ui/button'; +import { + DEFAULT_VIEW_SETTINGS, + EnhancedBoard, + STORAGE_KEYS, + SmartFilters, + ViewSettings, +} from './types'; +import { CustomDataTable } from '@/components/custom-data-table'; import { Badge } from '@tuturuuu/ui/badge'; -import { - Grid3X3, - LayoutGrid, - List, - TrendingUp, - Clock, +import { Button } from '@tuturuuu/ui/button'; +import { AlertCircle, + Clock, + Columns, Flag, + Grid3X3, + LayoutGrid, + List, RefreshCw, - Columns + TrendingUp, } from '@tuturuuu/ui/icons'; -import { CustomDataTable } from '@/components/custom-data-table'; -import { projectColumns } from './columns'; +import dynamic from 'next/dynamic'; +import { useEffect, useState } from 'react'; interface EnhancedTaskBoardsContentProps { boards: EnhancedBoard[]; @@ -28,11 +34,11 @@ interface EnhancedTaskBoardsContentProps { isOwner?: boolean; } -function EnhancedTaskBoardsContentInner({ - boards, +function EnhancedTaskBoardsContentInner({ + boards, count, wsId: _wsId, - isOwner = false + isOwner = false, }: EnhancedTaskBoardsContentProps) { const [settings, setSettings] = useState(DEFAULT_VIEW_SETTINGS); @@ -52,14 +58,17 @@ function EnhancedTaskBoardsContentInner({ // Save settings to localStorage const handleSettingsChange = (newSettings: ViewSettings) => { setSettings(newSettings); - localStorage.setItem(STORAGE_KEYS.VIEW_SETTINGS, JSON.stringify(newSettings)); + localStorage.setItem( + STORAGE_KEYS.VIEW_SETTINGS, + JSON.stringify(newSettings) + ); }; // Create smart filters data const smartFilters: SmartFilters = { - hasUrgentTasks: boards.some(b => b.stats.hasUrgentTasks), - hasMultipleOverdue: boards.some(b => b.stats.hasMultipleOverdue), - hasWorkloadImbalance: boards.some(b => b.stats.hasWorkloadImbalance), + hasUrgentTasks: boards.some((b) => b.stats.hasUrgentTasks), + hasMultipleOverdue: boards.some((b) => b.stats.hasMultipleOverdue), + hasWorkloadImbalance: boards.some((b) => b.stats.hasWorkloadImbalance), }; // Refresh function @@ -76,50 +85,67 @@ function EnhancedTaskBoardsContentInner({ ]; // Summary statistics - const totalActiveBoards = boards.filter(b => b.stats.activeTasks > 0).length; - const totalUrgentTasks = boards.reduce((sum, b) => sum + b.stats.priorityDistribution.urgent, 0); - const totalOverdueBoards = boards.filter(b => b.stats.overdueTasks > 0).length; - const avgProgress = boards.length > 0 - ? Math.round(boards.reduce((sum, b) => sum + b.stats.completionRate, 0) / boards.length) - : 0; + const totalActiveBoards = boards.filter( + (b) => b.stats.activeTasks > 0 + ).length; + const totalUrgentTasks = boards.reduce( + (sum, b) => sum + b.stats.priorityDistribution.urgent, + 0 + ); + const totalOverdueBoards = boards.filter( + (b) => b.stats.overdueTasks > 0 + ).length; + const avgProgress = + boards.length > 0 + ? Math.round( + boards.reduce((sum, b) => sum + b.stats.completionRate, 0) / + boards.length + ) + : 0; return (
{/* Enhanced Header with Quick Stats */} -
-
+
+
{avgProgress}% - avg progress + avg progress
- +
{totalActiveBoards} - active boards + active boards
- + {totalUrgentTasks > 0 && (
- {totalUrgentTasks} - urgent tasks + + {totalUrgentTasks} + + urgent tasks
)} - + {totalOverdueBoards > 0 && (
- {totalOverdueBoards} - overdue boards + + {totalOverdueBoards} + + + overdue boards +
)} @@ -127,21 +153,22 @@ function EnhancedTaskBoardsContentInner({
{/* Enhanced Controls */} -
+
{count} board{count !== 1 ? 's' : ''} - {(smartFilters.hasUrgentTasks || - smartFilters.hasMultipleOverdue || - smartFilters.hasWorkloadImbalance) && !settings.forceShowAll && ( - - Smart filters active - - )} + {(smartFilters.hasUrgentTasks || + smartFilters.hasMultipleOverdue || + smartFilters.hasWorkloadImbalance) && + !settings.forceShowAll && ( + + Smart filters active + + )}
-
+
{/* View Mode Buttons */}
{viewModeButtons.map(({ mode, icon: Icon, label }) => ( @@ -149,7 +176,9 @@ function EnhancedTaskBoardsContentInner({ key={mode} variant={settings.viewMode === mode ? 'default' : 'ghost'} size="sm" - onClick={() => handleSettingsChange({ ...settings, viewMode: mode })} + onClick={() => + handleSettingsChange({ ...settings, viewMode: mode }) + } className="flex items-center gap-2 text-xs" > @@ -170,7 +199,7 @@ function EnhancedTaskBoardsContentInner({ Refresh - +