Merged all the changes from bhakti's branch into main.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
const Ai = () => {
|
||||
const [selectedFile, setSelectedFile] = useState(null);
|
||||
const [prediction, setPrediction] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
setSelectedFile(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!selectedFile) return;
|
||||
|
||||
setLoading(true);
|
||||
setPrediction("");
|
||||
setError("");
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("image", selectedFile);
|
||||
|
||||
const response = await fetch("http://localhost:3000/predict", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Prediction request failed");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
// Assuming the API returns a JSON object with an "output" field.
|
||||
setPrediction(result.data || "No prediction returned");
|
||||
} catch (err) {
|
||||
console.error("Error during prediction:", err);
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto p-6 bg-white shadow rounded-lg">
|
||||
<h2 className="text-2xl font-bold mb-4 text-center">
|
||||
Plant disease prediction
|
||||
</h2>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleFileChange}
|
||||
className="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 p-2"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !selectedFile}
|
||||
className="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
{loading ? "Predicting..." : "Predict"}
|
||||
</button>
|
||||
</form>
|
||||
{error && <p className="mt-4 text-center text-red-600">{error}</p>}
|
||||
{prediction && (
|
||||
<div className="mt-4 p-4 bg-green-100 border border-green-300 rounded">
|
||||
<h3 className="text-lg font-semibold">Prediction:</h3>
|
||||
<p>{prediction}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Ai;
|
||||
@@ -12,7 +12,7 @@ const Dashboard = () => {
|
||||
<AddFarm />
|
||||
</div>
|
||||
|
||||
<div className="mb-4 flex space-x-4">
|
||||
<div className="mb-4 flex space-x-10">
|
||||
<Piechart />
|
||||
<TotalSpent />
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { useCreateCropMutation } from "../../../store/api/cropApi";
|
||||
|
||||
const AddCrop = ({ farmId }) => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
@@ -12,6 +13,7 @@ const AddCrop = ({ farmId }) => {
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [success, setSuccess] = useState("");
|
||||
const [createCrop] = useCreateCropMutation();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
@@ -28,16 +30,19 @@ const AddCrop = ({ farmId }) => {
|
||||
if (image) {
|
||||
formData.append("image", image);
|
||||
}
|
||||
console.log(formData);
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:8000/api/v1/crop`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: formData,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to create crop");
|
||||
}
|
||||
const response = await createCrop(formData);
|
||||
// const response = await fetch(`http://localhost:8000/api/v1/crop`, {
|
||||
// method: "POST",
|
||||
// credentials: "include",
|
||||
// body: formData,
|
||||
// });
|
||||
// if (!response.ok) {
|
||||
// throw new Error("Failed to create crop");
|
||||
// }
|
||||
|
||||
|
||||
setSuccess("Crop created successfully!");
|
||||
// Reset form fields
|
||||
setName("");
|
||||
@@ -46,6 +51,7 @@ const AddCrop = ({ farmId }) => {
|
||||
setGrowthStage("");
|
||||
setHealthStatus("");
|
||||
setImage(null);
|
||||
setIsModalOpen(false);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useCreateFarmMutation } from "../../../store/api/farmApi";
|
||||
|
||||
const AddFarm = () => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
@@ -11,6 +12,8 @@ const AddFarm = () => {
|
||||
const [error, setError] = useState(null);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const navigator = useNavigate();
|
||||
|
||||
const [createFarm] = useCreateFarmMutation();
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const farmData = {
|
||||
@@ -21,26 +24,16 @@ const AddFarm = () => {
|
||||
size: sizeContent,
|
||||
};
|
||||
|
||||
console.log(farmData);
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch("http://localhost:8000/api/v1/farm", {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(farmData),
|
||||
});
|
||||
const res = await createFarm(farmData);
|
||||
|
||||
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to add farm");
|
||||
if (res.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
navigator("farmpage");
|
||||
setSuccess(true);
|
||||
setError(null);
|
||||
setIsModalOpen(false);
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
import React, { useState } from "react";
|
||||
import Loader from "../../../components/Loader";
|
||||
import { useAddTransactionMutation } from "../../../store/api/financeApi";
|
||||
|
||||
const AddTransaction = ({ farmId, financeId }) => {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [type, setType] = useState("Expense");
|
||||
const [amount, setAmount] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState("");
|
||||
const [addTransaction] = useAddTransactionMutation();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setMessage("");
|
||||
|
||||
const transactionData = {
|
||||
type,
|
||||
amount: parseFloat(amount),
|
||||
description,
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
const response = await addTransaction({ financeId, transactionData });
|
||||
// const response = await fetch(
|
||||
// `http://localhost:8000/api/v1/finance/${farmId}/transaction`,
|
||||
// {
|
||||
// method: "POST",
|
||||
// credentials: "include",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// type,
|
||||
// amount: parseFloat(amount),
|
||||
// description,
|
||||
// }),
|
||||
// }
|
||||
// );
|
||||
|
||||
// if (!response.ok) {
|
||||
// throw new Error("Failed to create transaction");
|
||||
// }
|
||||
|
||||
// const data = await response.json();
|
||||
|
||||
setMessage("Transaction created successfully!");
|
||||
// Optionally clear the form
|
||||
setType("Expense");
|
||||
setAmount("");
|
||||
setDescription("");
|
||||
} catch (error) {
|
||||
console.error("Error creating transaction:", error);
|
||||
setMessage("Error creating transaction.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setModalOpen(true)}
|
||||
className="block text-white bg-green-600 hover:bg-green-700 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center"
|
||||
type="button"
|
||||
>
|
||||
Add Transaction
|
||||
</button>
|
||||
|
||||
{modalOpen && (
|
||||
<div className="fixed inset-0 z-50 flex justify-center items-center overflow-y-auto overflow-x-hidden bg-black bg-opacity-50">
|
||||
<div className="relative p-4 w-full max-w-md">
|
||||
<div className="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
||||
{/* Modal Header */}
|
||||
<div className="flex justify-between items-center p-4 border-b rounded-t dark:border-gray-600">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
Add Transaction
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => setModalOpen(false)}
|
||||
type="button"
|
||||
className="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
||||
>
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/* Modal Body */}
|
||||
<div className="p-4">
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="transaction-type"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Type
|
||||
</label>
|
||||
<select
|
||||
id="transaction-type"
|
||||
value={type}
|
||||
onChange={(e) => setType(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
|
||||
required
|
||||
>
|
||||
<option value="Expense">Expense</option>
|
||||
<option value="Revenue">Revenue</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="transaction-amount"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Amount
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="transaction-amount"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="transaction-description"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
id="transaction-description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full inline-flex items-center justify-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
{loading ? <Loader></Loader> : "Add Transaction"}
|
||||
</button>
|
||||
</form>
|
||||
{message && (
|
||||
<p className="mt-4 text-sm text-center text-green-600">
|
||||
{message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddTransaction;
|
||||
@@ -0,0 +1,180 @@
|
||||
import React, { useState } from "react";
|
||||
import { useCreateTaskMutation } from "../../../store/api/taskApi";
|
||||
|
||||
const CreateTask = ({ farmId, onTaskCreated }) => {
|
||||
const [farm, setFarm] = useState(farmId);
|
||||
const [taskType, setTaskType] = useState("Sowing");
|
||||
const [description, setDescription] = useState("Started Sowing the seeds");
|
||||
const [assignedDate, setAssignedDate] = useState("2025-04-15");
|
||||
const [status, setStatus] = useState("Pending");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState("");
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
const [createTask] = useCreateTaskMutation();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setMessage("");
|
||||
|
||||
// In a real app, you might also fetch or choose a crop.
|
||||
// For now, we assume crop is provided.
|
||||
const crop = "67ba388f9c4979463e64a39a";
|
||||
const taskData = {
|
||||
farm,
|
||||
crop,
|
||||
taskType,
|
||||
description,
|
||||
assignedDate,
|
||||
status,
|
||||
};
|
||||
|
||||
|
||||
|
||||
try {
|
||||
const res = await createTask(taskData).unwrap();
|
||||
|
||||
setMessage("Task created successfully!");
|
||||
setModalOpen(false);
|
||||
// Call the parent's callback with the newly created task
|
||||
if (onTaskCreated) onTaskCreated(data);
|
||||
} catch (error) {
|
||||
console.error("Error creating task:", error);
|
||||
setMessage("Error creating task.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setModalOpen(true)}
|
||||
className="block text-white bg-green-600 hover:bg-green-700 focus:ring-4 focus:outline-none focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center"
|
||||
type="button"
|
||||
>
|
||||
Create Task
|
||||
</button>
|
||||
|
||||
{modalOpen && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-75">
|
||||
<div className="relative w-full max-w-2xl p-6 bg-white rounded-lg shadow-xl">
|
||||
<div className="flex justify-between items-center border-b pb-3">
|
||||
<h2 className="text-2xl font-bold text-gray-800">Create Task</h2>
|
||||
<button
|
||||
onClick={() => setModalOpen(false)}
|
||||
type="button"
|
||||
className="text-gray-600 hover:text-gray-800"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<form onSubmit={handleSubmit} className="space-y-5">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="task-type"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Task Type
|
||||
</label>
|
||||
<select
|
||||
id="task-type"
|
||||
value={taskType}
|
||||
onChange={(e) => setTaskType(e.target.value)}
|
||||
className="mt-2 block w-full rounded-md border-gray-300 shadow-sm p-3 focus:ring-2 focus:ring-green-400"
|
||||
required
|
||||
>
|
||||
<option value="Sowing">Sowing</option>
|
||||
<option value="Watering">Watering</option>
|
||||
<option value="Fertilization">Fertilization</option>
|
||||
<option value="Pest Control">Pest Control</option>
|
||||
<option value="Harvesting">Harvesting</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="description"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
id="description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm p-2 focus:ring-2 focus:ring-green-400"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="assignedDate"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Assigned Date
|
||||
</label>
|
||||
<input
|
||||
type="date"
|
||||
id="assignedDate"
|
||||
value={assignedDate}
|
||||
onChange={(e) => setAssignedDate(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm p-2 focus:ring-2 focus:ring-green-400"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="status"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Status
|
||||
</label>
|
||||
<select
|
||||
id="status"
|
||||
value={status}
|
||||
onChange={(e) => setStatus(e.target.value)}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm p-2 focus:ring-2 focus:ring-green-400"
|
||||
required
|
||||
>
|
||||
<option value="Pending">Pending</option>
|
||||
<option value="Completed">Completed</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full inline-flex justify-center py-2 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
{loading ? "Creating Task..." : "Create Task"}
|
||||
</button>
|
||||
</form>
|
||||
{message && (
|
||||
<p className="mt-4 text-center text-sm text-green-600">
|
||||
{message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateTask;
|
||||
@@ -1,29 +1,47 @@
|
||||
import React, { useState } from "react";
|
||||
import Laoder from "../../../components/Laoder";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Loader from "../../../components/Loader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useCreateFinanceMutation } from "../../../store/api/financeApi";
|
||||
import { useGetFarmByIdQuery } from "../../../store/api/farmApi";
|
||||
|
||||
const CreateFinance = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState("");
|
||||
// Hardcoded farm ID from your example
|
||||
const farmId = "67b9e6829c4979463e64a0fc";
|
||||
|
||||
const [farm, setFarm] = useState("");
|
||||
// Hardcoded farm ID from your example
|
||||
const { farmId } = useParams();
|
||||
const [createFinance] = useCreateFinanceMutation();
|
||||
|
||||
const { data: farmData, isLoading, error } = useGetFarmByIdQuery(farmId);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !error && farmData) {
|
||||
setFarm(farmData);
|
||||
}
|
||||
}, [farmData]);
|
||||
const handleCreateFinance = async () => {
|
||||
setLoading(true);
|
||||
setMessage("");
|
||||
try {
|
||||
const response = await fetch("http://localhost:8000/api/v1/finance", {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ farm: farmId }),
|
||||
});
|
||||
const responce = await createFinance({ farm: farmId });
|
||||
// const response = await fetch("http://localhost:8000/api/v1/finance", {
|
||||
// method: "POST",
|
||||
// credentials: "include",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// body: JSON.stringify({ farm: farmId }),
|
||||
// });
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to create finance");
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("Finance response:", data);
|
||||
|
||||
setMessage("Finance created successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error creating finance:", error);
|
||||
@@ -34,17 +52,13 @@ const CreateFinance = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto p-4 border rounded shadow">
|
||||
<h2 className="text-xl font-bold mb-4">Create Finance</h2>
|
||||
<button
|
||||
onClick={handleCreateFinance}
|
||||
disabled={loading}
|
||||
className="mt-4 w-full inline-flex items-center justify-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
{loading ? <Laoder></Laoder> : "Create Finance"}
|
||||
</button>
|
||||
{message && <p className="mt-4 text-sm text-green-600">{message}</p>}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleCreateFinance}
|
||||
disabled={loading}
|
||||
className="mt-4 w-30 inline-flex items-center justify-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
Create Finance
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import Farm from "./Farm";
|
||||
import CropTable from "./CropTable";
|
||||
import Transactions from "./Transactions";
|
||||
import CreateTransactions from "./CreateTransactions";
|
||||
import Loader from "../../../components/Loader";
|
||||
import AddTransaction from "./AddTransactions";
|
||||
import FinanceSummary from "./FinanceSummary";
|
||||
import CreateTask from "./CreateTask";
|
||||
import DisplayTast from "./DisplayTask";
|
||||
import { useGetFarmByIdQuery } from "../../../store/api/farmApi";
|
||||
import {
|
||||
useCropHarvestQuery,
|
||||
useGetCropByIdQuery,
|
||||
useSuggestFertilizersQuery,
|
||||
useSuggestNextCropQuery,
|
||||
useSuggestPesticidesQuery,
|
||||
} from "../../../store/api/cropApi";
|
||||
import { PiPottedPlantFill } from "react-icons/pi";
|
||||
import { GiGrimReaper } from "react-icons/gi";
|
||||
import { GiProgression } from "react-icons/gi";
|
||||
|
||||
function formatDate(isoString) {
|
||||
const date = new Date(isoString);
|
||||
return date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
export default function CropPage() {
|
||||
const { cropId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [farmData, setFarmData] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const farmId = cropId;
|
||||
|
||||
|
||||
|
||||
const { data: farm, error, isLoading } = useGetFarmByIdQuery(farmId);
|
||||
const {
|
||||
data: crop,
|
||||
error: cropError,
|
||||
isLoading: cropLoading,
|
||||
} = useGetCropByIdQuery(cropId);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !error && farm) {
|
||||
setFarmData(farm);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [farm]);
|
||||
|
||||
|
||||
const {
|
||||
data: harvest,
|
||||
isLoading: harvestLoading,
|
||||
error: harvestError,
|
||||
} = useCropHarvestQuery(cropId);
|
||||
|
||||
|
||||
|
||||
const {
|
||||
data: pesticides,
|
||||
isLoading: pesticideLoading,
|
||||
error: pesticideError,
|
||||
} = useSuggestPesticidesQuery(cropId);
|
||||
|
||||
const {
|
||||
data: fertilizers,
|
||||
isLoading: fertilizerLoading,
|
||||
error: fertilizerError,
|
||||
} = useSuggestFertilizersQuery(cropId);
|
||||
|
||||
const {
|
||||
data: nextCrop,
|
||||
isLoading: nextCropLoading,
|
||||
error: nextCropError,
|
||||
} = useSuggestNextCropQuery(cropId);
|
||||
|
||||
return (
|
||||
<div className="w-full bg-white rounded-lg shadow p-4 space-y-8">
|
||||
{/* Header Section */}
|
||||
<header className="mb-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<h2 className="text-2xl font-bold">{crop?.name}</h2>
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
type="button"
|
||||
className="bg-green-500 hover:bg-green-600 text-white font-semibold py-2 px-4 rounded"
|
||||
onClick={() => navigate(`/user/dashboard`)}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Crop Table Section */}
|
||||
<section className="w-full flex justify-center">
|
||||
<img src={crop?.image} className="w-1/2 h-auto rounded-md" alt="" />
|
||||
</section>
|
||||
|
||||
<section className="w-full flex justify-center flex-col">
|
||||
<div className="w-full h-auto flex items-center justify-between gap-5">
|
||||
<div className="w-full">
|
||||
<button
|
||||
data-tooltip-target="tooltip-light"
|
||||
data-tooltip-style="light"
|
||||
type="button"
|
||||
class="text-white w-full text-md font-semibold bg-green-700 hover:bg-green-800 focus:ring-4 inline-flex items-center gap-2 focus:outline-none focus:ring-green-300 rounded-lg px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||
>
|
||||
<PiPottedPlantFill className="text-2xl" />{" "}
|
||||
{formatDate(crop?.plantedDate)}
|
||||
</button>
|
||||
<div
|
||||
id="tooltip-light"
|
||||
role="tooltip"
|
||||
class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 tooltip"
|
||||
>
|
||||
Planted Date
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<button
|
||||
data-tooltip-target="tooltip-light1"
|
||||
data-tooltip-style="light"
|
||||
type="button"
|
||||
class=" w-full text-black text-md font-semibold bg-green-200 hover:bg-green-300 focus:ring-4 inline-flex items-center gap-2 focus:outline-none focus:ring-green-300 rounded-lg px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||
>
|
||||
<GiGrimReaper className="text-2xl" />{" "}
|
||||
{formatDate(crop?.harvestDate)}
|
||||
</button>
|
||||
<div
|
||||
id="tooltip-light1"
|
||||
role="tooltip"
|
||||
class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 tooltip"
|
||||
>
|
||||
Harvest Date
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<button
|
||||
data-tooltip-target="tooltip-light2"
|
||||
data-tooltip-style="light"
|
||||
type="button"
|
||||
class="text-black w-full text-md font-semibold bg-white hover:bg-green-50 focus:ring-4 inline-flex items-center gap-2 focus:outline-none focus:ring-orange-300 border-2 border-green-200 rounded-lg px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||
>
|
||||
<GiProgression className="text-2xl" />{" "}
|
||||
{formatDate(crop?.harvestDate)}
|
||||
</button>
|
||||
<div
|
||||
id="tooltip-light2"
|
||||
role="tooltip"
|
||||
class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 tooltip"
|
||||
>
|
||||
Progress
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="w-full flex justify-center flex-col">
|
||||
<h1 className="text-2xl font-bold text-center my-3 mb-10">
|
||||
Automated Mentoring For Crop Health And Groth
|
||||
</h1>
|
||||
|
||||
<div className="overflow-hidden rounded-xl">
|
||||
<table>
|
||||
<thead className="bg-gray-50 ">
|
||||
<tr>
|
||||
<th className="px-6 py-3 font-extrabold text-left text-sm text-gray-800 uppercase tracking-wider border-2">
|
||||
Expected Doubts
|
||||
</th>
|
||||
<th className="px-6 py-3 font-extrabold text-left text-sm text-gray-800 uppercase tracking-wider border-2">
|
||||
Approximate Solution
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr className="hover:bg-gray-50">
|
||||
<td className="px-6 py-4 whitespace-nowrap border-2">
|
||||
Approximate Harvest Date
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-pre-wrap border-2">
|
||||
{harvest?.message}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="hover:bg-gray-50">
|
||||
<td className="px-6 py-4 whitespace-nowrap border-2">
|
||||
Suitable Pesticides For Crop
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-pre-wrap border-2">
|
||||
{pesticides?.message}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="hover:bg-gray-50">
|
||||
<td className="px-6 py-4 whitespace-nowrap border-2">
|
||||
Suitable fertilizers For Crop
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-pre-wrap border-2">
|
||||
{fertilizers?.message}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="hover:bg-gray-50">
|
||||
<td className="px-6 py-4 whitespace-nowrap border-2">
|
||||
Best Crop To Grow
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-pre-wrap border-2">
|
||||
{nextCrop?.message}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* <div className="flex items-start justify-start gap-5">
|
||||
<h6 className="text-lg font-semibold min-w-[200px] text-nowrap">
|
||||
Approximate Harvest Date:
|
||||
</h6>
|
||||
|
||||
<p className="w-auto"></p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start justify-start gap-5">
|
||||
<h6 className="text-lg font-semibold text-nowrap">
|
||||
Suitable Pesticides For Crop:
|
||||
</h6>
|
||||
<p>{pesticides?.message}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start justify-start gap-5">
|
||||
<h6 className="text-lg font-semibold text-nowrap">
|
||||
Suitable fertilizers For Crop:
|
||||
</h6>
|
||||
<p>{fertilizers?.message}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start justify-start gap-5">
|
||||
<h6 className="text-lg font-semibold text-nowrap">
|
||||
Best Crop To Grow:
|
||||
</h6>
|
||||
<p>{nextCrop?.message}</p>
|
||||
</div> */}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,22 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Laoder from "../../../components/Laoder";
|
||||
import Loader from "../../../components/Loader";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { useGetCropsByFarmQuery } from "../../../store/api/cropApi";
|
||||
|
||||
const CropTable = ({ farmId }) => {
|
||||
const [crops, setCrops] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
data: cropsData,
|
||||
error: cropsError,
|
||||
isLoading: cropsLoading,
|
||||
} = useGetCropsByFarmQuery(farmId);
|
||||
|
||||
|
||||
const handleRemoveCrop = async (cropId) => {
|
||||
try {
|
||||
await fetch(`http://localhost:8000/api/v1/crop/${cropId}`, {
|
||||
@@ -19,37 +31,16 @@ const CropTable = ({ farmId }) => {
|
||||
setError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCrops = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`http://localhost:8000/api/v1/crop/farm/${farmId}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch crops");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setCrops(data || []);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCrops();
|
||||
}, []);
|
||||
if (cropsData) {
|
||||
setCrops(cropsData);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [cropsData]);
|
||||
|
||||
if (loading) {
|
||||
return <Laoder></Laoder>;
|
||||
return <Loader></Loader>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
@@ -102,7 +93,13 @@ const CropTable = ({ farmId }) => {
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{crops.map((crop) => (
|
||||
<tr key={crop._id} className="hover:bg-gray-50">
|
||||
<tr
|
||||
key={crop._id}
|
||||
className="hover:bg-gray-50"
|
||||
onClick={() => {
|
||||
navigate(`/user/dashboard/croppage/${crop._id}`);
|
||||
}}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="h-12 w-12 rounded-full overflow-hidden">
|
||||
{crop.image ? (
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Loader from "../../../components/Loader";
|
||||
import { useGetTasksByFarmQuery } from "../../../store/api/taskApi";
|
||||
|
||||
const DisplayTast = ({ farmId }) => {
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const {
|
||||
data: taskList,
|
||||
error: taskError,
|
||||
isLoading,
|
||||
} = useGetTasksByFarmQuery(farmId);
|
||||
|
||||
|
||||
|
||||
// Function to delete a task and update the state
|
||||
const handleDeleteTask = async (taskId) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`http://localhost:8000/api/v1/task/${taskId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
}
|
||||
);
|
||||
// Assuming that a successful deletion returns a 200 status code
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to delete task");
|
||||
}
|
||||
// Optionally check the response, but we'll update state regardless.
|
||||
// Remove the deleted task from state.
|
||||
setTasks((prevTasks) => prevTasks.filter((task) => task._id !== taskId));
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error deleting task:", error);
|
||||
// Optionally, you could set an error state here.
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (taskList) {
|
||||
setTasks(taskList);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [farmId, taskList]);
|
||||
|
||||
if (loading) return <div>Loading tasks...</div>;
|
||||
if (error) return <div className="text-red-600">Error: {error}</div>;
|
||||
if (!tasks || tasks.length === 0) {
|
||||
return <div className="p-4">No tasks found for this farm.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto w-full p-4">
|
||||
<table className="w-full text-sm text-left border">
|
||||
<thead className="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3">Task Type</th>
|
||||
<th className="px-6 py-3">Description</th>
|
||||
<th className="px-6 py-3">Assigned Date</th>
|
||||
<th className="px-6 py-3">Status</th>
|
||||
<th className="px-6 py-3">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{tasks.map((task) => (
|
||||
<tr key={task._id} className="bg-white border-b hover:bg-gray-50">
|
||||
<td className="px-6 py-4">{task.taskType}</td>
|
||||
<td className="px-6 py-4">{task.description}</td>
|
||||
<td className="px-6 py-4">
|
||||
{new Date(task.assignedDate).toLocaleDateString()}
|
||||
</td>
|
||||
<td className="px-6 py-4">{task.status}</td>
|
||||
<td className="px-6 py-4">
|
||||
<button
|
||||
onClick={() => handleDeleteTask(task._id)}
|
||||
className="block text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5"
|
||||
type="button"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DisplayTast;
|
||||
@@ -1,23 +1,26 @@
|
||||
import React, { useState } from "react";
|
||||
import { useDeleteFarmMutation } from "../../../store/api/farmApi";
|
||||
|
||||
const EditFarm = ({ _id, onDelete }) => {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [deleteFarm] = useDeleteFarmMutation();
|
||||
|
||||
// This function will run when the "Yes, I'm sure" button is clicked.
|
||||
const handleDeleteFarm = async () => {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:8000/api/v1/farm/${_id}`, {
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
});
|
||||
const data = await response.json();
|
||||
console.log("Delete response:", data);
|
||||
if (data.success) {
|
||||
// Notify the parent component to update its state
|
||||
if (onDelete) onDelete(_id);
|
||||
const res = await deleteFarm(_id);
|
||||
// const response = await fetch(`http://localhost:8000/api/v1/farm/${_id}`, {
|
||||
// method: "DELETE",
|
||||
// credentials: "include",
|
||||
// });
|
||||
// const data = await response.json();
|
||||
|
||||
|
||||
if (!res) {
|
||||
return null;
|
||||
}
|
||||
|
||||
setModalOpen(false); // Close the modal after the operation
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
console.error("Error deleting farm:", error);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ const Farm = ({ farmData, farmId }) => {
|
||||
<td className="px-6 py-4">{farmData.soilType}</td>
|
||||
<td className="px-6 py-4">{farmData.size}</td>
|
||||
<td className="px-6 py-4">{farmData.waterContent}</td>
|
||||
<td className="px-6 py-4">Edit</td>
|
||||
<td className="px-6 py-4">
|
||||
<EditFarm></EditFarm>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -4,69 +4,89 @@ import Farm from "./Farm";
|
||||
import CropTable from "./CropTable";
|
||||
import Transactions from "./Transactions";
|
||||
import CreateTransactions from "./CreateTransactions";
|
||||
import Laoder from "../../../components/Laoder";
|
||||
import Loader from "../../../components/Loader";
|
||||
import AddTransaction from "./AddTransactions";
|
||||
import FinanceSummary from "./FinanceSummary";
|
||||
import CreateTask from "./CreateTask";
|
||||
import DisplayTast from "./DisplayTask";
|
||||
import { useGetFarmByIdQuery } from "../../../store/api/farmApi";
|
||||
|
||||
export default function FarmPage() {
|
||||
const { farmId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [farmData, setFarmData] = useState(null);
|
||||
const [farmData, setFarmData] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
||||
|
||||
const { data: farm, error, isLoading } = useGetFarmByIdQuery(farmId);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetching() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`http://localhost:8000/api/v1/farm/${farmId}`,
|
||||
{
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
const jsonData = await response.json();
|
||||
console.log(jsonData);
|
||||
setFarmData(jsonData);
|
||||
} catch (error) {
|
||||
console.error("Error fetching farm data: ", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
if (!isLoading && !error && farm) {
|
||||
setFarmData(farm);
|
||||
setLoading(false);
|
||||
}
|
||||
fetching();
|
||||
}, [farmId]);
|
||||
}, [farm]);
|
||||
|
||||
if (loading) {
|
||||
return <Laoder></Laoder>;
|
||||
}
|
||||
|
||||
|
||||
if (!farmData) {
|
||||
return (
|
||||
<div className="w-full bg-white rounded-lg shadow p-4">
|
||||
<p>No farm data found.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
console.log("My farm id is : ", farmId);
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full bg-white rounded-lg shadow p-4">
|
||||
{/* Back Button */}
|
||||
<div className="w-full bg-white rounded-lg shadow p-4 space-y-8">
|
||||
{/* Header Section */}
|
||||
<header className="mb-4">
|
||||
<div className="flex justify-end">
|
||||
<Farm farmData={farmData} farmId={farmId} />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="mb-4 flex justify-end">
|
||||
<Farm farmData={farmData} farmId={farmId}></Farm>
|
||||
</div>
|
||||
<div className="mb-4 ">
|
||||
<CropTable farmId={farmId}></CropTable>
|
||||
</div>
|
||||
<div className="mb-4 flex justify-end">
|
||||
<CreateTransactions farmId={farmId}></CreateTransactions>
|
||||
</div>
|
||||
<div className="mb-4 ">
|
||||
<Transactions farmId={farmId}></Transactions>
|
||||
</div>
|
||||
{/* Crop Table Section */}
|
||||
<section>
|
||||
<CropTable farmId={farmId} />
|
||||
</section>
|
||||
|
||||
{/* Create Transactions Section */}
|
||||
<section>
|
||||
<div className="flex justify-end">
|
||||
<CreateTransactions farmId={farmId} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Transactions Table Section */}
|
||||
<section>
|
||||
<Transactions farmId={farmId} />
|
||||
</section>
|
||||
|
||||
{/* Add Transaction Modal Section */}
|
||||
<section>
|
||||
<div className="flex justify-end">
|
||||
<AddTransaction farmId={farmId} financeId={farmData?.finances?._id} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Finance Summary Section */}
|
||||
<section>
|
||||
<div className="flex justify-end">
|
||||
<FinanceSummary farmId={farmId} financeId={farmData?.finances?._id} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Create Task Section */}
|
||||
<section>
|
||||
<div className="flex justify-end">
|
||||
<CreateTask farmId={farmId} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Display Task Section */}
|
||||
<section>
|
||||
<div className="flex justify-end">
|
||||
<DisplayTast farmId={farmId} />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Loader from "../../../components/Loader";
|
||||
import { useGetTransactionsQuery } from "../../../store/api/financeApi";
|
||||
|
||||
function formatTimestamp(isoString) {
|
||||
const date = new Date(isoString);
|
||||
return date.toLocaleString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: true, // Ensures AM/PM format
|
||||
});
|
||||
}
|
||||
|
||||
const FinanceSummary = ({ farmId, financeId }) => {
|
||||
const [summary, setSummary] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
|
||||
|
||||
const {
|
||||
data: transaction,
|
||||
error: transactionError,
|
||||
isLoading,
|
||||
} = useGetTransactionsQuery(financeId);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSummary = async () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
try {
|
||||
const response = await fetch(
|
||||
`http://localhost:8000/api/v1/finance/summary/${financeId}`,
|
||||
{ credentials: "include" }
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch summary");
|
||||
}
|
||||
const data = await response.json();
|
||||
setSummary(data);
|
||||
} catch (err) {
|
||||
console.error("Error fetching finance summary:", err);
|
||||
setError("Error fetching summary");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchSummary();
|
||||
}, [farmId]);
|
||||
|
||||
// if (loading) return <Loader />;
|
||||
//if (error) return <div className="p-4 text-center text-red-600">{error}</div>;
|
||||
|
||||
// Extract only the important fields.
|
||||
const { totalExpenses, totalRevenue, transactions } = summary || {};
|
||||
const transactionsCount = Array.isArray(transactions)
|
||||
? transactions.length
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<div className="w-full mx-auto p-8 bg-gray-50">
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
<header className="bg-blue-600 px-6 py-4">
|
||||
<h2 className="text-3xl font-bold text-white">
|
||||
Transactions Summary
|
||||
</h2>
|
||||
</header>
|
||||
<table className="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
||||
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Type of Transaction
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Amount
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Description
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Date
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{transaction?.map((transaction) => (
|
||||
<tr>
|
||||
<td scope="col" className="px-6 py-3">
|
||||
{transaction.type}
|
||||
</td>
|
||||
<td scope="col" className="px-6 py-3">
|
||||
{transaction.amount}
|
||||
</td>
|
||||
<td scope="col" className="px-6 py-3">
|
||||
{transaction.description}
|
||||
</td>
|
||||
<td scope="col" className="px-6 py-3">
|
||||
{formatTimestamp(transaction.date)}
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<button
|
||||
className="block text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5"
|
||||
type="button"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FinanceSummary;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Td from "../../../components/Td";
|
||||
import Laoder from "../../../components/Laoder";
|
||||
import Loader from "../../../components/Loader";
|
||||
|
||||
const Transactions = ({ farmId }) => {
|
||||
const [data, setData] = useState([]);
|
||||
@@ -13,7 +13,7 @@ const Transactions = ({ farmId }) => {
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
setData(data);
|
||||
console.log("Fetched data:", data);
|
||||
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -25,38 +25,28 @@ const Transactions = ({ farmId }) => {
|
||||
return (
|
||||
<div className="relative overflow-x-auto shadow-md sm:rounded-lg">
|
||||
{loading ? (
|
||||
<Laoder></Laoder>
|
||||
<Loader />
|
||||
) : (
|
||||
<table className="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
||||
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Farm name
|
||||
Total Expenses
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Location
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Type
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Size (acres)
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Action
|
||||
totalRevenue
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Array.isArray(data) && data.length > 0 ? (
|
||||
data.map((item) => <Td key={item.id} children={item} />)
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={5} className="text-center">
|
||||
No data available
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
<tr>
|
||||
<td className="px-6 py-3">
|
||||
{data.totalExpenses ? data.totalExpenses : "N/A"}
|
||||
</td>
|
||||
<td className="px-6 py-3">
|
||||
{data.totalRevenue ? data.totalRevenue : "N/A"}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
|
||||
@@ -25,7 +25,7 @@ const MainUserPanel = () => {
|
||||
|
||||
const data = await responce.json();
|
||||
|
||||
//console.log("User Logged out data is : ", data);
|
||||
|
||||
|
||||
if (data.success === true) {
|
||||
navigate("/user/login");
|
||||
@@ -38,7 +38,7 @@ const MainUserPanel = () => {
|
||||
<div className="container mx-auto p-4">
|
||||
<div className="flex items-center mb-4 md:hidden">
|
||||
<img
|
||||
src={`${user.avatar}`}
|
||||
src={`/images/default1.png`}
|
||||
alt="Profile Picture"
|
||||
className="rounded-full w-10 h-10 mr-2"
|
||||
/>
|
||||
@@ -49,7 +49,7 @@ const MainUserPanel = () => {
|
||||
<div className="w-full md:w-1/4 bg-gradient-to-br from-white/30 to-gray-50/30 rounded-xl shadow-lg p-6 backdrop-blur-lg">
|
||||
<div className="hidden md:flex items-center mb-6">
|
||||
<img
|
||||
src={`${user.avatar}`}
|
||||
src={`/images/default1.png`}
|
||||
alt="Profile Picture"
|
||||
className="w-12 h-12 rounded-full mr-3 border-2 border-green-500"
|
||||
/>
|
||||
@@ -69,7 +69,7 @@ const MainUserPanel = () => {
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
{/* <li>
|
||||
<Link
|
||||
to={"/user/dashboard/scheduledmeetings"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
@@ -79,7 +79,7 @@ const MainUserPanel = () => {
|
||||
Scheduled Meeting
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
</li> */}
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard/monitoring"}
|
||||
@@ -91,7 +91,7 @@ const MainUserPanel = () => {
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
{/* <li>
|
||||
<Link
|
||||
to={"/user/dashboard/notifications"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
@@ -101,8 +101,8 @@ const MainUserPanel = () => {
|
||||
Notifications
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
</li> */}
|
||||
{/* <li>
|
||||
<Link
|
||||
to={"/user/dashboard/feedback"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
@@ -113,7 +113,8 @@ const MainUserPanel = () => {
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
*/}
|
||||
{/* <li>
|
||||
<Link
|
||||
to={"/user/dashboard/support"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
@@ -124,6 +125,7 @@ const MainUserPanel = () => {
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
*/}
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard/settings"}
|
||||
@@ -229,7 +231,7 @@ const MainUserPanel = () => {
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<img
|
||||
src={`${user.avatar}`}
|
||||
src={`/images/default1.png`}
|
||||
alt="Profile Picture"
|
||||
className="rounded-full w-24 h-24 mx-auto"
|
||||
/>
|
||||
@@ -238,13 +240,15 @@ const MainUserPanel = () => {
|
||||
Join on {user.createdAt && user.createdAt.substring(0, 10)}
|
||||
</p>
|
||||
<p className="text-gray-500 text-sm mt-2">
|
||||
{user.description == null &&
|
||||
"I am a Senior Software Engineer at Google and also mentored 50+ students to get their dream job."}
|
||||
{user.address == null && "Maharashtra, Pune"}
|
||||
</p>
|
||||
<div className="flex justify-center mt-4">
|
||||
<button className="bg-gray-300 hover:bg-gray-400 text-gray-700 font-bold py-2 px-4 rounded mr-2">
|
||||
<Link
|
||||
to="/"
|
||||
className="bg-gray-300 hover:bg-gray-400 text-gray-700 font-bold py-2 px-4 rounded mr-2"
|
||||
>
|
||||
<FaHome className="text-lg font-extrabold" />
|
||||
</button>
|
||||
</Link>
|
||||
<button className="bg-gray-300 hover:bg-gray-400 text-gray-700 font-bold py-2 px-4 rounded mr-2">
|
||||
<IoMdSettings className="text-lg font-extrabold" />
|
||||
</button>
|
||||
|
||||
@@ -20,14 +20,14 @@ const MentorSessionCard = ({ session }) => {
|
||||
|
||||
const user = useSelector((store) => store.user);
|
||||
|
||||
//console.log("User in the Dashborde : ");
|
||||
|
||||
|
||||
let timeStringToDayName = (dateStr) => {
|
||||
// for getting day name by time string
|
||||
// const dateStr = "2024-09-26T04:31:50.646+00:00";
|
||||
const date = new Date(dateStr);
|
||||
const dayName = date.toLocaleDateString("en-US", { weekday: "long" });
|
||||
//console.log(dayName);
|
||||
|
||||
return dayName;
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ const MentorSessionCard = ({ session }) => {
|
||||
};
|
||||
|
||||
const istDate = date.toLocaleString("en-US", options);
|
||||
console.log(istDate); // Output: "September 26, 2024, 10:01:50 AM"
|
||||
|
||||
return istDate;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,31 +5,67 @@ import PerformanceChart from "../../components/monitoring charts/PerformanceChar
|
||||
import AlertsPanel from "../../components/monitoring charts/AlertsPanel";
|
||||
import ActivityFeed from "../../components/monitoring charts/ActivityField";
|
||||
import Piechart from "../../components/monitoring charts/Piechart";
|
||||
import { useGetFarmsQuery } from "../../store/api/farmApi";
|
||||
import PerformanceChart2 from "../../components/monitoring charts/PerformanceChart2";
|
||||
|
||||
const calculateSpend = (farms) => {
|
||||
let totalSpend = 0;
|
||||
for (let i = 0; i < farms.length; i++) {
|
||||
if (!farms[i]) continue;
|
||||
if (!farms[i]?.finances) continue;
|
||||
if (!farms[i]?.finances?.totalExpenses) continue;
|
||||
totalSpend += farms[i]?.finances?.totalExpenses;
|
||||
}
|
||||
return totalSpend;
|
||||
};
|
||||
|
||||
const calculateRevenue = (farms) => {
|
||||
let totalSpend = 0;
|
||||
for (let i = 0; i < farms.length; i++) {
|
||||
if (!farms[i]) continue;
|
||||
if (!farms[i]?.finances) continue;
|
||||
if (!farms[i]?.finances?.totalRevenue) continue;
|
||||
totalSpend += farms[i]?.finances?.totalRevenue;
|
||||
}
|
||||
return totalSpend;
|
||||
};
|
||||
|
||||
const MonitoringPage = () => {
|
||||
const { data: farms, error, isLoading } = useGetFarmsQuery();
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full bg-white rounded-lg shadow p-4">
|
||||
<div className="p-6">
|
||||
{/* Summary Metrics */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
|
||||
<MetricsCard title="Active Farms" value="12" />
|
||||
<MetricsCard title="Total Yield" value="3500 kg" />
|
||||
<MetricsCard title="Alerts" value="3" />
|
||||
<MetricsCard title="Active Farms" value={farms?.length} />
|
||||
<MetricsCard
|
||||
title="Total Expence"
|
||||
value={farms && calculateSpend(farms)}
|
||||
/>
|
||||
<MetricsCard
|
||||
title="Total Revenue"
|
||||
value={farms && calculateRevenue(farms)}
|
||||
/>
|
||||
<MetricsCard title="Uptime" value="99.9%" />
|
||||
</div>
|
||||
|
||||
{/* Performance Trend Chart */}
|
||||
<div className="mb-6 bg-white p-4 rounded-lg shadow">
|
||||
<h2 className="text-lg font-semibold mb-2">Performance Trend</h2>
|
||||
<h2 className="text-lg font-semibold mb-2">Expence Trend</h2>
|
||||
<PerformanceChart />
|
||||
<h2 className="text-lg font-semibold mb-2 mt-10">Revenue Trend</h2>
|
||||
<PerformanceChart2 />
|
||||
</div>
|
||||
<div className="mb-6 bg-white p-4 rounded-lg shadow">
|
||||
{/* <div className="mb-6 bg-white p-4 rounded-lg shadow">
|
||||
<h2 className="text-lg font-semibold mb-2">Performance Trend</h2>
|
||||
<Piechart></Piechart>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* Alerts and Activity Feed */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="bg-white p-4 rounded-lg shadow">
|
||||
<h2 className="text-lg font-semibold mb-2">Alerts</h2>
|
||||
<AlertsPanel />
|
||||
@@ -38,7 +74,7 @@ const MonitoringPage = () => {
|
||||
<h2 className="text-lg font-semibold mb-2">Recent Activity</h2>
|
||||
<ActivityFeed />
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -75,29 +75,7 @@ const Notifications = () => {
|
||||
},
|
||||
];
|
||||
|
||||
// for getting day name by time string
|
||||
// const dateStr = '2024-09-26T04:31:50.646+00:00';
|
||||
// const date = new Date(dateStr);
|
||||
// const dayName = date.toLocaleDateString('en-US', { weekday: 'long' });
|
||||
// console.log(dayName); // Output: "Thursday"
|
||||
|
||||
// for converting the to get time in am or pm
|
||||
// const utcDateStr = '2024-09-26T04:31:50.646+00:00';
|
||||
// const date = new Date(utcDateStr);
|
||||
// India TimeZone is Asia/Kolkata, which is UTC+5:30
|
||||
// const options = {
|
||||
// timeZone: 'Asia/Kolkata',
|
||||
// hour: 'numeric',
|
||||
// minute: 'numeric',
|
||||
// second: 'numeric',
|
||||
// hour12: true,
|
||||
// year: 'numeric',
|
||||
// month: 'long',
|
||||
// day: 'numeric'
|
||||
// };
|
||||
|
||||
// const istDate = date.toLocaleString('en-US', options);
|
||||
// console.log(istDate); // Output: "September 26, 2024, 10:01:50 AM"
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -18,8 +18,6 @@ const Settings = () => {
|
||||
|
||||
const loader = useSelector((store) => store.loader);
|
||||
|
||||
//console.log("Before the user is : ", user);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// Optimise the call for the database here you are refreshing the page again and again which makes read and write operation
|
||||
@@ -27,11 +25,9 @@ const Settings = () => {
|
||||
event.preventDefault();
|
||||
formData.append("avatar", avatar);
|
||||
|
||||
//console.log("forma daata is : ", formData);
|
||||
|
||||
if (avatar) {
|
||||
dispatch(loaderSliceActions.showLoader());
|
||||
//console.log("The loader values is : ", loader);
|
||||
|
||||
const responce = await fetch(`${BACKEND_URL}/api/v1/user/avatar`, {
|
||||
method: "PUT",
|
||||
credentials: "include",
|
||||
@@ -40,13 +36,11 @@ const Settings = () => {
|
||||
|
||||
const finalResponce = await responce.json();
|
||||
|
||||
//console.log("Our final responce is : ", finalResponce);
|
||||
|
||||
if (finalResponce.success) {
|
||||
dispatch(loaderSliceActions.hideLoader());
|
||||
//console.log("The loader values is : ", loader);
|
||||
|
||||
dispatch(userSliceActions.addUser(finalResponce.data));
|
||||
// console.log("Updated User is : ", user);
|
||||
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
@@ -68,8 +62,6 @@ const Settings = () => {
|
||||
|
||||
const user = await responce.json();
|
||||
|
||||
//console.log("User Login Data is here : ", user);
|
||||
|
||||
dispatch(userSliceActions.addUser(user.data));
|
||||
|
||||
emailElement.current.value = "";
|
||||
@@ -90,7 +82,7 @@ const Settings = () => {
|
||||
|
||||
<div className="w-full h-auto flex items-center justify-center py-7">
|
||||
<div className="w-[9rem] h-[9rem] overflow-hidden rounded-full object-center">
|
||||
<img src={`${user.avatar}`} alt="Avatar" />
|
||||
<img src={`/images/default1.png`} alt="Avatar" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -144,7 +136,6 @@ const Settings = () => {
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
setAvatar(e.target.files[0]);
|
||||
//console.log(e.target.files[0]);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Reference in New Issue
Block a user