modcomms/frontend/components/AgencyFilterBar.tsx
michael 05e74becfe Add frontend RBAC: UserContext, role-based sidebar, agency filter, user management
- Add UserRole type and AppUser interface to types.ts
- Create UserContext with useUser() hook providing role-based permission booleans
- Split App into App (auth wrapper) + AppContent (uses UserContext)
- Update Sidebar to filter nav items by UserRole instead of boolean isAdmin
- Add User Management nav item (super_admin only)
- Add AgencyFilterBar component for oversight_admin/super_admin session-level filtering
- Pass agencyId to getCampaigns, getAnalytics, audit endpoints in apiService
- Add getMe, getUsers, updateUser, createAgency to apiService
- Build UserManagement page with user table (role/agency dropdowns) and agency CRUD
- Add readOnly prop to Campaigns (hides create/delete/status-toggle for oversight_admin)
- Add readOnly prop to Settings (disables all ManagementCards, shows view-only banner)
- Pass agencyId to Analytics component for filtered data
- Update urlState with Knowledge Base and User Management views

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 08:36:38 -06:00

46 lines
1.9 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import apiService from '../services/apiService';
import type { AgencyResponse } from '../services/apiService';
import { ChevronDownIcon } from './icons/ChevronDownIcon';
interface AgencyFilterBarProps {
selectedAgencyId: string | null;
onAgencyChange: (agencyId: string | null) => void;
}
export const AgencyFilterBar: React.FC<AgencyFilterBarProps> = ({ selectedAgencyId, onAgencyChange }) => {
const [agencies, setAgencies] = useState<AgencyResponse[]>([]);
useEffect(() => {
const loadAgencies = async () => {
try {
const data = await apiService.getAgencies();
setAgencies(data);
} catch (err) {
console.error('Failed to load agencies for filter:', err);
}
};
loadAgencies();
}, []);
return (
<div className="bg-white border-b border-grey-300 px-6 py-3 flex items-center gap-3 flex-shrink-0">
<label className="text-sm font-medium text-primary-blue whitespace-nowrap">
Filter by Agency:
</label>
<div className="relative max-w-xs">
<select
value={selectedAgencyId || ''}
onChange={(e) => onAgencyChange(e.target.value || null)}
className="bg-white border-2 border-active-blue text-black-title py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none min-w-[200px]"
>
<option value="">All Agencies</option>
{agencies.map(a => (
<option key={a.id} value={a.id}>{a.name}</option>
))}
</select>
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-active-blue pointer-events-none" />
</div>
</div>
);
};