Merged frontend changes from frontend branch to main.
This commit is contained in:
@@ -44,6 +44,7 @@ const AddFarm = () => {
|
||||
setSuccess(true);
|
||||
setError(null);
|
||||
setIsModalOpen(false);
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
setSuccess(false);
|
||||
@@ -53,7 +54,9 @@ const AddFarm = () => {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
onClick={() => {
|
||||
setIsModalOpen(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"
|
||||
>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import React, { useState } from "react";
|
||||
import Laoder from "../../../components/Laoder";
|
||||
|
||||
const CreateFinance = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState("");
|
||||
// Hardcoded farm ID from your example
|
||||
const farmId = "67b9e6829c4979463e64a0fc";
|
||||
|
||||
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 }),
|
||||
});
|
||||
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);
|
||||
setMessage("Error creating finance.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateFinance;
|
||||
@@ -1,9 +1,24 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Laoder from "../../../components/Laoder";
|
||||
|
||||
const CropTable = ({ farmId }) => {
|
||||
const [crops, setCrops] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const handleRemoveCrop = async (cropId) => {
|
||||
try {
|
||||
await fetch(`http://localhost:8000/api/v1/crop/${cropId}`, {
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
setCrops(crops.filter((crop) => crop._id !== cropId));
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
const fetchCrops = async () => {
|
||||
try {
|
||||
@@ -31,14 +46,10 @@ const CropTable = ({ farmId }) => {
|
||||
};
|
||||
|
||||
fetchCrops();
|
||||
}, [farmId]);
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-[400px]">
|
||||
Loading crops...
|
||||
</div>
|
||||
);
|
||||
return <Laoder></Laoder>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
@@ -84,6 +95,9 @@ const CropTable = ({ farmId }) => {
|
||||
<th className="px-6 py-3 text-left text-sm font-medium text-gray-500 uppercase tracking-wider">
|
||||
Harvest Date
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-medium text-gray-500 uppercase tracking-wider">
|
||||
Remove Crop
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
@@ -140,6 +154,17 @@ const CropTable = ({ farmId }) => {
|
||||
{new Date(crop.harvestDate).toLocaleDateString()}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
<button
|
||||
type="button"
|
||||
className="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900"
|
||||
onClick={() => handleRemoveCrop(crop._id)}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
const EditFarm = ({ _id, onDelete }) => {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
// 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);
|
||||
}
|
||||
setModalOpen(false); // Close the modal after the operation
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
console.error("Error deleting farm:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setModalOpen(true)}
|
||||
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 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||
type="button"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
|
||||
{modalOpen && (
|
||||
<div
|
||||
id="popup-modal"
|
||||
tabIndex="-1"
|
||||
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 max-h-full">
|
||||
<div className="relative bg-white rounded-lg shadow-sm dark:bg-gray-700">
|
||||
<button
|
||||
onClick={() => setModalOpen(false)}
|
||||
type="button"
|
||||
className="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
||||
>
|
||||
<svg
|
||||
className="w-3 h-3"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 14 14"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Close modal</span>
|
||||
</button>
|
||||
<div className="p-4 md:p-5 text-center">
|
||||
<svg
|
||||
className="mx-auto mb-4 text-gray-400 w-12 h-12 dark:text-gray-200"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<h3 className="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">
|
||||
Are you sure you want to delete this product?
|
||||
</h3>
|
||||
<button
|
||||
onClick={handleDeleteFarm}
|
||||
type="button"
|
||||
className="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center"
|
||||
>
|
||||
Yes, I'm sure
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setModalOpen(false)}
|
||||
type="button"
|
||||
className="py-2.5 px-5 ml-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
||||
>
|
||||
No, cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditFarm;
|
||||
@@ -1,7 +1,7 @@
|
||||
import AddCrop from "./AddCrop";
|
||||
import EditFarm from "./EditFarm";
|
||||
|
||||
const Farm = ({ farmData, farmId }) => {
|
||||
console.log("Farm id is : ", farmId);
|
||||
return (
|
||||
<div className="w-full ">
|
||||
<div className="flex justify-between">
|
||||
@@ -48,16 +48,7 @@ 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">
|
||||
<button
|
||||
onClick={() =>
|
||||
navigate(`/user/dashboard/updatefarm?farmId=${farmData._id}`)
|
||||
}
|
||||
className="font-medium text-blue-600 dark:text-blue-500 hover:underline"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</td>
|
||||
<td className="px-6 py-4">Edit</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -2,6 +2,9 @@ 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 Laoder from "../../../components/Laoder";
|
||||
|
||||
export default function FarmPage() {
|
||||
const { farmId } = useParams();
|
||||
@@ -35,11 +38,7 @@ export default function FarmPage() {
|
||||
}, [farmId]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="w-full bg-white rounded-lg shadow p-4">
|
||||
<p>Loading farm data...</p>
|
||||
</div>
|
||||
);
|
||||
return <Laoder></Laoder>;
|
||||
}
|
||||
|
||||
if (!farmData) {
|
||||
@@ -59,9 +58,15 @@ export default function FarmPage() {
|
||||
<div className="mb-4 flex justify-end">
|
||||
<Farm farmData={farmData} farmId={farmId}></Farm>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Td from "../../../components/Td";
|
||||
import Laoder from "../../../components/Laoder";
|
||||
|
||||
const Transactions = ({ farmId }) => {
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`http://localhost:8000/api/v1/finance/${farmId}`, {
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
setData(data);
|
||||
console.log("Fetched data:", data);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error fetching transactions:", error);
|
||||
setLoading(false);
|
||||
});
|
||||
}, [farmId]);
|
||||
|
||||
return (
|
||||
<div className="relative overflow-x-auto shadow-md sm:rounded-lg">
|
||||
{loading ? (
|
||||
<Laoder></Laoder>
|
||||
) : (
|
||||
<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
|
||||
</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
|
||||
</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>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Transactions;
|
||||
@@ -5,7 +5,6 @@ import { RiLogoutBoxLine } from "react-icons/ri";
|
||||
import { IoMdSettings } from "react-icons/io";
|
||||
import { FaHome } from "react-icons/fa";
|
||||
import { useSelector } from "react-redux";
|
||||
import { MdHistory } from "react-icons/md";
|
||||
import { IoIosNotifications } from "react-icons/io";
|
||||
import { MdFeedback } from "react-icons/md";
|
||||
import { MdOutlineSupportAgent } from "react-icons/md";
|
||||
@@ -13,6 +12,7 @@ import { IoSettings } from "react-icons/io5";
|
||||
import { RiCalendarScheduleLine } from "react-icons/ri";
|
||||
import { IoIosHome } from "react-icons/io";
|
||||
import { BACKEND_URL } from "../../constants";
|
||||
import { IoMdStats } from "react-icons/io";
|
||||
|
||||
const MainUserPanel = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -46,7 +46,7 @@ const MainUserPanel = () => {
|
||||
</div>
|
||||
<div className="flex flex-row gap-4">
|
||||
{/* Updated Sidebar */}
|
||||
<div className="w-full md:w-1/4 bg-gradient-to-br from-white to-gray-50 rounded-xl shadow-lg p-6">
|
||||
<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}`}
|
||||
@@ -61,7 +61,7 @@ const MainUserPanel = () => {
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100 transition-colors"
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
>
|
||||
<IoIosHome className="text-2xl text-green-600" />
|
||||
<span className="ml-3 text-lg font-medium text-gray-700 hidden md:block">
|
||||
@@ -72,7 +72,7 @@ const MainUserPanel = () => {
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard/scheduledmeetings"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100 transition-colors"
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
>
|
||||
<RiCalendarScheduleLine className="text-2xl text-green-600" />
|
||||
<span className="ml-3 text-lg font-medium text-gray-700 hidden md:block">
|
||||
@@ -82,19 +82,19 @@ const MainUserPanel = () => {
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard/history"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100 transition-colors"
|
||||
to={"/user/dashboard/monitoring"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
>
|
||||
<MdHistory className="text-2xl text-green-600" />
|
||||
<IoMdStats className="text-2xl text-green-600" />
|
||||
<span className="ml-3 text-lg font-medium text-gray-700 hidden md:block">
|
||||
History
|
||||
Monitor
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard/notifications"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100 transition-colors"
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
>
|
||||
<IoIosNotifications className="text-2xl text-green-600" />
|
||||
<span className="ml-3 text-lg font-medium text-gray-700 hidden md:block">
|
||||
@@ -105,7 +105,7 @@ const MainUserPanel = () => {
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard/feedback"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100 transition-colors"
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
>
|
||||
<MdFeedback className="text-2xl text-green-600" />
|
||||
<span className="ml-3 text-lg font-medium text-gray-700 hidden md:block">
|
||||
@@ -116,7 +116,7 @@ const MainUserPanel = () => {
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard/support"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100 transition-colors"
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
>
|
||||
<MdOutlineSupportAgent className="text-2xl text-green-600" />
|
||||
<span className="ml-3 text-lg font-medium text-gray-700 hidden md:block">
|
||||
@@ -127,7 +127,7 @@ const MainUserPanel = () => {
|
||||
<li>
|
||||
<Link
|
||||
to={"/user/dashboard/settings"}
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100 transition-colors"
|
||||
className="flex items-center p-2 rounded-md hover:bg-green-100/30 transition-colors backdrop-blur-md"
|
||||
>
|
||||
<IoSettings className="text-2xl text-green-600" />
|
||||
<span className="ml-3 text-lg font-medium text-gray-700 hidden md:block">
|
||||
|
||||
Reference in New Issue
Block a user