feat(accounts): add Aliases count column to Accounts and Groups

Same pattern as the existing quotaUsage deviation: neither list's
schema exposes an alias count as a column, only the full `aliases`
objectList on the detail view. Added a synthetic `aliasCount` column
(SCHEMA-DEVIATION: account-alias-count-column, documented in
SCHEMA_DEVIATIONS.md) that DynamicList resolves by fetching the real
`aliases` property and counting its entries.

Verified against a live test server: adding a real alias to an
account correctly bumps its Aliases count from 0 to 1 in the list.
This commit is contained in:
Steven RYDELL
2026-08-01 20:18:01 +02:00
parent 0aa92c394c
commit 3241dea5e4
3 changed files with 41 additions and 2 deletions
+22 -1
View File
@@ -503,6 +503,8 @@ export function DynamicList({ viewName }: DynamicListProps) {
// (see withAccountListColumns, account-quota-usage-column deviation)
// include the synthetic `quotaUsage` column gets it resolved and rendered.
const hasQuotaUsageColumn = (resolved?.list?.columns ?? []).some((c) => c.name === 'quotaUsage');
// SCHEMA-DEVIATION: account-alias-count-column (see SCHEMA_DEVIATIONS.md)
const hasAliasCountColumn = (resolved?.list?.columns ?? []).some((c) => c.name === 'aliasCount');
const displayColumns = useMemo(() => {
const columns = resolved?.list?.columns ?? [];
@@ -671,6 +673,12 @@ export function DynamicList({ viewName }: DynamicListProps) {
properties.splice(quotaIdx, 1, 'usedDiskQuota', 'quotas');
}
}
if (hasAliasCountColumn) {
const aliasIdx = properties.indexOf('aliasCount');
if (aliasIdx !== -1) {
properties.splice(aliasIdx, 1, 'aliases');
}
}
if (isMailboxList && !properties.includes('parentId')) {
properties.push('parentId');
}
@@ -755,7 +763,18 @@ export function DynamicList({ viewName }: DynamicListProps) {
setLoading(false);
}
},
[resolved, schema, buildFilter, buildSort, isWebApplications, isAccountsList, hasQuotaUsageColumn, isMailboxList, activeClientFilters],
[
resolved,
schema,
buildFilter,
buildSort,
isWebApplications,
isAccountsList,
hasQuotaUsageColumn,
hasAliasCountColumn,
isMailboxList,
activeClientFilters,
],
);
useEffect(() => {
@@ -1680,6 +1699,8 @@ export function DynamicList({ viewName }: DynamicListProps) {
formatUserRole(item, schema!)
) : hasQuotaUsageColumn && col.name === 'quotaUsage' ? (
renderQuotaUsage(item, t)
) : hasAliasCountColumn && col.name === 'aliasCount' ? (
Object.keys((item.aliases as Record<string, unknown>) ?? {}).length
) : isMailboxList && col.name === 'name' ? (
(() => {
const depth = mailboxDepths.get(item.id as string) ?? 0;
+12 -1
View File
@@ -19,6 +19,12 @@ import type { Schema } from '@/types/schema';
* The Groups list gets the same `quotaUsage` column (groups have their own
* `usedDiskQuota`/`quotas`, same as users), but not `roles` — that column
* is specific to the Users list.
*
* SCHEMA-DEVIATION: account-alias-count-column (see SCHEMA_DEVIATIONS.md)
*
* Both lists also get a synthetic `aliasCount` column — DynamicList
* resolves it from the real `aliases` objectList property (an id-keyed
* map, per JMAP's objectList wire format) and renders its entry count.
*/
export function withAccountListColumns(schema: Schema): Schema {
let lists = schema.lists;
@@ -29,13 +35,18 @@ export function withAccountListColumns(schema: Schema): Schema {
...userList.columns.filter((c) => c.name !== 'createdAt'),
{ name: 'roles', label: 'Role' },
{ name: 'quotaUsage', label: 'Usage / Quota' },
{ name: 'aliasCount', label: 'Aliases' },
];
lists = { ...lists, 'x:Account/User': { ...userList, columns } };
}
const groupList = lists['x:Account/Group'];
if (groupList) {
const columns = [...groupList.columns, { name: 'quotaUsage', label: 'Usage / Quota' }];
const columns = [
...groupList.columns,
{ name: 'quotaUsage', label: 'Usage / Quota' },
{ name: 'aliasCount', label: 'Aliases' },
];
lists = { ...lists, 'x:Account/Group': { ...groupList, columns } };
}