demeter

Autonomous Hydroponic Intelligence

frontend/src/api/farmApi.jsx (4025B)


  1import { USE_MOCK_DATA, MOCK_DASHBOARD, MOCK_HISTORY } from "../data/mockData";
  2
  3const API_BASE_URL =
  4  process.env.REACT_APP_FARM_API_URL || "http://localhost:3001/api";
  5
  6// MongoDB CRUD
  7
  8/**
  9 * Fetches all crops
 10 */
 11export const fetchDashboardData = async () => {
 12  if (USE_MOCK_DATA) {
 13    await new Promise((r) => setTimeout(r, 300));
 14    return MOCK_DASHBOARD;
 15  }
 16
 17  try {
 18    const res = await fetch(`${API_BASE_URL}/crops/all`);
 19    if (!res.ok) throw new Error("Network error");
 20    const json = await res.json();
 21    // Unwrap { data: [...] } response from the node server
 22    return json.data || json || [];
 23  } catch {
 24    return [];
 25  }
 26};
 27
 28/**
 29 * Creates a new crop
 30 */
 31export const createCrop = async (cropData) => {
 32  const res = await fetch(`${API_BASE_URL}/crops/create`, {
 33    method: "POST",
 34    headers: { "Content-Type": "application/json" },
 35    body: JSON.stringify(cropData),
 36  });
 37  if (!res.ok) {
 38    const err = await res.json().catch(() => ({}));
 39    throw new Error(err.error || "Failed to create crop");
 40  }
 41  return res.json();
 42};
 43
 44/**
 45 * Fetches a single crop document by ID
 46 */
 47export const fetchCropById = async (cropId) => {
 48  if (USE_MOCK_DATA) {
 49    await new Promise((r) => setTimeout(r, 150));
 50    const found = MOCK_DASHBOARD.find(
 51      (d) => (d.crop_id || d._id || d.id) === cropId,
 52    );
 53    return found || null;
 54  }
 55
 56  try {
 57    const res = await fetch(`${API_BASE_URL}/crops/${cropId}`);
 58    if (!res.ok) return null;
 59    const json = await res.json();
 60    return json.data || null;
 61  } catch {
 62    return null;
 63  }
 64};
 65
 66/**
 67 * Partially updates a crop
 68 */
 69export const updateCrop = async (cropId, fields) => {
 70  const res = await fetch(`${API_BASE_URL}/crops/${cropId}`, {
 71    method: "PUT",
 72    headers: { "Content-Type": "application/json" },
 73    body: JSON.stringify(fields),
 74  });
 75  if (!res.ok) {
 76    const err = await res.json().catch(() => ({}));
 77    throw new Error(err.error || "Failed to update crop");
 78  }
 79  return res.json();
 80};
 81
 82/**
 83 * Deletes a crop
 84 */
 85export const deleteCrop = async (cropId) => {
 86  const res = await fetch(`${API_BASE_URL}/crops/${cropId}`, {
 87    method: "DELETE",
 88  });
 89  if (!res.ok) {
 90    const err = await res.json().catch(() => ({}));
 91    throw new Error(err.error || "Failed to delete crop");
 92  }
 93  return res.json();
 94};
 95
 96// Qdrant (history / analytics)
 97
 98/**
 99 * Fetches the full cycle history for a specific crop
100 */
101export const fetchCropDetails = async (cropId) => {
102  if (USE_MOCK_DATA) {
103    await new Promise((r) => setTimeout(r, 200));
104    return MOCK_HISTORY.filter((h) => h.payload?.crop_id === cropId);
105  }
106
107  try {
108    const res = await fetch(`${API_BASE_URL}/crop/${cropId}`);
109    if (!res.ok) throw new Error("Network error");
110    return res.json();
111  } catch {
112    return [];
113  }
114};
115
116/**
117 * Fetches full history for every crop present in the dashboard snapshot
118 * dashboardItems is the raw normalized array (each item has .payload.crop_id or .crop_id)
119 */
120export const fetchAllCropHistories = async (dashboardItems) => {
121  if (USE_MOCK_DATA) {
122    await new Promise((r) => setTimeout(r, 200));
123    return MOCK_HISTORY.sort((a, b) => {
124      const ta = new Date(a.payload?.timestamp || 0).getTime();
125      const tb = new Date(b.payload?.timestamp || 0).getTime();
126      return ta - tb;
127    });
128  }
129
130  if (!dashboardItems?.length) return [];
131
132  // Support both normalized { payload: { crop_id } } and flat { crop_id }
133  const cropIds = [
134    ...new Set(
135      dashboardItems
136        .map((i) => i.payload?.crop_id || i.crop_id)
137        .filter(Boolean),
138    ),
139  ];
140
141  const results = await Promise.allSettled(
142    cropIds.map((id) =>
143      fetch(`${API_BASE_URL}/crop/${id}`)
144        .then((r) => (r.ok ? r.json() : []))
145        .catch(() => []),
146    ),
147  );
148
149  const all = results.flatMap((r) => (r.status === "fulfilled" ? r.value : []));
150  return all.sort((a, b) => {
151    const ta = new Date(a.payload?.timestamp || 0).getTime();
152    const tb = new Date(b.payload?.timestamp || 0).getTime();
153    return ta - tb;
154  });
155};