Skip to content

Add Settings page#162

Open
fzaninotto wants to merge 28 commits intomainfrom
user-config
Open

Add Settings page#162
fzaninotto wants to merge 28 commits intomainfrom
user-config

Conversation

@fzaninotto
Copy link
Member

@fzaninotto fzaninotto commented Feb 11, 2026

Problem

Settings (title, deal categories, etc) can only be customized by code.

Solution

Let admins edit these settings and store the result in the database.

settings

How To Test

  1. go to the user menu and select App Settings
  2. update the app title and save
  3. the title changes in the app bar
  4. logout, then login again
  5. the title is still the custom title

Key changes

  1. Rename SettingsPAge to ProfilePage to avoid confusion

  2. New Settings UI (AppConfigPage.tsx - 320 lines)

  • Full admin settings page at /app-settings with sections for: Branding, Company Sectors, Deal Stages, Deal Categories, Note Statuses, and Task Types
  • Left-side navigation, sticky save button, uses EditBase/Form from ra-core
  • Gated behind CanAccess resource="configuration" action="edit" (admin-only)
  1. Config storage moved from React Context to ra-core Store (ConfigurationContext.tsx)
  • Replaced ConfigurationProvider (React context + provider component) with useStore-based hooks (useConfigurationContext, useConfigurationUpdater)
  • Config is now read/written via ra-core's store under key app.configuration
  1. Database-backed persistence
  • New configuration singleton table with migration containing JSONB config column, constrained to id=1
  • RLS: all authenticated users can read, only admins can insert/update
  • is_admin() helper function for RLS policies
  • getConfiguration() and updateConfiguration() methods added to both Supabase and FakeRest data providers
  1. Backwards compatibility
  • CRM component seeds the store with prop values on first load if nothing is stored yet
  • Auth provider wrapper pre-fetches config on login, clears on logout
  • useConfigurationLoader hook (called from both Layout and MobileLayout) fetches config from DB and syncs to store with 5-min stale time
  • If the db config doesn't define a particular setting, Atomic CRM uses the defaults. This allows forward compatibility (if we ever add new settings but the JSONB column doesn't contain them)
  1. Removed ContactGender customization

To do

  • Add custom methods in data providers
  •  Add migrations for supabase
  •  Fetch configuration on login and on mount
  • Add settings screen (admin only)
  • Allow to upload custom logo
  • Handle conflicts when old config value is removed (e.g. in deal stages)
  • Add documentation

Additional Checks

@@ -0,0 +1,256 @@
import { useMutation } from "@tanstack/react-query";
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the old Settings page that was actually renamed to Profile. It appears as new on GitHub but only the title has changed.

@@ -1,256 +1,407 @@
import { useMutation } from "@tanstack/react-query";
Copy link
Member Author

Choose a reason for hiding this comment

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

Although it appears as a changed file, this is indeed a new page. It's just that I reused the old name for the user profile page, so GitHub shows it as a diff.

@fzaninotto fzaninotto added the RFR label Feb 19, 2026
* Also rejects duplicate slug values.
* Returns undefined if valid, or an error message string.
*/
const validateItemsInUse = (
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this can be moved into its own files.
I'd also add unit test on this function.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added unit tests but I'm -1 for moving this function to its own file.

Comment on lines +47 to +56
const slugs = items.map((i) => i.value || toSlug(i.label));
const seen = new Set<string>();
const duplicates = new Set<string>();
for (const slug of slugs) {
if (seen.has(slug)) duplicates.add(slug);
seen.add(slug);
}
if (duplicates.size > 0) {
return `Duplicate ${displayName}: ${[...duplicates].join(", ")}`;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You could use lodash uniqBy followed by differenceBy to get the duplicated items.

Copy link
Member Author

Choose a reason for hiding this comment

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

i don't think this would be significantly better or shorter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments