* feat: reorganize admin panel around accounts * feat: expand admin customer account controls * feat: add admin account detail management * fix: remove unused admin account icon import * refactor: design critique fixes for account pages - Admin accounts: replace dense card grid with compact DataTable - Account settings: remove redundant hero card, stat grid, header pills - Fix bg-accent (orange) misuse on decorative elements across 7 files - Add ConfirmButton for destructive actions (deactivate, remove member) - Replace single-field modals with inline editing (plan, trial) - Add contextual help: display code tooltip, improved empty states - Non-owner aside explanation for hidden owner-only sections - Admin sidebar: group 11 items into 5 labeled sections - Rename UsersPage.tsx → AccountsPage.tsx to match route - Fix border radius consistency, hide zero-count badges Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use get_admin_db for all new admin account endpoints All admin endpoints query across tenants without a tenant context. get_db (app-role, subject to RLS) was never imported and would crash at runtime — replace all 6 occurrences with get_admin_db (BYPASSRLS). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
133 lines
3.6 KiB
TypeScript
133 lines
3.6 KiB
TypeScript
import { Link, useLocation } from 'react-router-dom'
|
|
import {
|
|
LayoutDashboard,
|
|
Building2,
|
|
Ticket,
|
|
FileText,
|
|
Gauge,
|
|
ToggleLeft,
|
|
Settings,
|
|
FolderTree,
|
|
ClipboardList,
|
|
MessageSquareText,
|
|
LayoutGrid,
|
|
ArrowLeft,
|
|
} from 'lucide-react'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
interface NavItem {
|
|
path: string
|
|
label: string
|
|
icon: typeof LayoutDashboard
|
|
end?: boolean
|
|
}
|
|
|
|
interface NavSection {
|
|
label?: string
|
|
items: NavItem[]
|
|
}
|
|
|
|
const navSections: NavSection[] = [
|
|
{
|
|
items: [
|
|
{ path: '/admin', label: 'Dashboard', icon: LayoutDashboard, end: true },
|
|
{ path: '/admin/accounts', label: 'Accounts', icon: Building2 },
|
|
{ path: '/admin/invite-codes', label: 'Invite Codes', icon: Ticket },
|
|
],
|
|
},
|
|
{
|
|
label: 'Platform',
|
|
items: [
|
|
{ path: '/admin/plan-limits', label: 'Plan Limits', icon: Gauge },
|
|
{ path: '/admin/feature-flags', label: 'Feature Flags', icon: ToggleLeft },
|
|
{ path: '/admin/settings', label: 'Settings', icon: Settings },
|
|
],
|
|
},
|
|
{
|
|
label: 'Content',
|
|
items: [
|
|
{ path: '/admin/categories', label: 'Categories', icon: FolderTree },
|
|
{ path: '/admin/gallery', label: 'Gallery', icon: LayoutGrid },
|
|
],
|
|
},
|
|
{
|
|
label: 'Feedback',
|
|
items: [
|
|
{ path: '/admin/survey-invites', label: 'Survey Invites', icon: ClipboardList },
|
|
{ path: '/admin/survey-responses', label: 'Survey Responses', icon: MessageSquareText },
|
|
],
|
|
},
|
|
{
|
|
label: 'Audit',
|
|
items: [
|
|
{ path: '/admin/audit-logs', label: 'Audit Logs', icon: FileText },
|
|
],
|
|
},
|
|
]
|
|
|
|
interface AdminSidebarProps {
|
|
className?: string
|
|
onNavigate?: () => void
|
|
}
|
|
|
|
export function AdminSidebar({ className, onNavigate }: AdminSidebarProps) {
|
|
const location = useLocation()
|
|
|
|
const isActive = (path: string, end?: boolean) => {
|
|
if (end) return location.pathname === path
|
|
return location.pathname.startsWith(path)
|
|
}
|
|
|
|
return (
|
|
<aside className={cn('flex h-full flex-col', className)}>
|
|
<div className="p-4">
|
|
<h2 className="text-lg font-bold text-foreground">Admin Panel</h2>
|
|
</div>
|
|
<nav className="flex-1 space-y-4 overflow-y-auto px-3">
|
|
{navSections.map((section, i) => (
|
|
<div key={i}>
|
|
{section.label && (
|
|
<p className="mb-1 px-3 text-[11px] font-medium uppercase tracking-[0.14em] text-muted-foreground">
|
|
{section.label}
|
|
</p>
|
|
)}
|
|
<div className="space-y-0.5">
|
|
{section.items.map((item) => (
|
|
<Link
|
|
key={item.path}
|
|
to={item.path}
|
|
onClick={onNavigate}
|
|
className={cn(
|
|
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
|
|
isActive(item.path, item.end)
|
|
? 'bg-elevated text-foreground'
|
|
: 'text-muted-foreground hover:bg-elevated hover:text-foreground'
|
|
)}
|
|
>
|
|
<item.icon className="h-4 w-4" />
|
|
{item.label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</nav>
|
|
<div className="border-t border-border p-3">
|
|
<Link
|
|
to="/trees"
|
|
onClick={onNavigate}
|
|
className={cn(
|
|
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium',
|
|
'text-muted-foreground hover:bg-elevated hover:text-foreground'
|
|
)}
|
|
>
|
|
<ArrowLeft className="h-4 w-4" />
|
|
Back to App
|
|
</Link>
|
|
</div>
|
|
</aside>
|
|
)
|
|
}
|
|
|
|
export default AdminSidebar
|