diff --git "a/frontend/src/pages/SuperAdminPanel.jsx" "b/frontend/src/pages/SuperAdminPanel.jsx" --- "a/frontend/src/pages/SuperAdminPanel.jsx" +++ "b/frontend/src/pages/SuperAdminPanel.jsx" @@ -1,1241 +1,1275 @@ -import React, { useState, useEffect } from 'react'; -import { Link } from 'react-router-dom'; -import { useAuth } from '../components/AuthContext'; -import { CustomModal } from '../components/CustomModal'; -import { Building2, Users, CreditCard, Shield, Trash2, Plus, Server, Activity, Database, HardDrive, RefreshCw, BarChart2, Edit, Download, Eye } from 'lucide-react'; -import toast from 'react-hot-toast'; - -const BillingTierCard = ({ tier, onSave }) => { - // Convert from cents (stored in DB) to dollars for UI display - const [monthly, setMonthly] = useState((tier.monthly_price / 100).toFixed(2)); - const [yearly, setYearly] = useState((tier.yearly_price / 100).toFixed(2)); - const [saving, setSaving] = useState(false); - const [saved, setSaved] = useState(false); - - const handleSave = async () => { - setSaving(true); - // Convert dollars back to cents before sending to API - await onSave(tier.id, Math.round(parseFloat(monthly) * 100), Math.round(parseFloat(yearly) * 100)); - setSaving(false); - setSaved(true); - setTimeout(() => setSaved(false), 2000); - }; - - return ( -
- {/* Accent Top Border based on tier name */} -
- -
-
-

- {tier.name} -

-

- {tier.id} -

-
-
- -
-
- -
- $ - setMonthly(e.target.value)} - className="w-full bg-surface-container-high border border-outline-variant rounded-lg pl-8 pr-3 py-2 focus:border-primary focus:ring-1 focus:ring-primary outline-none text-on-surface font-mono font-bold transition-all" - /> -
-
- -
- -
- $ - setYearly(e.target.value)} - className="w-full bg-surface-container-high border border-outline-variant rounded-lg pl-8 pr-3 py-2 focus:border-primary focus:ring-1 focus:ring-primary outline-none text-on-surface font-mono font-bold transition-all" - /> -
-
-
- -
- -
-
- ); -}; - -const SuperAdminPanel = () => { - const { user } = useAuth(); - const [organizations, setOrganizations] = useState([]); - const [recentPayments, setRecentPayments] = useState([]); - const [trends, setTrends] = useState([]); - const [auditLogs, setAuditLogs] = useState([]); - const [users, setUsers] = useState([]); - const [demoBookings, setDemoBookings] = useState([]); - const [emailLogs, setEmailLogs] = useState([]); - const [loading, setLoading] = useState(true); - const [activeTab, setActiveTab] = useState('overview'); - - const [sortOrgCol, setSortOrgCol] = useState('Tenant Name'); - const [sortOrgDir, setSortOrgDir] = useState('asc'); - - const [sortUserCol, setSortUserCol] = useState('Email'); - const [sortUserDir, setSortUserDir] = useState('asc'); - - const [sortBillCol, setSortBillCol] = useState('Date'); - const [sortBillDir, setSortBillDir] = useState('desc'); - - const handleOrgSort = (column) => { - if (column === 'Quotas' || column === 'Actions') return; - if (sortOrgCol === column) { - setSortOrgDir(sortOrgDir === 'asc' ? 'desc' : 'asc'); - } else { - setSortOrgCol(column); - setSortOrgDir('asc'); - } - }; - - const getSortedOrgs = () => { - return [...organizations].sort((a, b) => { - let aVal, bVal; - switch (sortOrgCol) { - case 'Tenant Name': aVal = a.name || ''; bVal = b.name || ''; break; - case 'Tier': aVal = a.tier || a.subscription_tier || ''; bVal = b.tier || b.subscription_tier || ''; break; - case 'Status': aVal = a.status || (a.is_active ? 'active' : 'inactive'); bVal = b.status || (b.is_active ? 'active' : 'inactive'); break; - default: return 0; - } - if (aVal < bVal) return sortOrgDir === 'asc' ? -1 : 1; - if (aVal > bVal) return sortOrgDir === 'asc' ? 1 : -1; - return 0; - }); - }; - - const handleUserSort = (column) => { - if (column === 'Actions') return; - if (sortUserCol === column) { - setSortUserDir(sortUserDir === 'asc' ? 'desc' : 'asc'); - } else { - setSortUserCol(column); - setSortUserDir('asc'); - } - }; - - const getSortedUsers = () => { - return [...users].sort((a, b) => { - let aVal, bVal; - switch (sortUserCol) { - case 'Email': aVal = a.email || ''; bVal = b.email || ''; break; - case 'Role': aVal = a.role || ''; bVal = b.role || ''; break; - case 'Organization': aVal = a.org_name || ''; bVal = b.org_name || ''; break; - default: return 0; - } - if (aVal < bVal) return sortUserDir === 'asc' ? -1 : 1; - if (aVal > bVal) return sortUserDir === 'asc' ? 1 : -1; - return 0; - }); - }; - - const handleBillSort = (column) => { - if (column === 'Invoice') return; - if (sortBillCol === column) { - setSortBillDir(sortBillDir === 'asc' ? 'desc' : 'asc'); - } else { - setSortBillCol(column); - setSortBillDir('asc'); - } - }; - - const getSortedBills = () => { - return [...recentPayments].sort((a, b) => { - let aVal, bVal; - switch (sortBillCol) { - case 'Date': aVal = new Date(a.created_at).getTime(); bVal = new Date(b.created_at).getTime(); break; - case 'Organization / User': aVal = a.org_name || ''; bVal = b.org_name || ''; break; - case 'Tier': aVal = a.tier_id || ''; bVal = b.tier_id || ''; break; - case 'Amount': aVal = a.amount || 0; bVal = b.amount || 0; break; - case 'Status': aVal = a.status || ''; bVal = b.status || ''; break; - default: return 0; - } - if (aVal < bVal) return sortBillDir === 'asc' ? -1 : 1; - if (aVal > bVal) return sortBillDir === 'asc' ? 1 : -1; - return 0; - }); - }; - - const [metrics, setMetrics] = useState({ - total_tenants: 0, - active_licenses: 0, - global_users: 0, - active_scanners: 0, - total_scanners: 15, - arr: 0, - db_connections: 0, - db_query_time: 0, - queue_size: 0, - active_threads: 0 - }); - - const fetchStats = async () => { - setLoading(true); - try { - const token = localStorage.getItem('wss_token'); - const [globalRes, bookingsRes, emailLogsRes] = await Promise.all([ - fetch('/api/auth/global-stats', { headers: { 'Authorization': `Bearer ${token}` } }), - fetch('/api/demo/bookings', { headers: { 'Authorization': `Bearer ${token}` } }), - fetch('/api/auth/email-logs', { headers: { 'Authorization': `Bearer ${token}` } }) - ]); - if (globalRes.ok) { - const data = await globalRes.json(); - setOrganizations(data.organizations || []); - setMetrics(data.metrics || {}); - setRecentPayments(data.recent_payments || []); - setTrends(data.trends || []); - setAuditLogs(data.audit_logs || []); - setUsers(data.users || []); - } - if (bookingsRes.ok) { - const data = await bookingsRes.json(); - setDemoBookings(data.bookings || []); - } - if (emailLogsRes.ok) { - const data = await emailLogsRes.json(); - setEmailLogs(data.logs || []); - } - } catch (err) { - console.error('Failed to fetch global stats', err); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - fetchStats(); - }, []); - - // Modal States - const [pricingModalOpen, setPricingModalOpen] = useState(false); - const [activeScansModalOpen, setActiveScansModalOpen] = useState(false); - - // Custom Prompts & Confirms - const [confirmModal, setConfirmModal] = useState({ isOpen: false, title: '', desc: '', onConfirm: null, type: 'primary' }); - const [promptModal, setPromptModal] = useState({ isOpen: false, title: '', desc: '', inputs: [], onConfirm: null }); - const [promptValues, setPromptValues] = useState({}); - - const closeConfirm = () => setConfirmModal({ isOpen: false, title: '', desc: '', onConfirm: null, type: 'primary' }); - const closePrompt = () => { setPromptModal({ isOpen: false, title: '', desc: '', inputs: [], onConfirm: null }); setPromptValues({}); }; - - const handlePromptChange = (key, value) => setPromptValues(prev => ({ ...prev, [key]: value })); - - const handleSuspend = (orgId, currentStatus) => { - const action = currentStatus === 'suspended' ? 'activate' : 'suspend'; - setConfirmModal({ - isOpen: true, - title: `${action === 'activate' ? 'Activate' : 'Suspend'} Tenant`, - desc: `Are you sure you want to ${action} this tenant?`, - type: action === 'suspend' ? 'error' : 'primary', - onConfirm: async () => { - try { - const res = await fetch(`/api/auth/organizations/${orgId}/suspend`, { - method: 'POST', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } - }); - if (res.ok) fetchStats(); - } catch (err) { } - closeConfirm(); - } - }); - }; - - const handleDeleteTenant = (orgId, orgName) => { - setConfirmModal({ - isOpen: true, - title: 'Delete Tenant', - desc: `Are you sure you want to completely delete ${orgName}? This action cannot be undone.`, - type: 'error', - onConfirm: async () => { - try { - const res = await fetch(`/api/auth/organizations/${orgId}`, { - method: 'DELETE', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } - }); - if (res.ok) { - toast.success('Tenant deleted successfully'); - fetchStats(); - } else { - toast.error('Failed to delete tenant'); - } - } catch (err) { - toast.error('Network error'); - } - closeConfirm(); - } - }); - }; - - const handleImpersonate = (orgId, orgName) => { - setConfirmModal({ - isOpen: true, - title: 'Impersonate Tenant', - desc: `Log in as administrator for ${orgName}?`, - type: 'primary', - onConfirm: async () => { - try { - const res = await fetch(`/api/auth/impersonate/${orgId}`, { - method: 'POST', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } - }); - if (res.ok) { - const data = await res.json(); - localStorage.setItem('original_admin_token', localStorage.getItem('wss_token')); - localStorage.setItem('wss_token', data.access_token); - window.location.href = '/dashboard'; - } - } catch (err) { } - closeConfirm(); - } - }); - }; - - const [tiers, setTiers] = useState([]); - const [fetchingTiers, setFetchingTiers] = useState(false); - - const openPricingModal = async () => { - setPricingModalOpen(true); - setFetchingTiers(true); - try { - const res = await fetch('/api/billing/tiers', { headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } }); - if (res.ok) setTiers(await res.json()); - } catch (err) { } - setFetchingTiers(false); - }; - - const handleUpdateTier = async (tierId, monthly, yearly) => { - try { - await fetch(`/api/billing/tiers/${tierId}`, { - method: 'PUT', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, - body: JSON.stringify({ monthly_price: parseInt(monthly), yearly_price: parseInt(yearly) }) - }); - fetchStats(); - } catch (err) { } - }; - - const [activeScans, setActiveScans] = useState([]); - const [fetchingActiveScans, setFetchingActiveScans] = useState(false); - - const openActiveScansModal = async () => { - setActiveScansModalOpen(true); - setFetchingActiveScans(true); - try { - const res = await fetch('/api/scans/active', { headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } }); - if (res.ok) { - const data = await res.json(); - setActiveScans(data.scans || []); - } - } catch (err) { } - setFetchingActiveScans(false); - }; - - const handleKillScan = (scanId) => { - setConfirmModal({ - isOpen: true, - title: 'Terminate Scan', - desc: 'Are you sure you want to forcibly terminate this scan?', - type: 'error', - onConfirm: async () => { - try { - await fetch(`/api/scans/${scanId}/terminate`, { - method: 'POST', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } - }); - openActiveScansModal(); - } catch (err) { } - closeConfirm(); - } - }); - }; - - const handleProvisionTenant = () => { - setPromptValues({ tier: 'free' }); - setPromptModal({ - isOpen: true, - title: 'Add New Organization', - desc: 'Create a new tenant organization.', - inputs: [ - { key: 'name', label: 'Organization Name', placeholder: 'Enter name...' }, - { key: 'tier', label: 'Subscription Tier', placeholder: 'free, quick, standard, advanced, enterprise' } - ], - onConfirm: async (values) => { - try { - const res = await fetch('/api/auth/organizations', { - method: 'POST', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, - body: JSON.stringify({ name: values.name, tier: values.tier }) - }); - if (res.ok) fetchStats(); - } catch (err) { } - closePrompt(); - } - }); - }; - - const handleAssignScans = (org) => { - setPromptValues({ scan_type: 'Deep', count: '1' }); - setPromptModal({ - isOpen: true, - title: 'Assign Custom Scans', - desc: `Grant specific scan limits for ${org.name}`, - inputs: [ - { - key: 'scan_type', - label: 'Scan Type', - type: 'select', - options: ['Quick', 'Advanced', 'Deep'] - }, - { key: 'count', label: 'Number of Scans', placeholder: 'e.g., 5' } - ], - onConfirm: async (values) => { - try { - const res = await fetch(`/api/auth/organizations/${org.id}/quotas`, { - method: 'POST', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, - body: JSON.stringify({ scan_type: values.scan_type, count: parseInt(values.count) }) - }); - if (res.ok) toast.success('Custom scans assigned successfully!'); - else toast.error('Failed to assign scans.'); - } catch (err) { } - closePrompt(); - } - }); - }; - - const handleEditTenant = (org) => { - setPromptValues({ name: org.name, tier: org.tier.toLowerCase() }); - setPromptModal({ - isOpen: true, - title: 'Edit Organization', - desc: `Modify settings for ${org.name}`, - inputs: [ - { key: 'name', label: 'Organization Name' }, - { key: 'tier', label: 'Subscription Tier' } - ], - onConfirm: async (values) => { - try { - const res = await fetch(`/api/auth/organizations/${org.id}`, { - method: 'PUT', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, - body: JSON.stringify({ name: values.name, tier: values.tier }) - }); - if (res.ok) fetchStats(); - } catch (err) { } - closePrompt(); - } - }); - }; - - // Member CRUD - const handleAddMember = () => { - setPromptValues({ role: 'soc_analyst', email: '', org_id: '' }); - setPromptModal({ - isOpen: true, - title: 'Add Global Member', - desc: 'Invite a user to an organization.', - inputs: [ - { key: 'email', label: 'User Email', placeholder: 'user@example.com' }, - { key: 'role', label: 'Role', placeholder: 'soc_analyst, executive, org_admin, etc' }, - { key: 'org_id', label: 'Organization ID', placeholder: 'Optional' } - ], - onConfirm: async (values) => { - try { - const res = await fetch('/api/auth/users', { - method: 'POST', - headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, - body: JSON.stringify({ email: values.email, role: values.role, org_id: values.org_id }) - }); - if (res.ok) fetchStats(); - } catch (err) { } - closePrompt(); - } - }); - }; - - const handleDownloadInvoice = (payment) => { - const invoiceHtml = ` - - - Invoice - ${payment.id} - - - -
-

LarShield

-

Payment Receipt & Invoice

-
-
Date: ${new Date(payment.created_at).toLocaleString()}
-
Organization: ${payment.org_name}
-
Email: ${payment.user_email}
-
Subscription Tier: ${payment.tier_id} Plan
-
Status: ${payment.status.toUpperCase()}
-
- Total Amount: - ${payment.currency === 'INR' ? '₹' : '$'}${payment.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })} -
- - - - `; - const blob = new Blob([invoiceHtml], { type: 'text/html' }); - const url = URL.createObjectURL(blob); - window.open(url, '_blank'); - }; - - const [viewInvoice, setViewInvoice] = useState(null); - - const isSupportEngineer = user?.role === 'support_engineer'; - const isSuperAdmin = user?.role === 'super_admin' || user?.role === 'admin'; - - if (!isSuperAdmin && !isSupportEngineer) { - return
Access Denied. You do not have LarShield Management permissions.
; - } - - const handleCompleteBooking = async (bookingId) => { - try { - const res = await fetch(`/api/demo/bookings/${bookingId}`, { - method: 'PUT', - headers: { - 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ status: 'completed' }) - }); - if (res.ok) { - toast.success("Booking marked as completed"); - fetchStats(); - } else { - toast.error("Failed to update booking status"); - } - } catch (err) { - toast.error("Network error updating booking"); - } - }; - - return ( -
- {/* Support Engineer Information Banner */} - {isSupportEngineer && ( -
-
- support_agent -
-
Support Engineer Portal (Client Support)
-
- Permissions: Can view customer environments, assist troubleshooting (impersonation), and inspect logs & active scans. - (Cannot delete organizations or change subscription pricing). -
-
-
- - Support Role - -
- )} - - {/* Header Section */} -
-
-

- {isSupportEngineer ? 'Support Engineer Operations' : 'LarShield Global Management'} -

-

- {isSupportEngineer - ? 'Client environment inspection, troubleshooting assistance, and system logs.' - : 'Centralized oversight for all client organizations and scanning nodes.'} -

-
-
- - {!isSupportEngineer && ( - - )} - - Org Dashboard - - - Logs & Threats - - {!isSupportEngineer && ( - - )} -
-
- - {/* Tab Navigation */} -
- {['overview', 'organizations', 'members', 'audit', 'bookings', 'emails'].map(tab => ( - - ))} -
- - {activeTab === 'overview' && ( - <> -
-
-
-
-

Total Tenants

-

{metrics.total_tenants}

-
-
-
-
-
-
-
-

Active Licenses

-

{metrics.active_licenses}

-
-
-
-
-
-
-
-

Global Users

-

{metrics.global_users}

-
-
-
-
-
-
-
-

Active Scanners

-

{metrics.active_scanners}/{metrics.total_scanners}

-
-
-
-
-
- -
-
-

Node Infrastructure

-
-
-
PostgreSQL Cluster
- HEALTHY -
-
Connections{metrics.db_connections || 0} / 500
-
-
-
-
Celery Workers
- 5 ? 'bg-yellow-500/10 text-yellow-600 border-yellow-500/20' : 'bg-green-500/10 text-green-600 border-green-500/20'}`}>{metrics.queue_size > 5 ? 'HEAVY LOAD' : 'NORMAL'} -
-
Queue Size{metrics.queue_size || 0} scans
-
- -
- -
-

- receipt_long Global Transaction History -

-
- {loading ?
Fetching logs...
: ( -
- - - - {['Date', 'Organization / User', 'Tier', 'Amount', 'Status', 'Invoice'].map((h, i) => ( - - ))} - - - - {recentPayments.length === 0 ? : getSortedBills().map(p => ( - - - - - - - - - ))} - -
handleBillSort(h)} - className={`px-md py-sm font-bold text-[12px] uppercase tracking-wider ${i === 5 ? 'text-right' : ''} ${h !== 'Invoice' ? 'cursor-pointer hover:bg-surface-container-highest transition-colors group' : ''}`} - > -
- {h} - {h !== 'Invoice' && ( - - {sortBillCol === h && sortBillDir === 'desc' ? 'arrow_downward' : 'arrow_upward'} - - )} -
-
No recent transactions.
{new Date(p.created_at).toLocaleDateString()}
{p.org_name}
{p.tier_id}{p.currency === 'INR' ? '₹' : '$'}{p.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })}
{p.status === 'successful' ? 'Success' : 'Failed'}
-
- - -
-
-
- )} -
-
- -
-
-

- history System Audit Logs -

- -
-
- {loading ?
Fetching logs...
: ( -
- - - - - - - - - - - {auditLogs.length === 0 ? : auditLogs.slice(0, 5).map(log => ( - - - - - - - ))} - -
Date & TimeAdmin UserAction / EventOrganization / Target
No audit logs found.
{new Date(log.created_at || log.timestamp).toLocaleString()}
{log.user_email}
- {log.action.includes('Terminated') ? {log.action} : log.action} - {log.target_name || log.target_id || '-'}
-
- )} -
-
-
- - )} - - {activeTab === 'organizations' && ( -
-

Client Organizations Directory

-
- {loading ?
Fetching directory...
: ( - - - - {['Tenant Name', 'Tier', 'Status', 'Quotas', 'Actions'].map((h, i) => ( - - ))} - - - - {getSortedOrgs().map((org) => ( - - - - - - - - ))} - -
handleOrgSort(h)} - className={`px-md py-sm font-bold text-[12px] uppercase tracking-wider ${i === 4 ? 'text-right' : ''} ${(h !== 'Actions' && h !== 'Quotas') ? 'cursor-pointer hover:bg-surface-container-highest transition-colors group' : ''}`} - > -
- {h} - {(h !== 'Actions' && h !== 'Quotas') && ( - - {sortOrgCol === h && sortOrgDir === 'desc' ? 'arrow_downward' : 'arrow_upward'} - - )} -
-
{org.name}
{org.tier || org.subscription_tier}
{org.status ? org.status.charAt(0).toUpperCase() + org.status.slice(1) : (org.is_active ? 'Active' : 'Inactive')}
-
- {org.quotas?.map((q, idx) => { - const remaining = q.allocated_count === -1 ? '∞' : Math.max(0, q.allocated_count - (q.used_count || 0)); - const style = q.scan_type === 'Deep' ? 'bg-orange-500/10 text-orange-600 border-orange-500/30' : - q.scan_type === 'Advanced' ? 'bg-purple-500/10 text-purple-600 border-purple-500/30' : - 'bg-blue-500/10 text-blue-600 border-blue-500/30'; - return ( -
- {q.scan_type}: - {remaining} -
- ); - })} -
-
- - {!isSupportEngineer && ( - <> - - - - - - )} -
- )} -
-
- )} - - {activeTab === 'members' && ( -
-
-

Global Members

- {!isSupportEngineer && ( - - )} -
-
- {loading ?
Fetching users...
: ( - - - - {['Email', 'Role', 'Organization', 'Actions'].map((h, i) => ( - - ))} - - - - {getSortedUsers().map((u) => ( - - - - - - - ))} - -
handleUserSort(h)} - className={`px-md py-sm font-bold text-[12px] uppercase tracking-wider ${i === 3 ? 'text-right' : ''} ${h !== 'Actions' ? 'cursor-pointer hover:bg-surface-container-highest transition-colors group' : ''}`} - > -
- {h} - {h !== 'Actions' && ( - - {sortUserCol === h && sortUserDir === 'desc' ? 'arrow_downward' : 'arrow_upward'} - - )} -
-
{u.email}{u.role}{u.org_name} - {!isSupportEngineer ? ( - <> - - - - ) : ( - Read Only - )} -
- )} -
-
- )} - - {activeTab === 'audit' && ( -
-

history Full System Audit Logs

-
- {loading ?
Fetching logs...
: ( -
- - - - - - - - - - - {auditLogs.length === 0 ? : auditLogs.map((log) => ( - - - - - - - ))} - -
Date & TimeAdmin UserAction / EventOrganization / Target
No audit logs found.
{new Date(log.created_at || log.timestamp).toLocaleString()}
{log.user_email}
- {log.action.includes('Terminated') ? {log.action} : log.action} - {log.target_name || log.target_id || '-'}
-
- )} -
-
- )} - - {activeTab === 'bookings' && ( -
-

event Demo Bookings & Leads

-
- {demoBookings.length === 0 ? ( -
No demo bookings found.
- ) : ( -
- - - - - - - - - - - - {demoBookings.map(b => ( - - - - - - - - ))} - -
EmailSizeDate & TimeStatusActions
{b.email}{b.company_size.replace('Company Size: ', '')} - {b.meeting_date}
- {b.meeting_time} -
- - {b.status} - - - {b.status !== 'completed' && ( - - )} -
-
- )} -
-
- )} - - {activeTab === 'emails' && ( -
-

mail Outbound Email Logs

-
- {emailLogs.length === 0 ? ( -
No emails sent yet.
- ) : ( -
- - - - - - - - - - - {emailLogs.map(log => ( - - - - - - - ))} - -
TimestampRecipientSubjectStatus
{new Date(log.sent_at).toLocaleString()}{log.recipient}{log.subject} - - {log.status} - -
-
- )} -
-
- )} - - {/* Pricing Modal using CustomModal */} - setPricingModalOpen(false)} - title="Dynamic Billing Control" - description="Manage subscription tiers and pricing across the platform." - maxWidth="max-w-4xl" - > - {fetchingTiers ? ( -
- sync - Loading Billing Data... -
- ) : ( -
- {tiers.map(tier => ( - - ))} -
- )} -
- - {/* Active Scans Modal */} - setActiveScansModalOpen(false)} title="Active Scans"> - {fetchingActiveScans ?
Loading...
: activeScans.length === 0 ?
No active scans.
: ( -
- {activeScans.map(scan => ( -
-
{scan.target_url}
Org ID: {scan.org_id}
- -
- ))} -
- )} -
- - {/* Prompt Modal */} - - - - - } - > -
- {promptModal.inputs.map(input => ( -
- - {input.type === 'select' ? ( - - ) : ( - handlePromptChange(input.key, e.target.value)} - placeholder={input.placeholder} - className="bg-surface-container border border-outline-variant rounded-lg px-3 py-2 focus:border-primary outline-none text-on-surface" - /> - )} -
- ))} -
-
- - {/* Confirm Modal */} - - - - - } - /> - - {/* Invoice View Modal */} - setViewInvoice(null)} - title="Invoice Details" - footer={ - <> - - - - } - > - {viewInvoice && ( -
-
-
-

LarShield

-

Payment Receipt & Invoice

-
-
-
Date
-
{new Date(viewInvoice.created_at).toLocaleString()}
-
-
- -
-
-
Billed To
-
{viewInvoice.org_name}
-
{viewInvoice.user_email}
-
-
-
Status
-
{viewInvoice.status}
-
-
- - - - - - - - - - - - - - -
DescriptionAmount
- {viewInvoice.tier_id} Subscription Plan - - {viewInvoice.currency === 'INR' ? '₹' : '$'}{viewInvoice.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })} -
- -
-
Total Amount
-
- {viewInvoice.currency === 'INR' ? '₹' : '$'}{viewInvoice.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })} -
-
-
- )} -
-
- ); -}; - -export default SuperAdminPanel; +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { useAuth } from '../components/AuthContext'; +import { CustomModal } from '../components/CustomModal'; +import { Building2, Users, CreditCard, Shield, Trash2, Plus, Server, Activity, Database, HardDrive, RefreshCw, BarChart2, Edit, Download, Eye } from 'lucide-react'; +import toast from 'react-hot-toast'; + +const BillingTierCard = ({ tier, onSave }) => { + // Convert from cents (stored in DB) to dollars for UI display + const [monthly, setMonthly] = useState((tier.monthly_price / 100).toFixed(2)); + const [yearly, setYearly] = useState((tier.yearly_price / 100).toFixed(2)); + const [saving, setSaving] = useState(false); + const [saved, setSaved] = useState(false); + + const handleSave = async () => { + setSaving(true); + // Convert dollars back to cents before sending to API + await onSave(tier.id, Math.round(parseFloat(monthly) * 100), Math.round(parseFloat(yearly) * 100)); + setSaving(false); + setSaved(true); + setTimeout(() => setSaved(false), 2000); + }; + + return ( +
+ {/* Accent Top Border based on tier name */} +
+ +
+
+

+ {tier.name} +

+

+ {tier.id} +

+
+
+ +
+
+ +
+ $ + setMonthly(e.target.value)} + className="w-full bg-surface-container-high border border-outline-variant rounded-lg pl-8 pr-3 py-2 focus:border-primary focus:ring-1 focus:ring-primary outline-none text-on-surface font-mono font-bold transition-all" + /> +
+
+ +
+ +
+ $ + setYearly(e.target.value)} + className="w-full bg-surface-container-high border border-outline-variant rounded-lg pl-8 pr-3 py-2 focus:border-primary focus:ring-1 focus:ring-primary outline-none text-on-surface font-mono font-bold transition-all" + /> +
+
+
+ +
+ +
+
+ ); +}; + +const SuperAdminPanel = () => { + const { user } = useAuth(); + const [organizations, setOrganizations] = useState([]); + const [recentPayments, setRecentPayments] = useState([]); + const [trends, setTrends] = useState([]); + const [auditLogs, setAuditLogs] = useState([]); + const [users, setUsers] = useState([]); + const [demoBookings, setDemoBookings] = useState([]); + const [emailLogs, setEmailLogs] = useState([]); + const [loading, setLoading] = useState(true); + const [activeTab, setActiveTab] = useState('overview'); + + const [sortOrgCol, setSortOrgCol] = useState('Tenant Name'); + const [sortOrgDir, setSortOrgDir] = useState('asc'); + + const [sortUserCol, setSortUserCol] = useState('Email'); + const [sortUserDir, setSortUserDir] = useState('asc'); + + const [sortBillCol, setSortBillCol] = useState('Date'); + const [sortBillDir, setSortBillDir] = useState('desc'); + + const handleOrgSort = (column) => { + if (column === 'Quotas' || column === 'Actions') return; + if (sortOrgCol === column) { + setSortOrgDir(sortOrgDir === 'asc' ? 'desc' : 'asc'); + } else { + setSortOrgCol(column); + setSortOrgDir('asc'); + } + }; + + const getSortedOrgs = () => { + return [...organizations].sort((a, b) => { + let aVal, bVal; + switch (sortOrgCol) { + case 'Tenant Name': aVal = a.name || ''; bVal = b.name || ''; break; + case 'Tier': aVal = a.tier || a.subscription_tier || ''; bVal = b.tier || b.subscription_tier || ''; break; + case 'Status': aVal = a.status || (a.is_active ? 'active' : 'inactive'); bVal = b.status || (b.is_active ? 'active' : 'inactive'); break; + default: return 0; + } + if (aVal < bVal) return sortOrgDir === 'asc' ? -1 : 1; + if (aVal > bVal) return sortOrgDir === 'asc' ? 1 : -1; + return 0; + }); + }; + + const handleUserSort = (column) => { + if (column === 'Actions') return; + if (sortUserCol === column) { + setSortUserDir(sortUserDir === 'asc' ? 'desc' : 'asc'); + } else { + setSortUserCol(column); + setSortUserDir('asc'); + } + }; + + const getSortedUsers = () => { + return [...users].sort((a, b) => { + let aVal, bVal; + switch (sortUserCol) { + case 'Email': aVal = a.email || ''; bVal = b.email || ''; break; + case 'Role': aVal = a.role || ''; bVal = b.role || ''; break; + case 'Organization': aVal = a.org_name || ''; bVal = b.org_name || ''; break; + default: return 0; + } + if (aVal < bVal) return sortUserDir === 'asc' ? -1 : 1; + if (aVal > bVal) return sortUserDir === 'asc' ? 1 : -1; + return 0; + }); + }; + + const handleBillSort = (column) => { + if (column === 'Invoice') return; + if (sortBillCol === column) { + setSortBillDir(sortBillDir === 'asc' ? 'desc' : 'asc'); + } else { + setSortBillCol(column); + setSortBillDir('asc'); + } + }; + + const getSortedBills = () => { + return [...recentPayments].sort((a, b) => { + let aVal, bVal; + switch (sortBillCol) { + case 'Date': aVal = new Date(a.created_at).getTime(); bVal = new Date(b.created_at).getTime(); break; + case 'Organization / User': aVal = a.org_name || ''; bVal = b.org_name || ''; break; + case 'Tier': aVal = a.tier_id || ''; bVal = b.tier_id || ''; break; + case 'Amount': aVal = a.amount || 0; bVal = b.amount || 0; break; + case 'Status': aVal = a.status || ''; bVal = b.status || ''; break; + default: return 0; + } + if (aVal < bVal) return sortBillDir === 'asc' ? -1 : 1; + if (aVal > bVal) return sortBillDir === 'asc' ? 1 : -1; + return 0; + }); + }; + + const [metrics, setMetrics] = useState({ + total_tenants: 0, + active_licenses: 0, + global_users: 0, + active_scanners: 0, + total_scanners: 15, + arr: 0, + db_connections: 0, + db_query_time: 0, + queue_size: 0, + active_threads: 0 + }); + + const fetchStats = async () => { + setLoading(true); + try { + const token = localStorage.getItem('wss_token'); + const [globalRes, bookingsRes, emailLogsRes] = await Promise.all([ + fetch('/api/auth/global-stats', { headers: { 'Authorization': `Bearer ${token}` } }), + fetch('/api/demo/bookings', { headers: { 'Authorization': `Bearer ${token}` } }), + fetch('/api/auth/email-logs', { headers: { 'Authorization': `Bearer ${token}` } }) + ]); + if (globalRes.ok) { + const data = await globalRes.json(); + setOrganizations(data.organizations || []); + setMetrics(data.metrics || {}); + setRecentPayments(data.recent_payments || []); + setTrends(data.trends || []); + setAuditLogs(data.audit_logs || []); + setUsers(data.users || []); + } + if (bookingsRes.ok) { + const data = await bookingsRes.json(); + setDemoBookings(data.bookings || []); + } + if (emailLogsRes.ok) { + const data = await emailLogsRes.json(); + setEmailLogs(data.logs || []); + } + } catch (err) { + console.error('Failed to fetch global stats', err); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchStats(); + }, []); + + // Modal States + const [pricingModalOpen, setPricingModalOpen] = useState(false); + const [activeScansModalOpen, setActiveScansModalOpen] = useState(false); + + // Custom Prompts & Confirms + const [confirmModal, setConfirmModal] = useState({ isOpen: false, title: '', desc: '', onConfirm: null, type: 'primary' }); + const [promptModal, setPromptModal] = useState({ isOpen: false, title: '', desc: '', inputs: [], onConfirm: null }); + const [promptValues, setPromptValues] = useState({}); + + const closeConfirm = () => setConfirmModal({ isOpen: false, title: '', desc: '', onConfirm: null, type: 'primary' }); + const closePrompt = () => { setPromptModal({ isOpen: false, title: '', desc: '', inputs: [], onConfirm: null }); setPromptValues({}); }; + + const handlePromptChange = (key, value) => setPromptValues(prev => ({ ...prev, [key]: value })); + + const handleSuspend = (orgId, currentStatus) => { + const action = currentStatus === 'suspended' ? 'activate' : 'suspend'; + setConfirmModal({ + isOpen: true, + title: `${action === 'activate' ? 'Activate' : 'Suspend'} Tenant`, + desc: `Are you sure you want to ${action} this tenant?`, + type: action === 'suspend' ? 'error' : 'primary', + onConfirm: async () => { + try { + const res = await fetch(`/api/auth/organizations/${orgId}/suspend`, { + method: 'POST', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } + }); + if (res.ok) fetchStats(); + } catch (err) { } + closeConfirm(); + } + }); + }; + + const handleDeleteTenant = (orgId, orgName) => { + setConfirmModal({ + isOpen: true, + title: 'Delete Tenant', + desc: `Are you sure you want to completely delete ${orgName}? This action cannot be undone.`, + type: 'error', + onConfirm: async () => { + try { + const res = await fetch(`/api/auth/organizations/${orgId}`, { + method: 'DELETE', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } + }); + if (res.ok) { + toast.success('Tenant deleted successfully'); + fetchStats(); + } else { + toast.error('Failed to delete tenant'); + } + } catch (err) { + toast.error('Network error'); + } + closeConfirm(); + } + }); + }; + + const handleImpersonate = (orgId, orgName) => { + setConfirmModal({ + isOpen: true, + title: 'Impersonate Tenant', + desc: `Log in as administrator for ${orgName}?`, + type: 'primary', + onConfirm: async () => { + try { + const res = await fetch(`/api/auth/impersonate/${orgId}`, { + method: 'POST', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } + }); + if (res.ok) { + const data = await res.json(); + localStorage.setItem('original_admin_token', localStorage.getItem('wss_token')); + localStorage.setItem('wss_token', data.access_token); + window.location.href = '/dashboard'; + } + } catch (err) { } + closeConfirm(); + } + }); + }; + + const [tiers, setTiers] = useState([]); + const [fetchingTiers, setFetchingTiers] = useState(false); + + const openPricingModal = async () => { + setPricingModalOpen(true); + setFetchingTiers(true); + try { + const res = await fetch('/api/billing/tiers', { headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } }); + if (res.ok) setTiers(await res.json()); + } catch (err) { } + setFetchingTiers(false); + }; + + const handleUpdateTier = async (tierId, monthly, yearly) => { + try { + await fetch(`/api/billing/tiers/${tierId}`, { + method: 'PUT', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ monthly_price: parseInt(monthly), yearly_price: parseInt(yearly) }) + }); + fetchStats(); + } catch (err) { } + }; + + const [activeScans, setActiveScans] = useState([]); + const [fetchingActiveScans, setFetchingActiveScans] = useState(false); + + const openActiveScansModal = async () => { + setActiveScansModalOpen(true); + setFetchingActiveScans(true); + try { + const res = await fetch('/api/scans/active', { headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } }); + if (res.ok) { + const data = await res.json(); + setActiveScans(data.scans || []); + } + } catch (err) { } + setFetchingActiveScans(false); + }; + + const handleKillScan = (scanId) => { + setConfirmModal({ + isOpen: true, + title: 'Terminate Scan', + desc: 'Are you sure you want to forcibly terminate this scan?', + type: 'error', + onConfirm: async () => { + try { + await fetch(`/api/scans/${scanId}/terminate`, { + method: 'POST', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}` } + }); + openActiveScansModal(); + } catch (err) { } + closeConfirm(); + } + }); + }; + + const handleProvisionTenant = () => { + setPromptValues({ tier: 'free' }); + setPromptModal({ + isOpen: true, + title: 'Add New Organization', + desc: 'Create a new tenant organization.', + inputs: [ + { key: 'name', label: 'Organization Name', placeholder: 'Enter name...' }, + { key: 'tier', label: 'Subscription Tier', placeholder: 'free, quick, standard, advanced, enterprise' } + ], + onConfirm: async (values) => { + try { + const res = await fetch('/api/auth/organizations', { + method: 'POST', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ name: values.name, tier: values.tier }) + }); + if (res.ok) fetchStats(); + } catch (err) { } + closePrompt(); + } + }); + }; + + const handleAssignScans = (org) => { + setPromptValues({ scan_type: 'Deep', count: '1' }); + setPromptModal({ + isOpen: true, + title: 'Assign Custom Scans', + desc: `Grant specific scan limits for ${org.name}`, + inputs: [ + { + key: 'scan_type', + label: 'Scan Type', + type: 'select', + options: ['Quick', 'Advanced', 'Deep'] + }, + { key: 'count', label: 'Number of Scans', placeholder: 'e.g., 5' } + ], + onConfirm: async (values) => { + const addedCount = parseInt(values.count); + if (isNaN(addedCount) || addedCount <= 0) { + toast.error('Please enter a valid scan count'); + return; + } + try { + const res = await fetch(`/api/auth/organizations/${org.id}/quotas`, { + method: 'POST', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ scan_type: values.scan_type, count: addedCount }) + }); + if (res.ok) { + toast.success(`${addedCount} ${values.scan_type} scan(s) assigned successfully!`); + // Optimistic instant state update + setOrganizations(prevOrgs => prevOrgs.map(o => { + if (o.id === org.id) { + const existingQuotas = o.quotas || []; + let found = false; + const updatedQuotas = existingQuotas.map(q => { + if (q.scan_type?.toLowerCase() === values.scan_type?.toLowerCase()) { + found = true; + return { + ...q, + allocated_count: q.allocated_count === -1 ? -1 : (q.allocated_count || 0) + addedCount + }; + } + return q; + }); + if (!found) { + updatedQuotas.push({ scan_type: values.scan_type, allocated_count: addedCount, used_count: 0 }); + } + return { ...o, quotas: updatedQuotas }; + } + return o; + })); + fetchStats(); + } else { + const data = await res.json(); + toast.error(data.message || 'Failed to assign scans.'); + } + } catch (err) { + toast.error('Network error assigning scans.'); + } + closePrompt(); + } + }); + }; + + const handleEditTenant = (org) => { + setPromptValues({ name: org.name, tier: org.tier.toLowerCase() }); + setPromptModal({ + isOpen: true, + title: 'Edit Organization', + desc: `Modify settings for ${org.name}`, + inputs: [ + { key: 'name', label: 'Organization Name' }, + { key: 'tier', label: 'Subscription Tier' } + ], + onConfirm: async (values) => { + try { + const res = await fetch(`/api/auth/organizations/${org.id}`, { + method: 'PUT', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ name: values.name, tier: values.tier }) + }); + if (res.ok) fetchStats(); + } catch (err) { } + closePrompt(); + } + }); + }; + + // Member CRUD + const handleAddMember = () => { + setPromptValues({ role: 'soc_analyst', email: '', org_id: '' }); + setPromptModal({ + isOpen: true, + title: 'Add Global Member', + desc: 'Invite a user to an organization.', + inputs: [ + { key: 'email', label: 'User Email', placeholder: 'user@example.com' }, + { key: 'role', label: 'Role', placeholder: 'soc_analyst, executive, org_admin, etc' }, + { key: 'org_id', label: 'Organization ID', placeholder: 'Optional' } + ], + onConfirm: async (values) => { + try { + const res = await fetch('/api/auth/users', { + method: 'POST', + headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: values.email, role: values.role, org_id: values.org_id }) + }); + if (res.ok) fetchStats(); + } catch (err) { } + closePrompt(); + } + }); + }; + + const handleDownloadInvoice = (payment) => { + const invoiceHtml = ` + + + Invoice - ${payment.id} + + + +
+

LarShield

+

Payment Receipt & Invoice

+
+
Date: ${new Date(payment.created_at).toLocaleString()}
+
Organization: ${payment.org_name}
+
Email: ${payment.user_email}
+
Subscription Tier: ${payment.tier_id} Plan
+
Status: ${payment.status.toUpperCase()}
+
+ Total Amount: + ${payment.currency === 'INR' ? '₹' : '$'}${payment.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })} +
+ + + + `; + const blob = new Blob([invoiceHtml], { type: 'text/html' }); + const url = URL.createObjectURL(blob); + window.open(url, '_blank'); + }; + + const [viewInvoice, setViewInvoice] = useState(null); + + const isSupportEngineer = user?.role === 'support_engineer'; + const isSuperAdmin = user?.role === 'super_admin' || user?.role === 'admin'; + + if (!isSuperAdmin && !isSupportEngineer) { + return
Access Denied. You do not have LarShield Management permissions.
; + } + + const handleCompleteBooking = async (bookingId) => { + try { + const res = await fetch(`/api/demo/bookings/${bookingId}`, { + method: 'PUT', + headers: { + 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ status: 'completed' }) + }); + if (res.ok) { + toast.success("Booking marked as completed"); + fetchStats(); + } else { + toast.error("Failed to update booking status"); + } + } catch (err) { + toast.error("Network error updating booking"); + } + }; + + return ( +
+ {/* Support Engineer Information Banner */} + {isSupportEngineer && ( +
+
+ support_agent +
+
Support Engineer Portal (Client Support)
+
+ Permissions: Can view customer environments, assist troubleshooting (impersonation), and inspect logs & active scans. + (Cannot delete organizations or change subscription pricing). +
+
+
+ + Support Role + +
+ )} + + {/* Header Section */} +
+
+

+ {isSupportEngineer ? 'Support Engineer Operations' : 'LarShield Global Management'} +

+

+ {isSupportEngineer + ? 'Client environment inspection, troubleshooting assistance, and system logs.' + : 'Centralized oversight for all client organizations and scanning nodes.'} +

+
+
+ + {!isSupportEngineer && ( + + )} + + Org Dashboard + + + Logs & Threats + + {!isSupportEngineer && ( + + )} +
+
+ + {/* Tab Navigation */} +
+ {['overview', 'organizations', 'members', 'audit', 'bookings', 'emails'].map(tab => ( + + ))} +
+ + {activeTab === 'overview' && ( + <> +
+
+
+
+

Total Tenants

+

{metrics.total_tenants}

+
+
+
+
+
+
+
+

Active Licenses

+

{metrics.active_licenses}

+
+
+
+
+
+
+
+

Global Users

+

{metrics.global_users}

+
+
+
+
+
+
+
+

Active Scanners

+

{metrics.active_scanners}/{metrics.total_scanners}

+
+
+
+
+
+ +
+
+

Node Infrastructure

+
+
+
PostgreSQL Cluster
+ HEALTHY +
+
Connections{metrics.db_connections || 0} / 500
+
+
+
+
Celery Workers
+ 5 ? 'bg-yellow-500/10 text-yellow-600 border-yellow-500/20' : 'bg-green-500/10 text-green-600 border-green-500/20'}`}>{metrics.queue_size > 5 ? 'HEAVY LOAD' : 'NORMAL'} +
+
Queue Size{metrics.queue_size || 0} scans
+
+ +
+ +
+

+ receipt_long Global Transaction History +

+
+ {loading ?
Fetching logs...
: ( +
+ + + + {['Date', 'Organization / User', 'Tier', 'Amount', 'Status', 'Invoice'].map((h, i) => ( + + ))} + + + + {recentPayments.length === 0 ? : getSortedBills().map(p => ( + + + + + + + + + ))} + +
handleBillSort(h)} + className={`px-md py-sm font-bold text-[12px] uppercase tracking-wider ${i === 5 ? 'text-right' : ''} ${h !== 'Invoice' ? 'cursor-pointer hover:bg-surface-container-highest transition-colors group' : ''}`} + > +
+ {h} + {h !== 'Invoice' && ( + + {sortBillCol === h && sortBillDir === 'desc' ? 'arrow_downward' : 'arrow_upward'} + + )} +
+
No recent transactions.
{new Date(p.created_at).toLocaleDateString()}
{p.org_name}
{p.tier_id}{p.currency === 'INR' ? '₹' : '$'}{p.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })}
{p.status === 'successful' ? 'Success' : 'Failed'}
+
+ + +
+
+
+ )} +
+
+ +
+
+

+ history System Audit Logs +

+ +
+
+ {loading ?
Fetching logs...
: ( +
+ + + + + + + + + + + {auditLogs.length === 0 ? : auditLogs.slice(0, 5).map(log => ( + + + + + + + ))} + +
Date & TimeAdmin UserAction / EventOrganization / Target
No audit logs found.
{new Date(log.created_at || log.timestamp).toLocaleString()}
{log.user_email}
+ {log.action.includes('Terminated') ? {log.action} : log.action} + {log.target_name || log.target_id || '-'}
+
+ )} +
+
+
+ + )} + + {activeTab === 'organizations' && ( +
+

Client Organizations Directory

+
+ {loading ?
Fetching directory...
: ( + + + + {['Tenant Name', 'Tier', 'Status', 'Quotas', 'Actions'].map((h, i) => ( + + ))} + + + + {getSortedOrgs().map((org) => ( + + + + + + + + ))} + +
handleOrgSort(h)} + className={`px-md py-sm font-bold text-[12px] uppercase tracking-wider ${i === 4 ? 'text-right' : ''} ${(h !== 'Actions' && h !== 'Quotas') ? 'cursor-pointer hover:bg-surface-container-highest transition-colors group' : ''}`} + > +
+ {h} + {(h !== 'Actions' && h !== 'Quotas') && ( + + {sortOrgCol === h && sortOrgDir === 'desc' ? 'arrow_downward' : 'arrow_upward'} + + )} +
+
{org.name}
{org.tier || org.subscription_tier}
{org.status ? org.status.charAt(0).toUpperCase() + org.status.slice(1) : (org.is_active ? 'Active' : 'Inactive')}
+
+ {org.quotas?.map((q, idx) => { + const remaining = q.allocated_count === -1 ? '∞' : Math.max(0, q.allocated_count - (q.used_count || 0)); + const style = q.scan_type === 'Deep' ? 'bg-orange-500/10 text-orange-600 border-orange-500/30' : + q.scan_type === 'Advanced' ? 'bg-purple-500/10 text-purple-600 border-purple-500/30' : + 'bg-blue-500/10 text-blue-600 border-blue-500/30'; + return ( +
+ {q.scan_type}: + {remaining} +
+ ); + })} +
+
+ + {!isSupportEngineer && ( + <> + + + + + + )} +
+ )} +
+
+ )} + + {activeTab === 'members' && ( +
+
+

Global Members

+ {!isSupportEngineer && ( + + )} +
+
+ {loading ?
Fetching users...
: ( + + + + {['Email', 'Role', 'Organization', 'Actions'].map((h, i) => ( + + ))} + + + + {getSortedUsers().map((u) => ( + + + + + + + ))} + +
handleUserSort(h)} + className={`px-md py-sm font-bold text-[12px] uppercase tracking-wider ${i === 3 ? 'text-right' : ''} ${h !== 'Actions' ? 'cursor-pointer hover:bg-surface-container-highest transition-colors group' : ''}`} + > +
+ {h} + {h !== 'Actions' && ( + + {sortUserCol === h && sortUserDir === 'desc' ? 'arrow_downward' : 'arrow_upward'} + + )} +
+
{u.email}{u.role}{u.org_name} + {!isSupportEngineer ? ( + <> + + + + ) : ( + Read Only + )} +
+ )} +
+
+ )} + + {activeTab === 'audit' && ( +
+

history Full System Audit Logs

+
+ {loading ?
Fetching logs...
: ( +
+ + + + + + + + + + + {auditLogs.length === 0 ? : auditLogs.map((log) => ( + + + + + + + ))} + +
Date & TimeAdmin UserAction / EventOrganization / Target
No audit logs found.
{new Date(log.created_at || log.timestamp).toLocaleString()}
{log.user_email}
+ {log.action.includes('Terminated') ? {log.action} : log.action} + {log.target_name || log.target_id || '-'}
+
+ )} +
+
+ )} + + {activeTab === 'bookings' && ( +
+

event Demo Bookings & Leads

+
+ {demoBookings.length === 0 ? ( +
No demo bookings found.
+ ) : ( +
+ + + + + + + + + + + + {demoBookings.map(b => ( + + + + + + + + ))} + +
EmailSizeDate & TimeStatusActions
{b.email}{b.company_size.replace('Company Size: ', '')} + {b.meeting_date}
+ {b.meeting_time} +
+ + {b.status} + + + {b.status !== 'completed' && ( + + )} +
+
+ )} +
+
+ )} + + {activeTab === 'emails' && ( +
+

mail Outbound Email Logs

+
+ {emailLogs.length === 0 ? ( +
No emails sent yet.
+ ) : ( +
+ + + + + + + + + + + {emailLogs.map(log => ( + + + + + + + ))} + +
TimestampRecipientSubjectStatus
{new Date(log.sent_at).toLocaleString()}{log.recipient}{log.subject} + + {log.status} + +
+
+ )} +
+
+ )} + + {/* Pricing Modal using CustomModal */} + setPricingModalOpen(false)} + title="Dynamic Billing Control" + description="Manage subscription tiers and pricing across the platform." + maxWidth="max-w-4xl" + > + {fetchingTiers ? ( +
+ sync + Loading Billing Data... +
+ ) : ( +
+ {tiers.map(tier => ( + + ))} +
+ )} +
+ + {/* Active Scans Modal */} + setActiveScansModalOpen(false)} title="Active Scans"> + {fetchingActiveScans ?
Loading...
: activeScans.length === 0 ?
No active scans.
: ( +
+ {activeScans.map(scan => ( +
+
{scan.target_url}
Org ID: {scan.org_id}
+ +
+ ))} +
+ )} +
+ + {/* Prompt Modal */} + + + + + } + > +
+ {promptModal.inputs.map(input => ( +
+ + {input.type === 'select' ? ( + + ) : ( + handlePromptChange(input.key, e.target.value)} + placeholder={input.placeholder} + className="bg-surface-container border border-outline-variant rounded-lg px-3 py-2 focus:border-primary outline-none text-on-surface" + /> + )} +
+ ))} +
+
+ + {/* Confirm Modal */} + + + + + } + /> + + {/* Invoice View Modal */} + setViewInvoice(null)} + title="Invoice Details" + footer={ + <> + + + + } + > + {viewInvoice && ( +
+
+
+

LarShield

+

Payment Receipt & Invoice

+
+
+
Date
+
{new Date(viewInvoice.created_at).toLocaleString()}
+
+
+ +
+
+
Billed To
+
{viewInvoice.org_name}
+
{viewInvoice.user_email}
+
+
+
Status
+
{viewInvoice.status}
+
+
+ + + + + + + + + + + + + + +
DescriptionAmount
+ {viewInvoice.tier_id} Subscription Plan + + {viewInvoice.currency === 'INR' ? '₹' : '$'}{viewInvoice.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })} +
+ +
+
Total Amount
+
+ {viewInvoice.currency === 'INR' ? '₹' : '$'}{viewInvoice.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })} +
+
+
+ )} +
+
+ ); +}; + +export default SuperAdminPanel;