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:
@@ -26,6 +26,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { formatRole } from '@/lib/constants';
|
||||
import { useUIStore } from '@/stores/ui-store';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -49,18 +50,6 @@ interface SidebarProps {
|
||||
ports?: Port[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a snake_cased DB role identifier (e.g. "super_admin") into a human
|
||||
* label ("Super Admin"). Empty/missing → "Staff" fallback.
|
||||
*/
|
||||
function humanizeRole(roleName: string | null | undefined): string {
|
||||
if (!roleName) return 'Staff';
|
||||
return roleName
|
||||
.split('_')
|
||||
.map((part) => (part ? part[0]!.toUpperCase() + part.slice(1) : part))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
interface NavItem {
|
||||
href: string;
|
||||
label: string;
|
||||
@@ -423,7 +412,7 @@ function SidebarContent({
|
||||
variant="outline"
|
||||
className="text-[10px] px-1.5 py-0 text-slate-500 border-slate-300 mt-0.5"
|
||||
>
|
||||
{isSuperAdmin ? 'Super Admin' : humanizeRole(portRoles[0]?.role?.name)}
|
||||
{isSuperAdmin ? 'Super Admin' : formatRole(portRoles[0]?.role?.name)}
|
||||
</Badge>
|
||||
{currentPortName && (
|
||||
<p className="mt-1 text-[10px] text-slate-400 truncate">{currentPortName}</p>
|
||||
|
||||
Reference in New Issue
Block a user