update kpi mortality card to change color based on the status

This commit is contained in:
Alberto-Audrix committed 2026-10-05 10:43:28 +07:00
1 parent cceaf38a25
commit 3fdd9b936f
3 files changed
+105 -1

No files matched your search

+4 -1
View File
@@ -21,6 +21,7 @@ import {
} from '../utils/format.ts';
import { throughVisibleDate } from '../utils/visibility.ts';
import { getCountingAccuracyWarning } from '../utils/countingAccuracyWarning.ts';
import { getDailyMortalityStatus } from '../utils/mortality.ts';
import {
buildDashboardInsightContext,
buildDashboardInsightMetrics,
@@ -176,6 +177,8 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
}));
const age = latest?.age ?? null;
const mortalityToday = latest?.mortality ?? null;
const docInCount = selectedCycle?.doc_in_count ?? null;
const mortalityStatus = getDailyMortalityStatus(mortalityToday, docInCount);
const feedToday = latest != null ? latest.feed : null;
const weightToday =
latest && latest.iot_weight > 0
@@ -198,7 +201,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
unit: 'ekor',
icon: '📉',
trend: mortalityTrend,
status: mortalityToday == null ? 'na' : 'ok',
status: mortalityStatus,
targetView: 'dataManagement',
},
{
+52
View File
@@ -0,0 +1,52 @@
import { describe, expect, it } from 'vitest';
import { getDailyMortalityPct, getDailyMortalityStatus } from '../mortality.ts';
describe('getDailyMortalityPct', () => {
it('returns null when values are missing or invalid', () => {
expect(getDailyMortalityPct(null, 10000)).toBeNull();
expect(getDailyMortalityPct(undefined, 10000)).toBeNull();
expect(getDailyMortalityPct(10, null)).toBeNull();
expect(getDailyMortalityPct(10, 0)).toBeNull();
expect(getDailyMortalityPct(10, -100)).toBeNull();
});
it('calculates correct percentage', () => {
expect(getDailyMortalityPct(5, 10000)).toBe(0.05);
expect(getDailyMortalityPct(10, 10000)).toBe(0.1);
});
});
describe('getDailyMortalityStatus', () => {
it('returns na when mortalityToday is null or undefined', () => {
expect(getDailyMortalityStatus(null, 10000)).toBe('na');
expect(getDailyMortalityStatus(undefined, 10000)).toBe('na');
});
it('returns ok when docInCount is null or 0', () => {
expect(getDailyMortalityStatus(5, null)).toBe('ok');
expect(getDailyMortalityStatus(5, 0)).toBe('ok');
});
it('returns ok when daily mortality <= 0.05%', () => {
// 0 out of 10,000 = 0%
expect(getDailyMortalityStatus(0, 10000)).toBe('ok');
// 5 out of 10,000 = 0.05%
expect(getDailyMortalityStatus(5, 10000)).toBe('ok');
// 10 out of 20,000 = 0.05%
expect(getDailyMortalityStatus(10, 20000)).toBe('ok');
});
it('returns warning when daily mortality > 0.05% and < 0.10%', () => {
// 6 out of 10,000 = 0.06%
expect(getDailyMortalityStatus(6, 10000)).toBe('warning');
// 9 out of 10,000 = 0.09%
expect(getDailyMortalityStatus(9, 10000)).toBe('warning');
});
it('returns critical when daily mortality >= 0.10%', () => {
// 10 out of 10,000 = 0.10%
expect(getDailyMortalityStatus(10, 10000)).toBe('critical');
// 25 out of 10,000 = 0.25%
expect(getDailyMortalityStatus(25, 10000)).toBe('critical');
});
});
+49
View File
@@ -0,0 +1,49 @@
import type { KpiStatus } from '../components/KpiCard.tsx';
/**
* Daily mortality % of DOC thresholds (manager operational rules):
* - <= 0.05%: Normal / OK (green)
* - > 0.05% and < 0.10%: Warning (yellow)
* - >= 0.10%: Critical (red)
*/
export const DAILY_MORTALITY_DOC_THRESHOLDS = {
warning_pct: 0.05,
critical_pct: 0.1,
} as const;
/**
* Calculates daily mortality percentage relative to initial DOC count.
*/
export function getDailyMortalityPct(
mortalityToday: number | null | undefined,
docInCount: number | null | undefined
): number | null {
if (mortalityToday == null || docInCount == null || docInCount <= 0) {
return null;
}
return (mortalityToday / docInCount) * 100;
}
/**
* Evaluates the KPI status for daily mortality based on DOC thresholds.
* - 'na' when data is unavailable
* - 'critical' when daily mortality >= 0.10%
* - 'warning' when daily mortality > 0.05%
* - 'ok' when daily mortality <= 0.05%
*/
export function getDailyMortalityStatus(
mortalityToday: number | null | undefined,
docInCount: number | null | undefined
): KpiStatus {
if (mortalityToday == null) return 'na';
if (docInCount == null || docInCount <= 0) return 'ok';
const dailyPct = (mortalityToday / docInCount) * 100;
if (dailyPct >= DAILY_MORTALITY_DOC_THRESHOLDS.critical_pct) {
return 'critical';
}
if (dailyPct > DAILY_MORTALITY_DOC_THRESHOLDS.warning_pct) {
return 'warning';
}
return 'ok';
}