33 lines
1.2 KiB
TypeScript
33 lines
1.2 KiB
TypeScript
|
|
import { useEffect, useState } from 'react';
|
|||
|
|
import PageHeader from '../../components/PageHeader';
|
|||
|
|
import SlaHeatmap from '../../components/charts/SlaHeatmap';
|
|||
|
|
import { getSla } from '../../services/analytics.service';
|
|||
|
|
import type { SlaMetric } from '../../types/analytics.types';
|
|||
|
|
import styles from './SlaKpi.module.scss';
|
|||
|
|
|
|||
|
|
export default function SlaKpi({ refreshKey }: { refreshKey: number }) {
|
|||
|
|
const [metrics, setMetrics] = useState<SlaMetric[] | null>(null);
|
|||
|
|
const [error, setError] = useState(false);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
let alive = true;
|
|||
|
|
setError(false);
|
|||
|
|
getSla()
|
|||
|
|
.then(r => { if (alive) setMetrics(r.metrics); })
|
|||
|
|
.catch(() => { if (alive) setError(true); });
|
|||
|
|
return () => { alive = false; };
|
|||
|
|
}, [refreshKey]);
|
|||
|
|
|
|||
|
|
if (error) return <div className={styles.stateError}>Failed to load SLA metrics.</div>;
|
|||
|
|
if (!metrics) return <div className={styles.state}>Loading SLA metrics…</div>;
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className={styles.page}>
|
|||
|
|
<PageHeader title="PM KPIs — SLA" subtitle="Per-PM × ticket-size averages vs configured norms (green = on target, red = over)" />
|
|||
|
|
<div className={styles.grid}>
|
|||
|
|
{metrics.map(m => <SlaHeatmap key={m.key} metric={m} />)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|