bug fixed
This commit is contained in:
parent
821c913b59
commit
2e14579ec5
@ -97,16 +97,39 @@ const SearchableSelect = ({
|
|||||||
}, [options, searchTerm]);
|
}, [options, searchTerm]);
|
||||||
|
|
||||||
// Get selected option label
|
// Get selected option label
|
||||||
|
const [internalDisplayValue, setInternalDisplayValue] = React.useState(displayValue || '');
|
||||||
const selectedOption = options.find(opt => opt.value === value);
|
const selectedOption = options.find(opt => opt.value === value);
|
||||||
const displayValueToShow = displayValue !== undefined
|
|
||||||
? displayValue
|
// Update internal display value when value, displayValue prop or options change
|
||||||
: (selectedOption ? selectedOption.label : '');
|
React.useEffect(() => {
|
||||||
|
if (displayValue !== undefined && displayValue !== '') {
|
||||||
|
setInternalDisplayValue(displayValue);
|
||||||
|
} else if (selectedOption) {
|
||||||
|
setInternalDisplayValue(selectedOption.label);
|
||||||
|
} else if (value) {
|
||||||
|
// If we have a value but no matching option, try to find it in the options
|
||||||
|
const matchedOption = options.find(opt => opt.value === value);
|
||||||
|
setInternalDisplayValue(matchedOption?.label || '');
|
||||||
|
} else {
|
||||||
|
setInternalDisplayValue('');
|
||||||
|
}
|
||||||
|
}, [value, displayValue, selectedOption, options]);
|
||||||
|
|
||||||
|
const displayValueToShow = internalDisplayValue;
|
||||||
|
|
||||||
// Handle option selection
|
// Handle option selection
|
||||||
const handleSelect = (option) => {
|
const handleSelect = (option) => {
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
const event = { target: { value: option.value } };
|
const event = {
|
||||||
|
target: {
|
||||||
|
value: option.value,
|
||||||
|
name: name,
|
||||||
|
selectedDisplay: option.label,
|
||||||
|
option: option
|
||||||
|
}
|
||||||
|
};
|
||||||
onChange(event);
|
onChange(event);
|
||||||
|
setInternalDisplayValue(option.label);
|
||||||
}
|
}
|
||||||
setSearchTerm('');
|
setSearchTerm('');
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@ -1235,140 +1258,135 @@ const location = useLocation();
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div data-field={`product_${idx}`}>
|
<div data-field={`product_${idx}`}>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Product (HS Code - Name) <span className="text-red-500">*</span></label>
|
<label className="block text-sm font-medium text-gray-700 mb-1">Product (HS Code - Name) <span className="text-red-500">*</span></label>
|
||||||
<div>
|
<SearchableSelect
|
||||||
<SearchableSelect
|
placeholder={isLoadingProducts ? 'Loading products...' : 'Search product HS code...'}
|
||||||
placeholder={isLoadingProducts ? 'Loading products...' : 'Search product HS code...'}
|
options={getAvailableProducts(p.id)}
|
||||||
options={getAvailableProducts(p.id)}
|
value={p.productId?.toString() || p.product_id?.toString() || p.product?.toString() || ''}
|
||||||
value={p.productId?.toString() || p.product_id?.toString() || p.product?.toString() || ''}
|
displayValue={p.name || (p.hs_code && p.productName ? `${p.hs_code} - ${p.productName}` : p.productName) || ''}
|
||||||
displayValue={p.name || (p.hs_code && p.productName ? `${p.hs_code} - ${p.productName}` : p.productName) || ''}
|
onChange={async (e) => {
|
||||||
onChange={async (e) => {
|
const selectedValue = e.target.value;
|
||||||
const selectedValue = e.target.value;
|
// Clear error when user selects a product
|
||||||
// Clear error when user selects a product
|
if (formErrors[`product_${idx}`]) {
|
||||||
if (formErrors[`product_${idx}`]) {
|
const newErrors = { ...formErrors };
|
||||||
const newErrors = { ...formErrors };
|
delete newErrors[`product_${idx}`];
|
||||||
delete newErrors[`product_${idx}`];
|
setFormErrors(newErrors);
|
||||||
setFormErrors(newErrors);
|
}
|
||||||
}
|
|
||||||
|
// Find the selected product from options
|
||||||
|
const selectedProduct = productOptions.find(opt => opt.value === selectedValue || opt.productId?.toString() === selectedValue);
|
||||||
|
|
||||||
|
// Get unit information from the selected product
|
||||||
|
const unitId = selectedProduct?.originalData?.['product.unit.id'] ||
|
||||||
|
selectedProduct?.unitId ||
|
||||||
|
selectedProduct?.originalData?.unit_id;
|
||||||
|
|
||||||
|
const unitName = selectedProduct?.originalData?.['product.unit.uom'] ||
|
||||||
|
selectedProduct?.originalData?.unit?.uom ||
|
||||||
|
selectedProduct?.unitName ||
|
||||||
|
selectedProduct?.unit || '';
|
||||||
|
|
||||||
|
// Get the display name for the product - check multiple possible locations
|
||||||
|
let displayName = '';
|
||||||
|
if (selectedProduct) {
|
||||||
|
// Get HS code from various possible locations
|
||||||
|
const hsCode = selectedProduct.hsCode ||
|
||||||
|
selectedProduct.originalData?.['product.hs_code'] ||
|
||||||
|
selectedProduct.originalData?.hs_code ||
|
||||||
|
'';
|
||||||
|
|
||||||
// Find the selected product from options
|
// Get product name from various possible locations
|
||||||
const selectedProduct = productOptions.find(opt => opt.value === selectedValue || opt.productId?.toString() === selectedValue);
|
const productName = selectedProduct.name ||
|
||||||
|
selectedProduct.originalData?.['product.name'] ||
|
||||||
|
selectedProduct.originalData?.product_name ||
|
||||||
|
selectedProduct.originalData?.name ||
|
||||||
|
'';
|
||||||
|
|
||||||
// Get unit information from the selected product
|
// Combine HS Code and Product Name for display
|
||||||
const unitId = selectedProduct?.originalData?.['product.unit.id'] ||
|
displayName = hsCode && productName ?
|
||||||
selectedProduct?.unitId ||
|
`${hsCode} - ${productName}` :
|
||||||
selectedProduct?.originalData?.unit_id;
|
productName || selectedProduct.label ||
|
||||||
|
(selectedProduct.originalData ?
|
||||||
const unitName = selectedProduct?.originalData?.['product.unit.uom'] ||
|
(selectedProduct.originalData['product.product_name'] ||
|
||||||
selectedProduct?.originalData?.unit?.uom ||
|
selectedProduct.originalData.product_name ||
|
||||||
selectedProduct?.unitName ||
|
selectedProduct.originalData.name) :
|
||||||
selectedProduct?.unit || '';
|
(selectedProduct.name || '')
|
||||||
|
);
|
||||||
// Get the display name for the product - check multiple possible locations
|
}
|
||||||
let displayName = '';
|
|
||||||
if (selectedProduct) {
|
|
||||||
// Get HS code from various possible locations
|
|
||||||
const hsCode = selectedProduct.hsCode ||
|
|
||||||
selectedProduct.originalData?.['product.hs_code'] ||
|
|
||||||
selectedProduct.originalData?.hs_code ||
|
|
||||||
'';
|
|
||||||
|
|
||||||
// Get product name from various possible locations
|
|
||||||
const productName = selectedProduct.name ||
|
|
||||||
selectedProduct.originalData?.['product.name'] ||
|
|
||||||
selectedProduct.originalData?.product_name ||
|
|
||||||
selectedProduct.originalData?.name ||
|
|
||||||
'';
|
|
||||||
|
|
||||||
// Combine HS Code and Product Name for display
|
|
||||||
displayName = hsCode && productName ?
|
|
||||||
`${hsCode} - ${productName}` :
|
|
||||||
productName || selectedProduct.label ||
|
|
||||||
(selectedProduct.originalData ?
|
|
||||||
(selectedProduct.originalData['product.product_name'] ||
|
|
||||||
selectedProduct.originalData.product_name ||
|
|
||||||
selectedProduct.originalData.name) :
|
|
||||||
(selectedProduct.name || '')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create updated product with all necessary fields
|
// Create updated product with all necessary fields
|
||||||
const updatedProduct = {
|
const updatedProduct = {
|
||||||
...p,
|
...p,
|
||||||
product: selectedValue, // Store the ID as string
|
product: selectedValue, // Store the ID as string
|
||||||
productId: selectedValue, // Also store as productId for consistency
|
productId: selectedValue, // Also store as productId for consistency
|
||||||
product_id: selectedValue, // For backward compatibility
|
product_id: selectedValue, // For backward compatibility
|
||||||
hs_code: selectedProduct?.hsCode ||
|
hs_code: selectedProduct?.hsCode ||
|
||||||
(selectedProduct?.originalData ?
|
(selectedProduct?.originalData ?
|
||||||
(selectedProduct.originalData['product.hs_code'] ||
|
(selectedProduct.originalData['product.hs_code'] ||
|
||||||
selectedProduct.originalData.hs_code) :
|
selectedProduct.originalData.hs_code) :
|
||||||
'') || p.hs_code,
|
'') || p.hs_code,
|
||||||
name: displayName || p.name, // Use the formatted display name
|
name: displayName || p.name, // Use the formatted display name
|
||||||
originalData: selectedProduct?.originalData || p.originalData,
|
originalData: selectedProduct?.originalData || p.originalData,
|
||||||
// Set unit information
|
// Set unit information
|
||||||
unit: unitId ? unitId.toString() : '',
|
unit: unitId ? unitId.toString() : '',
|
||||||
unitName: unitName,
|
unitName: unitName,
|
||||||
unit_id: unitId ? unitId.toString() : '',
|
unit_id: unitId ? unitId.toString() : '',
|
||||||
// Clear any previous unit-related errors
|
// Clear any previous unit-related errors
|
||||||
...(formErrors[`unit_${idx}`] && { unitError: undefined })
|
...(formErrors[`unit_${idx}`] && { unitError: undefined })
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the products array with the updated product
|
// Update the products array with the updated product
|
||||||
const updatedProducts = products.map((prod, i) =>
|
const updatedProducts = products.map((prod, i) =>
|
||||||
i === idx ? updatedProduct : prod
|
i === idx ? updatedProduct : prod
|
||||||
);
|
);
|
||||||
// Update the parent component's state
|
// Update the parent component's state
|
||||||
onProductsChange(updatedProducts);
|
onProductsChange(updatedProducts);
|
||||||
|
|
||||||
// After updating the product, fetch forecast data if we have a valid product and establishment ID
|
// After updating the product, fetch forecast data if we have a valid product and establishment ID
|
||||||
if (selectedProduct?.value) {
|
if (selectedProduct?.value) {
|
||||||
try {
|
try {
|
||||||
// Get establishment ID from sessionStorage or localStorage
|
// Get establishment ID from sessionStorage or localStorage
|
||||||
const establishmentId = sessionStorage.getItem('establishment_id') ||
|
const establishmentId = sessionStorage.getItem('establishment_id') ||
|
||||||
localStorage.getItem('establishmentId') ||
|
localStorage.getItem('establishmentId') ||
|
||||||
localStorage.getItem('establishment_id');
|
localStorage.getItem('establishment_id');
|
||||||
|
|
||||||
|
if (establishmentId && quarter && year) {
|
||||||
|
const selectedProductId = selectedProduct.originalData?.id || selectedProduct.value;
|
||||||
|
const forecastData = await fetchPreviousForecastData(selectedProductId, establishmentId, p.id);
|
||||||
|
|
||||||
if (establishmentId && quarter && year) {
|
if (forecastData) {
|
||||||
const selectedProductId = selectedProduct.originalData?.id || selectedProduct.value;
|
const data = forecastData.data || forecastData;
|
||||||
const forecastData = await fetchPreviousForecastData(selectedProductId, establishmentId, p.id);
|
const updatedWithForecast = {
|
||||||
|
...updatedProduct,
|
||||||
|
octQuantity: data.previous_quantity_period_one || '',
|
||||||
|
novQuantity: data.previous_quantity_period_two || '',
|
||||||
|
decQuantity: data.previous_quantity_period_three || '',
|
||||||
|
octCost: data.previous_cost_period_one || '',
|
||||||
|
novCost: data.previous_cost_period_two || '',
|
||||||
|
decCost: data.previous_cost_period_three || '',
|
||||||
|
capacity: data.annual_installed_capacity || ''
|
||||||
|
};
|
||||||
|
|
||||||
if (forecastData) {
|
const finalUpdatedProducts = updatedProducts.map((prod, i) =>
|
||||||
const data = forecastData.data || forecastData;
|
i === idx ? updatedWithForecast : prod
|
||||||
const updatedWithForecast = {
|
);
|
||||||
...updatedProduct,
|
|
||||||
octQuantity: data.previous_quantity_period_one || '',
|
onProductsChange(finalUpdatedProducts);
|
||||||
novQuantity: data.previous_quantity_period_two || '',
|
|
||||||
decQuantity: data.previous_quantity_period_three || '',
|
|
||||||
octCost: data.previous_cost_period_one || '',
|
|
||||||
novCost: data.previous_cost_period_two || '',
|
|
||||||
decCost: data.previous_cost_period_three || '',
|
|
||||||
capacity: data.annual_installed_capacity || ''
|
|
||||||
};
|
|
||||||
|
|
||||||
const finalUpdatedProducts = updatedProducts.map((prod, i) =>
|
|
||||||
i === idx ? updatedWithForecast : prod
|
|
||||||
);
|
|
||||||
|
|
||||||
onProductsChange(finalUpdatedProducts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching forecast data:', error);
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching forecast data:', error);
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
loading={isLoadingProducts}
|
}}
|
||||||
disabled={isLoadingProducts}
|
loading={isLoadingProducts}
|
||||||
error={!!formErrors[`product_${idx}`]}
|
disabled={isLoadingProducts}
|
||||||
/>
|
error={!!formErrors[`product_${idx}`]}
|
||||||
<div className="h-5">
|
/>
|
||||||
{formErrors[`product_${idx}`] && (
|
<div className="h-5">
|
||||||
<p className="text-sm text-red-600">{formErrors[`product_${idx}`]}</p>
|
{formErrors[`product_${idx}`] && (
|
||||||
)}
|
<p className="text-sm text-red-600">{formErrors[`product_${idx}`]}</p>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
{productsError && productOptions.length === 0 && (
|
|
||||||
<p className="mt-1 text-xs text-red-600">{productsError}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div data-field={`unit_${idx}`}>
|
<div data-field={`unit_${idx}`}>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Unit <span className="text-red-500">*</span></label>
|
<label className="block text-sm font-medium text-gray-700 mb-1">Unit <span className="text-red-500">*</span></label>
|
||||||
@ -1379,17 +1397,24 @@ const location = useLocation();
|
|||||||
displayValue={p.unitName || ''}
|
displayValue={p.unitName || ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const unitId = e.target.value;
|
const unitId = e.target.value;
|
||||||
const selectedUnit = unitOptions.find(u => u.value === unitId);
|
const displayName = e.target.selectedDisplay ||
|
||||||
const originalUnit = selectedUnit?.originalData || {};
|
unitOptions.find(u => u.value === unitId)?.label ||
|
||||||
const displayName = originalUnit.uom_short_name && originalUnit.uom
|
'';
|
||||||
? `${originalUnit.uom_short_name.toUpperCase()} - ${originalUnit.uom}`
|
|
||||||
: selectedUnit?.label || '';
|
// Update both unit and unitName in a single update
|
||||||
updateProductField(idx, 'unit', unitId);
|
const updatedProduct = {
|
||||||
updateProductField(idx, 'unitName', displayName);
|
...p,
|
||||||
|
unit: unitId,
|
||||||
|
unitName: displayName
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatedProducts = [...products];
|
||||||
|
updatedProducts[idx] = updatedProduct;
|
||||||
|
onProductsChange(updatedProducts);
|
||||||
}}
|
}}
|
||||||
loading={isLoadingUnits}
|
loading={isLoadingUnits}
|
||||||
error={!!formErrors[`unit_${idx}`]}
|
error={!!formErrors[`unit_${idx}`]}
|
||||||
// disabled={!!p.unitName} // Disable if unit is auto-filled from product
|
name={`unit_${idx}`}
|
||||||
/>
|
/>
|
||||||
<div className="h-5">
|
<div className="h-5">
|
||||||
{formErrors[`unit_${idx}`] && (
|
{formErrors[`unit_${idx}`] && (
|
||||||
|
|||||||
@ -28,7 +28,7 @@ const createEmptyCodeForm = () => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Toast notification component
|
// Toast notification component
|
||||||
const Toast = ({ message, type = 'success', onClose }) => {
|
const Toast = ({ message, type = 'success', onClose, position = 'page' }) => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
onClose();
|
onClose();
|
||||||
@ -49,6 +49,18 @@ const Toast = ({ message, type = 'success', onClose }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For modal toasts, we'll use relative positioning
|
||||||
|
if (position === 'modal') {
|
||||||
|
return (
|
||||||
|
<div className="w-full mb-4">
|
||||||
|
<div className={`px-4 py-2 rounded-md text-sm ${getToastStyles()}`}>
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default page-level toast
|
||||||
return (
|
return (
|
||||||
<div className="fixed top-0 left-0 w-full flex justify-center z-50 mt-4">
|
<div className="fixed top-0 left-0 w-full flex justify-center z-50 mt-4">
|
||||||
<div className={`px-6 py-3 rounded-md shadow-lg font-medium ${getToastStyles()}`}>
|
<div className={`px-6 py-3 rounded-md shadow-lg font-medium ${getToastStyles()}`}>
|
||||||
@ -354,7 +366,12 @@ const IsicHsCodes = () => {
|
|||||||
const [modalMode, setModalMode] = React.useState(null);
|
const [modalMode, setModalMode] = React.useState(null);
|
||||||
const [form, setForm] = React.useState(createEmptyCodeForm());
|
const [form, setForm] = React.useState(createEmptyCodeForm());
|
||||||
const [unitOptions, setUnitOptions] = React.useState([]);
|
const [unitOptions, setUnitOptions] = React.useState([]);
|
||||||
const [toast, setToast] = React.useState({ show: false, message: '', type: 'success' });
|
const [toast, setToast] = React.useState({
|
||||||
|
show: false,
|
||||||
|
message: '',
|
||||||
|
type: 'success',
|
||||||
|
position: 'page' // 'page' or 'modal'
|
||||||
|
});
|
||||||
const [showImportModal, setShowImportModal] = React.useState(false);
|
const [showImportModal, setShowImportModal] = React.useState(false);
|
||||||
const [isRefreshing, setIsRefreshing] = React.useState(false);
|
const [isRefreshing, setIsRefreshing] = React.useState(false);
|
||||||
|
|
||||||
@ -767,8 +784,8 @@ const IsicHsCodes = () => {
|
|||||||
setForm(createEmptyCodeForm());
|
setForm(createEmptyCodeForm());
|
||||||
};
|
};
|
||||||
|
|
||||||
const showToast = (message, type = 'success') => {
|
const showToast = (message, type = 'success', position = 'page') => {
|
||||||
setToast({ show: true, message, type });
|
setToast({ show: true, message, type, position });
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setToast(prev => ({ ...prev, show: false }));
|
setToast(prev => ({ ...prev, show: false }));
|
||||||
}, 3000);
|
}, 3000);
|
||||||
@ -833,7 +850,7 @@ const IsicHsCodes = () => {
|
|||||||
if (!form.code || !form.product) {
|
if (!form.code || !form.product) {
|
||||||
const errorMsg = 'Please fill in all required fields';
|
const errorMsg = 'Please fill in all required fields';
|
||||||
console.error(errorMsg);
|
console.error(errorMsg);
|
||||||
setError(errorMsg);
|
showToast(errorMsg, 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -875,8 +892,9 @@ const IsicHsCodes = () => {
|
|||||||
const productId = form.id || (editingRow !== null ? rowsData[editingRow]?.id : null);
|
const productId = form.id || (editingRow !== null ? rowsData[editingRow]?.id : null);
|
||||||
|
|
||||||
if (!productId) {
|
if (!productId) {
|
||||||
console.error('Product ID not found for editing');
|
const errorMsg = 'Cannot update product: Product ID not found';
|
||||||
setError('Cannot update product: Product ID not found');
|
console.error(errorMsg);
|
||||||
|
showToast(errorMsg, 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -934,7 +952,7 @@ const IsicHsCodes = () => {
|
|||||||
if (error.response?.data?.message) {
|
if (error.response?.data?.message) {
|
||||||
errorMessage = error.response.data.message;
|
errorMessage = error.response.data.message;
|
||||||
}
|
}
|
||||||
setError(errorMessage);
|
showToast(errorMessage, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,7 +990,7 @@ const IsicHsCodes = () => {
|
|||||||
if (createError.response?.data?.message) {
|
if (createError.response?.data?.message) {
|
||||||
errorMessage = createError.response.data.message;
|
errorMessage = createError.response.data.message;
|
||||||
}
|
}
|
||||||
setError(errorMessage);
|
showToast(errorMessage, 'error');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in handleSaveForm:', error);
|
console.error('Error in handleSaveForm:', error);
|
||||||
@ -1075,10 +1093,12 @@ const IsicHsCodes = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{toast.show && (
|
{/* Page-level toasts */}
|
||||||
|
{toast.show && toast.position === 'page' && (
|
||||||
<Toast
|
<Toast
|
||||||
message={toast.message}
|
message={toast.message}
|
||||||
type={toast.type}
|
type={toast.type}
|
||||||
|
position="page"
|
||||||
onClose={() => setToast(prev => ({ ...prev, show: false }))}
|
onClose={() => setToast(prev => ({ ...prev, show: false }))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -1373,6 +1393,15 @@ const IsicHsCodes = () => {
|
|||||||
|
|
||||||
{/* Form */}
|
{/* Form */}
|
||||||
<div className="p-6 space-y-4">
|
<div className="p-6 space-y-4">
|
||||||
|
{/* Modal-level toast */}
|
||||||
|
{toast.show && toast.position === 'modal' && (
|
||||||
|
<Toast
|
||||||
|
message={toast.message}
|
||||||
|
type={toast.type}
|
||||||
|
position="modal"
|
||||||
|
onClose={() => setToast(prev => ({ ...prev, show: false }))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-[#374151] mb-1">
|
<label className="block text-sm font-medium text-[#374151] mb-1">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user