Files
Frontend/src/libs/api/services/farmDashboardService.ts
T

268 lines
7.7 KiB
TypeScript
Raw Normal View History

/**
* Farm Dashboard Service
* Handles API calls for dashboard config and card data.
* - Config: disabled cards, row order, drag reorder
* - Cards: all 15 card payloads from /api/farm-dashboard/
*/
2026-03-26 15:25:17 +03:30
import { apiClient } from "../client";
import {
CARD_IDS,
CARD_TO_ROW,
ROW_CARDS,
ROW_IDS,
type FarmDashboardConfig,
type CardId,
type RowId,
} from "@/views/dashboards/farm/farmDashboardConfig";
export interface ApiResponse<T> {
2026-03-26 15:25:17 +03:30
code: number;
msg: string;
data: T;
}
export interface FarmDashboardConfigResponse {
2026-04-02 23:44:24 +03:30
farm_uuid?: string;
2026-03-26 15:25:17 +03:30
disabled_card_ids: string[];
row_order: string[];
enable_drag_reorder?: boolean;
}
/** API response shape for /api/farm-dashboard/ - each key matches CardId */
export interface FarmDashboardCardsResponse {
2026-03-26 15:25:17 +03:30
farmOverviewKpis?: Record<string, unknown>;
farmWeatherCard?: Record<string, unknown>;
farmAlertsTracker?: Record<string, unknown>;
sensorValuesList?: Record<string, unknown>;
sensorRadarChart?: Record<string, unknown>;
sensorComparisonChart?: Record<string, unknown>;
anomalyDetectionCard?: Record<string, unknown>;
farmAlertsTimeline?: Record<string, unknown>;
waterNeedPrediction?: Record<string, unknown>;
harvestPredictionCard?: Record<string, unknown>;
yieldPredictionChart?: Record<string, unknown>;
soilMoistureHeatmap?: Record<string, unknown>;
ndviHealthCard?: Record<string, unknown>;
recommendationsList?: Record<string, unknown>;
economicOverview?: Record<string, unknown>;
}
2026-03-26 15:25:17 +03:30
interface FarmDashboardCardsTaskResult {
2026-04-02 23:44:24 +03:30
farm_uuid?: string;
2026-03-26 15:25:17 +03:30
all_cards?: FarmDashboardCardsResponse;
}
interface FarmDashboardCardsTaskData {
task_id?: string;
status?: string;
result?: FarmDashboardCardsTaskResult;
}
2026-04-02 23:44:24 +03:30
const STORAGE_KEY_PREFIX = "farm_dashboard_config";
function getStorageKey(farmUuid: string): string {
return `${STORAGE_KEY_PREFIX}:${farmUuid}`;
}
2026-03-26 15:25:17 +03:30
function isCardId(value: string): value is CardId {
return (CARD_IDS as readonly string[]).includes(value);
}
function isRowId(value: string): value is RowId {
return (ROW_IDS as readonly string[]).includes(value);
}
function normalizeDisabledCardIds(disabledIds: string[] = []): string[] {
return Array.from(
new Set(
disabledIds.flatMap((id) => {
if (isCardId(id)) return [id];
if (isRowId(id)) return ROW_CARDS[id];
return [];
}),
),
);
}
function normalizeRowOrder(rowOrder: string[] = []): string[] {
const normalized = rowOrder
.map((id) => {
if (isRowId(id)) return id;
if (isCardId(id)) return CARD_TO_ROW[id];
return null;
})
.filter((id): id is RowId => !!id);
const deduped = Array.from(new Set(normalized));
return deduped.length
? [...deduped, ...ROW_IDS.filter((id) => !deduped.includes(id))]
: [...ROW_IDS];
}
function extractCardsPayload(
response:
| ApiResponse<FarmDashboardCardsResponse>
| ApiResponse<FarmDashboardCardsTaskData>
| FarmDashboardCardsResponse
| FarmDashboardCardsTaskData,
): Partial<Record<CardId, Record<string, unknown>>> {
const raw = response && "data" in response ? response.data : response;
if (!raw || typeof raw !== "object") {
return {};
}
if ("result" in raw && raw.result && typeof raw.result === "object") {
return (raw.result.all_cards ?? {}) as Partial<
Record<CardId, Record<string, unknown>>
>;
}
return raw as Partial<Record<CardId, Record<string, unknown>>>;
}
/**
* Transform API response to frontend config format
*/
2026-03-26 15:25:17 +03:30
function fromApiResponse(
data: FarmDashboardConfigResponse,
): FarmDashboardConfig {
return {
2026-03-26 15:25:17 +03:30
disabledCardIds: normalizeDisabledCardIds(data.disabled_card_ids),
rowOrder: normalizeRowOrder(data.row_order),
enableDragReorder: data.enable_drag_reorder ?? true,
};
}
/**
* Transform frontend config to API request format
*/
2026-03-26 15:25:17 +03:30
function toApiRequest(
config: Partial<FarmDashboardConfig>,
): Partial<FarmDashboardConfigResponse> {
const req: Partial<FarmDashboardConfigResponse> = {};
if (config.disabledCardIds !== undefined)
req.disabled_card_ids = config.disabledCardIds;
if (config.rowOrder !== undefined) req.row_order = config.rowOrder;
if (config.enableDragReorder !== undefined)
req.enable_drag_reorder = config.enableDragReorder;
return req;
}
/**
* localStorage fallback when backend is not ready
*/
2026-04-02 23:44:24 +03:30
function getLocalConfig(farmUuid: string): FarmDashboardConfig | null {
2026-03-26 15:25:17 +03:30
if (typeof window === "undefined") return null;
try {
2026-04-02 23:44:24 +03:30
const stored = localStorage.getItem(getStorageKey(farmUuid));
2026-03-26 15:25:17 +03:30
return stored ? (JSON.parse(stored) as FarmDashboardConfig) : null;
} catch {
2026-03-26 15:25:17 +03:30
return null;
}
}
2026-04-02 23:44:24 +03:30
function setLocalConfig(farmUuid: string, config: FarmDashboardConfig): void {
2026-03-26 15:25:17 +03:30
if (typeof window === "undefined") return;
try {
2026-04-02 23:44:24 +03:30
localStorage.setItem(getStorageKey(farmUuid), JSON.stringify(config));
} catch (e) {
2026-03-26 15:25:17 +03:30
console.error("Failed to save farm dashboard config to localStorage", e);
}
}
2026-04-02 23:44:24 +03:30
function buildFarmQuery(farmUuid: string): string {
return `farm_uuid=${encodeURIComponent(farmUuid)}`;
}
export const farmDashboardService = {
/**
2026-04-02 23:44:24 +03:30
* Get farm dashboard config for the selected farm
*/
2026-04-02 23:44:24 +03:30
async getConfig(farmUuid: string): Promise<FarmDashboardConfig> {
try {
const response = await apiClient.get<
ApiResponse<FarmDashboardConfigResponse> | FarmDashboardConfigResponse
2026-04-02 23:44:24 +03:30
>(`/api/farm-dashboard-config/?${buildFarmQuery(farmUuid)}`);
2026-03-26 15:25:17 +03:30
const raw = response && "data" in response ? response.data : response;
if (
raw &&
typeof raw === "object" &&
("disabled_card_ids" in raw || "row_order" in raw)
) {
return fromApiResponse(raw as FarmDashboardConfigResponse);
}
2026-03-26 15:25:17 +03:30
throw new Error("Invalid response");
} catch {
2026-04-02 23:44:24 +03:30
const local = getLocalConfig(farmUuid);
2026-03-26 15:25:17 +03:30
if (local) return local;
return { disabledCardIds: [], rowOrder: [], enableDragReorder: true };
}
},
/**
* Update farm dashboard config
*/
2026-03-26 15:25:17 +03:30
async updateConfig(
2026-04-02 23:44:24 +03:30
farmUuid: string,
2026-03-26 15:25:17 +03:30
data: Partial<FarmDashboardConfig>,
): Promise<FarmDashboardConfig> {
try {
const response = await apiClient.patch<
ApiResponse<FarmDashboardConfigResponse> | FarmDashboardConfigResponse
2026-04-02 23:44:24 +03:30
>("/api/farm-dashboard-config/", {
farm_uuid: farmUuid,
...toApiRequest(data),
});
2026-03-26 15:25:17 +03:30
const raw = response && "data" in response ? response.data : response;
if (
raw &&
typeof raw === "object" &&
("disabled_card_ids" in raw || "row_order" in raw)
) {
const config = fromApiResponse(raw as FarmDashboardConfigResponse);
2026-04-02 23:44:24 +03:30
setLocalConfig(farmUuid, config);
2026-03-26 15:25:17 +03:30
return config;
}
2026-03-26 15:25:17 +03:30
throw new Error("Update failed");
} catch (err) {
2026-04-02 23:44:24 +03:30
const local = getLocalConfig(farmUuid);
if (local) {
const merged: FarmDashboardConfig = {
disabledCardIds: data.disabledCardIds ?? local.disabledCardIds,
rowOrder: data.rowOrder ?? local.rowOrder,
2026-03-26 15:25:17 +03:30
enableDragReorder:
data.enableDragReorder ?? local.enableDragReorder ?? true,
};
2026-04-02 23:44:24 +03:30
setLocalConfig(farmUuid, merged);
2026-03-26 15:25:17 +03:30
return merged;
}
2026-03-26 15:25:17 +03:30
throw err;
}
},
/**
* Get all dashboard card data from API
* Response: { code: 200, msg: "OK", data: { farmOverviewKpis, farmWeatherCard, ... } }
*/
2026-04-02 23:44:24 +03:30
async getAllCards(
farmUuid: string,
): Promise<
2026-03-26 15:25:17 +03:30
Partial<Record<CardId, Record<string, unknown>>>
> {
try {
2026-03-26 15:25:17 +03:30
const response = await apiClient.get<
| ApiResponse<FarmDashboardCardsResponse>
| ApiResponse<FarmDashboardCardsTaskData>
| FarmDashboardCardsResponse
| FarmDashboardCardsTaskData
2026-04-02 23:44:24 +03:30
>(`/api/farm-dashboard/?${buildFarmQuery(farmUuid)}`);
2026-03-26 15:25:17 +03:30
return extractCardsPayload(response);
} catch {
2026-03-26 15:25:17 +03:30
return {};
}
2026-03-26 15:25:17 +03:30
},
};