feat(admin+search): user-mgmt polish, role labels, search keyword index
Admin search now matches against per-card keyword lists so typing "client portal", "smtp", "tier ladder" lands on the System Settings card (which hosts those flags). The same keyword list extends the topbar global search (NAV_CATALOG) so any setting key resolves from the cmd-K input — settings results sort to the bottom of the dropdown beneath entity hits. User management: - Third action button (Power/PowerOff) enables/disables sign-in from the desktop list; mobile card dropdown gains the same item. Backed by the existing userProfiles.isActive flag — withAuth already refuses disabled sessions with 403. - UserForm collects first + last name (canonical) alongside displayName, with admin email-change behind a confirmation modal. On confirm we send the OLD address an automated "your admin changed your sign-in email" notice (new template at admin-email-change.ts) and rewrite the Better Auth user row. - Phone field swaps the bare tel input for the shared PhoneInput (country combobox + AsYouType formatting + E.164 storage). - "Manage permissions" link points to /admin/roles?focusUser=… as a stepping stone for the future fine-tuned-permissions UI. Role names normalize through a new ROLE_LABELS + formatRole() helper in constants.ts. Replaces the ad-hoc humanizeRole in sidebar and the prettifyRoleName in role-list; user-list and user-card now render "Sales Agent" instead of "sales_agent". Custom roles pass through unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,15 @@ interface PreferencesResponse {
|
||||
};
|
||||
}
|
||||
|
||||
interface UseDashboardWidgetsOptions {
|
||||
/** SSR-prefetched visibility map. When provided, seeds the react-query
|
||||
* cache so the first render uses the rep's saved layout — no reflow when
|
||||
* the client fetch resolves. Pass `null` (not `undefined`) when the
|
||||
* caller has confirmed there is no stored preference so we don't keep
|
||||
* showing the loading layout. */
|
||||
initialVisibility?: Record<string, boolean> | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dashboard widget list filtered by the user's visibility
|
||||
* preferences and exposes a toggle. Single source of truth for "what's
|
||||
@@ -25,7 +34,7 @@ interface PreferencesResponse {
|
||||
* Missing keys fall back to the registry's `defaultVisible`, so a newly
|
||||
* added widget surfaces for everyone without a migration.
|
||||
*/
|
||||
export function useDashboardWidgets() {
|
||||
export function useDashboardWidgets(options: UseDashboardWidgetsOptions = {}) {
|
||||
const queryClient = useQueryClient();
|
||||
const integrations = useDashboardIntegrations();
|
||||
|
||||
@@ -33,6 +42,10 @@ export function useDashboardWidgets() {
|
||||
queryKey: ['me', 'preferences', 'dashboard-widgets'],
|
||||
queryFn: () => apiFetch<PreferencesResponse>('/api/v1/users/me/preferences'),
|
||||
staleTime: 60_000,
|
||||
initialData:
|
||||
options.initialVisibility !== undefined
|
||||
? ({ data: { dashboardWidgets: options.initialVisibility ?? {} } } as PreferencesResponse)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
// The registry is the universe of declared widgets. `availableWidgets`
|
||||
|
||||
Reference in New Issue
Block a user