feat(governance): Frontend integration and pages
- 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
This commit is contained in:
116
src/pages/GovernancePage.tsx
Normal file
116
src/pages/GovernancePage.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
Reference in New Issue
Block a user