From 398a81798bedf3176435aca40b66a460dfe3fb67 Mon Sep 17 00:00:00 2001 From: Gregor Vostrak Date: Mon, 28 Sep 2026 17:18:40 +0200 Subject: [PATCH] move project task count into its own sortable column Also removes the ring around the project color dot, shrinks it slightly and tightens its spacing to the name. --- .../Common/Project/ProjectTable.vue | 27 +++++++++++++++++-- .../Common/Project/ProjectTableHeading.vue | 3 +++ .../Common/Project/ProjectTableRow.vue | 16 ++++++----- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/resources/js/Components/Common/Project/ProjectTable.vue b/resources/js/Components/Common/Project/ProjectTable.vue index 148beca5..bdb73edc 100644 --- a/resources/js/Components/Common/Project/ProjectTable.vue +++ b/resources/js/Components/Common/Project/ProjectTable.vue @@ -10,13 +10,21 @@ import Pagination from '@/packages/ui/src/Pagination.vue'; import LoadingSpinner from '@/packages/ui/src/LoadingSpinner.vue'; export type SortColumn = - 'name' | 'client_name' | 'spent_time' | 'progress' | 'billable_rate' | 'status' | 'visibility'; + | 'name' + | 'tasks' + | 'client_name' + | 'spent_time' + | 'progress' + | 'billable_rate' + | 'status' + | 'visibility'; export type { SortDirection } from '@/utils/useSortableTable'; import { canCreateProjects } from '@/utils/permissions'; import type { CreateProjectBody, Project, Client, CreateClientBody } from '@/packages/api/src'; import { useProjectsStore } from '@/utils/useProjects'; import { useClientsStore } from '@/utils/useClients'; import { useClientsQuery } from '@/utils/useClientsQuery'; +import { useTasksQuery } from '@/utils/useTasksQuery'; import { getOrganizationCurrencyString } from '@/utils/money'; import { isAllowedToPerformPremiumAction } from '@/utils/billing'; import { useOrganizationQuery } from '@/utils/useOrganizationQuery'; @@ -56,6 +64,16 @@ const clientNameMap = computed(() => { return map; }); +const { tasks } = useTasksQuery(); + +const taskCountMap = computed(() => { + const map = new Map(); + tasks.value.forEach((task) => { + map.set(task.project_id, (map.get(task.project_id) ?? 0) + 1); + }); + return map; +}); + // Define column accessors for sorting. // Numeric columns use sortDescFirst so that the first click (chevron down) sorts highest-first, // while text columns default to ascending (A-Z) on first click (chevron down). @@ -64,6 +82,11 @@ const columns = computed[]>(() => [ id: 'name', accessorFn: (row: Project) => row.name.toLowerCase(), }, + { + id: 'tasks', + sortDescFirst: true, + accessorFn: (row: Project) => taskCountMap.value.get(row.id) ?? 0, + }, { id: 'client_name', accessorFn: (row: Project) => { @@ -161,7 +184,7 @@ async function createClient(client: CreateClientBody): Promise { - return `grid-template-columns: minmax(300px, 1fr) minmax(150px, auto) minmax(140px, auto) minmax(130px, auto) ${props.showBillableRate ? 'minmax(130px, auto)' : ''} minmax(120px, auto) minmax(120px, auto) 80px;`; + return `grid-template-columns: minmax(300px, 1fr) minmax(100px, auto) minmax(150px, auto) minmax(140px, auto) minmax(130px, auto) ${props.showBillableRate ? 'minmax(130px, auto)' : ''} minmax(120px, auto) minmax(120px, auto) 80px;`; }); diff --git a/resources/js/Components/Common/Project/ProjectTableHeading.vue b/resources/js/Components/Common/Project/ProjectTableHeading.vue index 9d4c295d..13ee6e81 100644 --- a/resources/js/Components/Common/Project/ProjectTableHeading.vue +++ b/resources/js/Components/Common/Project/ProjectTableHeading.vue @@ -36,6 +36,9 @@ function handleSort(column: SortColumn) { @sort="handleSort"> Name + + Tasks + Client diff --git a/resources/js/Components/Common/Project/ProjectTableRow.vue b/resources/js/Components/Common/Project/ProjectTableRow.vue index 5be8d918..943b29b3 100644 --- a/resources/js/Components/Common/Project/ProjectTableRow.vue +++ b/resources/js/Components/Common/Project/ProjectTableRow.vue @@ -88,17 +88,19 @@ const showEditProjectModal = ref(false);
+ class="whitespace-nowrap min-w-0 flex items-center space-x-3 py-4 pr-3 text-sm font-medium text-text-primary pl-2 sm:pl-4 lg:pl-6">
+ :style="{ backgroundColor: project.color }" + class="w-2.5 h-2.5 ml-1 rounded-full">
{{ project.name }} - {{ projectTasksCount }} Tasks + +
+ + {{ projectTasksCount }} {{ projectTasksCount === 1 ? 'Task' : 'Tasks' }} + + --