Add comprehensive platform administration panel
- Database: Add login_history, ai_recommendation_log tables; is_platform_owner column on users; subscription fields on organizations (payment_date, confirmation_number, renewal_date) - Backend: New AdminAnalyticsService with platform metrics, tenant detail, and health score calculations (0-100 based on activity, budget, transactions, members, AI usage) - Backend: Login/org-switch now records to login_history; AI recommendations logged to ai_recommendation_log; platform owner protected from superadmin toggle - Frontend: 4-tab admin panel (Dashboard, Organizations, Users, Tenant Health) with tenant detail drawer, subscription management, health scoring visualization - Platform owner account (admin@hoaledgeriq.com) auto-redirects to admin panel - Seed data includes platform owner account and sample login history Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
IconChartAreaLine,
|
||||
IconClipboardCheck,
|
||||
IconSparkles,
|
||||
IconHeartRateMonitor,
|
||||
} from '@tabler/icons-react';
|
||||
import { useAuthStore } from '../../stores/authStore';
|
||||
|
||||
@@ -87,12 +88,44 @@ export function Sidebar({ onNavigate }: SidebarProps) {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const currentOrg = useAuthStore((s) => s.currentOrg);
|
||||
const organizations = useAuthStore((s) => s.organizations);
|
||||
const isAdminOnly = location.pathname.startsWith('/admin') && !currentOrg;
|
||||
|
||||
const go = (path: string) => {
|
||||
navigate(path);
|
||||
onNavigate?.();
|
||||
};
|
||||
|
||||
// When on admin route with no org selected, show admin-only sidebar
|
||||
if (isAdminOnly && user?.isSuperadmin) {
|
||||
return (
|
||||
<ScrollArea p="sm">
|
||||
<Text size="xs" c="dimmed" fw={700} tt="uppercase" px="sm" pb={4}>
|
||||
Platform Administration
|
||||
</Text>
|
||||
<NavLink
|
||||
label="Admin Panel"
|
||||
leftSection={<IconCrown size={18} />}
|
||||
active={location.pathname === '/admin'}
|
||||
onClick={() => go('/admin')}
|
||||
color="red"
|
||||
/>
|
||||
{organizations && organizations.length > 0 && (
|
||||
<>
|
||||
<Divider my="sm" />
|
||||
<NavLink
|
||||
label="Switch to Tenant"
|
||||
leftSection={<IconBuildingBank size={18} />}
|
||||
onClick={() => go('/select-org')}
|
||||
variant="subtle"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollArea p="sm">
|
||||
{navSections.map((section, sIdx) => (
|
||||
|
||||
Reference in New Issue
Block a user