(Feat-Fix): Lots of fixes done, reporting system fixed, stricter types
This commit is contained in:
@@ -1,36 +1,43 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { Plus, RefreshCw, Trash2, Edit, DollarSign, Search } from 'lucide-react';
|
||||
import { Card, CardHeader, CardContent } from '../components/ui/Card';
|
||||
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '../components/ui/Table';
|
||||
import { Button } from '../components/ui/Button';
|
||||
import { Input, Select } from '../components/ui/Input';
|
||||
import { api } from '../services/api';
|
||||
import { useDepartments, useSubDepartments } from '../hooks/useDepartments';
|
||||
import { useActivities } from '../hooks/useActivities';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
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.tsx";
|
||||
|
||||
export const RatesPage: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<'list' | 'add'>('list');
|
||||
const [activeTab, setActiveTab] = useState<"list" | "add">("list");
|
||||
const { user } = useAuth();
|
||||
const { departments } = useDepartments();
|
||||
const [rates, setRates] = useState<any[]>([]);
|
||||
const [contractors, setContractors] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [error, setError] = useState("");
|
||||
// Form state
|
||||
const [formData, setFormData] = useState({
|
||||
contractorId: '',
|
||||
subDepartmentId: '',
|
||||
activity: '',
|
||||
rate: '',
|
||||
effectiveDate: new Date().toISOString().split('T')[0],
|
||||
contractorId: "",
|
||||
subDepartmentId: "",
|
||||
activity: "",
|
||||
rate: "",
|
||||
effectiveDate: new Date().toISOString().split("T")[0],
|
||||
});
|
||||
const [selectedDept, setSelectedDept] = useState('');
|
||||
const [selectedDept, setSelectedDept] = useState("");
|
||||
const { subDepartments } = useSubDepartments(selectedDept);
|
||||
const { activities } = useActivities(formData.subDepartmentId);
|
||||
const [formError, setFormError] = useState('');
|
||||
const [formError, setFormError] = useState("");
|
||||
const [formLoading, setFormLoading] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
// Edit mode
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
@@ -38,12 +45,12 @@ export const RatesPage: React.FC = () => {
|
||||
// Fetch rates
|
||||
const fetchRates = async () => {
|
||||
setLoading(true);
|
||||
setError('');
|
||||
setError("");
|
||||
try {
|
||||
const data = await api.getContractorRates();
|
||||
setRates(data);
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to fetch rates');
|
||||
setError(err.message || "Failed to fetch rates");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -52,10 +59,10 @@ export const RatesPage: React.FC = () => {
|
||||
// Fetch contractors
|
||||
const fetchContractors = async () => {
|
||||
try {
|
||||
const data = await api.getUsers({ role: 'Contractor' });
|
||||
const data = await api.getUsers({ role: "Contractor" });
|
||||
setContractors(data);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch contractors:', err);
|
||||
console.error("Failed to fetch contractors:", err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -66,54 +73,62 @@ export const RatesPage: React.FC = () => {
|
||||
|
||||
// Auto-select department for supervisors
|
||||
useEffect(() => {
|
||||
if (user?.role === 'Supervisor' && user?.department_id) {
|
||||
if (user?.role === "Supervisor" && user?.department_id) {
|
||||
setSelectedDept(String(user.department_id));
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>,
|
||||
) => {
|
||||
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 (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: '' }));
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
subDepartmentId: "",
|
||||
activity: "",
|
||||
}));
|
||||
} else {
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
}
|
||||
// Clear activity when sub-department changes
|
||||
else if (name === 'subDepartmentId') {
|
||||
setFormData(prev => ({ ...prev, [name]: value, activity: '' }));
|
||||
} // Clear activity when sub-department changes
|
||||
else if (name === "subDepartmentId") {
|
||||
setFormData((prev) => ({ ...prev, [name]: value, activity: "" }));
|
||||
} else {
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
setFormError('');
|
||||
setFormError("");
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
contractorId: '',
|
||||
subDepartmentId: '',
|
||||
activity: '',
|
||||
rate: '',
|
||||
effectiveDate: new Date().toISOString().split('T')[0],
|
||||
contractorId: "",
|
||||
subDepartmentId: "",
|
||||
activity: "",
|
||||
rate: "",
|
||||
effectiveDate: new Date().toISOString().split("T")[0],
|
||||
});
|
||||
setEditingId(null);
|
||||
setFormError('');
|
||||
setFormError("");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.contractorId || !formData.rate || !formData.effectiveDate) {
|
||||
setFormError('Contractor, rate, and effective date are required');
|
||||
setFormError("Contractor, rate, and effective date are required");
|
||||
return;
|
||||
}
|
||||
|
||||
setFormLoading(true);
|
||||
setFormError('');
|
||||
setFormError("");
|
||||
|
||||
try {
|
||||
if (editingId) {
|
||||
@@ -125,7 +140,9 @@ export const RatesPage: React.FC = () => {
|
||||
} else {
|
||||
await api.setContractorRate({
|
||||
contractorId: parseInt(formData.contractorId),
|
||||
subDepartmentId: formData.subDepartmentId ? parseInt(formData.subDepartmentId) : undefined,
|
||||
subDepartmentId: formData.subDepartmentId
|
||||
? parseInt(formData.subDepartmentId)
|
||||
: undefined,
|
||||
activity: formData.activity || undefined,
|
||||
rate: parseFloat(formData.rate),
|
||||
effectiveDate: formData.effectiveDate,
|
||||
@@ -133,10 +150,10 @@ export const RatesPage: React.FC = () => {
|
||||
}
|
||||
|
||||
resetForm();
|
||||
setActiveTab('list');
|
||||
setActiveTab("list");
|
||||
fetchRates();
|
||||
} catch (err: any) {
|
||||
setFormError(err.message || 'Failed to save rate');
|
||||
setFormError(err.message || "Failed to save rate");
|
||||
} finally {
|
||||
setFormLoading(false);
|
||||
}
|
||||
@@ -145,32 +162,36 @@ export const RatesPage: React.FC = () => {
|
||||
const handleEdit = (rate: any) => {
|
||||
setFormData({
|
||||
contractorId: String(rate.contractor_id),
|
||||
subDepartmentId: rate.sub_department_id ? String(rate.sub_department_id) : '',
|
||||
activity: rate.activity || '',
|
||||
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],
|
||||
effectiveDate: rate.effective_date?.split("T")[0] ||
|
||||
new Date().toISOString().split("T")[0],
|
||||
});
|
||||
setEditingId(rate.id);
|
||||
setActiveTab('add');
|
||||
setActiveTab("add");
|
||||
};
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
if (!confirm('Are you sure you want to delete this rate?')) return;
|
||||
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');
|
||||
alert(err.message || "Failed to delete rate");
|
||||
}
|
||||
};
|
||||
|
||||
const canManageRates = user?.role === 'SuperAdmin' || user?.role === 'Supervisor';
|
||||
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 =>
|
||||
return rates.filter((rate) =>
|
||||
rate.contractor_name?.toLowerCase().includes(query) ||
|
||||
rate.sub_department_name?.toLowerCase().includes(query) ||
|
||||
rate.activity?.toLowerCase().includes(query)
|
||||
@@ -183,36 +204,42 @@ export const RatesPage: React.FC = () => {
|
||||
<div className="border-b border-gray-200">
|
||||
<div className="flex space-x-8 px-6">
|
||||
<button
|
||||
onClick={() => { setActiveTab('list'); resetForm(); }}
|
||||
onClick={() => {
|
||||
setActiveTab("list");
|
||||
resetForm();
|
||||
}}
|
||||
className={`py-4 px-2 border-b-2 font-medium text-sm ${
|
||||
activeTab === 'list'
|
||||
? 'border-blue-500 text-blue-600'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700'
|
||||
activeTab === "list"
|
||||
? "border-blue-500 text-blue-600"
|
||||
: "border-transparent text-gray-500 hover:text-gray-700"
|
||||
}`}
|
||||
>
|
||||
Rate List
|
||||
</button>
|
||||
{canManageRates && (
|
||||
<button
|
||||
onClick={() => setActiveTab('add')}
|
||||
onClick={() => setActiveTab("add")}
|
||||
className={`py-4 px-2 border-b-2 font-medium text-sm ${
|
||||
activeTab === 'add'
|
||||
? 'border-blue-500 text-blue-600'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700'
|
||||
activeTab === "add"
|
||||
? "border-blue-500 text-blue-600"
|
||||
: "border-transparent text-gray-500 hover:text-gray-700"
|
||||
}`}
|
||||
>
|
||||
{editingId ? 'Edit Rate' : 'Add Rate'}
|
||||
{editingId ? "Edit Rate" : "Add Rate"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CardContent>
|
||||
{activeTab === 'list' && (
|
||||
{activeTab === "list" && (
|
||||
<div>
|
||||
<div className="flex gap-4 mb-4">
|
||||
<div className="relative min-w-[300px] flex-1">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={18} />
|
||||
<Search
|
||||
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
|
||||
size={18}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search by contractor, sub-department, activity..."
|
||||
@@ -226,7 +253,7 @@ export const RatesPage: React.FC = () => {
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="mb-4 text-sm text-gray-600">
|
||||
Total Rates: {filteredRates.length}
|
||||
</div>
|
||||
@@ -237,91 +264,113 @@ export const RatesPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<div className="text-center py-8">Loading rates...</div>
|
||||
) : filteredRates.length > 0 ? (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableHead>Contractor</TableHead>
|
||||
<TableHead>Sub-Department</TableHead>
|
||||
<TableHead>Activity</TableHead>
|
||||
<TableHead>Rate Type</TableHead>
|
||||
<TableHead>Rate (₹)</TableHead>
|
||||
<TableHead>Effective Date</TableHead>
|
||||
{canManageRates && <TableHead>Actions</TableHead>}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredRates.map((rate) => (
|
||||
<TableRow key={rate.id}>
|
||||
<TableCell className="font-medium">{rate.contractor_name}</TableCell>
|
||||
<TableCell>{rate.sub_department_name || '-'}</TableCell>
|
||||
<TableCell>
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
||||
rate.unit_of_measurement === 'Per Bag'
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'bg-gray-100 text-gray-700'
|
||||
}`}>
|
||||
{rate.activity || 'Standard'}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="text-xs text-gray-500">
|
||||
{rate.unit_of_measurement === 'Per Bag'
|
||||
? 'Per Unit'
|
||||
: 'Flat Rate'}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="text-green-600 font-semibold">₹{rate.rate}</span>
|
||||
</TableCell>
|
||||
<TableCell>{new Date(rate.effective_date).toLocaleDateString()}</TableCell>
|
||||
{canManageRates && (
|
||||
<TableCell>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleEdit(rate)}
|
||||
className="text-blue-600"
|
||||
title="Edit"
|
||||
>
|
||||
<Edit size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleDelete(rate.id)}
|
||||
className="text-red-600"
|
||||
title="Delete"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
{loading
|
||||
? <div className="text-center py-8">Loading rates...</div>
|
||||
: filteredRates.length > 0
|
||||
? (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableHead>Contractor</TableHead>
|
||||
<TableHead>Sub-Department</TableHead>
|
||||
<TableHead>Activity</TableHead>
|
||||
<TableHead>Rate Type</TableHead>
|
||||
<TableHead>Rate (₹)</TableHead>
|
||||
<TableHead>Effective Date</TableHead>
|
||||
{canManageRates && <TableHead>Actions</TableHead>}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredRates.map((rate) => (
|
||||
<TableRow key={rate.id}>
|
||||
<TableCell className="font-medium">
|
||||
{rate.contractor_name}
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
) : (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
{searchQuery ? 'No matching rates found' : 'No rates configured yet. Add one to get started!'}
|
||||
</div>
|
||||
)}
|
||||
<TableCell>
|
||||
{rate.sub_department_name || "-"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span
|
||||
className={`px-2 py-1 rounded text-xs font-medium ${
|
||||
rate.unit_of_measurement === "Per Bag"
|
||||
? "bg-blue-100 text-blue-700"
|
||||
: "bg-gray-100 text-gray-700"
|
||||
}`}
|
||||
>
|
||||
{rate.activity || "Standard"}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="text-xs text-gray-500">
|
||||
{rate.unit_of_measurement === "Per Bag"
|
||||
? "Per Unit"
|
||||
: "Flat Rate"}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="text-green-600 font-semibold">
|
||||
₹{rate.rate}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{new Date(rate.effective_date).toLocaleDateString()}
|
||||
</TableCell>
|
||||
{canManageRates && (
|
||||
<TableCell>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleEdit(rate)}
|
||||
className="text-blue-600"
|
||||
title="Edit"
|
||||
>
|
||||
<Edit size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleDelete(rate.id)}
|
||||
className="text-red-600"
|
||||
title="Delete"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
: (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
{searchQuery
|
||||
? "No matching rates found"
|
||||
: "No rates configured yet. Add one to get started!"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'add' && canManageRates && (
|
||||
{activeTab === "add" && canManageRates && (
|
||||
<div className="max-w-2xl space-y-6">
|
||||
<h3 className="text-lg font-semibold text-gray-800">
|
||||
{editingId ? 'Edit Rate' : 'Add New Rate'}
|
||||
{editingId ? "Edit Rate" : "Add New Rate"}
|
||||
</h3>
|
||||
|
||||
<div className="p-4 bg-blue-50 border border-blue-200 rounded-md">
|
||||
<h4 className="font-medium text-blue-800 mb-2">Rate Calculation Info</h4>
|
||||
<h4 className="font-medium text-blue-800 mb-2">
|
||||
Rate Calculation Info
|
||||
</h4>
|
||||
<ul className="text-sm text-blue-700 space-y-1">
|
||||
<li><strong>Per Bag Activities:</strong> Total = Units × Rate per Unit</li>
|
||||
<li><strong>Fixed Rate Activities:</strong> Total = Flat Rate (no unit calculation)</li>
|
||||
<li>
|
||||
<strong>Per Bag Activities:</strong>{" "}
|
||||
Total = Units × Rate per Unit
|
||||
</li>
|
||||
<li>
|
||||
<strong>Fixed Rate Activities:</strong>{" "}
|
||||
Total = Flat Rate (no unit calculation)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -340,27 +389,37 @@ export const RatesPage: React.FC = () => {
|
||||
required
|
||||
disabled={!!editingId}
|
||||
options={[
|
||||
{ value: '', label: 'Select Contractor' },
|
||||
...contractors.map(c => ({ value: String(c.id), label: c.name }))
|
||||
{ value: "", label: "Select Contractor" },
|
||||
...contractors.map((c) => ({
|
||||
value: String(c.id),
|
||||
label: c.name,
|
||||
})),
|
||||
]}
|
||||
/>
|
||||
{user?.role === 'Supervisor' ? (
|
||||
<Input
|
||||
label="Department"
|
||||
value={departments.find(d => d.id === user?.department_id)?.name || 'Loading...'}
|
||||
disabled
|
||||
/>
|
||||
) : (
|
||||
<Select
|
||||
label="Department"
|
||||
value={selectedDept}
|
||||
onChange={(e) => setSelectedDept(e.target.value)}
|
||||
options={[
|
||||
{ value: '', label: 'Select Department' },
|
||||
...departments.map(d => ({ value: String(d.id), label: d.name }))
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{user?.role === "Supervisor"
|
||||
? (
|
||||
<Input
|
||||
label="Department"
|
||||
value={departments.find((d) =>
|
||||
d.id === user?.department_id
|
||||
)?.name || "Loading..."}
|
||||
disabled
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<Select
|
||||
label="Department"
|
||||
value={selectedDept}
|
||||
onChange={(e) => setSelectedDept(e.target.value)}
|
||||
options={[
|
||||
{ value: "", label: "Select Department" },
|
||||
...departments.map((d) => ({
|
||||
value: String(d.id),
|
||||
label: d.name,
|
||||
})),
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<Select
|
||||
label="Sub-Department"
|
||||
name="subDepartmentId"
|
||||
@@ -368,8 +427,11 @@ export const RatesPage: React.FC = () => {
|
||||
onChange={handleInputChange}
|
||||
disabled={!!editingId}
|
||||
options={[
|
||||
{ value: '', label: 'Select Sub-Department (Optional)' },
|
||||
...subDepartments.map(s => ({ value: String(s.id), label: s.name }))
|
||||
{ value: "", label: "Select Sub-Department (Optional)" },
|
||||
...subDepartments.map((s) => ({
|
||||
value: String(s.id),
|
||||
label: s.name,
|
||||
})),
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
@@ -379,18 +441,29 @@ export const RatesPage: React.FC = () => {
|
||||
onChange={handleInputChange}
|
||||
disabled={!formData.subDepartmentId}
|
||||
options={[
|
||||
{ value: '', label: formData.subDepartmentId ? 'Select Activity (Optional)' : 'Select Sub-Department First' },
|
||||
...activities.map(a => ({
|
||||
value: a.name,
|
||||
label: `${a.name} (${a.unit_of_measurement === 'Per Bag' ? 'per unit × rate' : 'flat rate'})`
|
||||
}))
|
||||
{
|
||||
value: "",
|
||||
label: formData.subDepartmentId
|
||||
? "Select Activity (Optional)"
|
||||
: "Select Sub-Department First",
|
||||
},
|
||||
...activities.map((a) => ({
|
||||
value: a.name,
|
||||
label: `${a.name} (${
|
||||
a.unit_of_measurement === "Per Bag"
|
||||
? "per unit × rate"
|
||||
: "flat rate"
|
||||
})`,
|
||||
})),
|
||||
]}
|
||||
/>
|
||||
<Input
|
||||
label={(() => {
|
||||
const selectedActivity = activities.find(a => a.name === formData.activity);
|
||||
return selectedActivity?.unit_of_measurement === 'Per Bag'
|
||||
? "Rate per Unit (₹)"
|
||||
const selectedActivity = activities.find((a) =>
|
||||
a.name === formData.activity
|
||||
);
|
||||
return selectedActivity?.unit_of_measurement === "Per Bag"
|
||||
? "Rate per Unit (₹)"
|
||||
: "Rate Amount (₹)";
|
||||
})()}
|
||||
name="rate"
|
||||
@@ -411,14 +484,20 @@ export const RatesPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-4">
|
||||
<Button variant="outline" onClick={() => { setActiveTab('list'); resetForm(); }}>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setActiveTab("list");
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSubmit} disabled={formLoading}>
|
||||
{formLoading ? 'Saving...' : (
|
||||
{formLoading ? "Saving..." : (
|
||||
<>
|
||||
<DollarSign size={16} className="mr-2" />
|
||||
{editingId ? 'Update Rate' : 'Add Rate'}
|
||||
{editingId ? "Update Rate" : "Add Rate"}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user