From 71638ce52a1ffc18bd15e3ef3f3ac9bf025f5f77 Mon Sep 17 00:00:00 2001 From: chamiakJ Date: Mon, 2 Jun 2025 23:04:03 +0530 Subject: [PATCH] refactor(task-list): update task list components and remove deprecated files - Replaced StatusGroupTables with TaskGroupList in multiple components to streamline task grouping functionality. - Updated imports to reflect new component structure and paths. - Removed obsolete task list components and styles to clean up the codebase. - Enhanced task list filters for improved user experience and consistency across the application. --- worklenz-backend/package-lock.json | 34 +- .../WithStartAndEndDates.tsx | 10 +- .../WithStartAndEndDates.tsx | 8 +- .../task-row-time-tracking.tsx | 2 +- .../src/lib/project/project-view-constants.ts | 14 +- .../projects/project-view-1/board/card.tsx | 35 -- .../projects/project-view-1/board/column.tsx | 45 -- .../board/project-view-board.tsx | 141 ------ .../roadmap/project-view-roadmap.css | 116 ----- .../roadmap/project-view-roadmap.tsx | 35 -- .../roadmap/roadmap-grant-chart.tsx | 91 ---- .../roadmap/roadmap-table/roadmap-table.tsx | 189 ------- .../roadmap-table/roadmap-task-cell.tsx | 112 ----- .../project-view-1/roadmap/time-filter.tsx | 74 --- .../project-view-1/task-list/table-v2.tsx | 142 ------ .../task-list-columns/task-list-columns.tsx | 239 --------- .../task-list/task-list-custom.css | 44 -- .../task-list/task-list-custom.tsx | 272 ---------- .../task-list-header/task-list-header.tsx | 116 ----- .../task-list-instant-task-input.tsx | 160 ------ .../task-list-table-old.tsx | 471 ------------------ .../task-list-table-old/task-list-table.css | 19 - .../task-list-table-wrapper.css | 15 - .../task-list-table-wrapper.tsx | 190 ------- .../project-view-1/task-list/task-list.tsx | 65 --- .../taskList/ProjectViewTaskList.tsx | 56 --- .../statusTables/StatusGroupTables.tsx | 67 --- .../taskListFilters/GroupByFilterDropdown.tsx | 69 --- .../taskListFilters/LabelsFilterDropdown.tsx | 134 ----- .../taskListFilters/MembersFilterDropdown.tsx | 140 ------ .../PriorityFilterDropdown.tsx | 73 --- .../taskListFilters/SearchDropdown.tsx | 54 -- .../ShowFieldsFilterDropdown.tsx | 96 ---- .../taskListFilters/SortFilterDropdown.tsx | 110 ---- .../taskListFilters/TaskListFilters.tsx | 73 --- .../taskList/taskListTable/TaskListTable.tsx | 425 ---------------- .../taskListTable/TaskListTableWrapper.tsx | 216 -------- .../taskListTable/columns/columnList.ts | 90 ---- .../contextMenu/TaskContextMenu.tsx | 90 ---- .../taskListTableCells/TaskCell.tsx | 121 ----- .../taskListTableCells/TaskProgress.css | 22 - .../taskListTableCells/TaskProgress.tsx | 29 -- .../taskListTableCells/TimeTracker.tsx | 66 --- .../taskListTableRows/AddSubTaskListRow.tsx | 37 -- .../taskListTableRows/AddTaskListRow.tsx | 37 -- .../taskListTable/taskListTableWrapper.css | 15 - .../updates/project-view-updates.css | 19 - .../updates/project-view-updates.tsx | 263 ---------- .../workload/ProjectViewWorkload.tsx | 7 - .../workload/projectViewWorkload.css | 0 .../components/task-group/task-group.tsx | 241 --------- .../ProjectTemplateEditView.tsx | 17 +- .../mockTimeLogs.ts | 2 +- 53 files changed, 22 insertions(+), 5186 deletions(-) delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/board/card.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/board/column.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/board/project-view-board.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/roadmap/project-view-roadmap.css delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/roadmap/project-view-roadmap.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-grant-chart.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-table/roadmap-table.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-table/roadmap-task-cell.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/roadmap/time-filter.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/table-v2.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-columns/task-list-columns.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-custom.css delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-custom.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-header/task-list-header.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-instant-task-input/task-list-instant-task-input.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-old/task-list-table-old.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-old/task-list-table.css delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-wrapper/task-list-table-wrapper.css delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-wrapper/task-list-table-wrapper.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/ProjectViewTaskList.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/statusTables/StatusGroupTables.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/GroupByFilterDropdown.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/LabelsFilterDropdown.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/MembersFilterDropdown.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/PriorityFilterDropdown.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/SearchDropdown.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/ShowFieldsFilterDropdown.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/SortFilterDropdown.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/TaskListFilters.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/TaskListTable.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/TaskListTableWrapper.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/columns/columnList.ts delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/contextMenu/TaskContextMenu.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskCell.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress.css delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TimeTracker.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddSubTaskListRow.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddTaskListRow.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableWrapper.css delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/updates/project-view-updates.css delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/updates/project-view-updates.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/workload/ProjectViewWorkload.tsx delete mode 100644 worklenz-frontend/src/pages/projects/project-view-1/workload/projectViewWorkload.css delete mode 100644 worklenz-frontend/src/pages/projects/projectView/taskList/components/task-group/task-group.tsx rename worklenz-frontend/src/{pages/projects/project-view-1/taskList/taskListTable/taskListTableCells => shared}/mockTimeLogs.ts (99%) diff --git a/worklenz-backend/package-lock.json b/worklenz-backend/package-lock.json index 09dbb1c0..a94d430a 100644 --- a/worklenz-backend/package-lock.json +++ b/worklenz-backend/package-lock.json @@ -3528,7 +3528,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -3546,7 +3545,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3559,7 +3557,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3572,14 +3569,12 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -3597,7 +3592,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -3613,7 +3607,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -7649,7 +7642,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -8043,7 +8035,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, "license": "MIT" }, "node_modules/ecdsa-sig-formatter": { @@ -9104,7 +9095,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -9121,7 +9111,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -9882,8 +9871,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", @@ -10024,7 +10012,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -12688,7 +12675,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/pako": { @@ -12844,7 +12830,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -12858,7 +12843,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", @@ -12875,7 +12859,6 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", - "dev": true, "license": "ISC", "engines": { "node": "20 || >=22" @@ -12885,7 +12868,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -13857,7 +13839,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "dev": true, "license": "ISC", "dependencies": { "glob": "^11.0.0", @@ -13877,7 +13858,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -13887,7 +13867,6 @@ "version": "11.0.2", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", - "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -13911,7 +13890,6 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -13927,7 +13905,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -14225,7 +14202,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -14237,7 +14213,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -14590,7 +14565,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -14617,7 +14591,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -15594,7 +15567,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -15684,7 +15656,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -15702,7 +15673,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -15718,7 +15688,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -15731,7 +15700,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/wrap-ansi/node_modules/ansi-styles": { diff --git a/worklenz-frontend/src/components/schedule-old/tabs/withStartAndEndDates/WithStartAndEndDates.tsx b/worklenz-frontend/src/components/schedule-old/tabs/withStartAndEndDates/WithStartAndEndDates.tsx index db2bed7c..f5491b3f 100644 --- a/worklenz-frontend/src/components/schedule-old/tabs/withStartAndEndDates/WithStartAndEndDates.tsx +++ b/worklenz-frontend/src/components/schedule-old/tabs/withStartAndEndDates/WithStartAndEndDates.tsx @@ -1,11 +1,13 @@ -import StatusGroupTables from '@/pages/projects/project-view-1/taskList/statusTables/StatusGroupTables'; +import TaskGroupList from '@/pages/projects/projectView/taskList/groupTables/TaskGroupList'; import { TaskType } from '@/types/task.types'; import { useAppSelector } from '@/hooks/useAppSelector'; -import GroupByFilterDropdown from '@/pages/projects/project-view-1/taskList/taskListFilters/GroupByFilterDropdown'; +import GroupByFilterDropdown from '@/components/project-task-filters/filter-dropdowns/group-by-filter-dropdown'; import { useTranslation } from 'react-i18next'; +import { ITaskListGroup } from '@/types/tasks/taskList.types'; const WithStartAndEndDates = () => { - const dataSource: TaskType[] = useAppSelector(state => state.taskReducer.tasks); + const dataSource: ITaskListGroup[] = useAppSelector(state => state.taskReducer.taskGroups); + const groupBy = useAppSelector(state => state.taskReducer.groupBy); const { t } = useTranslation('schedule'); return (
@@ -57,7 +59,7 @@ const WithStartAndEndDates = () => {
- +
); diff --git a/worklenz-frontend/src/components/schedule/grant-chart/tabs/withStartAndEndDates/WithStartAndEndDates.tsx b/worklenz-frontend/src/components/schedule/grant-chart/tabs/withStartAndEndDates/WithStartAndEndDates.tsx index c52356c4..34de8155 100644 --- a/worklenz-frontend/src/components/schedule/grant-chart/tabs/withStartAndEndDates/WithStartAndEndDates.tsx +++ b/worklenz-frontend/src/components/schedule/grant-chart/tabs/withStartAndEndDates/WithStartAndEndDates.tsx @@ -2,13 +2,14 @@ import { TaskType } from '@/types/task.types'; import { useAppSelector } from '@/hooks/useAppSelector'; import GroupByFilterDropdown from '@/components/project-task-filters/filter-dropdowns/group-by-filter-dropdown'; import { useTranslation } from 'react-i18next'; -import StatusGroupTables from '@/pages/projects/project-view-1/taskList/statusTables/StatusGroupTables'; +import TaskGroupList from '@/pages/projects/projectView/taskList/groupTables/TaskGroupList'; import PriorityGroupTables from '@/pages/projects/projectView/taskList/groupTables/priorityTables/PriorityGroupTables'; import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; import { ITaskListGroup } from '@/types/tasks/taskList.types'; const WithStartAndEndDates = () => { const dataSource: ITaskListGroup[] = useAppSelector(state => state.taskReducer.taskGroups); + const groupBy = useAppSelector(state => state.taskReducer.groupBy); const { t } = useTranslation('schedule'); return (
@@ -60,10 +61,7 @@ const WithStartAndEndDates = () => {
- {dataSource.map(group => ( - - ))} - {/* */} +
); diff --git a/worklenz-frontend/src/components/task-list-common/task-row/task-row-time-tracking/task-row-time-tracking.tsx b/worklenz-frontend/src/components/task-list-common/task-row/task-row-time-tracking/task-row-time-tracking.tsx index 2d4f7395..40173be5 100644 --- a/worklenz-frontend/src/components/task-list-common/task-row/task-row-time-tracking/task-row-time-tracking.tsx +++ b/worklenz-frontend/src/components/task-list-common/task-row/task-row-time-tracking/task-row-time-tracking.tsx @@ -3,7 +3,7 @@ import { Divider, Empty, Flex, Popover, Typography } from 'antd'; import { PlayCircleFilled } from '@ant-design/icons'; import { colors } from '@/styles/colors'; import CustomAvatar from '@components/CustomAvatar'; -import { mockTimeLogs } from '@/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/mockTimeLogs'; +import { mockTimeLogs } from '@/shared/mockTimeLogs'; type TaskListTimeTrackerCellProps = { taskId: string | null; diff --git a/worklenz-frontend/src/lib/project/project-view-constants.ts b/worklenz-frontend/src/lib/project/project-view-constants.ts index 43571cc5..fccbddd8 100644 --- a/worklenz-frontend/src/lib/project/project-view-constants.ts +++ b/worklenz-frontend/src/lib/project/project-view-constants.ts @@ -2,7 +2,7 @@ import React, { ReactNode } from 'react'; import ProjectViewInsights from '@/pages/projects/projectView/insights/project-view-insights'; import ProjectViewFiles from '@/pages/projects/projectView/files/project-view-files'; import ProjectViewMembers from '@/pages/projects/projectView/members/project-view-members'; -import ProjectViewUpdates from '@/pages/projects/project-view-1/updates/project-view-updates'; +import ProjectViewUpdates from '@/pages/projects/projectView/updates/ProjectViewUpdates'; import ProjectViewTaskList from '@/pages/projects/projectView/taskList/project-view-task-list'; import ProjectViewBoard from '@/pages/projects/projectView/board/project-view-board'; import ProjectViewFinance from '@/pages/projects/projectView/finance/project-view-finance'; @@ -32,18 +32,6 @@ export const tabItems: TabItems[] = [ isPinned: true, element: React.createElement(ProjectViewBoard), }, - // { - // index: 2, - // key: 'workload', - // label: 'Workload', - // element: React.createElement(ProjectViewWorkload), - // }, - // { - // index: 3, - // key: 'roadmap', - // label: 'Roadmap', - // element: React.createElement(ProjectViewRoadmap), - // }, { index: 4, key: 'project-insights-member-overview', diff --git a/worklenz-frontend/src/pages/projects/project-view-1/board/card.tsx b/worklenz-frontend/src/pages/projects/project-view-1/board/card.tsx deleted file mode 100644 index b8019bb4..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/board/card.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { FC } from 'react'; -import { CSS } from '@dnd-kit/utilities'; -import { useSortable } from '@dnd-kit/sortable'; -export type CardType = { - id: string; - title: string; -}; - -const Card: FC = ({ id, title }) => { - // useSortableに指定するidは一意になるよう設定する必要があります。s - const { attributes, listeners, setNodeRef, transform } = useSortable({ - id: id, - }); - - const style = { - margin: '10px', - opacity: 1, - color: '#333', - background: 'white', - padding: '10px', - transform: CSS.Transform.toString(transform), - }; - - return ( - // attributes、listenersはDOMイベントを検知するために利用します。 - // listenersを任意の領域に付与することで、ドラッグするためのハンドルを作ることもできます。 -
-
-

{title}

-
-
- ); -}; - -export default Card; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/board/column.tsx b/worklenz-frontend/src/pages/projects/project-view-1/board/column.tsx deleted file mode 100644 index bacf2d2a..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/board/column.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { FC } from 'react'; -import { SortableContext, rectSortingStrategy } from '@dnd-kit/sortable'; -import { useDroppable } from '@dnd-kit/core'; -import Card, { CardType } from './card'; -import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; - -export type ColumnType = { - id: string; - title: string; - cards: IProjectTask[]; -}; - -const Column: FC = ({ id, title, cards }) => { - const { setNodeRef } = useDroppable({ id: id }); - return ( - // ソートを行うためのContextです。 - // strategyは4つほど存在しますが、今回は縦・横移動可能なリストを作るためrectSortingStrategyを採用 - -
-

- {title} -

- {cards.map(card => ( - - ))} -
-
- ); -}; - -export default Column; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/board/project-view-board.tsx b/worklenz-frontend/src/pages/projects/project-view-1/board/project-view-board.tsx deleted file mode 100644 index b9d407e4..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/board/project-view-board.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import React, { useEffect } from 'react'; -import { - DndContext, - DragEndEvent, - DragOverEvent, - PointerSensor, - useSensor, - useSensors, -} from '@dnd-kit/core'; - -import { useAppSelector } from '@/hooks/useAppSelector'; -import TaskListFilters from '../taskList/taskListFilters/TaskListFilters'; -import { Button, Skeleton } from 'antd'; -import { PlusOutlined } from '@ant-design/icons'; -import { useDispatch } from 'react-redux'; -import { toggleDrawer } from '@/features/projects/status/StatusSlice'; -import KanbanGroup from '@/components/board/kanban-group/kanban-group'; - -const ProjectViewBoard: React.FC = () => { - const dispatch = useDispatch(); - - const { taskGroups, loadingGroups } = useAppSelector(state => state.taskReducer); - const { statusCategories } = useAppSelector(state => state.taskStatusReducer); - const groupBy = useAppSelector(state => state.groupByFilterDropdownReducer.groupBy); - const projectId = useAppSelector(state => state.projectReducer.projectId); - - useEffect(() => { - console.log('projectId', projectId); - // if (projectId) { - // const config: ITaskListConfigV2 = { - // id: projectId, - // field: 'id', - // order: 'desc', - // search: '', - // statuses: '', - // members: '', - // projects: '', - // isSubtasksInclude: false, - // }; - // dispatch(fetchTaskGroups(config) as any); - // } - // if (!statusCategories.length) { - // dispatch(fetchStatusesCategories() as any); - // } - }, [dispatch, projectId, groupBy]); - - const sensors = useSensors( - useSensor(PointerSensor, { - activationConstraint: { - distance: 8, - }, - }) - ); - - const handleDragOver = (event: DragOverEvent) => { - const { active, over } = event; - if (!over) return; - - const activeTask = active.data.current?.task; - const overId = over.id; - - // Find which group the task is being dragged over - const targetGroup = taskGroups.find( - group => group.id === overId || group.tasks.some(task => task.id === overId) - ); - - if (targetGroup && activeTask) { - // Here you would dispatch an action to update the task's status - // For example: - // dispatch(updateTaskStatus({ taskId: activeTask.id, newStatus: targetGroup.id })); - console.log('Moving task', activeTask.id, 'to group', targetGroup.id); - } - }; - - const handleDragEnd = (event: DragEndEvent) => { - const { active, over } = event; - if (!over) return; - - const activeTask = active.data.current?.task; - const overId = over.id; - - // Similar to handleDragOver, but this is where you'd make the final update - const targetGroup = taskGroups.find( - group => group.id === overId || group.tasks.some(task => task.id === overId) - ); - - if (targetGroup && activeTask) { - // Make the final update to your backend/state - console.log('Final move of task', activeTask.id, 'to group', targetGroup.id); - } - }; - - return ( -
- - - -
- -
- {taskGroups.map(group => ( - - ))} - -
-
-
-
-
- ); -}; - -export default ProjectViewBoard; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/project-view-roadmap.css b/worklenz-frontend/src/pages/projects/project-view-1/roadmap/project-view-roadmap.css deleted file mode 100644 index 6db27608..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/project-view-roadmap.css +++ /dev/null @@ -1,116 +0,0 @@ -:root { - --odd-row-color: #fff; - --even-row-color: #4e4e4e10; - --text-color: #181818; - --border: 1px solid #e0e0e0; - --stroke: #e0e0e0; - - --calender-header-bg: #fafafa; -} - -.dark-theme { - --odd-row-color: #141414; - --even-row-color: #4e4e4e10; - --text-color: #fff; - --border: 1px solid #505050; - --stroke: #505050; - - --calender-header-bg: #1d1d1d; -} - -/* scroll bar size override */ -._2k9Ys { - scrollbar-width: unset; -} - -/* ----------------------------------------------------------------------- */ -/* task details side even rows */ -._34SS0:nth-of-type(even) { - background-color: var(--even-row-color); -} - -/* task details side header and body */ -._3_ygE { - border-top: var(--border); - border-left: var(--border); - position: relative; -} -._2B2zv { - border-bottom: var(--border); - border-left: var(--border); - position: relative; -} - -._3ZbQT { - border: none; -} - -._3_ygE::after, -._2B2zv::after { - content: ""; - position: absolute; - top: 0; - right: -25px; - width: 30px; - height: 100%; - box-shadow: inset 10px 0 8px -8px #00000026; -} - -/* ._3lLk3:nth-child(1), -._WuQ0f:nth-child(1) { - min-width: 300px !important; - max-width: 300px !important; -} - -._2eZzQ, -._WuQ0f:nth-child(3), -._WuQ0f:last-child, -._3lLk3:nth-child(2), -._3lLk3:nth-child(3) { - display: none; -} */ - -/* ----------------------------------------------------------------------- */ -/* calender side header */ -._35nLX { - fill: var(--calender-header-bg); - stroke: var(--stroke); - stroke-width: 1px; -} - -/* calender side header texts */ -._9w8d5, -._2q1Kt { - fill: var(--text-color); -} - -/* calender side odd rows */ -._2dZTy:nth-child(odd) { - fill: var(--odd-row-color); -} -/* calender side even rows */ -._2dZTy:nth-child(even) { - fill: var(--even-row-color); -} - -/* calender side body row lines */ -._3rUKi { - stroke: var(--stroke); - stroke-width: 0.3px; -} - -/* calender side body ticks */ -._RuwuK { - stroke: var(--stroke); - stroke-width: 0.3px; -} - -/* calender side header ticks */ -._1rLuZ { - stroke: var(--stroke); - stroke-width: 1px; -} - -.roadmap-table .ant-table-thead .ant-table-cell { - height: 50px; -} diff --git a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/project-view-roadmap.tsx b/worklenz-frontend/src/pages/projects/project-view-1/roadmap/project-view-roadmap.tsx deleted file mode 100644 index a5e7d828..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/project-view-roadmap.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useState } from 'react'; -import { ViewMode } from 'gantt-task-react'; -import 'gantt-task-react/dist/index.css'; -import './project-view-roadmap.css'; -import { Flex } from 'antd'; -import { useAppSelector } from '../../../../hooks/useAppSelector'; -import { TimeFilter } from './time-filter'; -import RoadmapTable from './roadmap-table/roadmap-table'; -import RoadmapGrantChart from './roadmap-grant-chart'; - -const ProjectViewRoadmap = () => { - const [view, setView] = useState(ViewMode.Day); - - // get theme details - const themeMode = useAppSelector(state => state.themeReducer.mode); - - return ( - - {/* time filter */} - setView(viewMode)} /> - - - {/* table */} -
- -
- - {/* gantt Chart */} - -
-
- ); -}; - -export default ProjectViewRoadmap; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-grant-chart.tsx b/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-grant-chart.tsx deleted file mode 100644 index 067d0723..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-grant-chart.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Gantt, Task, ViewMode } from 'gantt-task-react'; -import React from 'react'; -import { colors } from '../../../../styles/colors'; -import { - NewTaskType, - updateTaskDate, - updateTaskProgress, -} from '../../../../features/roadmap/roadmap-slice'; -import { useAppSelector } from '../../../../hooks/useAppSelector'; -import { useAppDispatch } from '../../../../hooks/useAppDispatch'; -import { toggleTaskDrawer } from '../../../../features/tasks/tasks.slice'; - -type RoadmapGrantChartProps = { - view: ViewMode; -}; - -const RoadmapGrantChart = ({ view }: RoadmapGrantChartProps) => { - // get task list from roadmap slice - const tasks = useAppSelector(state => state.roadmapReducer.tasksList); - - const dispatch = useAppDispatch(); - - // column widths for each view mods - let columnWidth = 60; - if (view === ViewMode.Year) { - columnWidth = 350; - } else if (view === ViewMode.Month) { - columnWidth = 300; - } else if (view === ViewMode.Week) { - columnWidth = 250; - } - - // function to handle double click - const handleDoubleClick = () => { - dispatch(toggleTaskDrawer()); - }; - - // function to handle date change - const handleTaskDateChange = (task: Task) => { - dispatch(updateTaskDate({ taskId: task.id, start: task.start, end: task.end })); - }; - - // function to handle progress change - const handleTaskProgressChange = (task: Task) => { - dispatch(updateTaskProgress({ taskId: task.id, progress: task.progress })); - }; - - // function to convert the tasklist comming form roadmap slice which has NewTaskType converted to Task type which is the default type of the tasks list in the grant chart - const flattenTasks = (tasks: NewTaskType[]): Task[] => { - const flattened: Task[] = []; - - const addTaskAndSubTasks = (task: NewTaskType, parentExpanded: boolean) => { - // add the task to the flattened list if its parent is expanded or it is a top-level task - if (parentExpanded) { - const { subTasks, isExpanded, ...rest } = task; // destructure to exclude properties not in Task type - flattened.push(rest as Task); - - // recursively add subtasks if this task is expanded - if (subTasks && isExpanded) { - subTasks.forEach(subTask => addTaskAndSubTasks(subTask as NewTaskType, true)); - } - } - }; - - // top-level tasks are always visible, start with parentExpanded = true - tasks.forEach(task => addTaskAndSubTasks(task, true)); - - return flattened; - }; - - const flattenedTasksList = flattenTasks(tasks); - - return ( -
- -
- ); -}; - -export default RoadmapGrantChart; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-table/roadmap-table.tsx b/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-table/roadmap-table.tsx deleted file mode 100644 index ca3c6e77..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-table/roadmap-table.tsx +++ /dev/null @@ -1,189 +0,0 @@ -import React from 'react'; -import { DatePicker, Typography } from 'antd'; -import dayjs, { Dayjs } from 'dayjs'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { useAppDispatch } from '@/hooks/useAppDispatch'; -import { NewTaskType, updateTaskDate } from '@features/roadmap/roadmap-slice'; -import { colors } from '@/styles/colors'; -import RoadmapTaskCell from './roadmap-task-cell'; - -const RoadmapTable = () => { - // Get task list and expanded tasks from roadmap slice - const tasks = useAppSelector(state => state.roadmapReducer.tasksList); - - // Get theme data from theme slice - const themeMode = useAppSelector(state => state.themeReducer.mode); - - const dispatch = useAppDispatch(); - - // function to handle date changes - const handleDateChange = (taskId: string, dateType: 'start' | 'end', date: Dayjs) => { - const updatedDate = date.toDate(); - - dispatch( - updateTaskDate({ - taskId, - start: dateType === 'start' ? updatedDate : new Date(), - end: dateType === 'end' ? updatedDate : new Date(), - }) - ); - }; - - // Adjusted column type with a string or ReactNode for the title - const columns: { key: string; title: React.ReactNode; width: number }[] = [ - { - key: 'name', - title: 'Task Name', - width: 240, - }, - { - key: 'start', - title: 'Start Date', - width: 130, - }, - { - key: 'end', - title: 'End Date', - width: 130, - }, - ]; - - // Function to render the column content based on column key - const renderColumnContent = ( - columnKey: string, - task: NewTaskType, - isSubtask: boolean = false - ) => { - switch (columnKey) { - case 'name': - return ; - case 'start': - const startDayjs = task.start ? dayjs(task.start) : null; - return ( - handleDateChange(task.id, 'end', date)} - style={{ - backgroundColor: colors.transparent, - border: 'none', - boxShadow: 'none', - }} - /> - ); - case 'end': - const endDayjs = task.end ? dayjs(task.end) : null; - return ( - handleDateChange(task.id, 'end', date)} - style={{ - backgroundColor: colors.transparent, - border: 'none', - boxShadow: 'none', - }} - /> - ); - - default: - return null; - } - }; - - const dataSource = tasks.map(task => ({ - id: task.id, - name: task.name, - start: task.start, - end: task.end, - type: task.type, - progress: task.progress, - subTasks: task.subTasks, - isExpanded: task.isExpanded, - })); - - // Layout styles for table and columns - const customHeaderColumnStyles = `border px-2 h-[50px] text-left z-10 after:content after:absolute after:top-0 after:-right-1 after:-z-10 after:h-[42px] after:w-1.5 after:bg-transparent after:bg-gradient-to-r after:from-[rgba(0,0,0,0.12)] after:to-transparent ${themeMode === 'dark' ? 'bg-[#1d1d1d] border-[#303030]' : 'bg-[#fafafa]'}`; - - const customBodyColumnStyles = `border px-2 h-[50px] z-10 after:content after:absolute after:top-0 after:-right-1 after:-z-10 after:min-h-[40px] after:w-1.5 after:bg-transparent after:bg-gradient-to-r after:from-[rgba(0,0,0,0.12)] after:to-transparent ${themeMode === 'dark' ? 'bg-transparent border-[#303030]' : 'bg-transparent'}`; - - const rowBackgroundStyles = - themeMode === 'dark' ? 'even:bg-[#1b1b1b] odd:bg-[#141414]' : 'even:bg-[#f4f4f4] odd:bg-white'; - - return ( -
- - - - {/* table header */} - {columns.map(column => ( - - ))} - - - - {dataSource.length === 0 ? ( - - - - ) : ( - dataSource.map(task => ( - - - {columns.map(column => ( - - ))} - - - {/* subtasks */} - {task.isExpanded && - task?.subTasks?.map(subtask => ( - - {columns.map(column => ( - - ))} - - ))} - - )) - )} - -
- {column.title} -
- No tasks available -
- {renderColumnContent(column.key, task)} -
- {renderColumnContent(column.key, subtask, true)} -
-
- ); -}; - -export default RoadmapTable; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-table/roadmap-task-cell.tsx b/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-table/roadmap-task-cell.tsx deleted file mode 100644 index 246026b8..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/roadmap-table/roadmap-task-cell.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Flex, Typography, Button, Tooltip } from 'antd'; -import { - DoubleRightOutlined, - DownOutlined, - RightOutlined, - ExpandAltOutlined, -} from '@ant-design/icons'; -import { NewTaskType, toggleTaskExpansion } from '@features/roadmap/roadmap-slice'; -import { useAppDispatch } from '@/hooks/useAppDispatch'; -import { toggleTaskDrawer } from '@features/tasks/taskSlice'; -import { colors } from '@/styles/colors'; - -type RoadmapTaskCellProps = { - task: NewTaskType; - isSubtask?: boolean; -}; - -const RoadmapTaskCell = ({ task, isSubtask = false }: RoadmapTaskCellProps) => { - const dispatch = useAppDispatch(); - - // render the toggle arrow icon for tasks with subtasks - const renderToggleButtonForHasSubTasks = (id: string, hasSubtasks: boolean) => { - if (!hasSubtasks) return null; - return ( - - ); - }; - - // show expand button on hover for tasks without subtasks - const renderToggleButtonForNonSubtasks = (id: string, isSubtask: boolean) => { - return !isSubtask ? ( - - ) : ( -
- ); - }; - - // render the double arrow icon and count label for tasks with subtasks - const renderSubtasksCountLabel = (id: string, isSubtask: boolean, subTasksCount: number) => { - return ( - !isSubtask && ( - - ) - ); - }; - - return ( - - - {!!task?.subTasks?.length ? ( - renderToggleButtonForHasSubTasks(task.id, !!task?.subTasks?.length) - ) : ( -
- {renderToggleButtonForNonSubtasks(task.id, isSubtask)} -
- )} - - {isSubtask && } - - - - {task.name} - - - - {renderSubtasksCountLabel(task.id, isSubtask, task?.subTasks?.length || 0)} -
- - -
- ); -}; - -export default RoadmapTaskCell; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/time-filter.tsx b/worklenz-frontend/src/pages/projects/project-view-1/roadmap/time-filter.tsx deleted file mode 100644 index ff1461f1..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/roadmap/time-filter.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import 'gantt-task-react/dist/index.css'; -import { ViewMode } from 'gantt-task-react'; -import { Flex, Select } from 'antd'; -type TimeFilterProps = { - onViewModeChange: (viewMode: ViewMode) => void; -}; -export const TimeFilter = ({ onViewModeChange }: TimeFilterProps) => { - // function to handle time change - const handleChange = (value: string) => { - switch (value) { - case 'hour': - return onViewModeChange(ViewMode.Hour); - case 'quaterDay': - return onViewModeChange(ViewMode.QuarterDay); - case 'halfDay': - return onViewModeChange(ViewMode.HalfDay); - case 'day': - return onViewModeChange(ViewMode.Day); - case 'week': - return onViewModeChange(ViewMode.Week); - case 'month': - return onViewModeChange(ViewMode.Month); - case 'year': - return onViewModeChange(ViewMode.Year); - default: - return onViewModeChange(ViewMode.Day); - } - }; - - const timeFilterItems = [ - { - value: 'hour', - label: 'Hour', - }, - { - value: 'quaterDay', - label: 'Quater Day', - }, - { - value: 'halfDay', - label: 'Half Day', - }, - { - value: 'day', - label: 'Day', - }, - { - value: 'week', - label: 'Week', - }, - { - value: 'month', - label: 'Month', - }, - { - value: 'year', - label: 'Year', - }, - ]; - - return ( - - - - ), - }), - - // columnHelper.accessor('time_tracking', { - // header: 'Time Tracking', - // size: 120, - // enablePinning: false, - // cell: ({ row }) => ( - // - // ) - // }) - ]; -}; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-custom.css b/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-custom.css deleted file mode 100644 index a4780500..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-custom.css +++ /dev/null @@ -1,44 +0,0 @@ -.table-header { - border-bottom: 1px solid #d9d9d9; - /* Border below header */ -} - -.table-body { - background-color: #ffffff; - /* White background for body */ -} - -.table-row { - display: flex; - /* Use flexbox for row layout */ - align-items: center; - /* Center items vertically */ - transition: background-color 0.2s; - /* Smooth background transition */ -} - -.table-row:hover { - background-color: #f5f5f5; - /* Light gray background on hover */ -} - -/* Optional: Add styles for sticky headers */ -.table-header > div { - position: sticky; - /* Make header cells sticky */ - top: 0; - /* Stick to the top */ - z-index: 1; - /* Ensure it stays above other content */ -} - -/* Optional: Add styles for cell borders */ -.table-row > div { - border-right: 1px solid #d9d9d9; - /* Right border for cells */ -} - -.table-row > div:last-child { - border-right: none; - /* Remove right border for last cell */ -} diff --git a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-custom.tsx b/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-custom.tsx deleted file mode 100644 index e2ca07f2..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-custom.tsx +++ /dev/null @@ -1,272 +0,0 @@ -import { useCallback, useMemo, useRef, useState } from 'react'; -import { Checkbox, theme } from 'antd'; -import { - useReactTable, - getCoreRowModel, - getFilteredRowModel, - getPaginationRowModel, - flexRender, - VisibilityState, - Row, - Column, -} from '@tanstack/react-table'; -import { useVirtualizer } from '@tanstack/react-virtual'; -import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import React from 'react'; -import './task-list-custom.css'; -import TaskListInstantTaskInput from './task-list-instant-task-input/task-list-instant-task-input'; -import { useAuthService } from '@/hooks/useAuth'; -import { createColumns } from './task-list-columns/task-list-columns'; - -interface TaskListCustomProps { - tasks: IProjectTask[]; - color: string; - groupId?: string | null; - onTaskSelect?: (taskId: string) => void; -} - -const TaskListCustom: React.FC = ({ tasks, color, groupId, onTaskSelect }) => { - const [rowSelection, setRowSelection] = useState({}); - const [columnVisibility, setColumnVisibility] = useState({}); - const [expandedRows, setExpandedRows] = useState>({}); - - const statuses = useAppSelector(state => state.taskStatusReducer.status); - const tableContainerRef = useRef(null); - const { token } = theme.useToken(); - const { getCurrentSession } = useAuthService(); - - const handleExpandClick = useCallback((rowId: string) => { - setExpandedRows(prev => ({ - ...prev, - [rowId]: !prev[rowId], - })); - }, []); - - const handleTaskSelect = useCallback( - (taskId: string) => { - onTaskSelect?.(taskId); - }, - [onTaskSelect] - ); - - const columns = useMemo( - () => - createColumns({ - expandedRows, - statuses, - handleTaskSelect, - getCurrentSession, - }), - [expandedRows, statuses, handleTaskSelect, getCurrentSession] - ); - - const table = useReactTable({ - data: tasks, - columns, - state: { - rowSelection, - columnVisibility, - }, - enableRowSelection: true, - onRowSelectionChange: setRowSelection, - onColumnVisibilityChange: setColumnVisibility, - getCoreRowModel: getCoreRowModel(), - getFilteredRowModel: getFilteredRowModel(), - getPaginationRowModel: getPaginationRowModel(), - }); - - const { rows } = table.getRowModel(); - - const rowVirtualizer = useVirtualizer({ - count: rows.length, - getScrollElement: () => tableContainerRef.current, - estimateSize: () => 50, - overscan: 20, - }); - - const virtualRows = rowVirtualizer.getVirtualItems(); - const totalSize = rowVirtualizer.getTotalSize(); - const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0; - const paddingBottom = - virtualRows.length > 0 ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0) : 0; - - const columnToggleItems = columns.map(column => ({ - key: column.id as string, - label: ( - - - {typeof column.header === 'string' ? column.header : column.id} - - - ), - onClick: () => { - const columnData = table.getColumn(column.id as string); - if (columnData) { - columnData.toggleVisibility(); - } - }, - })); - - return ( -
-
-
-
- {table.getHeaderGroups().map(headerGroup => ( -
- {headerGroup.headers.map((header, index) => ( -
- {header.isPlaceholder - ? null - : flexRender(header.column.columnDef.header, header.getContext())} -
- ))} -
- ))} -
-
- {paddingTop > 0 &&
} - {virtualRows.map(virtualRow => { - const row = rows[virtualRow.index]; - return ( - -
- {row.getVisibleCells().map((cell, index) => ( -
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
- ))} -
- {expandedRows[row.id] && - row.original.sub_tasks?.map(subTask => ( -
- {columns.map((col, index) => ( -
- {flexRender(col.cell, { - getValue: () => subTask[col.id as keyof typeof subTask] ?? null, - row: { original: subTask } as Row, - column: col as Column, - table, - })} -
- ))} -
- ))} -
- ); - })} - {paddingBottom > 0 &&
} -
-
-
- - {/* {selectedCount > 0 && ( - - {selectedCount} tasks selected - - - - - - )} */} -
- ); -}; - -export default TaskListCustom; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-header/task-list-header.tsx b/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-header/task-list-header.tsx deleted file mode 100644 index 12043805..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-header/task-list-header.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import React, { useState } from 'react'; -import { Button, Dropdown, Input, Menu, Badge, Tooltip } from 'antd'; -import { - RightOutlined, - LoadingOutlined, - EllipsisOutlined, - EditOutlined, - RetweetOutlined, -} from '@ant-design/icons'; -import { ITaskListGroup } from '@/types/tasks/taskList.types'; -import { ITaskStatusCategory } from '@/types/status.types'; -import { useAppSelector } from '@/hooks/useAppSelector'; -// import WorklenzTaskListPhaseDuration from "./WorklenzTaskListPhaseDuration"; -// import WorklenzTasksProgressBar from "./WorklenzTasksProgressBar"; - -interface Props { - group: ITaskListGroup; - projectId: string | null; - categories: ITaskStatusCategory[]; -} - -const TaskListGroupSettings: React.FC = ({ group, projectId, categories }) => { - const [edit, setEdit] = useState(false); - const [showMenu, setShowMenu] = useState(false); - const [isEditColProgress, setIsEditColProgress] = useState(false); - const [isGroupByPhases, setIsGroupByPhases] = useState(false); - const [isGroupByStatus, setIsGroupByStatus] = useState(false); - const [isAdmin, setIsAdmin] = useState(false); - - const menu = ( - - - - Rename - - {isGroupByStatus && ( - - - Change category - - } - > - {categories.map(item => ( - - - - - - ))} - - )} - - ); - - const onBlurEditColumn = (group: ITaskListGroup) => { - setEdit(false); - }; - - const onToggleClick = () => { - console.log('onToggleClick'); - }; - - const canDisplayActions = () => { - return true; - }; - - return ( -
-
- - - {canDisplayActions() && ( - setShowMenu(visible)} - > - - - )} -
- - {/* {isGroupByPhases && group.name !== "Unmapped" && ( -
- -
- )} - - {isProgressBarAvailable() && ( - - )} */} -
- ); -}; - -export default TaskListGroupSettings; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-instant-task-input/task-list-instant-task-input.tsx b/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-instant-task-input/task-list-instant-task-input.tsx deleted file mode 100644 index 2312800a..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-instant-task-input/task-list-instant-task-input.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { Input, InputRef, theme } from 'antd'; -import React, { useState, useMemo, useRef } from 'react'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { colors } from '@/styles/colors'; -import { useTranslation } from 'react-i18next'; -import { ILocalSession } from '@/types/auth/local-session.types'; -import { ITaskCreateRequest } from '@/types/tasks/task-create-request.types'; -import { - addTask, - getCurrentGroup, - GROUP_BY_PHASE_VALUE, - GROUP_BY_PRIORITY_VALUE, - GROUP_BY_STATUS_VALUE, -} from '@/features/tasks/tasks.slice'; -import { useSocket } from '@/socket/socketContext'; -import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; -import { SocketEvents } from '@/shared/socket-events'; -import { DRAWER_ANIMATION_INTERVAL } from '@/shared/constants'; -import { useAppDispatch } from '@/hooks/useAppDispatch'; - -interface ITaskListInstantTaskInputProps { - session: ILocalSession | null; - groupId?: string | null; - parentTask?: string | null; -} -interface IAddNewTask extends IProjectTask { - groupId: string; -} - -const TaskListInstantTaskInput = ({ - session, - groupId = null, - parentTask = null, -}: ITaskListInstantTaskInputProps) => { - const [isEdit, setIsEdit] = useState(false); - const [taskName, setTaskName] = useState(''); - const [creatingTask, setCreatingTask] = useState(false); - const taskInputRef = useRef(null); - const dispatch = useAppDispatch(); - - const { socket } = useSocket(); - const { token } = theme.useToken(); - - const { t } = useTranslation('task-list-table'); - - const themeMode = useAppSelector(state => state.themeReducer.mode); - const customBorderColor = useMemo(() => themeMode === 'dark' && ' border-[#303030]', [themeMode]); - const projectId = useAppSelector(state => state.projectReducer.projectId); - - const createRequestBody = (): ITaskCreateRequest | null => { - if (!projectId || !session) return null; - const body: ITaskCreateRequest = { - project_id: projectId, - name: taskName, - reporter_id: session.id, - team_id: session.team_id, - }; - - const groupBy = getCurrentGroup(); - if (groupBy.value === GROUP_BY_STATUS_VALUE) { - body.status_id = groupId || undefined; - } else if (groupBy.value === GROUP_BY_PRIORITY_VALUE) { - body.priority_id = groupId || undefined; - } else if (groupBy.value === GROUP_BY_PHASE_VALUE) { - body.phase_id = groupId || undefined; - } - - if (parentTask) { - body.parent_task_id = parentTask; - } - console.log('createRequestBody', body); - - return body; - }; - - const reset = (scroll = true) => { - setIsEdit(false); - - setCreatingTask(false); - - setTaskName(''); - setIsEdit(true); - - setTimeout(() => { - taskInputRef.current?.focus(); - if (scroll) window.scrollTo(0, document.body.scrollHeight); - }, DRAWER_ANIMATION_INTERVAL); // wait for the animation end - }; - - const onNewTaskReceived = (task: IAddNewTask) => { - if (!groupId) return; - console.log('onNewTaskReceived', task); - task.groupId = groupId; - if (groupId && task.id) { - dispatch(addTask(task)); - reset(false); - // if (this.map.has(task.id)) return; - - // this.service.addTask(task, this.groupId); - // this.reset(false); - } - }; - - const addInstantTask = () => { - if (creatingTask) return; - console.log('addInstantTask', projectId, taskName.trim()); - if (!projectId || !session || taskName.trim() === '') return; - - try { - setCreatingTask(true); - const body = createRequestBody(); - if (!body) return; - socket?.emit(SocketEvents.QUICK_TASK.toString(), JSON.stringify(body)); - socket?.once(SocketEvents.QUICK_TASK.toString(), (task: IProjectTask) => { - setCreatingTask(false); - if (task.parent_task_id) { - } - onNewTaskReceived(task as IAddNewTask); - }); - } catch (error) { - console.error(error); - } finally { - setCreatingTask(false); - } - }; - - const handleAddTask = () => { - setIsEdit(false); - addInstantTask(); - }; - - return ( -
- {isEdit ? ( - setTaskName(e.target.value)} - onBlur={handleAddTask} - onPressEnter={handleAddTask} - ref={taskInputRef} - /> - ) : ( - setIsEdit(true)} - className="w-[300px] border-none" - style={{ height: '34px' }} - value={t('addTaskText')} - ref={taskInputRef} - /> - )} -
- ); -}; - -export default TaskListInstantTaskInput; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-old/task-list-table-old.tsx b/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-old/task-list-table-old.tsx deleted file mode 100644 index 23ba7623..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-old/task-list-table-old.tsx +++ /dev/null @@ -1,471 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Avatar, Checkbox, DatePicker, Flex, Tag, Tooltip, Typography } from 'antd'; - -import { useAppSelector } from '@/hooks/useAppSelector'; -import { columnList } from '@/pages/projects/project-view-1/taskList/taskListTable/columns/columnList'; -import AddTaskListRow from '@/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddTaskListRow'; - -import CustomAvatar from '@/components/CustomAvatar'; -import LabelsSelector from '@components/task-list-common/labelsSelector/labels-selector'; -import { useSelectedProject } from '@/hooks/useSelectedProject'; -import StatusDropdown from '@/components/task-list-common/status-dropdown/status-dropdown'; -import PriorityDropdown from '@/components/task-list-common/priorityDropdown/priority-dropdown'; -import { simpleDateFormat } from '@/utils/simpleDateFormat'; -import { durationDateFormat } from '@/utils/durationDateFormat'; -import CustomColorLabel from '@components/task-list-common/labelsSelector/custom-color-label'; -import CustomNumberLabel from '@components/task-list-common/labelsSelector/custom-number-label'; -import PhaseDropdown from '@components/task-list-common/phaseDropdown/PhaseDropdown'; -import AssigneeSelector from '@components/task-list-common/assigneeSelector/AssigneeSelector'; -import TaskCell from '@/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskCell'; -import AddSubTaskListRow from '@/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddSubTaskListRow'; -import { colors } from '@/styles/colors'; -import TimeTracker from '@/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TimeTracker'; -import TaskContextMenu from '@/pages/projects/project-view-1/taskList/taskListTable/contextMenu/TaskContextMenu'; -import TaskProgress from '@/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress'; -import { useAppDispatch } from '@/hooks/useAppDispatch'; -import { deselectAll } from '@/features/projects/bulkActions/bulkActionSlice'; -import { useTranslation } from 'react-i18next'; -import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; -import { ITaskListGroup } from '@/types/tasks/taskList.types'; -import Avatars from '@/components/avatars/avatars'; - -const TaskListTable = ({ - taskList, - tableId, -}: { - taskList: ITaskListGroup; - tableId: string | undefined; -}) => { - // these states manage the necessary states - const [hoverRow, setHoverRow] = useState(null); - const [selectedRows, setSelectedRows] = useState([]); - const [selectedTaskId, setSelectedTaskId] = useState(null); - const [expandedTasks, setExpandedTasks] = useState([]); - const [isSelectAll, setIsSelectAll] = useState(false); - // context menu state - const [contextMenuVisible, setContextMenuVisible] = useState(false); - const [contextMenuPosition, setContextMenuPosition] = useState({ - x: 0, - y: 0, - }); - // state to check scroll - const [scrollingTables, setScrollingTables] = useState<{ - [key: string]: boolean; - }>({}); - - // localization - const { t } = useTranslation('task-list-table'); - - const dispatch = useAppDispatch(); - - // get data theme data from redux - const themeMode = useAppSelector(state => state.themeReducer.mode); - - // get the selected project details - const selectedProject = useSelectedProject(); - - // get columns list details - const columnsVisibility = useAppSelector( - state => state.projectViewTaskListColumnsReducer.columnsVisibility - ); - const visibleColumns = columnList.filter( - column => columnsVisibility[column.key as keyof typeof columnsVisibility] - ); - - // toggle subtasks visibility - const toggleTaskExpansion = (taskId: string) => { - setExpandedTasks(prev => - prev.includes(taskId) ? prev.filter(id => id !== taskId) : [...prev, taskId] - ); - }; - - // toggle all task select when header checkbox click - const toggleSelectAll = () => { - if (isSelectAll) { - setSelectedRows([]); - dispatch(deselectAll()); - } else { - // const allTaskIds = - // task-list?.flatMap((task) => [ - // task.taskId, - // ...(task.subTasks?.map((subtask) => subtask.taskId) || []), - // ]) || []; - // setSelectedRows(allTaskIds); - // dispatch(selectTaskIds(allTaskIds)); - // console.log('selected tasks and subtasks (all):', allTaskIds); - } - setIsSelectAll(!isSelectAll); - }; - - // toggle selected row - const toggleRowSelection = (task: IProjectTask) => { - setSelectedRows(prevSelectedRows => - prevSelectedRows.includes(task.id || '') - ? prevSelectedRows.filter(id => id !== task.id) - : [...prevSelectedRows, task.id || ''] - ); - }; - - // this use effect for realtime update the selected rows - useEffect(() => { - console.log('Selected tasks and subtasks:', selectedRows); - }, [selectedRows]); - - // select one row this triggers only in handle the context menu ==> righ click mouse event - const selectOneRow = (task: IProjectTask) => { - setSelectedRows([task.id || '']); - - // log the task object when selected - if (!selectedRows.includes(task.id || '')) { - console.log('Selected task:', task); - } - }; - - // handle custom task context menu - const handleContextMenu = (e: React.MouseEvent, task: IProjectTask) => { - e.preventDefault(); - setSelectedTaskId(task.id || ''); - selectOneRow(task); - setContextMenuPosition({ x: e.clientX, y: e.clientY }); - setContextMenuVisible(true); - }; - - // trigger the table scrolling - useEffect(() => { - const tableContainer = document.querySelector(`.tasklist-container-${tableId}`); - const handleScroll = () => { - if (tableContainer) { - setScrollingTables(prev => ({ - ...prev, - [tableId]: tableContainer.scrollLeft > 0, - })); - } - }; - tableContainer?.addEventListener('scroll', handleScroll); - return () => tableContainer?.removeEventListener('scroll', handleScroll); - }, [tableId]); - - // layout styles for table and the columns - const customBorderColor = themeMode === 'dark' && ' border-[#303030]'; - - const customHeaderColumnStyles = (key: string) => - `border px-2 text-left ${key === 'selector' && 'sticky left-0 z-10'} ${key === 'task' && `sticky left-[33px] z-10 after:content after:absolute after:top-0 after:-right-1 after:-z-10 after:h-[42px] after:w-1.5 after:bg-transparent ${scrollingTables[tableId] ? 'after:bg-gradient-to-r after:from-[rgba(0,0,0,0.12)] after:to-transparent' : ''}`} ${themeMode === 'dark' ? 'bg-[#1d1d1d] border-[#303030]' : 'bg-[#fafafa]'}`; - - const customBodyColumnStyles = (key: string) => - `border px-2 ${key === 'selector' && 'sticky left-0 z-10'} ${key === 'task' && `sticky left-[33px] z-10 after:content after:absolute after:top-0 after:-right-1 after:-z-10 after:min-h-[40px] after:w-1.5 after:bg-transparent ${scrollingTables[tableId] ? 'after:bg-gradient-to-r after:from-[rgba(0,0,0,0.12)] after:to-transparent' : ''}`} ${themeMode === 'dark' ? 'bg-[#141414] border-[#303030]' : 'bg-white'}`; - - // function to render the column content based on column key - const renderColumnContent = ( - columnKey: string, - task: IProjectTask, - isSubtask: boolean = false - ) => { - switch (columnKey) { - // task ID column - case 'taskId': - return ( - - {task.task_key || ''} - - ); - - // task name column - case 'task': - return ( - // custom task cell component - - ); - - // description column - case 'description': - return ; - - // progress column - case 'progress': { - return task?.progress || task?.progress === 0 ? ( - - ) : ( -
- ); - } - - // members column - case 'members': - return ( - - - {/* - {task.assignees?.map(member => ( - - ))} - */} - - - ); - - // labels column - case 'labels': - return ( - - {task?.labels && task?.labels?.length <= 2 ? ( - task?.labels?.map(label => ) - ) : ( - - - - {/* this component show other label names */} - - - )} - - - ); - - // phase column - case 'phases': - return ; - - // status column - case 'status': - return ; - - // priority column - case 'priority': - return ; - - // time tracking column - case 'timeTracking': - return ; - - // estimation column - case 'estimation': - return 0h 0m; - - // start date column - case 'startDate': - return task.start_date ? ( - {simpleDateFormat(task.start_date)} - ) : ( - - ); - - // due date column - case 'dueDate': - return task.end_date ? ( - {simpleDateFormat(task.end_date)} - ) : ( - - ); - - // completed date column - case 'completedDate': - return {durationDateFormat(task.completed_at || null)}; - - // created date column - case 'createdDate': - return {durationDateFormat(task.created_at || null)}; - - // last updated column - case 'lastUpdated': - return {durationDateFormat(task.updated_at || null)}; - - // recorder column - case 'reporter': - return {task.reporter}; - - // default case for unsupported columns - default: - return null; - } - }; - - return ( -
-
- - - - {/* this cell render the select all task checkbox */} - - {/* other header cells */} - {visibleColumns.map(column => ( - - ))} - - - - {taskList?.tasks?.map(task => ( - - handleContextMenu(e, task)} - className={`${taskList.tasks.length === 0 ? 'h-0' : 'h-[42px]'}`} - > - {/* this cell render the select the related task checkbox */} - - {/* other cells */} - {visibleColumns.map(column => ( - - ))} - - - {/* this is for sub tasks */} - {expandedTasks.includes(task.id || '') && - task?.sub_tasks?.map(subtask => ( - handleContextMenu(e, subtask)} - className={`${taskList.tasks.length === 0 ? 'h-0' : 'h-[42px]'}`} - > - {/* this cell render the select the related task checkbox */} - - - {/* other sub tasks cells */} - {visibleColumns.map(column => ( - - ))} - - ))} - - {expandedTasks.includes(task.id || '') && ( - - - - )} - - ))} - -
- - - {column.key === 'phases' - ? column.columnHeader - : t(`${column.columnHeader}Column`)} -
- toggleRowSelection(task)} - /> - - {renderColumnContent(column.key, task)} -
- toggleRowSelection(subtask)} - /> - - {renderColumnContent(column.key, subtask, true)} -
- -
-
- - {/* add a main task to the table */} - - - {/* custom task context menu */} - setContextMenuVisible(false)} - /> -
- ); -}; - -export default TaskListTable; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-old/task-list-table.css b/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-old/task-list-table.css deleted file mode 100644 index ce92b84a..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-old/task-list-table.css +++ /dev/null @@ -1,19 +0,0 @@ -.tasks-table { - width: max-content; - margin-left: 3px; - border-right: 1px solid #f0f0f0; -} - -.flex-table { - display: flex; - width: max-content; -} - -.table-container { - overflow: auto; - display: flex; -} - -.position-relative { - position: relative; -} diff --git a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-wrapper/task-list-table-wrapper.css b/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-wrapper/task-list-table-wrapper.css deleted file mode 100644 index 6e5ca7c8..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-wrapper/task-list-table-wrapper.css +++ /dev/null @@ -1,15 +0,0 @@ -/* custom collapse styles for content box and the left border */ -.ant-collapse-header { - margin-bottom: 6px !important; -} - -.custom-collapse-content-box .ant-collapse-content-box { - padding: 0 !important; -} - -:where(.css-dev-only-do-not-override-1w6wsvq).ant-collapse-ghost - > .ant-collapse-item - > .ant-collapse-content - > .ant-collapse-content-box { - padding: 0; -} diff --git a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-wrapper/task-list-table-wrapper.tsx b/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-wrapper/task-list-table-wrapper.tsx deleted file mode 100644 index f3149767..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/task-list/task-list-table-wrapper/task-list-table-wrapper.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { Badge, Button, Collapse, ConfigProvider, Dropdown, Flex, Input, Typography } from 'antd'; -import { useState } from 'react'; -import { TaskType } from '@/types/task.types'; -import { EditOutlined, EllipsisOutlined, RetweetOutlined, RightOutlined } from '@ant-design/icons'; -import { colors } from '@/styles/colors'; -import './task-list-table-wrapper.css'; -import TaskListTable from '../task-list-table-old/task-list-table-old'; -import { MenuProps } from 'antd/lib'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { useTranslation } from 'react-i18next'; -import { ITaskListGroup } from '@/types/tasks/taskList.types'; -import TaskListCustom from '../task-list-custom'; - -type TaskListTableWrapperProps = { - taskList: ITaskListGroup; - groupId: string | undefined; - name: string | undefined; - color: string | undefined; - onRename?: (name: string) => void; - onStatusCategoryChange?: (category: string) => void; -}; - -const TaskListTableWrapper = ({ - taskList, - groupId, - name, - color, - onRename, - onStatusCategoryChange, -}: TaskListTableWrapperProps) => { - const [tableName, setTableName] = useState(name || ''); - const [isRenaming, setIsRenaming] = useState(false); - const [isExpanded, setIsExpanded] = useState(true); - - const type = 'status'; - - // localization - const { t } = useTranslation('task-list-table'); - - // function to handle toggle expand - const handlToggleExpand = () => { - setIsExpanded(!isExpanded); - }; - - // these codes only for status type tables - // function to handle rename this functionality only available for status type tables - const handleRename = () => { - if (onRename) { - onRename(tableName); - } - setIsRenaming(false); - }; - - // function to handle category change - const handleCategoryChange = (category: string) => { - if (onStatusCategoryChange) { - onStatusCategoryChange(category); - } - }; - - // find the available status for the currently active project - const statusList = useAppSelector(state => state.statusReducer.status); - - const getStatusColor = (status: string) => { - switch (status) { - case 'todo': - return '#d8d7d8'; - case 'doing': - return '#c0d5f6'; - case 'done': - return '#c2e4d0'; - default: - return '#d8d7d8'; - } - }; - - // dropdown options - const items: MenuProps['items'] = [ - { - key: '1', - icon: , - label: 'Rename', - onClick: () => setIsRenaming(true), - }, - { - key: '2', - icon: , - label: 'Change category', - children: statusList?.map(status => ({ - key: status.id, - label: ( - handleCategoryChange(status.category)}> - - {status.name} - - ), - })), - }, - ]; - - return ( - - - - - {type === 'status' && !isRenaming && ( - - - - ); -}; - -export default LabelsFilterDropdown; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/MembersFilterDropdown.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/MembersFilterDropdown.tsx deleted file mode 100644 index 1690b937..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/MembersFilterDropdown.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { CaretDownFilled } from '@ant-design/icons'; -import { - Badge, - Button, - Card, - Checkbox, - Dropdown, - Empty, - Flex, - Input, - InputRef, - List, - Space, - Typography, -} from 'antd'; -import { useMemo, useRef, useState } from 'react'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { colors } from '@/styles/colors'; -import CustomAvatar from '@components/CustomAvatar'; -import { useTranslation } from 'react-i18next'; - -const MembersFilterDropdown = () => { - const [selectedCount, setSelectedCount] = useState(0); - const membersInputRef = useRef(null); - - const members = useAppSelector(state => state.memberReducer.membersList); - - const { t } = useTranslation('task-list-filters'); - - const membersList = [ - ...members, - useAppSelector(state => state.memberReducer.owner), - ]; - - const themeMode = useAppSelector(state => state.themeReducer.mode); - - // this is for get the current string that type on search bar - const [searchQuery, setSearchQuery] = useState(''); - - // used useMemo hook for re render the list when searching - const filteredMembersData = useMemo(() => { - return membersList.filter(member => - member.memberName.toLowerCase().includes(searchQuery.toLowerCase()) - ); - }, [membersList, searchQuery]); - - // handle selected filters count - const handleSelectedFiltersCount = (checked: boolean) => { - setSelectedCount(prev => (checked ? prev + 1 : prev - 1)); - }; - - // custom dropdown content - const membersDropdownContent = ( - - - setSearchQuery(e.currentTarget.value)} - placeholder={t('searchInputPlaceholder')} - /> - - - {filteredMembersData.length ? ( - filteredMembersData.map(member => ( - - handleSelectedFiltersCount(e.target.checked)} - /> -
- -
- - {member.memberName} - - - {member.memberEmail} - - -
- )) - ) : ( - - )} -
-
-
- ); - - // function to focus members input - const handleMembersDropdownOpen = (open: boolean) => { - if (open) { - setTimeout(() => { - membersInputRef.current?.focus(); - }, 0); - } - }; - - return ( - membersDropdownContent} - onOpenChange={handleMembersDropdownOpen} - > - - - ); -}; - -export default MembersFilterDropdown; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/PriorityFilterDropdown.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/PriorityFilterDropdown.tsx deleted file mode 100644 index dafc5af2..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/PriorityFilterDropdown.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { CaretDownFilled } from '@ant-design/icons'; -import { Badge, Button, Card, Checkbox, Dropdown, List, Space } from 'antd'; -import { useState } from 'react'; - -import { colors } from '@/styles/colors'; -import { useTranslation } from 'react-i18next'; -import { ITaskPriority } from '@/types/tasks/taskPriority.types'; -import { useAppSelector } from '@/hooks/useAppSelector'; - -const PriorityFilterDropdown = (props: { priorities: ITaskPriority[] }) => { - const [selectedCount, setSelectedCount] = useState(0); - - // localization - const { t } = useTranslation('task-list-filters'); - const themeMode = useAppSelector(state => state.themeReducer.mode); - - // handle selected filters count - const handleSelectedFiltersCount = (checked: boolean) => { - setSelectedCount(prev => (checked ? prev + 1 : prev - 1)); - }; - - // custom dropdown content - const priorityDropdownContent = ( - - - {props.priorities?.map(item => ( - - - handleSelectedFiltersCount(e.target.checked)} /> - - {item.name} - - - ))} - - - ); - - return ( - priorityDropdownContent} - > - - - ); -}; - -export default PriorityFilterDropdown; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/SearchDropdown.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/SearchDropdown.tsx deleted file mode 100644 index 8eb0d3b0..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/SearchDropdown.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { SearchOutlined } from '@ant-design/icons'; -import { Button, Card, Dropdown, Flex, Input, InputRef, Space } from 'antd'; -import { useRef } from 'react'; -import { useTranslation } from 'react-i18next'; - -const SearchDropdown = () => { - // localization - const { t } = useTranslation('task-list-filters'); - - const searchInputRef = useRef(null); - - const handleSearchInputChange = (e: React.ChangeEvent) => { - const value = e.target.value; - }; - - // custom dropdown content - const searchDropdownContent = ( - - - - - - - - - - ); - - // function to focus search input - const handleSearchDropdownOpen = (open: boolean) => { - if (open) { - setTimeout(() => { - searchInputRef.current?.focus(); - }, 0); - } - }; - - return ( - searchDropdownContent} - onOpenChange={handleSearchDropdownOpen} - > - - - ); -}; - -export default ShowFieldsFilterDropdown; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/SortFilterDropdown.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/SortFilterDropdown.tsx deleted file mode 100644 index 85c2cfd5..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/SortFilterDropdown.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { CaretDownFilled, SortAscendingOutlined, SortDescendingOutlined } from '@ant-design/icons'; -import { Badge, Button, Card, Checkbox, Dropdown, List, Space } from 'antd'; -import React, { useState } from 'react'; -import { colors } from '../../../../../styles/colors'; -import { useTranslation } from 'react-i18next'; -import { useAppSelector } from '@/hooks/useAppSelector'; - -const SortFilterDropdown = () => { - const [selectedCount, setSelectedCount] = useState(0); - const [sortState, setSortState] = useState>({}); - - const themeMode = useAppSelector(state => state.themeReducer.mode); - - // localization - const { t } = useTranslation('task-list-filters'); - - // handle selected filters count - const handleSelectedFiltersCount = (checked: boolean) => { - setSelectedCount(prev => (checked ? prev + 1 : prev - 1)); - }; - - // fuction for handle sort - const handleSort = (key: string) => { - setSortState(prev => ({ - ...prev, - [key]: prev[key] === 'ascending' ? 'descending' : 'ascending', - })); - }; - - // sort dropdown items - type SortFieldsType = { - key: string; - label: string; - }; - - const sortFieldsList: SortFieldsType[] = [ - { key: 'task', label: t('taskText') }, - { key: 'status', label: t('statusText') }, - { key: 'priority', label: t('priorityText') }, - { key: 'startDate', label: t('startDateText') }, - { key: 'endDate', label: t('endDateText') }, - { key: 'completedDate', label: t('completedDateText') }, - { key: 'createdDate', label: t('createdDateText') }, - { key: 'lastUpdated', label: t('lastUpdatedText') }, - ]; - - // custom dropdown content - const sortDropdownContent = ( - - - {sortFieldsList.map(item => ( - - - handleSelectedFiltersCount(e.target.checked)} - /> - {item.label} - - - - ); -}; - -export default SortFilterDropdown; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/TaskListFilters.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/TaskListFilters.tsx deleted file mode 100644 index d794c414..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListFilters/TaskListFilters.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Checkbox, Flex, Typography } from 'antd'; -import SearchDropdown from './SearchDropdown'; -import SortFilterDropdown from './SortFilterDropdown'; -import LabelsFilterDropdown from './LabelsFilterDropdown'; -import MembersFilterDropdown from './MembersFilterDropdown'; -import GroupByFilterDropdown from './GroupByFilterDropdown'; -import ShowFieldsFilterDropdown from './ShowFieldsFilterDropdown'; -import PriorityFilterDropdown from './PriorityFilterDropdown'; -import { useTranslation } from 'react-i18next'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { useEffect } from 'react'; -import { fetchPriorities } from '@/features/taskAttributes/taskPrioritySlice'; -import { fetchLabels } from '@/features/taskAttributes/taskLabelSlice'; -import { useAppDispatch } from '@/hooks/useAppDispatch'; - -interface TaskListFiltersProps { - position: 'board' | 'list'; -} - -const TaskListFilters: React.FC = ({ position }) => { - const { t } = useTranslation('task-list-filters'); - const dispatch = useAppDispatch(); - - // Selectors - const priorities = useAppSelector(state => state.priorityReducer.priorities); - const labels = useAppSelector(state => state.taskLabelsReducer.labels); - - // Fetch initial data - useEffect(() => { - const fetchInitialData = async () => { - if (!priorities.length) { - await dispatch(fetchPriorities()); - } - if (!labels.length) { - await dispatch(fetchLabels()); - } - }; - - fetchInitialData(); - }, [dispatch, priorities.length, labels.length]); - - return ( - - - {/* search dropdown */} - - {/* sort dropdown */} - - {/* prioriy dropdown */} - - {/* labels dropdown */} - - {/* members dropdown */} - - {/* group by dropdown */} - {} - - - {position === 'list' && ( - - - - {t('showArchivedText')} - - {/* show fields dropdown */} - - - )} - - ); -}; - -export default TaskListFilters; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/TaskListTable.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/TaskListTable.tsx deleted file mode 100644 index 218ccc2d..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/TaskListTable.tsx +++ /dev/null @@ -1,425 +0,0 @@ -import { useAppSelector } from '@/hooks/useAppSelector'; -import { columnList } from './columns/columnList'; -import AddTaskListRow from './taskListTableRows/AddTaskListRow'; -import { Checkbox, Flex, Tag, Tooltip } from 'antd'; -import React, { useEffect, useState } from 'react'; -import { useSelectedProject } from '@/hooks/useSelectedProject'; -import TaskCell from './taskListTableCells/TaskCell'; -import AddSubTaskListRow from './taskListTableRows/AddSubTaskListRow'; -import { colors } from '@/styles/colors'; -import TaskContextMenu from './contextMenu/TaskContextMenu'; -import { useAppDispatch } from '@/hooks/useAppDispatch'; -import { deselectAll } from '@features/projects/bulkActions/bulkActionSlice'; -import { useTranslation } from 'react-i18next'; -import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; -import { HolderOutlined } from '@ant-design/icons'; - -const TaskListTable = ({ - taskList, - tableId, -}: { - taskList: IProjectTask[] | null; - tableId: string; -}) => { - // these states manage the necessary states - const [hoverRow, setHoverRow] = useState(null); - const [selectedRows, setSelectedRows] = useState([]); - const [selectedTaskId, setSelectedTaskId] = useState(null); - const [expandedTasks, setExpandedTasks] = useState([]); - const [isSelectAll, setIsSelectAll] = useState(false); - // context menu state - const [contextMenuVisible, setContextMenuVisible] = useState(false); - const [contextMenuPosition, setContextMenuPosition] = useState({ - x: 0, - y: 0, - }); - // state to check scroll - const [scrollingTables, setScrollingTables] = useState<{ - [key: string]: boolean; - }>({}); - - // localization - const { t } = useTranslation('task-list-table'); - - const dispatch = useAppDispatch(); - - // get data theme data from redux - const themeMode = useAppSelector(state => state.themeReducer.mode); - - // get the selected project details - const selectedProject = useSelectedProject(); - - // get columns list details - const columnsVisibility = useAppSelector( state => state.projectViewTaskListColumnsReducer.columnList ); - const visibleColumns = columnList.filter( - column => columnsVisibility[column.key as keyof typeof columnsVisibility] - ); - - // toggle subtasks visibility - const toggleTaskExpansion = (taskId: string) => { - setExpandedTasks(prev => - prev.includes(taskId) ? prev.filter(id => id !== taskId) : [...prev, taskId] - ); - }; - - // toggle all task select when header checkbox click - const toggleSelectAll = () => { - if (isSelectAll) { - setSelectedRows([]); - dispatch(deselectAll()); - } else { - const allTaskIds = - taskList?.flatMap(task => [ - task.id, - ...(task.sub_tasks?.map(subtask => subtask.id) || []), - ]) || []; - - // setSelectedRows(allTaskIds); - // dispatch(selectTaskIds(allTaskIds)); - // console.log('selected tasks and subtasks (all):', allTaskIds); - } - setIsSelectAll(!isSelectAll); - }; - - // toggle selected row - const toggleRowSelection = (task: IProjectTask) => { - setSelectedRows(prevSelectedRows => - prevSelectedRows.includes(task.id || '') - ? prevSelectedRows.filter(id => id !== task.id || '') - : [...prevSelectedRows, task.id || ''] - ); - }; - - // this use effect for realtime update the selected rows - useEffect(() => { - console.log('Selected tasks and subtasks:', selectedRows); - }, [selectedRows]); - - // select one row this triggers only in handle the context menu ==> righ click mouse event - const selectOneRow = (task: IProjectTask) => { - setSelectedRows([task.id || '']); - - // log the task object when selected - if (!selectedRows.includes(task.id || '')) { - console.log('Selected task:', task); - } - }; - - // handle custom task context menu - const handleContextMenu = (e: React.MouseEvent, task: IProjectTask) => { - e.preventDefault(); - setSelectedTaskId(task.id || ''); - selectOneRow(task); - setContextMenuPosition({ x: e.clientX, y: e.clientY }); - setContextMenuVisible(true); - }; - - // trigger the table scrolling - useEffect(() => { - const tableContainer = document.querySelector(`.tasklist-container-${tableId}`); - const handleScroll = () => { - if (tableContainer) { - setScrollingTables(prev => ({ - ...prev, - [tableId]: tableContainer.scrollLeft > 0, - })); - } - }; - tableContainer?.addEventListener('scroll', handleScroll); - return () => tableContainer?.removeEventListener('scroll', handleScroll); - }, [tableId]); - - // layout styles for table and the columns - const customBorderColor = themeMode === 'dark' && ' border-[#303030]'; - - const customHeaderColumnStyles = (key: string) => - `border px-2 text-left ${key === 'selector' && 'sticky left-0 z-10'} ${key === 'task' && `sticky left-[33px] z-10 after:content after:absolute after:top-0 after:-right-1 after:-z-10 after:h-[42px] after:w-1.5 after:bg-transparent ${scrollingTables[tableId] ? 'after:bg-gradient-to-r after:from-[rgba(0,0,0,0.12)] after:to-transparent' : ''}`} ${themeMode === 'dark' ? 'bg-[#1d1d1d] border-[#303030]' : 'bg-[#fafafa]'}`; - - const customBodyColumnStyles = (key: string) => - `border px-2 ${key === 'selector' && 'sticky left-0 z-10'} ${key === 'task' && `sticky left-[33px] z-10 after:content after:absolute after:top-0 after:-right-1 after:-z-10 after:min-h-[40px] after:w-1.5 after:bg-transparent ${scrollingTables[tableId] ? 'after:bg-gradient-to-r after:from-[rgba(0,0,0,0.12)] after:to-transparent' : ''}`} ${themeMode === 'dark' ? 'bg-[#141414] border-[#303030]' : 'bg-white'}`; - - // function to render the column content based on column key - const renderColumnContent = ( - columnKey: string, - task: IProjectTask, - isSubtask: boolean = false - ) => { - switch (columnKey) { - // task ID column - case 'taskId': - return ( - - {task.task_key} - - ); - - // task column - case 'task': - return ( - // custom task cell component - - ); - - // description column - case 'description': - return ( -
- {/* - {task.description || ''} - */} -
- ); - - // progress column - case 'progress': { - return
; - } - - // members column - case 'members': - return
; - - // labels column - case 'labels': - return
; - - // phase column - case 'phases': - return
; - - // status column - case 'status': - return
; - - // priority column - case 'priority': - return
; - - // // time tracking column - // case 'timeTracking': - // return ( - // - // ); - - // estimation column - case 'estimation': - return
; - - // start date column - case 'startDate': - return
; - - // due date column - case 'dueDate': - return
; - - // completed date column - case 'completedDate': - return
; - - // created date column - case 'createdDate': - return
; - - // last updated column - case 'lastUpdated': - return
; - - // recorder column - case 'reporter': - return
; - - // default case for unsupported columns - default: - return null; - } - }; - - return ( -
-
- - - - {/* this cell render the select all task checkbox */} - - {/* other header cells */} - {visibleColumns.map(column => ( - - ))} - - - - {taskList?.map(task => ( - - handleContextMenu(e, task)} - className={`${taskList.length === 0 ? 'h-0' : 'h-[42px]'}`} - > - {/* this cell render the select the related task checkbox */} - - {/* other cells */} - {visibleColumns.map(column => ( - - ))} - - - {/* this is for sub tasks */} - {expandedTasks.includes(task.id || '') && - task?.sub_tasks?.map(subtask => ( - handleContextMenu(e, subtask)} - onMouseEnter={() => setHoverRow(subtask.id || '')} - onMouseLeave={() => setHoverRow(null)} - className={`${taskList.length === 0 ? 'h-0' : 'h-[42px]'}`} - > - {/* this cell render the select the related task checkbox */} - - - {/* other sub tasks cells */} - {visibleColumns.map(column => ( - - ))} - - ))} - - {expandedTasks.includes(task.id || '') && ( - - - - )} - - ))} - -
- - - - - {column.key === 'phases' - ? column.columnHeader - : t(`${column.columnHeader}Column`)} -
- - - toggleRowSelection(task)} - /> - - - {renderColumnContent(column.key, task)} -
- toggleRowSelection(subtask)} - /> - - {renderColumnContent(column.key, subtask, true)} -
- -
-
- - {/* add a main task to the table */} - - - {/* custom task context menu */} - setContextMenuVisible(false)} - /> -
- ); -}; - -export default TaskListTable; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/TaskListTableWrapper.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/TaskListTableWrapper.tsx deleted file mode 100644 index d73c2714..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/TaskListTableWrapper.tsx +++ /dev/null @@ -1,216 +0,0 @@ -import { Badge, Button, Collapse, ConfigProvider, Dropdown, Flex, Input, Typography } from 'antd'; -import { useState } from 'react'; -import { TaskType } from '../../../../../types/task.types'; -import { EditOutlined, EllipsisOutlined, RetweetOutlined, RightOutlined } from '@ant-design/icons'; -import { colors } from '../../../../../styles/colors'; -import './taskListTableWrapper.css'; -import TaskListTable from './TaskListTable'; -import { MenuProps } from 'antd/lib'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { useTranslation } from 'react-i18next'; -import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; - -type TaskListTableWrapperProps = { - taskList: IProjectTask[]; - tableId: string; - type: string; - name: string; - color: string; - statusCategory?: string | null; - priorityCategory?: string | null; - onRename?: (name: string) => void; - onStatusCategoryChange?: (category: string) => void; -}; - -const TaskListTableWrapper = ({ - taskList, - tableId, - name, - type, - color, - statusCategory = null, - priorityCategory = null, - onRename, - onStatusCategoryChange, -}: TaskListTableWrapperProps) => { - const [tableName, setTableName] = useState(name); - const [isRenaming, setIsRenaming] = useState(false); - const [isExpanded, setIsExpanded] = useState(true); - const [currentCategory, setCurrentCategory] = useState(statusCategory); - - // localization - const { t } = useTranslation('task-list-table'); - - // function to handle toggle expand - const handlToggleExpand = () => { - setIsExpanded(!isExpanded); - }; - - const themeMode = useAppSelector(state => state.themeReducer.mode); - - // this is for get the color for every typed tables - const getBgColorClassName = (type: string) => { - switch (type) { - case 'status': - if (currentCategory === 'todo') - return themeMode === 'dark' ? 'after:bg-[#3a3a3a]' : 'after:bg-[#d8d7d8]'; - else if (currentCategory === 'doing') - return themeMode === 'dark' ? 'after:bg-[#3d506e]' : 'after:bg-[#c0d5f6]'; - else if (currentCategory === 'done') - return themeMode === 'dark' ? 'after:bg-[#3b6149]' : 'after:bg-[#c2e4d0]'; - else return themeMode === 'dark' ? 'after:bg-[#3a3a3a]' : 'after:bg-[#d8d7d8]'; - - case 'priority': - if (priorityCategory === 'low') - return themeMode === 'dark' ? 'after:bg-[#3b6149]' : 'after:bg-[#c2e4d0]'; - else if (priorityCategory === 'medium') - return themeMode === 'dark' ? 'after:bg-[#916c33]' : 'after:bg-[#f9e3b1]'; - else if (priorityCategory === 'high') - return themeMode === 'dark' ? 'after:bg-[#8b3a3b]' : 'after:bg-[#f6bfc0]'; - else return themeMode === 'dark' ? 'after:bg-[#916c33]' : 'after:bg-[#f9e3b1]'; - default: - return ''; - } - }; - - // these codes only for status type tables - // function to handle rename this functionality only available for status type tables - const handleRename = () => { - if (onRename) { - onRename(tableName); - } - setIsRenaming(false); - }; - - // function to handle category change - const handleCategoryChange = (category: string) => { - setCurrentCategory(category); - if (onStatusCategoryChange) { - onStatusCategoryChange(category); - } - }; - - // find the available status for the currently active project - const statusList = useAppSelector(state => state.statusReducer.status); - - const getStatusColor = (status: string) => { - switch (status) { - case 'todo': - return '#d8d7d8'; - case 'doing': - return '#c0d5f6'; - case 'done': - return '#c2e4d0'; - default: - return '#d8d7d8'; - } - }; - - // dropdown options - const items: MenuProps['items'] = [ - { - key: '1', - icon: , - label: 'Rename', - onClick: () => setIsRenaming(true), - }, - { - key: '2', - icon: , - label: 'Change category', - children: statusList?.map(status => ({ - key: status.id, - label: ( - handleCategoryChange(status.category)}> - - {status.name} - - ), - })), - }, - ]; - - return ( - - - - - {type === 'status' && !isRenaming && ( - - - ); - }; - - // show expand button on hover for tasks without subtasks - const renderToggleButtonForNonSubtasks = (taskId: string, isSubTask: boolean) => { - return !isSubTask ? ( - - ) : ( -
- ); - }; - - // render the double arrow icon and count label for tasks with subtasks - const renderSubtasksCountLabel = (taskId: string, isSubTask: boolean, subTasksCount: number) => { - return ( - !isSubTask && ( - - ) - ); - }; - - return ( - - - {!!task?.sub_tasks?.length && task.id ? ( - renderToggleButtonForHasSubTasks(task.id, !!task?.sub_tasks?.length) - ) : ( -
- )} - - {isSubTask && } - - {task.name} - - {renderSubtasksCountLabel(task.id || '', isSubTask, task?.sub_tasks?.length || 0)} -
- - -
- ); -}; - -export default TaskCell; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress.css b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress.css deleted file mode 100644 index 64d0697c..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress.css +++ /dev/null @@ -1,22 +0,0 @@ -/* Set the stroke width to 9px for the progress circle */ -.task-progress.ant-progress-circle .ant-progress-circle-path { - stroke-width: 9px !important; /* Adjust the stroke width */ -} - -/* Adjust the inner check mark for better alignment and visibility */ -.task-progress.ant-progress-circle.ant-progress-status-success .ant-progress-inner .anticon-check { - font-size: 8px; /* Adjust font size for the check mark */ - color: green; /* Optional: Set a color */ - transform: translate(-50%, -50%); /* Center align */ - position: absolute; - top: 50%; - left: 50%; - padding: 0; - width: 8px; -} - -/* Adjust the text inside the progress circle */ -.task-progress.ant-progress-circle .ant-progress-text { - font-size: 10px; /* Ensure the text size fits well */ - line-height: 1; -} diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress.tsx deleted file mode 100644 index 24df83f9..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TaskProgress.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Progress, Tooltip } from 'antd'; -import React from 'react'; -import './TaskProgress.css'; - -type TaskProgressProps = { - progress: number; - numberOfSubTasks: number; -}; - -const TaskProgress = ({ progress = 0, numberOfSubTasks = 0 }: TaskProgressProps) => { - const totalTasks = numberOfSubTasks + 1; - const completedTasks = 0; - - const size = progress === 100 ? 21 : 26; - - return ( - - - - ); -}; - -export default TaskProgress; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TimeTracker.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TimeTracker.tsx deleted file mode 100644 index 03496d0a..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableCells/TimeTracker.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import { Divider, Empty, Flex, Popover, Typography } from 'antd'; -import { PlayCircleFilled } from '@ant-design/icons'; -import { colors } from '../../../../../../styles/colors'; -import CustomAvatar from '../../../../../../components/CustomAvatar'; -import { mockTimeLogs } from './mockTimeLogs'; - -type TimeTrackerProps = { - taskId: string | null | undefined; - initialTime?: number; -}; - -const TimeTracker = ({ taskId, initialTime = 0 }: TimeTrackerProps) => { - const minutes = Math.floor(initialTime / 60); - const seconds = initialTime % 60; - const formattedTime = `${minutes}m ${seconds}s`; - - const timeTrackingLogCard = - initialTime > 0 ? ( - - {mockTimeLogs.map(log => ( - - - - - - - {log.username} - {` logged ${log.duration} ${ - log.via ? `via ${log.via}` : '' - }`} - - - {log.date} - - - - - - ))} - - ) : ( - - ); - - return ( - - - - Time Tracking Log - - - } - content={timeTrackingLogCard} - trigger="click" - placement="bottomRight" - > - {formattedTime} - - - ); -}; - -export default TimeTracker; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddSubTaskListRow.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddSubTaskListRow.tsx deleted file mode 100644 index 5fc71fdf..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddSubTaskListRow.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Input } from 'antd'; -import React, { useState } from 'react'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { colors } from '@/styles/colors'; -import { useTranslation } from 'react-i18next'; - -const AddSubTaskListRow = () => { - const [isEdit, setIsEdit] = useState(false); - - // localization - const { t } = useTranslation('task-list-table'); - - // get data theme data from redux - const themeMode = useAppSelector(state => state.themeReducer.mode); - const customBorderColor = themeMode === 'dark' && ' border-[#303030]'; - - return ( -
- {isEdit ? ( - setIsEdit(false)} - /> - ) : ( - setIsEdit(true)} - className="w-[300px] border-none" - value={t('addSubTaskText')} - /> - )} -
- ); -}; - -export default AddSubTaskListRow; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddTaskListRow.tsx b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddTaskListRow.tsx deleted file mode 100644 index fa7e9f85..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableRows/AddTaskListRow.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Input } from 'antd'; -import React, { useState } from 'react'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { colors } from '@/styles/colors'; -import { useTranslation } from 'react-i18next'; - -const AddTaskListRow = () => { - const [isEdit, setIsEdit] = useState(false); - - // localization - const { t } = useTranslation('task-list-table'); - - // get data theme data from redux - const themeMode = useAppSelector(state => state.themeReducer.mode); - const customBorderColor = themeMode === 'dark' && ' border-[#303030]'; - - return ( -
- {isEdit ? ( - setIsEdit(false)} - /> - ) : ( - setIsEdit(true)} - className="w-[300px] border-none" - value={t('addTaskText')} - /> - )} -
- ); -}; - -export default AddTaskListRow; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableWrapper.css b/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableWrapper.css deleted file mode 100644 index 6e5ca7c8..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/taskList/taskListTable/taskListTableWrapper.css +++ /dev/null @@ -1,15 +0,0 @@ -/* custom collapse styles for content box and the left border */ -.ant-collapse-header { - margin-bottom: 6px !important; -} - -.custom-collapse-content-box .ant-collapse-content-box { - padding: 0 !important; -} - -:where(.css-dev-only-do-not-override-1w6wsvq).ant-collapse-ghost - > .ant-collapse-item - > .ant-collapse-content - > .ant-collapse-content-box { - padding: 0; -} diff --git a/worklenz-frontend/src/pages/projects/project-view-1/updates/project-view-updates.css b/worklenz-frontend/src/pages/projects/project-view-1/updates/project-view-updates.css deleted file mode 100644 index 9bf0cab2..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/updates/project-view-updates.css +++ /dev/null @@ -1,19 +0,0 @@ -.mentions-light .mentions { - background-color: #e9e2e2; - font-weight: 500; - border-radius: 4px; - padding: 2px 4px; -} - -.mentions-dark .mentions { - background-color: #2c2c2c; - font-weight: 500; - border-radius: 4px; - padding: 2px 4px; -} - -.tooltip-comment .mentions { - background-color: transparent; - font-weight: 500; - padding: 0; -} diff --git a/worklenz-frontend/src/pages/projects/project-view-1/updates/project-view-updates.tsx b/worklenz-frontend/src/pages/projects/project-view-1/updates/project-view-updates.tsx deleted file mode 100644 index 684cb5f9..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/updates/project-view-updates.tsx +++ /dev/null @@ -1,263 +0,0 @@ -import { Button, ConfigProvider, Flex, Form, Mentions, Skeleton, Space, Tooltip, Typography } from 'antd'; -import { useEffect, useState, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import DOMPurify from 'dompurify'; -import { useParams } from 'react-router-dom'; - -import CustomAvatar from '@components/CustomAvatar'; -import { colors } from '@/styles/colors'; -import { - IMentionMemberSelectOption, - IMentionMemberViewModel, -} from '@/types/project/projectComments.types'; -import { projectsApiService } from '@/api/projects/projects.api.service'; -import { projectCommentsApiService } from '@/api/projects/comments/project-comments.api.service'; -import { IProjectUpdateCommentViewModel } from '@/types/project/project.types'; -import { calculateTimeDifference } from '@/utils/calculate-time-difference'; -import { getUserSession } from '@/utils/session-helper'; -import './project-view-updates.css'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { DeleteOutlined } from '@ant-design/icons'; - -const MAX_COMMENT_LENGTH = 2000; - -const ProjectViewUpdates = () => { - const { projectId } = useParams(); - const [characterLength, setCharacterLength] = useState(0); - const [isCommentBoxExpand, setIsCommentBoxExpand] = useState(false); - const [members, setMembers] = useState([]); - const [selectedMembers, setSelectedMembers] = useState<{ id: string; name: string }[]>([]); - const [comments, setComments] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [isLoadingComments, setIsLoadingComments] = useState(false); - const [isSubmitting, setIsSubmitting] = useState(false); - const [commentValue, setCommentValue] = useState(''); - const theme = useAppSelector(state => state.themeReducer.mode); - const { refreshTimestamp } = useAppSelector(state => state.projectReducer); - - const { t } = useTranslation('project-view-updates'); - const [form] = Form.useForm(); - - const getMembers = useCallback(async () => { - if (!projectId) return; - try { - setIsLoading(true); - const res = await projectCommentsApiService.getMentionMembers(projectId, 1, 15, null, null, null); - if (res.done) { - setMembers(res.body as IMentionMemberViewModel[]); - } - } catch (error) { - console.error('Failed to fetch members:', error); - } finally { - setIsLoading(false); - } - }, [projectId]); - - const getComments = useCallback(async () => { - if (!projectId) return; - try { - setIsLoadingComments(true); - const res = await projectCommentsApiService.getByProjectId(projectId); - if (res.done) { - setComments(res.body); - } - } catch (error) { - console.error('Failed to fetch comments:', error); - } finally { - setIsLoadingComments(false); - } - }, [projectId]); - - const handleAddComment = async () => { - if (!projectId || characterLength === 0) return; - - try { - setIsSubmitting(true); - - if (!commentValue) { - console.error('Comment content is empty'); - return; - } - - const body = { - project_id: projectId, - team_id: getUserSession()?.team_id, - content: commentValue.trim(), - mentions: selectedMembers - }; - - const res = await projectCommentsApiService.createProjectComment(body); - if (res.done) { - await getComments(); - handleCancel(); - } - } catch (error) { - console.error('Failed to add comment:', error); - } finally { - setIsSubmitting(false); - setCommentValue(''); - - - } - }; - - useEffect(() => { - void getMembers(); - void getComments(); - }, [getMembers, getComments,refreshTimestamp]); - - const handleCancel = useCallback(() => { - form.resetFields(['comment']); - setCharacterLength(0); - setIsCommentBoxExpand(false); - setSelectedMembers([]); - }, [form]); - - const mentionsOptions = - members?.map(member => ({ - value: member.id, - label: member.name, - })) ?? []; - - const memberSelectHandler = useCallback((member: IMentionMemberSelectOption) => { - if (!member?.value || !member?.label) return; - setSelectedMembers(prev => - prev.some(mention => mention.id === member.value) - ? prev - : [...prev, { id: member.value, name: member.label }] - ); - - setCommentValue(prev => { - const parts = prev.split('@'); - const lastPart = parts[parts.length - 1]; - const mentionText = member.label; - // Keep only the part before the @ and add the new mention - return prev.slice(0, prev.length - lastPart.length) + mentionText; - }); - }, []); - - const handleCommentChange = useCallback((value: string) => { - // Only update the value without trying to replace mentions - setCommentValue(value); - setCharacterLength(value.trim().length); - }, []); - - const handleDeleteComment = useCallback( - async (commentId: string | undefined) => { - if (!commentId) return; - try { - const res = await projectCommentsApiService.deleteComment(commentId); - if (res.done) { - void getComments(); - } - } catch (error) { - console.error('Failed to delete comment:', error); - } - }, - [getComments] - ); - - return ( - - - { - isLoadingComments ? ( - - ): - comments.map(comment => ( - - - - - - {comment.created_by || ''} - - - - {calculateTimeDifference(comment.created_at || '')} - - - - -
- - - - - - - )} - - - ); -}; - -export default ProjectViewUpdates; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/workload/ProjectViewWorkload.tsx b/worklenz-frontend/src/pages/projects/project-view-1/workload/ProjectViewWorkload.tsx deleted file mode 100644 index c36ea148..00000000 --- a/worklenz-frontend/src/pages/projects/project-view-1/workload/ProjectViewWorkload.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -const ProjectViewWorkload = () => { - return
ProjectViewWorkload
; -}; - -export default ProjectViewWorkload; diff --git a/worklenz-frontend/src/pages/projects/project-view-1/workload/projectViewWorkload.css b/worklenz-frontend/src/pages/projects/project-view-1/workload/projectViewWorkload.css deleted file mode 100644 index e69de29b..00000000 diff --git a/worklenz-frontend/src/pages/projects/projectView/taskList/components/task-group/task-group.tsx b/worklenz-frontend/src/pages/projects/projectView/taskList/components/task-group/task-group.tsx deleted file mode 100644 index e5800fe4..00000000 --- a/worklenz-frontend/src/pages/projects/projectView/taskList/components/task-group/task-group.tsx +++ /dev/null @@ -1,241 +0,0 @@ -import React, { useState, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useDroppable } from '@dnd-kit/core'; -import Flex from 'antd/es/flex'; -import Badge from 'antd/es/badge'; -import Button from 'antd/es/button'; -import Dropdown from 'antd/es/dropdown'; -import Input from 'antd/es/input'; -import Typography from 'antd/es/typography'; -import { MenuProps } from 'antd/es/menu'; -import { EditOutlined, EllipsisOutlined, RetweetOutlined, RightOutlined } from '@ant-design/icons'; - -import { colors } from '@/styles/colors'; -import { useAppSelector } from '@/hooks/useAppSelector'; -import { useAppDispatch } from '@/hooks/useAppDispatch'; -import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; -import { ITaskListGroup } from '@/types/tasks/taskList.types'; -import Collapsible from '@/components/collapsible/collapsible'; -import TaskListTable from '../../task-list-table/task-list-table'; -import { IGroupBy, updateTaskGroupColor } from '@/features/tasks/tasks.slice'; -import { useAuthService } from '@/hooks/useAuth'; -import { statusApiService } from '@/api/taskAttributes/status/status.api.service'; -import { phasesApiService } from '@/api/taskAttributes/phases/phases.api.service'; -import { ITaskPhase } from '@/types/tasks/taskPhase.types'; -import { fetchPhasesByProjectId } from '@/features/projects/singleProject/phase/phases.slice'; -import { fetchStatuses } from '@/features/taskAttributes/taskStatusSlice'; -import { useMixpanelTracking } from '@/hooks/useMixpanelTracking'; -import { evt_project_board_column_setting_click } from '@/shared/worklenz-analytics-events'; -import { ALPHA_CHANNEL } from '@/shared/constants'; -import useIsProjectManager from '@/hooks/useIsProjectManager'; -import logger from '@/utils/errorLogger'; - -interface TaskGroupProps { - taskGroup: ITaskListGroup; - groupBy: string; - color: string; - activeId?: string | null; -} - -const TaskGroup: React.FC = ({ - taskGroup, - groupBy, - color, - activeId -}) => { - const { t } = useTranslation('task-list-table'); - const dispatch = useAppDispatch(); - const { trackMixpanelEvent } = useMixpanelTracking(); - const isProjectManager = useIsProjectManager(); - const currentSession = useAuthService().getCurrentSession(); - - const [isExpanded, setIsExpanded] = useState(true); - const [isRenaming, setIsRenaming] = useState(false); - const [groupName, setGroupName] = useState(taskGroup.name || ''); - - const { projectId } = useAppSelector((state: any) => state.projectReducer); - const themeMode = useAppSelector((state: any) => state.themeReducer.mode); - - // Memoize droppable configuration - const { setNodeRef } = useDroppable({ - id: taskGroup.id, - data: { - type: 'group', - groupId: taskGroup.id, - }, - }); - - // Memoize task count - const taskCount = useMemo(() => taskGroup.tasks?.length || 0, [taskGroup.tasks]); - - // Memoize dropdown items - const dropdownItems: MenuProps['items'] = useMemo(() => { - if (groupBy !== IGroupBy.STATUS || !isProjectManager) return []; - - return [ - { - key: 'rename', - label: t('renameText'), - icon: , - onClick: () => setIsRenaming(true), - }, - { - key: 'change-category', - label: t('changeCategoryText'), - icon: , - children: [ - { - key: 'todo', - label: t('todoText'), - onClick: () => handleStatusCategoryChange('0'), - }, - { - key: 'doing', - label: t('doingText'), - onClick: () => handleStatusCategoryChange('1'), - }, - { - key: 'done', - label: t('doneText'), - onClick: () => handleStatusCategoryChange('2'), - }, - ], - }, - ]; - }, [groupBy, isProjectManager, t]); - - const handleStatusCategoryChange = async (category: string) => { - if (!projectId || !taskGroup.id) return; - - try { - await statusApiService.updateStatus({ - id: taskGroup.id, - category_id: category, - project_id: projectId, - }); - - dispatch(fetchStatuses()); - trackMixpanelEvent(evt_project_board_column_setting_click, { - column_id: taskGroup.id, - action: 'change_category', - category, - }); - } catch (error) { - logger.error('Error updating status category:', error); - } - }; - - const handleRename = async () => { - if (!projectId || !taskGroup.id || !groupName.trim()) return; - - try { - if (groupBy === IGroupBy.STATUS) { - await statusApiService.updateStatus({ - id: taskGroup.id, - name: groupName.trim(), - project_id: projectId, - }); - dispatch(fetchStatuses()); - } else if (groupBy === IGroupBy.PHASE) { - const phaseData: ITaskPhase = { - id: taskGroup.id, - name: groupName.trim(), - project_id: projectId, - color_code: taskGroup.color_code, - }; - await phasesApiService.updatePhase(phaseData); - dispatch(fetchPhasesByProjectId(projectId)); - } - - setIsRenaming(false); - } catch (error) { - logger.error('Error renaming group:', error); - } - }; - - const handleColorChange = async (newColor: string) => { - if (!projectId || !taskGroup.id) return; - - try { - const baseColor = newColor.endsWith(ALPHA_CHANNEL) - ? newColor.slice(0, -ALPHA_CHANNEL.length) - : newColor; - - if (groupBy === IGroupBy.PHASE) { - const phaseData: ITaskPhase = { - id: taskGroup.id, - name: taskGroup.name || '', - project_id: projectId, - color_code: baseColor, - }; - await phasesApiService.updatePhase(phaseData); - dispatch(fetchPhasesByProjectId(projectId)); - } - - dispatch(updateTaskGroupColor({ - groupId: taskGroup.id, - color: baseColor, - })); - } catch (error) { - logger.error('Error updating group color:', error); - } - }; - - return ( -
- - {/* Group Header */} - - - - {dropdownItems.length > 0 && !isRenaming && ( - -
- ); -}; - -export default React.memo(TaskGroup); \ No newline at end of file diff --git a/worklenz-frontend/src/pages/settings/project-templates/projectTemplateEditView/ProjectTemplateEditView.tsx b/worklenz-frontend/src/pages/settings/project-templates/projectTemplateEditView/ProjectTemplateEditView.tsx index de56a6e8..ba1d7b03 100644 --- a/worklenz-frontend/src/pages/settings/project-templates/projectTemplateEditView/ProjectTemplateEditView.tsx +++ b/worklenz-frontend/src/pages/settings/project-templates/projectTemplateEditView/ProjectTemplateEditView.tsx @@ -1,18 +1,19 @@ import { Button, Flex, Select, Typography } from 'antd'; import { useState } from 'react'; -import StatusGroupTables from '../../../projects/project-view-1/taskList/statusTables/StatusGroupTables'; +import TaskGroupList from '@/pages/projects/projectView/taskList/groupTables/TaskGroupList'; import { TaskType } from '../../../../types/task.types'; import { useAppSelector } from '../../../../hooks/useAppSelector'; import { PageHeader } from '@ant-design/pro-components'; import { ArrowLeftOutlined, CaretDownFilled } from '@ant-design/icons'; import { useNavigate, useParams } from 'react-router-dom'; -import SearchDropdown from '../../../projects/project-view-1/taskList/taskListFilters/SearchDropdown'; +import TaskListFilters from '@/pages/projects/projectView/taskList/task-list-filters/task-list-filters'; import { useSelectedProject } from '../../../../hooks/useSelectedProject'; import { useTranslation } from 'react-i18next'; import { toggleDrawer as togglePhaseDrawer } from '../../../../features/projects/singleProject/phase/phases.slice'; import { toggleDrawer } from '../../../../features/projects/status/StatusSlice'; import { useAppDispatch } from '../../../../hooks/useAppDispatch'; import React from 'react'; +import { ITaskListGroup } from '@/types/tasks/taskList.types'; const PhaseDrawer = React.lazy(() => import('@features/projects/singleProject/phase/PhaseDrawer')); const StatusDrawer = React.lazy( @@ -20,7 +21,8 @@ const StatusDrawer = React.lazy( ); const ProjectTemplateEditView = () => { - const dataSource: TaskType[] = useAppSelector(state => state.taskReducer.tasks); + const dataSource: ITaskListGroup[] = useAppSelector(state => state.taskReducer.taskGroups); + const groupBy = useAppSelector(state => state.taskReducer.groupBy); const dispatch = useAppDispatch(); const navigate = useNavigate(); const { templateId, templateName } = useParams(); @@ -40,7 +42,7 @@ const ProjectTemplateEditView = () => { //get phases details from phases slice const phase = useAppSelector(state => state.phaseReducer.phaseList).find( - phase => phase.projectId === selectedProject?.id + phase => phase.id === selectedProject?.id ) || null; const groupDropdownMenuItems = [ @@ -49,7 +51,7 @@ const ProjectTemplateEditView = () => { { key: 'phase', value: 'phase', - label: phase ? phase?.phase : t('phaseText'), + label: phase ? phase?.name : t('phaseText'), }, ]; return ( @@ -68,7 +70,7 @@ const ProjectTemplateEditView = () => { /> - + {t('groupByText')}: