feat: add multi-BU tenancy with per-user team scoping (Option B)
- Add bu_teams column to users table (migration + fresh schema) - Create shared KNOWN_TEAMS constant and validateTeams helper - Expose user teams in auth middleware, login, and /me responses - Add bu_teams CRUD to user management routes with audit logging - Make Ivanti FINDINGS_FILTERS configurable via IVANTI_BU_FILTER env var - Add query-time team filtering to GET /findings and /findings/counts - Update AuthContext with teams helpers and admin scope toggle - Create AdminScopeToggle component (My Teams / All BUs) - Scope ReportingPage findings fetch by user teams - Scope CompliancePage team selector by user teams - Scope ExportsPage findings exports by user teams - Add BU teams multi-select to UserManagement create/edit forms - Display team badges in user list table
This commit is contained in:
61
frontend/src/components/AdminScopeToggle.js
Normal file
61
frontend/src/components/AdminScopeToggle.js
Normal file
@@ -0,0 +1,61 @@
|
||||
// AdminScopeToggle.js
|
||||
// Two-state toggle for Admin users: "My Teams" vs "All BUs"
|
||||
// Controls whether data on Reporting, Compliance, and Exports pages
|
||||
// is scoped to the admin's assigned teams or shows everything.
|
||||
|
||||
import React from 'react';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
function AdminScopeToggle() {
|
||||
const { isAdmin, adminScope, toggleAdminScope, hasTeams } = useAuth();
|
||||
|
||||
// Only render for Admin users who have teams assigned
|
||||
// (if no teams assigned, both modes are identical — no toggle needed)
|
||||
if (!isAdmin() || !hasTeams()) return null;
|
||||
|
||||
const isAllMode = adminScope === 'all';
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.4rem',
|
||||
padding: '0.25rem 0.5rem',
|
||||
borderRadius: '0.375rem',
|
||||
background: 'rgba(14, 165, 233, 0.05)',
|
||||
border: '1px solid rgba(14, 165, 233, 0.2)',
|
||||
fontSize: '0.7rem',
|
||||
fontFamily: 'monospace',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#64748B', fontWeight: '500' }}>Scope:</span>
|
||||
<button
|
||||
onClick={toggleAdminScope}
|
||||
aria-label={`Switch to ${isAllMode ? 'My Teams' : 'All BUs'} view`}
|
||||
aria-pressed={isAllMode}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.25rem',
|
||||
padding: '0.2rem 0.5rem',
|
||||
borderRadius: '0.25rem',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.68rem',
|
||||
fontWeight: '600',
|
||||
letterSpacing: '0.02em',
|
||||
transition: 'all 0.15s ease',
|
||||
background: isAllMode ? 'rgba(139, 92, 246, 0.15)' : 'rgba(14, 165, 233, 0.15)',
|
||||
color: isAllMode ? '#8B5CF6' : '#0EA5E9',
|
||||
}}
|
||||
>
|
||||
{isAllMode ? '⊕ All BUs' : '⊙ My Teams'}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AdminScopeToggle;
|
||||
Reference in New Issue
Block a user