Fix:Fixed loader typo

This commit is contained in:
2025-02-23 10:26:18 +05:30
parent a9d63abf9b
commit 34bdbf9f9c
9 changed files with 626 additions and 51 deletions
@@ -0,0 +1,167 @@
import React, { useState } from "react";
import Loader from "../../../components/Loader";
const AddTransaction = ({ farmId }) => {
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 handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
setMessage("");
try {
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();
console.log("Transaction created:", data);
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,186 @@
import React, { useState } from "react";
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 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 response = await fetch("http://localhost:8000/api/v1/task", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(taskData),
});
if (!response.ok) {
throw new Error("Failed to create task");
}
const data = await response.json();
console.log("Task created:", data);
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,5 +1,5 @@
import React, { useState } from "react";
import Laoder from "../../../components/Laoder";
import Loader from "../../../components/Loader";
const CreateFinance = () => {
const [loading, setLoading] = useState(false);
@@ -34,17 +34,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"
>
{loading ? <Loader></Loader> : "Create Finance"}
</button>
);
};
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import Laoder from "../../../components/Laoder";
import Loader from "../../../components/Loader";
const CropTable = ({ farmId }) => {
const [crops, setCrops] = useState([]);
@@ -49,7 +49,7 @@ const CropTable = ({ farmId }) => {
}, []);
if (loading) {
return <Laoder></Laoder>;
return <Loader></Loader>;
}
if (error) {
@@ -0,0 +1,106 @@
import React, { useState, useEffect } from "react";
import Loader from "../../../components/Loader";
const DisplayTast = ({ farmId }) => {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// 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));
console.log(`Task ${taskId} deleted successfully.`);
} catch (error) {
console.error("Error deleting task:", error);
// Optionally, you could set an error state here.
}
};
useEffect(() => {
const fetchTasks = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(
`http://localhost:8000/api/v1/task/farm/${farmId}`,
{
credentials: "include",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to fetch tasks");
}
const data = await response.json();
setTasks(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchTasks();
}, [farmId]);
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;
+3 -1
View File
@@ -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>
+60 -21
View File
@@ -4,7 +4,11 @@ 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";
export default function FarmPage() {
const { farmId } = useParams();
@@ -13,7 +17,7 @@ export default function FarmPage() {
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetching() {
async function fetchFarmData() {
try {
const response = await fetch(
`http://localhost:8000/api/v1/farm/${farmId}`,
@@ -26,7 +30,7 @@ export default function FarmPage() {
}
);
const jsonData = await response.json();
console.log(jsonData);
console.log("Fetched farm data:", jsonData);
setFarmData(jsonData);
} catch (error) {
console.error("Error fetching farm data: ", error);
@@ -34,11 +38,11 @@ export default function FarmPage() {
setLoading(false);
}
}
fetching();
fetchFarmData();
}, [farmId]);
if (loading) {
return <Laoder></Laoder>;
return <Loader />;
}
if (!farmData) {
@@ -49,24 +53,59 @@ export default function FarmPage() {
);
}
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} />
</div>
</section>
{/* Finance Summary Section */}
<section>
<div className="flex justify-end">
<FinanceSummary farmId={farmId} />
</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,77 @@
import React, { useState, useEffect } from "react";
import Loader from "../../../components/Loader";
const FinanceSummary = ({ farmId }) => {
const [summary, setSummary] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
const fetchSummary = async () => {
setLoading(true);
setError("");
try {
const response = await fetch(
`http://localhost:8000/api/v1/finance/summary/${farmId}`,
{ 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="max-w-md 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">Finance Summary</h2>
</header>
<div className="p-6">
<table className="w-full table-auto">
<tbody className="divide-y divide-gray-200">
<tr className="hover:bg-gray-50">
<td className="px-6 py-4 font-medium text-gray-600">
Total Expenses
</td>
<td className="px-6 py-4 text-gray-800">{totalExpenses}</td>
</tr>
<tr className="hover:bg-gray-50">
<td className="px-6 py-4 font-medium text-gray-600">
Total Revenue
</td>
<td className="px-6 py-4 text-gray-800">{totalRevenue}</td>
</tr>
<tr className="hover:bg-gray-50">
<td className="px-6 py-4 font-medium text-gray-600">
Transactions
</td>
<td className="px-6 py-4 text-gray-800">{transactionsCount}</td>
</tr>
</tbody>
</table>
</div>
</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([]);
@@ -25,34 +25,36 @@ 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
Field
</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
Value
</th>
</tr>
</thead>
<tbody>
{Array.isArray(data) && data.length > 0 ? (
{!Array.isArray(data) ? (
// Data is an object: show key-value pairs
Object.entries(data).map(([key, value]) => (
<tr key={key}>
<td className="px-6 py-3 font-bold">{key}</td>
<td className="px-6 py-3">
{typeof value === "object" ? JSON.stringify(value) : value}
</td>
</tr>
))
) : // Data is an array: render using your Td component
data.length > 0 ? (
data.map((item) => <Td key={item.id} children={item} />)
) : (
<tr>
<td colSpan={5} className="text-center">
<td colSpan={2} className="text-center">
No data available
</td>
</tr>