refactor table sorting to composables and shared SortableTableHeaderCell

This commit is contained in:
Gregor Vostrak
2026-08-26 14:46:00 +02:00
parent d29f5706ce
commit 0e9f652d84
24 changed files with 596 additions and 611 deletions

View File

@@ -11,8 +11,8 @@ import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue'
import PageTitle from '@/Components/Common/PageTitle.vue';
import { canCreateClients } from '@/utils/permissions';
import { TabBar, TabBarItem } from '@/packages/ui/src';
import { useStorage } from '@vueuse/core';
import type { SortColumn, SortDirection } from '@/Components/Common/Client/ClientTable.vue';
import { useTableSortState } from '@/utils/useTableSortState';
import type { SortColumn } from '@/Components/Common/Client/ClientTable.vue';
const { clients } = useClientsQuery();
@@ -20,25 +20,10 @@ const activeTab = ref<'active' | 'archived'>('active');
const createClient = ref(false);
interface ClientTableState {
sortColumn: SortColumn;
sortDirection: SortDirection;
}
const tableState = useStorage<ClientTableState>(
'client-table-state',
{
sortColumn: 'name',
sortDirection: 'asc',
},
undefined,
{ mergeDefaults: true }
);
function handleSort(column: SortColumn, direction: SortDirection) {
tableState.value.sortColumn = column;
tableState.value.sortDirection = direction;
}
const { tableState, handleSort } = useTableSortState<SortColumn>('client-table-state', {
sortColumn: 'name',
sortDirection: 'asc',
});
const shownClients = computed(() => {
return clients.value.filter((client) => {

View File

@@ -12,8 +12,8 @@ import type { Role } from '@/types/jetstream';
import PageTitle from '@/Components/Common/PageTitle.vue';
import InvitationTable from '@/Components/Common/Invitation/InvitationTable.vue';
import { canCreateInvitations } from '@/utils/permissions';
import { useStorage } from '@vueuse/core';
import type { SortColumn, SortDirection } from '@/Components/Common/Member/MemberTable.vue';
import { useTableSortState } from '@/utils/useTableSortState';
import type { SortColumn } from '@/Components/Common/Member/MemberTable.vue';
const inviteMember = ref(false);
@@ -23,25 +23,10 @@ defineProps<{
const activeTab = ref<'all' | 'invitations'>('all');
interface MemberTableState {
sortColumn: SortColumn;
sortDirection: SortDirection;
}
const tableState = useStorage<MemberTableState>(
'member-table-state',
{
sortColumn: 'name',
sortDirection: 'asc',
},
undefined,
{ mergeDefaults: true }
);
function handleSort(column: SortColumn, direction: SortDirection) {
tableState.value.sortColumn = column;
tableState.value.sortDirection = direction;
}
const { tableState, handleSort } = useTableSortState<SortColumn>('member-table-state', {
sortColumn: 'name',
sortDirection: 'asc',
});
</script>
<template>

View File

@@ -15,7 +15,7 @@ import {
import { Link } from '@inertiajs/vue3';
import TaskCreateModal from '@/Components/Common/Task/TaskCreateModal.vue';
import TaskTable from '@/Components/Common/Task/TaskTable.vue';
import type { SortColumn, SortDirection } from '@/Components/Common/Task/TaskTable.vue';
import type { SortColumn } from '@/Components/Common/Task/TaskTable.vue';
import CardTitle from '@/packages/ui/src/CardTitle.vue';
import Card from '@/Components/Common/Card.vue';
import ProjectMemberTable from '@/Components/Common/ProjectMember/ProjectMemberTable.vue';
@@ -30,7 +30,7 @@ import { formatCents } from '../packages/ui/src/utils/money';
import { getOrganizationCurrencyString } from '../utils/money';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { getCurrentOrganizationId } from '@/utils/useUser';
import { useStorage } from '@vueuse/core';
import { useTableSortState } from '@/utils/useTableSortState';
const { projects } = useProjectsQuery();
@@ -67,25 +67,10 @@ const activeTab = ref<'active' | 'done'>('active');
const { tasks } = useTasksQuery();
interface TaskTableState {
sortColumn: SortColumn;
sortDirection: SortDirection;
}
const tableState = useStorage<TaskTableState>(
'task-table-state',
{
sortColumn: 'name',
sortDirection: 'asc',
},
undefined,
{ mergeDefaults: true }
);
function handleSort(column: SortColumn, direction: SortDirection) {
tableState.value.sortColumn = column;
tableState.value.sortDirection = direction;
}
const { tableState, handleSort } = useTableSortState<SortColumn>('task-table-state', {
sortColumn: 'name',
sortDirection: 'asc',
});
const shownTasks = computed(() => {
return tasks.value.filter((task) => {

View File

@@ -18,6 +18,7 @@ import { getCurrentOrganizationId, getCurrentRole } from '@/utils/useUser';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { isAllowedToPerformPremiumAction } from '@/utils/billing';
import { useStorage } from '@vueuse/core';
import { useTableSortState } from '@/utils/useTableSortState';
import ProjectsFilterDropdown from '@/Components/Common/Project/ProjectsFilterDropdown.vue';
import ProjectStatusFilterBadge from '@/Components/Common/Project/ProjectStatusFilterBadge.vue';
import ProjectVisibilityFilterBadge from '@/Components/Common/Project/ProjectVisibilityFilterBadge.vue';
@@ -41,7 +42,7 @@ interface ProjectTableState {
};
}
const tableState = useStorage<ProjectTableState>(
const { tableState, handleSort } = useTableSortState<SortColumn, ProjectTableState>(
'project-table-state',
{
sortColumn: 'name',
@@ -52,21 +53,15 @@ const tableState = useStorage<ProjectTableState>(
visibility: 'all',
},
},
undefined,
{
mergeDefaults: (storage, defaults) => ({
...defaults,
...storage,
filters: { ...defaults.filters, ...storage.filters },
}),
}
// The filters are merged key by key so a stored value missing a newer filter still
// picks up its default instead of the whole object falling back.
(storage, defaults) => ({
...defaults,
...storage,
filters: { ...defaults.filters, ...storage.filters },
})
);
function handleSort(column: SortColumn, direction: SortDirection) {
tableState.value.sortColumn = column;
tableState.value.sortDirection = direction;
}
// Filter projects based on current filters
const filteredProjects = computed(() => {
return projects.value.filter((project) => {

View File

@@ -9,30 +9,15 @@ import TagCreateModal from '@/packages/ui/src/Tag/TagCreateModal.vue';
import PageTitle from '@/Components/Common/PageTitle.vue';
import { canCreateTags } from '@/utils/permissions';
import { useTagsStore } from '@/utils/useTags';
import { useStorage } from '@vueuse/core';
import type { SortColumn, SortDirection } from '@/Components/Common/Tag/TagTable.vue';
import { useTableSortState } from '@/utils/useTableSortState';
import type { SortColumn } from '@/Components/Common/Tag/TagTable.vue';
const showCreateTagModal = ref(false);
interface TagTableState {
sortColumn: SortColumn;
sortDirection: SortDirection;
}
const tableState = useStorage<TagTableState>(
'tag-table-state',
{
sortColumn: 'name',
sortDirection: 'asc',
},
undefined,
{ mergeDefaults: true }
);
function handleSort(column: SortColumn, direction: SortDirection) {
tableState.value.sortColumn = column;
tableState.value.sortDirection = direction;
}
const { tableState, handleSort } = useTableSortState<SortColumn>('tag-table-state', {
sortColumn: 'name',
sortDirection: 'asc',
});
async function createTag(tag: string) {
return await useTagsStore().createTag(tag);