Add dashboard report snapshots feature with CRUD operations. Implemented new database migrations for dashboard_report_snapshots, including report type handling. Updated backend routes and models to support snapshot retrieval and upsert functionality. Enhanced frontend components to integrate report snapshot management, including UI for daily and end-cycle reports. Added tests for new functionality to ensure reliability.
CI / lint-and-test (push) Canceled after 0s

This commit is contained in:
Alberto-Audrix committed 2026-08-13 15:46:16 +07:00
1 parent 059087be1d
commit 34336b9bd1
41 files changed
+4824 -178

No files matched your search

+125
View File
@@ -0,0 +1,125 @@
import { describe, it, expect } from 'vitest';
import { latestValidRatio, mapKpiToEefRows, mapKpiToFcrRows } from '../reportFcrEef.ts';
const kpi = {
'0': {
food_total: 10,
iot_weight: 45.5,
chicken_life: 10000,
harvest_weight_total: 12000,
iot_fcr: 1.234,
chicken_life_percentage: 99.5,
iot_average_harvest_bw: 45.5,
iot_average_day_weight: 0.2,
iot_eef: 310.456,
},
'2': {
food_total: 20,
iot_weight: 80,
chicken_life: 9900,
harvest_weight_total: 50000,
iot_fcr: 0,
chicken_life_percentage: 98.1,
iot_average_harvest_bw: 80,
iot_average_day_weight: 2,
iot_eef: 0,
},
'8': {
food_total: 40,
iot_weight: 400,
chicken_life: 9700,
harvest_weight_total: 200000,
iot_fcr: 1.7,
chicken_life_percentage: 96.2,
iot_average_harvest_bw: 400,
iot_average_day_weight: 8,
iot_eef: 280.9,
},
};
describe('mapKpiToFcrRows', () => {
it('maps page FCR fields and keeps only days 0..upToDay', () => {
expect(mapKpiToFcrRows(kpi, 2)).toEqual([
{
day: 0,
feedTotal: 10,
iotWeight: 45.5,
chickenLife: 10000,
harvestWeightTotal: 12000,
iotFcr: 1.234,
},
{
day: 2,
feedTotal: 20,
iotWeight: 80,
chickenLife: 9900,
harvestWeightTotal: 50000,
iotFcr: null,
},
]);
});
it('returns an empty array for invalid KPI without throwing', () => {
expect(mapKpiToFcrRows(null, 8)).toEqual([]);
expect(mapKpiToFcrRows(undefined, 8)).toEqual([]);
expect(mapKpiToFcrRows('nope', 8)).toEqual([]);
});
});
describe('mapKpiToEefRows', () => {
it('maps page EEF fields and keeps only days 0..upToDay', () => {
expect(mapKpiToEefRows(kpi, 2)).toEqual([
{
day: 0,
chickenLifePercentage: 99.5,
iotAverageHarvestBw: 45.5,
iotAverageDayWeight: 0.2,
iotFcr: 1.234,
iotEef: 310.456,
},
{
day: 2,
chickenLifePercentage: 98.1,
iotAverageHarvestBw: 80,
iotAverageDayWeight: 2,
iotFcr: null,
iotEef: null,
},
]);
});
it('returns an empty array for invalid KPI without throwing', () => {
expect(mapKpiToEefRows(null, 8)).toEqual([]);
expect(mapKpiToEefRows(undefined, 8)).toEqual([]);
});
});
describe('latestValidRatio', () => {
it('returns the last non-null ratio, skipping empty later days', () => {
expect(
latestValidRatio(
[
{ day: 0, iotFcr: 1.234 },
{ day: 2, iotFcr: null },
{ day: 8, iotFcr: 1.7 },
],
'iotFcr'
)
).toBe(1.7);
expect(
latestValidRatio(
[
{ day: 0, iotEef: 310.456 },
{ day: 2, iotEef: null },
],
'iotEef'
)
).toBe(310.456);
});
it('returns null when every value is missing', () => {
expect(latestValidRatio([{ iotFcr: null }, { iotFcr: 0 }], 'iotFcr')).toBeNull();
expect(latestValidRatio([], 'iotFcr')).toBeNull();
expect(latestValidRatio(undefined, 'iotFcr')).toBeNull();
});
});