import React, { useEffect, useMemo, useState } from "react"; import { DollarSign, Edit, RefreshCw, Search, Trash2 } from "lucide-react"; import { Card, CardContent } from "../components/ui/Card.tsx"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "../components/ui/Table.tsx"; import { Button } from "../components/ui/Button.tsx"; import { Input, Select } from "../components/ui/Input.tsx"; import { api } from "../services/api.ts"; import { useDepartments, useSubDepartments } from "../hooks/useDepartments.ts"; import { useActivities } from "../hooks/useActivities.ts"; import { useAuth } from "../contexts/authContext.ts"; export const RatesPage: React.FC = () => { const [activeTab, setActiveTab] = useState<"list" | "add">("list"); const { user } = useAuth(); const { departments } = useDepartments(); const [rates, setRates] = useState([]); const [contractors, setContractors] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); // Form state const [formData, setFormData] = useState({ contractorId: "", subDepartmentId: "", activity: "", rate: "", effectiveDate: new Date().toISOString().split("T")[0], }); const [selectedDept, setSelectedDept] = useState(""); const { subDepartments } = useSubDepartments(selectedDept); const { activities } = useActivities(formData.subDepartmentId); const [formError, setFormError] = useState(""); const [formLoading, setFormLoading] = useState(false); const [searchQuery, setSearchQuery] = useState(""); // Edit mode const [editingId, setEditingId] = useState(null); // Fetch rates const fetchRates = async () => { setLoading(true); setError(""); try { const data = await api.getContractorRates(); setRates(data); } catch (err: any) { setError(err.message || "Failed to fetch rates"); } finally { setLoading(false); } }; // Fetch contractors const fetchContractors = async () => { try { const data = await api.getUsers({ role: "Contractor" }); setContractors(data); } catch (err) { console.error("Failed to fetch contractors:", err); } }; useEffect(() => { fetchRates(); fetchContractors(); }, []); // Auto-select department for supervisors useEffect(() => { if (user?.role === "Supervisor" && user?.department_id) { setSelectedDept(String(user.department_id)); } }, [user]); const handleInputChange = ( e: React.ChangeEvent, ) => { const { name, value } = e.target; // Auto-select department when contractor is selected if (name === "contractorId" && value) { const selectedContractor = contractors.find((c) => String(c.id) === value ); if (selectedContractor?.department_id) { setSelectedDept(String(selectedContractor.department_id)); // Clear sub-department and activity when contractor changes setFormData((prev) => ({ ...prev, [name]: value, subDepartmentId: "", activity: "", })); } else { setFormData((prev) => ({ ...prev, [name]: value })); } } // Clear activity when sub-department changes else if (name === "subDepartmentId") { setFormData((prev) => ({ ...prev, [name]: value, activity: "" })); } else { setFormData((prev) => ({ ...prev, [name]: value })); } setFormError(""); }; const resetForm = () => { setFormData({ contractorId: "", subDepartmentId: "", activity: "", rate: "", effectiveDate: new Date().toISOString().split("T")[0], }); setEditingId(null); setFormError(""); }; const handleSubmit = async () => { if (!formData.contractorId || !formData.rate || !formData.effectiveDate) { setFormError("Contractor, rate, and effective date are required"); return; } setFormLoading(true); setFormError(""); try { if (editingId) { await api.updateContractorRate(editingId, { rate: parseFloat(formData.rate), activity: formData.activity || undefined, effectiveDate: formData.effectiveDate, }); } else { await api.setContractorRate({ contractorId: parseInt(formData.contractorId), subDepartmentId: formData.subDepartmentId ? parseInt(formData.subDepartmentId) : undefined, activity: formData.activity || undefined, rate: parseFloat(formData.rate), effectiveDate: formData.effectiveDate, }); } resetForm(); setActiveTab("list"); fetchRates(); } catch (err: any) { setFormError(err.message || "Failed to save rate"); } finally { setFormLoading(false); } }; const handleEdit = (rate: any) => { setFormData({ contractorId: String(rate.contractor_id), subDepartmentId: rate.sub_department_id ? String(rate.sub_department_id) : "", activity: rate.activity || "", rate: String(rate.rate), effectiveDate: rate.effective_date?.split("T")[0] || new Date().toISOString().split("T")[0], }); setEditingId(rate.id); setActiveTab("add"); }; const handleDelete = async (id: number) => { if (!confirm("Are you sure you want to delete this rate?")) return; try { await api.deleteContractorRate(id); fetchRates(); } catch (err: any) { alert(err.message || "Failed to delete rate"); } }; const canManageRates = user?.role === "SuperAdmin" || user?.role === "Supervisor"; // Filter rates based on search const filteredRates = useMemo(() => { if (!searchQuery) return rates; const query = searchQuery.toLowerCase(); return rates.filter((rate) => rate.contractor_name?.toLowerCase().includes(query) || rate.sub_department_name?.toLowerCase().includes(query) || rate.activity?.toLowerCase().includes(query) ); }, [rates, searchQuery]); return (
{canManageRates && ( )}
{activeTab === "list" && (
setSearchQuery(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
Total Rates: {filteredRates.length}
{error && (
Error: {error}
)} {loading ?
Loading rates...
: filteredRates.length > 0 ? ( Contractor Sub-Department Activity Rate Type Rate (₹) Effective Date {canManageRates && Actions} {filteredRates.map((rate) => ( {rate.contractor_name} {rate.sub_department_name || "-"} {rate.activity || "Standard"} {rate.unit_of_measurement === "Per Bag" ? "Per Unit" : "Flat Rate"} ₹{rate.rate} {new Date(rate.effective_date).toLocaleDateString()} {canManageRates && (
)}
))}
) : (
{searchQuery ? "No matching rates found" : "No rates configured yet. Add one to get started!"}
)}
)} {activeTab === "add" && canManageRates && (

{editingId ? "Edit Rate" : "Add New Rate"}

Rate Calculation Info

  • Per Bag Activities:{" "} Total = Units × Rate per Unit
  • Fixed Rate Activities:{" "} Total = Flat Rate (no unit calculation)
{formError && (
{formError}
)}
d.id === user?.department_id )?.name || "Loading..."} disabled /> ) : ( ({ value: String(s.id), label: s.name, })), ]} /> { const selectedActivity = activities.find((a) => a.name === formData.activity ); return selectedActivity?.unit_of_measurement === "Per Bag" ? "Rate per Unit (₹)" : "Rate Amount (₹)"; })()} name="rate" type="number" value={formData.rate} onChange={handleInputChange} placeholder="Enter rate amount" required />
)}
); };