From 4bdd624755b5e36f5598d7020169b6a7db7958d8 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Fri, 4 Sep 2026 02:32:51 +0100 Subject: [PATCH] Add appearance settings to user settings schema and manager - Introduced a new `appearance` field in the user settings schema to allow customization options. - Updated the `TOP_LEVEL_CATEGORIES` to include `appearance` for better organization of settings. - Modified the `createEmptySettings` and `normalizeSettings` functions to handle the new `appearance` field. - Enhanced the `UserSettingsManager` to ensure proper retrieval and updating of appearance settings. - Added a test case to verify that appearance settings are written correctly at the top level, ensuring they are not nested under defaults. --- .../schemas/misc/usersettings.schema.js | 1 + src/usersettings/usersettingsmanager.js | 14 ++++---- src/usersettings/usersettingsmanager.test.js | 34 +++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/database/schemas/misc/usersettings.schema.js b/src/database/schemas/misc/usersettings.schema.js index 17e8df9..6c03158 100644 --- a/src/database/schemas/misc/usersettings.schema.js +++ b/src/database/schemas/misc/usersettings.schema.js @@ -24,6 +24,7 @@ const userSettingsSchema = new mongoose.Schema({ collapseState: { type: Schema.Types.Mixed, default: () => ({}) }, }, pageLayout: { type: Schema.Types.Mixed, default: () => ({}) }, + appearance: { type: Schema.Types.Mixed, default: () => ({}) }, createdAt: { type: Date, diff --git a/src/usersettings/usersettingsmanager.js b/src/usersettings/usersettingsmanager.js index 6f8bf44..700a34d 100644 --- a/src/usersettings/usersettingsmanager.js +++ b/src/usersettings/usersettingsmanager.js @@ -8,7 +8,7 @@ const DEFAULTS_CATEGORIES = [ 'collapseState' ]; -const TOP_LEVEL_CATEGORIES = ['pageLayout']; +const TOP_LEVEL_CATEGORIES = ['pageLayout', 'appearance']; const SETTINGS_CATEGORIES = [...DEFAULTS_CATEGORIES, ...TOP_LEVEL_CATEGORIES]; @@ -18,7 +18,8 @@ const createEmptySettings = () => ({ sortSidebarVisibility: {}, columnVisibility: {}, collapseState: {}, - pageLayout: {} + pageLayout: {}, + appearance: {} }); const normalizeCategory = category => @@ -36,7 +37,8 @@ const normalizeSettings = (userSettings = {}) => { sortSidebarVisibility: normalizeCategory(defaults?.sortSidebarVisibility), columnVisibility: normalizeCategory(defaults?.columnVisibility), collapseState: normalizeCategory(defaults?.collapseState), - pageLayout: normalizeCategory(userSettings?.pageLayout) + pageLayout: normalizeCategory(userSettings?.pageLayout), + appearance: normalizeCategory(userSettings?.appearance) }; }; @@ -70,7 +72,7 @@ export class UserSettingsManager { const userId = this.getUserId(); const userSettings = await userSettingsModel .findOne({ user: userId }) - .select('defaults pageLayout') + .select('defaults pageLayout appearance') .lean(); return normalizeSettings(userSettings); @@ -108,7 +110,7 @@ export class UserSettingsManager { upsert: true, setDefaultsOnInsert: true }) - .select('defaults pageLayout') + .select('defaults pageLayout appearance') .lean(); } catch (error) { if (error?.code !== 11000) { @@ -121,7 +123,7 @@ export class UserSettingsManager { { $set: update.$set }, { new: true } ) - .select('defaults pageLayout') + .select('defaults pageLayout appearance') .lean(); } diff --git a/src/usersettings/usersettingsmanager.test.js b/src/usersettings/usersettingsmanager.test.js index d82c931..2e34e49 100644 --- a/src/usersettings/usersettingsmanager.test.js +++ b/src/usersettings/usersettingsmanager.test.js @@ -73,6 +73,40 @@ describe('UserSettingsManager', () => { ); }); + it('writes appearance at the top level, not under defaults', async () => { + const appearance = { theme: 'dark', density: 'compact' } + findOneAndUpdate.mockReturnValue( + queryResult({ + defaults: createEmptySettings(), + appearance + }) + ) + + await expect( + manager.updateUserSettings({ + category: 'appearance', + key: 'theme', + value: 'dark' + }) + ).resolves.toEqual({ + ...createEmptySettings(), + appearance + }) + + expect(findOneAndUpdate).toHaveBeenCalledWith( + { user: 'user-id' }, + expect.objectContaining({ + $set: expect.objectContaining({ + 'appearance.theme': 'dark' + }) + }), + expect.anything() + ) + expect(findOneAndUpdate.mock.calls[0][1].$set).not.toHaveProperty( + 'defaults.appearance.theme' + ) + }) + it('writes pageLayout at the top level, not under defaults', async () => { const pageLayout = { InventoryOverview: {