update kpi mortality card to change color based on the status
This commit is contained in:
1 parent
cceaf38a25
commit
3fdd9b936f
3 files changed
+105
-1
No files matched your search
@@ -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',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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';
|
||||
}
|
||||
Reference in new issue
Block a user