Skip to content

enable sorting for all table category#1620

Merged
Artuomka merged 2 commits into
mainfrom
backend_table_categories_sorting_fix
Feb 20, 2026
Merged

enable sorting for all table category#1620
Artuomka merged 2 commits into
mainfrom
backend_table_categories_sorting_fix

Conversation

@Artuomka

Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings February 20, 2026 08:43
@Artuomka Artuomka enabled auto-merge February 20, 2026 08:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request enables user-defined sorting for the "All tables" category in the v2 table categories endpoint. The implementation introduces a special category identifier 'all-tables-kitten' that allows storing custom table ordering preferences, replacing the previous behavior where the "All tables" category had a null category_id.

Changes:

  • Modified v2 endpoint to use 'all-tables-kitten' as a persistable category ID instead of null
  • Implemented logic to preserve user-defined table order and automatically append newly discovered tables
  • Added comprehensive tests validating order persistence and dynamic table detection

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
backend/test/ava-tests/saas-tests/table-categories-e2e.test.ts Updated existing test assertion and added two new tests covering order preservation and new table appending scenarios
backend/src/entities/table-categories/use-cases/find-table-categories-with-tables.use.case.ts Implemented logic to retrieve stored table order, detect new tables, and persist updated ordering
backend/src/entities/table-categories/use-cases/create-or-update-table-categories.use.case.ts Changed filtering logic to handle 'all-tables-kitten' as a persistable category rather than excluding it

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const allTableCategory: FoundTableCategoriesWithTablesRo = {
category_id: null,
category_color: null,
category_id: 'all-tables-kitten',

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The identifier 'all-tables-kitten' is an unusual naming choice for a system-level constant. Consider using a more conventional identifier like 'all-tables', 'all', or '_all_tables' that better reflects its special system status. The '-kitten' suffix appears arbitrary and may confuse developers unfamiliar with the codebase context.

Copilot uses AI. Check for mistakes.
Comment on lines +93 to +115
const storedAllTablesCategory = foundTableCategories.find((category) => category.category_id === 'all-tables-kitten');
const otherCategories = foundTableCategories.filter((category) => category.category_id !== 'all-tables-kitten');

let allTablesOrdered: Array<FoundTableDs>;
if (storedAllTablesCategory) {
allTablesOrdered = storedAllTablesCategory.tables
.map((tableName) => tablesRO.find((tableRO) => tableRO.table === tableName))
.filter(Boolean);

const storedTableNames = new Set(storedAllTablesCategory.tables);
const newTables = tablesRO.filter((tableRO) => !storedTableNames.has(tableRO.table));

if (newTables.length > 0) {
allTablesOrdered = [...allTablesOrdered, ...newTables];
storedAllTablesCategory.tables = allTablesOrdered.map((t) => t.table);
await this._dbContext.tableCategoriesRepository.save(storedAllTablesCategory);
}
} else {
allTablesOrdered = tablesRO;
}

const allTableCategory: FoundTableCategoriesWithTablesRo = {
category_id: null,
category_color: null,
category_id: 'all-tables-kitten',

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic string 'all-tables-kitten' is duplicated across multiple files (in this file at lines 93, 94, 115, and in create-or-update-table-categories.use.case.ts at lines 26, 27). Consider extracting this to a shared constant to ensure consistency and make future changes easier. For example, define it as a constant like ALL_TABLES_CATEGORY_ID in a shared constants file.

Copilot uses AI. Check for mistakes.

const createdCategories = filteredCategories.map((category) => {
const categoriesToSave = [...filteredCategories];
if (allTablesCategory) {

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The all-tables-kitten category bypasses validation (line 34 validates only filteredCategories). This means invalid table names could be stored in all-tables-kitten without validation. While the find use case filters out non-existent tables at runtime, consider whether basic validation (non-empty category_name, tables as array) should still apply to all-tables-kitten for data integrity.

Suggested change
if (allTablesCategory) {
if (allTablesCategory) {
if (
typeof allTablesCategory.category_name !== 'string' ||
allTablesCategory.category_name.trim().length === 0 ||
!Array.isArray(allTablesCategory.tables)
) {
throw new Error('Invalid all-tables-kitten category payload');
}

Copilot uses AI. Check for mistakes.
category_color: null,
category_id: 'all-tables-kitten',
category_color: storedAllTablesCategory?.category_color ?? null,
category_name: 'All tables',

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The category_name is hardcoded to 'All tables' regardless of what name is stored in the database for the all-tables-kitten category. This means if a user creates an all-tables-kitten category with a different name, it will be silently changed to 'All tables' in the response. Consider whether this behavior should validate and enforce the name during creation, or document this override behavior clearly.

Suggested change
category_name: 'All tables',
category_name: storedAllTablesCategory?.category_name ?? 'All tables',

Copilot uses AI. Check for mistakes.
Comment on lines +97 to +109
if (storedAllTablesCategory) {
allTablesOrdered = storedAllTablesCategory.tables
.map((tableName) => tablesRO.find((tableRO) => tableRO.table === tableName))
.filter(Boolean);

const storedTableNames = new Set(storedAllTablesCategory.tables);
const newTables = tablesRO.filter((tableRO) => !storedTableNames.has(tableRO.table));

if (newTables.length > 0) {
allTablesOrdered = [...allTablesOrdered, ...newTables];
storedAllTablesCategory.tables = allTablesOrdered.map((t) => t.table);
await this._dbContext.tableCategoriesRepository.save(storedAllTablesCategory);
}

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic handles adding new tables but does not handle removing deleted tables from the stored order. If a table is deleted from the database but still exists in storedAllTablesCategory.tables, it will be filtered out by filter(Boolean) in line 100, but the database will not be updated to reflect this change. This can lead to the stored order containing references to non-existent tables that accumulate over time. Consider updating the stored order in the database when tables are removed, similar to how it's done when new tables are added.

Copilot uses AI. Check for mistakes.
if (storedAllTablesCategory) {
allTablesOrdered = storedAllTablesCategory.tables
.map((tableName) => tablesRO.find((tableRO) => tableRO.table === tableName))
.filter(Boolean);

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filter(Boolean) operation is potentially unsafe in TypeScript because the type system doesn't correctly narrow Array<FoundTableDs | undefined> to Array<FoundTableDs>. While it works at runtime, it would be more type-safe to use .filter((table): table is FoundTableDs => table !== undefined). This also applies to similar logic in lines 121-123 for other categories.

Suggested change
.filter(Boolean);
.filter((table): table is FoundTableDs => table !== undefined);

Copilot uses AI. Check for mistakes.
const allTableCategory: FoundTableCategoriesWithTablesRo = {
category_id: null,
category_color: null,
category_id: 'all-tables-kitten',

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the API contract for the v2 endpoint where category_id was previously null for the "All tables" category and is now 'all-tables-kitten'. This is a breaking change for any API consumers expecting null. Consider whether this should be versioned as v3 or if there's documentation/migration guide needed for existing API consumers.

Suggested change
category_id: 'all-tables-kitten',
category_id: null,

Copilot uses AI. Check for mistakes.
@Artuomka Artuomka merged commit ba1efe3 into main Feb 20, 2026
17 of 19 checks passed
@Artuomka Artuomka deleted the backend_table_categories_sorting_fix branch February 20, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants