feat(lists): make the most relevant columns sortable on the new tables

Mailing Lists, Roles, and Domains got new/existing columns in prior
commits but no way to sort them — the account-client-sort mechanism
was only wired up in accountColumns.ts. Extracted its one-line
clientSortable() column tagger into schemaDeviationTypes.ts (shared,
same intersection-type pattern) and used it in all four column-patch
files:

- Mailing Lists: Email Address, Description, Aliases
- Roles: Description, Enabled Permissions, Disabled Permissions
- Domains: Domain Name, Enabled, Aliases

Re-verified server sort support live, per list this time rather than
just Accounts: every property tried returns unsupportedSort except one
surprise — x:Domain/query actually accepts sort by "name", despite the
schema not declaring it. Documented in SCHEMA_DEVIATIONS.md and
deliberately routed through the same client-sort path as everything
else instead of adding a one-off "trust an undeclared sort" mechanism
for that single case.

Verified in the running dev server: sort indicators appear on the
intended columns for all three lists, clicking reorders rows correctly
(alphabetical on Mailing Lists' Email Address, numeric on Roles'
Enabled Permissions: 1 -> 3 -> 50 -> 229 -> 244 -> 452), Domains sorts
without error. No regression on Accounts/Groups.
This commit is contained in:
Steven RYDELL
2026-08-01 22:31:25 +02:00
parent 26a3878faa
commit 410a01700b
6 changed files with 50 additions and 14 deletions
+1 -5
View File
@@ -5,7 +5,7 @@
*/
import type { Schema } from '@/types/schema';
import type { ClientSortableColumn } from './schemaDeviationTypes';
import { clientSortable as sortable } from './schemaDeviationTypes';
/**
* SCHEMA-DEVIATION: account-quota-usage-column (see SCHEMA_DEVIATIONS.md)
@@ -35,10 +35,6 @@ import type { ClientSortableColumn } from './schemaDeviationTypes';
* itself, the same way it already resolves `quotaUsage`/`aliasCount`,
* instead of hardcoding which lists/columns support it.
*/
function sortable(column: { name: string; label: string }): ClientSortableColumn {
return { ...column, clientSortable: true };
}
export function withAccountListColumns(schema: Schema): Schema {
let lists = schema.lists;
+17 -1
View File
@@ -5,6 +5,7 @@
*/
import type { Schema } from '@/types/schema';
import { clientSortable } from './schemaDeviationTypes';
/**
* SCHEMA-DEVIATION: account-alias-count-column (see SCHEMA_DEVIATIONS.md)
@@ -14,16 +15,31 @@ import type { Schema } from '@/types/schema';
* `aliasCount` column already used on Accounts/Groups/Mailing Lists —
* DynamicList's generic count-column handling picks it up with no
* further wiring.
*
* SCHEMA-DEVIATION: account-client-sort (see SCHEMA_DEVIATIONS.md)
*
* Domain Name, Enabled, and `aliasCount` are tagged `clientSortable`.
* `name` is actually accepted by the live server's `sort` (unlike every
* other property tried on every other list — see SCHEMA_DEVIATIONS.md),
* but the schema still doesn't declare it in `list.sort`, so it's routed
* through the same client-sort mechanism as the rest for consistency
* rather than adding a second, one-off "trust an undeclared sort"
* pathway for a single column.
*/
export function withDomainColumns(schema: Schema): Schema {
const list = schema.lists['x:Domain'];
if (!list) return schema;
const columns = [
...list.columns.map((c) => (c.name === 'name' || c.name === 'isEnabled' ? clientSortable(c) : c)),
clientSortable({ name: 'aliasCount', label: 'Aliases' }),
];
return {
...schema,
lists: {
...schema.lists,
'x:Domain': { ...list, columns: [...list.columns, { name: 'aliasCount', label: 'Aliases' }] },
'x:Domain': { ...list, columns },
},
};
}
+13 -1
View File
@@ -5,6 +5,7 @@
*/
import type { Schema } from '@/types/schema';
import { clientSortable } from './schemaDeviationTypes';
/**
* SCHEMA-DEVIATION: account-alias-count-column (see SCHEMA_DEVIATIONS.md)
@@ -13,16 +14,27 @@ import type { Schema } from '@/types/schema';
* full `aliases` objectList on the detail view. Adds the same synthetic
* `aliasCount` column already used on Accounts/Groups — DynamicList's
* generic count-column handling picks it up with no further wiring.
*
* SCHEMA-DEVIATION: account-client-sort (see SCHEMA_DEVIATIONS.md)
*
* Email Address, Description, and `aliasCount` are tagged
* `clientSortable` — same reasoning as Accounts/Groups: the server
* doesn't support sorting on any `x:MailingList` property either.
*/
export function withMailingListColumns(schema: Schema): Schema {
const list = schema.lists['x:MailingList'];
if (!list) return schema;
const columns = [
...list.columns.map((c) => (c.name === 'emailAddress' || c.name === 'description' ? clientSortable(c) : c)),
clientSortable({ name: 'aliasCount', label: 'Aliases' }),
];
return {
...schema,
lists: {
...schema.lists,
'x:MailingList': { ...list, columns: [...list.columns, { name: 'aliasCount', label: 'Aliases' }] },
'x:MailingList': { ...list, columns },
},
};
}
+10 -3
View File
@@ -5,6 +5,7 @@
*/
import type { Schema } from '@/types/schema';
import { clientSortable } from './schemaDeviationTypes';
/**
* SCHEMA-DEVIATION: role-permission-count-columns (see SCHEMA_DEVIATIONS.md)
@@ -14,6 +15,12 @@ import type { Schema } from '@/types/schema';
* synthetic columns — DynamicList's generic count-column handling
* resolves them from the real `enabledPermissions`/`disabledPermissions`
* set properties and renders their entry counts.
*
* SCHEMA-DEVIATION: account-client-sort (see SCHEMA_DEVIATIONS.md)
*
* Description and both permission-count columns are tagged
* `clientSortable` — same reasoning as Accounts/Groups: the server
* doesn't support sorting on any `x:Role` property either.
*/
export function withRoleListColumns(schema: Schema): Schema {
const list = schema.lists['x:Role'];
@@ -26,9 +33,9 @@ export function withRoleListColumns(schema: Schema): Schema {
'x:Role': {
...list,
columns: [
...list.columns,
{ name: 'enabledPermissionCount', label: 'Enabled Permissions' },
{ name: 'disabledPermissionCount', label: 'Disabled Permissions' },
...list.columns.map((c) => (c.name === 'description' ? clientSortable(c) : c)),
clientSortable({ name: 'enabledPermissionCount', label: 'Enabled Permissions' }),
clientSortable({ name: 'disabledPermissionCount', label: 'Disabled Permissions' }),
],
},
},
+5
View File
@@ -30,3 +30,8 @@ export type ClientSortableColumn = Column & { clientSortable?: boolean };
export function isClientSortableColumn(c: Column): boolean {
return (c as ClientSortableColumn).clientSortable === true;
}
/** Tags a column `clientSortable`, for schema patches that opt a column into account-client-sort. */
export function clientSortable(column: Column): ClientSortableColumn {
return { ...column, clientSortable: true };
}