- Integrate GovernanceRolesBlock into AgentCabinet - Add ReportButton component for creating incidents - Add GovernancePage with tabs (City/Audit/Incidents) - Add /governance route to App.tsx - Export governance components from index.ts
117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
/**
|
||
* Governance Page
|
||
* Main governance dashboard with tabs for City, Audit, Incidents
|
||
*/
|
||
|
||
import React, { useState } from 'react';
|
||
import { useSearchParams } from 'react-router-dom';
|
||
import { CityGovernancePanel } from '../features/governance/components/CityGovernancePanel';
|
||
import { AuditDashboard } from '../features/governance/components/AuditDashboard';
|
||
import { IncidentsList } from '../features/governance/components/IncidentsList';
|
||
|
||
type Tab = 'city' | 'audit' | 'incidents';
|
||
|
||
export function GovernancePage() {
|
||
const [searchParams, setSearchParams] = useSearchParams();
|
||
const initialTab = (searchParams.get('tab') as Tab) || 'city';
|
||
const [activeTab, setActiveTab] = useState<Tab>(initialTab);
|
||
|
||
const handleTabChange = (tab: Tab) => {
|
||
setActiveTab(tab);
|
||
setSearchParams({ tab });
|
||
};
|
||
|
||
// TODO: Get actual actorDaisId from auth context
|
||
const actorDaisId = 'dais-demo-user';
|
||
|
||
return (
|
||
<div className="min-h-screen bg-slate-950 text-white">
|
||
{/* Header */}
|
||
<div className="bg-gradient-to-r from-slate-900 to-slate-800 border-b border-slate-700">
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||
<h1 className="text-3xl font-bold text-white flex items-center gap-3">
|
||
🏛️ DAARION.city Governance
|
||
</h1>
|
||
<p className="text-slate-400 mt-2">
|
||
Управління агентами, ролями, інцидентами та аудит подій
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Tabs */}
|
||
<div className="bg-slate-900 border-b border-slate-700 sticky top-0 z-10">
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||
<div className="flex gap-1">
|
||
<TabButton
|
||
active={activeTab === 'city'}
|
||
onClick={() => handleTabChange('city')}
|
||
icon="🏛️"
|
||
label="City Governance"
|
||
/>
|
||
<TabButton
|
||
active={activeTab === 'audit'}
|
||
onClick={() => handleTabChange('audit')}
|
||
icon="📊"
|
||
label="Audit"
|
||
/>
|
||
<TabButton
|
||
active={activeTab === 'incidents'}
|
||
onClick={() => handleTabChange('incidents')}
|
||
icon="⚠️"
|
||
label="Incidents"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content */}
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||
{activeTab === 'city' && (
|
||
<CityGovernancePanel actorId={actorDaisId} />
|
||
)}
|
||
|
||
{activeTab === 'audit' && (
|
||
<AuditDashboard />
|
||
)}
|
||
|
||
{activeTab === 'incidents' && (
|
||
<IncidentsList
|
||
actorDaisId={actorDaisId}
|
||
showCreateButton
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Tab Button Component
|
||
function TabButton({
|
||
active,
|
||
onClick,
|
||
icon,
|
||
label
|
||
}: {
|
||
active: boolean;
|
||
onClick: () => void;
|
||
icon: string;
|
||
label: string;
|
||
}) {
|
||
return (
|
||
<button
|
||
onClick={onClick}
|
||
className={`px-6 py-4 text-sm font-medium flex items-center gap-2 border-b-2 transition-colors ${
|
||
active
|
||
? 'text-white border-purple-500 bg-slate-800/50'
|
||
: 'text-slate-400 border-transparent hover:text-white hover:bg-slate-800/30'
|
||
}`}
|
||
>
|
||
<span>{icon}</span>
|
||
<span>{label}</span>
|
||
</button>
|
||
);
|
||
}
|
||
|
||
export default GovernancePage;
|
||
|