44 lines
1.6 KiB
TypeScript
44 lines
1.6 KiB
TypeScript
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
import { useAuth } from '../auth/AuthContext'
|
|
import { usePermission } from '../../hooks/usePermission'
|
|
import { useMqttTopic } from '../../hooks/useMqtt'
|
|
import { InfoBox } from '../../components/desktop/InfoBox'
|
|
import { ChartPanel } from '../../components/desktop/ChartPanel'
|
|
import { ActivityTimeline } from '../../components/desktop/ActivityTimeline'
|
|
import { fetchStats, fetchActivity } from './dashboardApi'
|
|
|
|
export function DashboardPage() {
|
|
const { user } = useAuth()
|
|
const { can } = usePermission()
|
|
const queryClient = useQueryClient()
|
|
|
|
const { data: stats } = useQuery({ queryKey: ['dashboard', 'stats'], queryFn: fetchStats })
|
|
const { data: activity } = useQuery({
|
|
queryKey: ['dashboard', 'activity'],
|
|
queryFn: fetchActivity,
|
|
enabled: can('audit.view'),
|
|
})
|
|
|
|
useMqttTopic('module/users/events', () => {
|
|
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
|
|
})
|
|
|
|
return (
|
|
<div>
|
|
<h1 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Dashboard</h1>
|
|
<p className="mt-1 mb-6 text-sm text-gray-600 dark:text-gray-400">Welcome, {user?.name}.</p>
|
|
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
<InfoBox label="Total users" value={stats?.total_users ?? '—'} />
|
|
{stats && <ChartPanel title="Users by role" data={stats.users_by_role} />}
|
|
</div>
|
|
|
|
{can('audit.view') && (
|
|
<div className="mt-4">
|
|
<ActivityTimeline items={activity ?? []} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|