145 lines
4.3 KiB
TypeScript
145 lines
4.3 KiB
TypeScript
import { apiClient } from '../client'
|
|
|
|
const PREFIX = '/api/soil'
|
|
|
|
export interface SoilSummary {
|
|
summaryKpis?: Record<string, unknown>
|
|
avg_soil_moisture?: Record<string, unknown>
|
|
anomalyDetectionCard?: Record<string, unknown>
|
|
soilMoistureHeatmap?: Record<string, unknown>
|
|
}
|
|
|
|
interface ApiResponse<T> {
|
|
code: number
|
|
msg: string
|
|
data: T
|
|
}
|
|
|
|
interface StatusResponse<T> {
|
|
status: string
|
|
data: T
|
|
}
|
|
|
|
type HeatmapPoint = {
|
|
x: string
|
|
y: number
|
|
}
|
|
|
|
type HeatmapSeries = {
|
|
name: string
|
|
data: HeatmapPoint[]
|
|
}
|
|
|
|
function extract<T>(res: ApiResponse<T> | StatusResponse<T> | T): T {
|
|
return res && typeof res === 'object' && 'data' in res ? (res as ApiResponse<T>).data : (res as T)
|
|
}
|
|
|
|
function getNumericValue(value: unknown): number {
|
|
if (typeof value === 'number' && Number.isFinite(value)) return value
|
|
|
|
const parsed = Number(value)
|
|
|
|
return Number.isFinite(parsed) ? parsed : 0
|
|
}
|
|
|
|
function normalizeHeatmapSeries(data: Record<string, unknown>): HeatmapSeries[] {
|
|
const legacySeries = Array.isArray(data.series) ? (data.series as HeatmapSeries[]) : []
|
|
|
|
if (legacySeries.length > 0) return legacySeries
|
|
|
|
const gridCells = Array.isArray(data.grid_cells) ? (data.grid_cells as Array<Record<string, unknown>>) : []
|
|
|
|
if (gridCells.length === 0) return []
|
|
|
|
const grouped = new Map<string, HeatmapPoint[]>()
|
|
|
|
gridCells.forEach((cell, index) => {
|
|
const rowLabel = String(
|
|
cell.zone_name ?? cell.zone ?? cell.row_label ?? cell.row ?? cell.y_label ?? `ردیف ${index + 1}`
|
|
)
|
|
const columnLabel = String(
|
|
cell.time_label ?? cell.col_label ?? cell.column_label ?? cell.x_label ?? cell.col ?? cell.x ?? `${index + 1}`
|
|
)
|
|
const rawValue = cell.value ?? cell.moisture ?? cell.moisture_percent ?? cell.intensity ?? cell.y
|
|
const value = getNumericValue(rawValue)
|
|
const current = grouped.get(rowLabel) ?? []
|
|
|
|
current.push({ x: columnLabel, y: value })
|
|
grouped.set(rowLabel, current)
|
|
})
|
|
|
|
return Array.from(grouped.entries()).map(([name, points]) => ({
|
|
name,
|
|
data: points
|
|
}))
|
|
}
|
|
|
|
function buildHealthScoreKpi(summary: Record<string, unknown>): Record<string, unknown> {
|
|
const healthScore = getNumericValue(summary.healthScore)
|
|
const profileSource = String(summary.profileSource ?? 'مرجع خاک')
|
|
const healthLanguage =
|
|
summary.healthLanguage && typeof summary.healthLanguage === 'object'
|
|
? (summary.healthLanguage as Record<string, unknown>)
|
|
: {}
|
|
const chipText = String(healthLanguage.short_chip_text ?? summary.avgSoilMoistureStatus ?? '-')
|
|
|
|
let chipColor: 'success' | 'warning' | 'error' = 'success'
|
|
|
|
if (healthScore < 45) chipColor = 'error'
|
|
else if (healthScore < 70) chipColor = 'warning'
|
|
|
|
return {
|
|
id: 'soil_health_score',
|
|
title: 'سلامت خاک',
|
|
subtitle: profileSource,
|
|
stats: `${Math.round(healthScore)} / 100`,
|
|
avatarColor: chipColor === 'error' ? 'error' : chipColor === 'warning' ? 'warning' : 'success',
|
|
avatarIcon: 'tabler-activity-heartbeat',
|
|
chipText,
|
|
chipColor
|
|
}
|
|
}
|
|
|
|
export const soilService = {
|
|
async getSummary(farmUuid: string): Promise<SoilSummary> {
|
|
const res = await apiClient.get<ApiResponse<Record<string, unknown>> | Record<string, unknown>>(
|
|
`${PREFIX}/summary/?farm_uuid=${encodeURIComponent(farmUuid)}`
|
|
)
|
|
const data = extract(res)
|
|
|
|
return {
|
|
summaryKpis: { kpis: [buildHealthScoreKpi(data)] }
|
|
}
|
|
},
|
|
|
|
async getAvgMoisture(farmUuid: string): Promise<Record<string, unknown>> {
|
|
const res = await apiClient.get<StatusResponse<Record<string, unknown>> | Record<string, unknown>>(
|
|
`${PREFIX}/avg-moisture/?farm_uuid=${encodeURIComponent(farmUuid)}`
|
|
)
|
|
const data = extract(res)
|
|
|
|
return {
|
|
kpis: [data]
|
|
}
|
|
},
|
|
|
|
async getAnomalies(farmUuid: string): Promise<Record<string, unknown>> {
|
|
const res = await apiClient.get<ApiResponse<Record<string, unknown>> | Record<string, unknown>>(
|
|
`${PREFIX}/anomalies/?farm_uuid=${encodeURIComponent(farmUuid)}`
|
|
)
|
|
return extract(res)
|
|
},
|
|
|
|
async getMoistureHeatmap(farmUuid: string): Promise<Record<string, unknown>> {
|
|
const res = await apiClient.get<ApiResponse<Record<string, unknown>> | Record<string, unknown>>(
|
|
`${PREFIX}/moisture-heatmap/?farm_uuid=${encodeURIComponent(farmUuid)}`
|
|
)
|
|
const data = extract(res)
|
|
|
|
return {
|
|
...data,
|
|
series: normalizeHeatmapSeries(data)
|
|
}
|
|
},
|
|
}
|