2026-03-23 17:43:17 +05:30

662 lines
28 KiB
TypeScript

import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { useParams, useNavigate, useSearchParams, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useItemDetails, useItemMutations } from '../hooks/useItem';
import { FaArrowLeft, FaSave, FaEdit, FaCheck, FaTrashAlt, FaSync } from 'react-icons/fa';
import type { CreateItemData } from '../services/itemService';
import LinkField from '../components/LinkField';
import API_CONFIG from '../config/api';
import CommentSection from '../components/CommentSection';
import ActivityLog from '../components/ActivityLog';
import DeleteRequestButton from '../components/DeleteRequestButton';
import type { DeleteStatus } from '../services/deleteRequestService';
import apiService from '../services/apiService';
const ItemDetail: React.FC = () => {
const { t } = useTranslation();
// const { itemName } = useParams<{ itemName: string }>();
// const navigate = useNavigate();
// const [searchParams] = useSearchParams();
// const duplicateFromItem = searchParams.get('duplicate');
// const isNewItem = itemName === 'new';
// const isDuplicating = isNewItem && !!duplicateFromItem;
const { itemName: rawItemName } = useParams<{ itemName: string }>();
const navigate = useNavigate();
const location = useLocation();
const [searchParams] = useSearchParams();
const duplicateFromItem = searchParams.get('duplicate');
// Extract item name from pathname directly to preserve # characters
// which browsers strip from useParams as URL fragments
// const itemName = useMemo(() => {
// const prefix = '/inventory/';
// const idx = location.pathname.indexOf(prefix);
// if (idx !== -1) {
// const encoded = location.pathname.slice(idx + prefix.length);
// const decoded = decodeURIComponent(encoded);
// return decoded;
// }
// return rawItemName || '';
// }, [location.pathname, rawItemName]);
const itemName = useMemo(() => {
if (rawItemName === 'new') return 'new';
// Use the raw encoded pathname and decode it ourselves
// to avoid React Router's automatic decoding losing # info
const prefix = '/inventory/';
const fullPath = window.location.pathname; // e.g. /asm_app/inventory/DELUGE%20VALVE%20%20NO%23%201
const idx = fullPath.indexOf(prefix);
if (idx !== -1) {
const encoded = fullPath.slice(idx + prefix.length);
try {
return decodeURIComponent(encoded);
} catch {
return encoded;
}
}
return rawItemName || '';
}, [rawItemName, location.pathname]);
const isNewItem = itemName === 'new';
const isDuplicating = isNewItem && !!duplicateFromItem;
// Balance Qty state (fetched from Bin doctype)
const [balanceQty, setBalanceQty] = useState<number>(0);
const [balanceQtyLoading, setBalanceQtyLoading] = useState<boolean>(false);
const [userRoles, setUserRoles] = useState<string[]>([]);
const [isSystemManager, setIsSystemManager] = useState(false);
const [rolesLoaded, setRolesLoaded] = useState(false);
useEffect(() => {
const fetchRoles = async () => {
try {
const response = await apiService.apiCall<any>(
'/api/method/asset_lite.api.user_roles.get_user_roles'
);
const roles = Array.isArray(response) ? response : (response?.message || []);
setUserRoles(roles);
setIsSystemManager(roles.includes('System Manager'));
} catch (err) {
console.error('Error fetching roles:', err);
} finally {
setRolesLoaded(true);
}
};
fetchRoles();
}, []);
// Form data state
const [formData, setFormData] = useState<CreateItemData>({
item_code: '',
item_name: '',
item_group: '',
custom_technical_department: '',
custom_hospital_name: '',
custom_part_description: '',
stock_uom: 'Nos',
custom_item_cost_per_unit: 0,
disabled: 0,
is_stock_item: 1,
is_fixed_asset: 0,
opening_stock: 0,
valuation_rate: 0,
standard_rate: 0,
custom_last_calibration_date: '',
custom_next_due_calibration_date: '',
description: '',
brand: '',
custom_warranty_in_months: '',
valuation_method: '',
has_batch_no: 0,
has_serial_no: 0,
custom_serial_no: '',
custom_date_in: '',
custom_code: '',
custom_type: '',
custom_volts: undefined as number | undefined,
custom_w: undefined as number | undefined,
is_purchase_item: 1,
is_sales_item: 1,
country_of_origin: 'Saudi Arabia',
});
const { item, loading, error, refetch: refetchItem } = useItemDetails(
isDuplicating ? duplicateFromItem : (isNewItem ? null : itemName || null)
);
const { createItem, updateItem, submitItem, loading: saving } = useItemMutations();
const [isEditing, setIsEditing] = useState(isNewItem);
// Check document status
const docstatus = item?.docstatus ?? 0;
const isSubmitted = docstatus === 1;
const isCancelled = docstatus === 2;
const isDraft = docstatus === 0;
const hasDeleteRequest = !!(item?.custom_delete_status);
// Check if Calibration Information should be shown
const showCalibrationInfo = formData.item_group === 'Tools';
// Fetch Balance Qty from Bin doctype
const fetchBalanceQty = useCallback(async (itemCode: string) => {
if (!itemCode) return;
setBalanceQtyLoading(true);
try {
// Get CSRF token
let csrfToken: string | null = null;
if (typeof window !== 'undefined' && (window as any).csrf_token) {
csrfToken = (window as any).csrf_token;
}
// Build filters and fields for Frappe API
const filters = JSON.stringify([['item_code', '=', itemCode]]);
const fields = JSON.stringify(['actual_qty', 'warehouse']);
const url = `${API_CONFIG.BASE_URL}/api/resource/Bin?filters=${encodeURIComponent(filters)}&fields=${encodeURIComponent(fields)}&limit_page_length=0`;
const headers: Record<string, string> = {
'Accept': 'application/json',
'Content-Type': 'application/json',
};
if (csrfToken) {
headers['X-Frappe-CSRF-Token'] = csrfToken;
}
const response = await fetch(url, {
method: 'GET',
headers,
credentials: 'include', // Include cookies for session auth
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
// Sum up actual_qty from all warehouses
const totalQty = result.data?.reduce((sum: number, bin: any) => {
return sum + (bin.actual_qty || 0);
}, 0) || 0;
setBalanceQty(totalQty);
} catch (err) {
console.error('Failed to fetch balance qty:', err);
setBalanceQty(0);
} finally {
setBalanceQtyLoading(false);
}
}, []);
// Fetch balance qty when item is loaded (for existing items)
useEffect(() => {
if (!isNewItem && item?.item_code) {
fetchBalanceQty(item.item_code);
}
}, [isNewItem, item?.item_code, fetchBalanceQty]);
// Load item data when item is fetched
useEffect(() => {
if (item && !isDuplicating) {
setFormData({
item_code: item.item_code || '',
item_name: item.item_name || '',
item_group: item.item_group || '',
custom_technical_department: item.custom_technical_department || '',
custom_hospital_name: item.custom_hospital_name || '',
custom_part_description: item.custom_part_description || '',
stock_uom: item.stock_uom || 'Nos',
custom_item_cost_per_unit: item.custom_item_cost_per_unit || 0,
disabled: item.disabled || 0,
is_stock_item: item.is_stock_item ?? 1,
is_fixed_asset: item.is_fixed_asset ?? 0,
opening_stock: item.opening_stock || 0,
valuation_rate: item.valuation_rate ?? 0,
standard_rate: item.standard_rate || 0,
custom_last_calibration_date: item.custom_last_calibration_date || '',
custom_next_due_calibration_date: item.custom_next_due_calibration_date || '',
description: item.description || '',
brand: item.brand || '',
custom_warranty_in_months: item.custom_warranty_in_months || '',
valuation_method: item.valuation_method || '',
has_batch_no: item.has_batch_no || 0,
has_serial_no: item.has_serial_no || 0,
is_purchase_item: item.is_purchase_item ?? 1,
is_sales_item: item.is_sales_item ?? 1,
country_of_origin: item.country_of_origin || 'Saudi Arabia',
uoms: item.uoms || [],
item_defaults: item.item_defaults || [],
custom_serial_no: item.custom_serial_no || '',
custom_date_in: item.custom_date_in || '',
custom_code: item.custom_code || '',
custom_type: item.custom_type || '',
custom_volts: item.custom_volts,
custom_w: item.custom_w,
});
setIsEditing(false);
} else if (isDuplicating && item) {
// When duplicating, copy data but clear name/code
setFormData({
item_code: '',
item_name: item.item_name || '',
item_group: item.item_group || '',
custom_technical_department: item.custom_technical_department || '',
custom_hospital_name: item.custom_hospital_name || '',
custom_part_description: item.custom_part_description || '',
stock_uom: item.stock_uom || 'Nos',
custom_item_cost_per_unit: item.custom_item_cost_per_unit || 0,
disabled: 0,
is_stock_item: item.is_stock_item ?? 1,
is_fixed_asset: item.is_fixed_asset ?? 0,
opening_stock: item.opening_stock || 0,
valuation_rate: item.valuation_rate ?? 0,
standard_rate: item.standard_rate || 0,
custom_last_calibration_date: item.custom_last_calibration_date || '',
custom_next_due_calibration_date: item.custom_next_due_calibration_date || '',
description: item.description || '',
brand: item.brand || '',
custom_warranty_in_months: item.custom_warranty_in_months || '',
valuation_method: item.valuation_method || '',
has_batch_no: item.has_batch_no || 0,
has_serial_no: item.has_serial_no || 0,
is_purchase_item: item.is_purchase_item ?? 1,
is_sales_item: item.is_sales_item ?? 1,
country_of_origin: item.country_of_origin || 'Saudi Arabia',
uoms: item.uoms || [],
item_defaults: item.item_defaults || [],
custom_serial_no: item.custom_serial_no || '',
custom_date_in: item.custom_date_in || '',
custom_code: item.custom_code || '',
custom_type: item.custom_type || '',
custom_volts: item.custom_volts,
custom_w: item.custom_w,
});
}
}, [item, isDuplicating]);
const handleSave = async () => {
try {
if (isNewItem) {
const newItem = await createItem(formData);
navigate(`/inventory/${newItem.name}`);
} else {
await updateItem(itemName!, formData);
await refetchItem();
// Refresh balance qty after update
if (formData.item_code) {
fetchBalanceQty(formData.item_code);
}
setIsEditing(false);
alert(t('items.itemUpdatedSuccessfully'));
}
} catch (err) {
alert(`${t('items.failedToSave')}: ${err instanceof Error ? err.message : 'Unknown error'}`);
}
};
const handleSubmit = async () => {
if (!itemName || isNewItem) {
alert(t('items.pleaseSaveFirst'));
return;
}
try {
await submitItem(itemName);
await refetchItem();
setIsEditing(false);
alert(t('items.submittedSuccessfully'));
} catch (err) {
alert(`${t('items.failedToSubmit')}: ${err instanceof Error ? err.message : 'Unknown error'}`);
}
};
const isFieldDisabled = useCallback((fieldname: string): boolean => {
if (!isEditing) return true;
if (isCancelled) return true;
if (hasDeleteRequest) return true;
if (isSubmitted) {
// For submitted items, most fields are read-only
// Only allow editing certain fields if needed
return true;
}
return false;
}, [isEditing, isCancelled, isSubmitted, hasDeleteRequest]);
if (loading) {
return (
<div className="flex items-center justify-center h-screen bg-gray-50 dark:bg-gray-900">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto"></div>
<p className="mt-4 text-gray-600 dark:text-gray-400">{t('items.loadingItem')}</p>
</div>
</div>
);
}
if (error && !isNewItem) {
return (
<div className="p-6 bg-gray-50 dark:bg-gray-900 min-h-screen">
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-6">
<h2 className="text-xl font-bold text-red-800 dark:text-red-300 mb-4">{t('items.errorLoadingItem')}</h2>
<p className="text-red-700 dark:text-red-400 mb-4">{error}</p>
<button
onClick={() => navigate(-1)}
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded"
>
{t('items.backToInventory')}
</button>
</div>
</div>
);
}
const inputClassName = "w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 bg-white dark:bg-gray-700 text-gray-900 dark:text-white";
const labelClassName = "block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1";
const sectionHeaderClassName = "text-base font-semibold text-gray-800 dark:text-white mb-4 pb-2 border-b border-gray-200 dark:border-gray-700";
const cardClassName = "bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border border-gray-200 dark:border-gray-700";
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 p-6">
{/* Header */}
<div className="mb-6 flex justify-between items-center">
<div className="flex items-center gap-4">
<button
onClick={() => navigate(-1)}
className="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 flex items-center gap-2"
>
<FaArrowLeft />
<span className="text-gray-900 dark:text-white font-medium">
{isNewItem ? t('items.newItem') : item?.item_name || item?.item_code || t('items.title')}
</span>
</button>
{!isNewItem && (
<span className="px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300">
{item?.item_code || itemName}
</span>
)}
</div>
<div className="flex gap-3">
{!isNewItem && !isEditing && isDraft && !hasDeleteRequest && (
<button
onClick={() => setIsEditing(true)}
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center gap-2"
>
<FaEdit />
{t('common.edit')}
</button>
)}
{/* {!isNewItem && !isEditing && rolesLoaded && (
<DeleteRequestButton
doctype="Item"
docname={itemName}
currentDeleteStatus={(item?.custom_delete_status ?? null) as DeleteStatus}
userRoles={userRoles}
isSystemManager={isSystemManager}
inline
redirectOnDelete="/inventory"
onStatusChange={() => refetchItem()}
/>
)} */}
{isEditing && (
<>
<button
type="button"
onClick={() => {
if (isNewItem) navigate(-1);
else { setIsEditing(false); refetchItem(); }
}}
className="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg"
>
{t('common.cancel')}
</button>
<button
type="button"
onClick={handleSave}
disabled={saving}
className="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg flex items-center gap-2 disabled:opacity-50"
>
<FaSave />
{saving ? t('common.saving') : t('common.save')}
</button>
{/* {!isNewItem && isDraft && (
<button
onClick={handleSubmit}
disabled={saving}
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center gap-2 disabled:opacity-50"
>
<FaCheck />
{t('common.submit')}
</button>
)} */}
</>
)}
</div>
</div>
{/* Form - Grid Layout matching AssetDetail */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{/* COLUMN 1: Basic Information */}
<div className={cardClassName}>
<h2 className={sectionHeaderClassName}>{t('items.basicInformation')}</h2>
<div className="space-y-4">
<div>
<label className={labelClassName}>{t('items.itemCode')} <span className="text-red-500">*</span></label>
<input
type="text"
value={formData.item_code}
onChange={(e) => setFormData({ ...formData, item_code: e.target.value })}
disabled={isFieldDisabled('item_code') || !isNewItem}
className={inputClassName}
required
/>
</div>
<LinkField
label={t('commonFields.hospital')}
doctype="Company"
value={formData.custom_hospital_name || ''}
onChange={(value) => setFormData({ ...formData, custom_hospital_name: value })}
disabled={isFieldDisabled('custom_hospital_name')}
placeholder={t('items.selectHospital')}
filters={{ domain: 'Healthcare' }}
/>
<LinkField
label={t('items.itemGroup')}
doctype="Item Group"
value={formData.item_group || ''}
onChange={(value) => setFormData({ ...formData, item_group: value })}
disabled={isFieldDisabled('item_group')}
placeholder={t('items.selectItemGroup')}
/>
<LinkField
label={t('items.technicalDepartment')}
doctype="Issue Type"
value={formData.custom_technical_department || ''}
onChange={(value) => setFormData({ ...formData, custom_technical_department: value })}
disabled={isFieldDisabled('custom_technical_department')}
placeholder={t('items.selectTechnicalDepartment')}
/>
<div>
<label className={labelClassName}>{t('items.stockUOM')}</label>
<input
type="text"
value={formData.stock_uom}
onChange={(e) => setFormData({ ...formData, stock_uom: e.target.value })}
disabled={isFieldDisabled('stock_uom')}
className={inputClassName}
/>
</div>
<div>
<label className={labelClassName}>{t('items.partDescription')}</label>
<input
type="text"
value={formData.custom_part_description}
onChange={(e) => setFormData({ ...formData, custom_part_description: e.target.value })}
disabled={isFieldDisabled('custom_part_description')}
className={inputClassName}
/>
</div>
</div>
</div>
{/* COLUMN 2: Inventory Details */}
<div className={cardClassName}>
<h2 className={sectionHeaderClassName}>{t('items.inventoryDetails')}</h2>
<div className="space-y-4">
<div>
<label className={labelClassName}>{t('items.serialNo')}</label>
<input type="text" value={formData.custom_serial_no} onChange={(e) => setFormData({ ...formData, custom_serial_no: e.target.value })} disabled={isFieldDisabled('custom_serial_no')} className={inputClassName} />
</div>
<div>
<label className={labelClassName}>{t('items.dateIn')}</label>
<input type="date" value={formData.custom_date_in} onChange={(e) => setFormData({ ...formData, custom_date_in: e.target.value })} disabled={isFieldDisabled('custom_date_in')} className={inputClassName} />
</div>
<div>
<label className={labelClassName}>{t('items.watts')}</label>
<input type="number" step="0.01" value={formData.custom_w ?? ''} onChange={(e) => { const v = parseFloat(e.target.value); setFormData({ ...formData, custom_w: e.target.value === '' || isNaN(v) ? undefined : v }); }} disabled={isFieldDisabled('custom_w')} className={inputClassName} />
</div>
<div>
<label className={labelClassName}>{t('items.volts')}</label>
<input type="number" step="0.01" value={formData.custom_volts ?? ''} onChange={(e) => { const v = parseFloat(e.target.value); setFormData({ ...formData, custom_volts: e.target.value === '' || isNaN(v) ? undefined : v }); }} disabled={isFieldDisabled('custom_volts')} className={inputClassName} />
</div>
<div>
<label className={labelClassName}>{t('items.type')}</label>
<input type="text" value={formData.custom_type} onChange={(e) => setFormData({ ...formData, custom_type: e.target.value })} disabled={isFieldDisabled('custom_type')} className={inputClassName} />
</div>
<div>
<label className={labelClassName}>{t('items.code')}</label>
<input type="text" value={formData.custom_code} onChange={(e) => setFormData({ ...formData, custom_code: e.target.value })} disabled={isFieldDisabled('custom_code')} className={inputClassName} />
</div>
</div>
</div>
{/* COLUMN 3: Stock & Additional Information */}
<div className={cardClassName}>
<h2 className={sectionHeaderClassName}>{t('items.stockInformation')}</h2>
<div className="space-y-4">
<div className="flex items-center gap-2">
<input type="checkbox" id="is_stock_item" checked={formData.is_stock_item === 1} onChange={(e) => setFormData({ ...formData, is_stock_item: e.target.checked ? 1 : 0 })} disabled={isFieldDisabled('is_stock_item')} className="w-4 h-4" />
<label htmlFor="is_stock_item" className="text-sm font-medium text-gray-700 dark:text-gray-300">{t('items.isStockItem')}</label>
</div>
<div className="flex items-center gap-2">
<input type="checkbox" id="is_fixed_asset" checked={formData.is_fixed_asset === 1} onChange={(e) => setFormData({ ...formData, is_fixed_asset: e.target.checked ? 1 : 0 })} disabled={isFieldDisabled('is_fixed_asset')} className="w-4 h-4" />
<label htmlFor="is_fixed_asset" className="text-sm font-medium text-gray-700 dark:text-gray-300">{t('items.isFixedAsset')}</label>
</div>
{isNewItem && formData.is_stock_item === 1 && (
<div>
<label className={labelClassName}>{t('items.openingStock')}</label>
<input type="number" value={formData.opening_stock} onChange={(e) => setFormData({ ...formData, opening_stock: parseFloat(e.target.value) || 0 })} disabled={isFieldDisabled('opening_stock')} className={inputClassName} />
</div>
)}
{formData.is_stock_item === 1 && (
<div>
<label className={labelClassName}>{t('items.valuationRate')}</label>
<input type="number" step="0.01" value={formData.valuation_rate} onChange={(e) => setFormData({ ...formData, valuation_rate: parseFloat(e.target.value) || 0 })} disabled={isFieldDisabled('valuation_rate')} className={inputClassName} />
</div>
)}
{!isNewItem && formData.is_stock_item === 1 && (
<div>
<label className={labelClassName}>{t('items.balanceQty')}</label>
<div className="flex items-center gap-2">
<input type="number" value={balanceQty} readOnly className={`${inputClassName} bg-gray-100 dark:bg-gray-800 cursor-not-allowed`} />
<button type="button" onClick={() => formData.item_code && fetchBalanceQty(formData.item_code)} disabled={balanceQtyLoading} className="p-2 text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 disabled:opacity-50" title={t('items.refreshBalanceQty')}>
<FaSync className={balanceQtyLoading ? 'animate-spin' : ''} />
</button>
</div>
</div>
)}
</div>
{/* Calibration - when Item Group is Tools */}
{showCalibrationInfo && (
<>
<h2 className={`${sectionHeaderClassName} mt-6`}>{t('items.calibrationInformation')}</h2>
<div className="space-y-4 mt-4">
<div>
<label className={labelClassName}>{t('items.lastCalibrationDate')}</label>
<input type="date" value={formData.custom_last_calibration_date} onChange={(e) => setFormData({ ...formData, custom_last_calibration_date: e.target.value })} disabled={isFieldDisabled('custom_last_calibration_date')} className={inputClassName} />
</div>
<div>
<label className={labelClassName}>{t('items.nextCalibrationDate')}</label>
<input type="date" value={formData.custom_next_due_calibration_date} onChange={(e) => setFormData({ ...formData, custom_next_due_calibration_date: e.target.value })} disabled={isFieldDisabled('custom_next_due_calibration_date')} className={inputClassName} />
</div>
</div>
</>
)}
<h2 className={`${sectionHeaderClassName} mt-6`}>{t('items.additionalInformation')}</h2>
<div className="space-y-4 mt-4">
<div>
<label className={labelClassName}>{t('commonFields.description')}</label>
<textarea value={formData.description} onChange={(e) => setFormData({ ...formData, description: e.target.value })} disabled={isFieldDisabled('description')} rows={3} className={inputClassName} />
</div>
<div>
<label className={labelClassName}>{t('items.warrantyMonths')}</label>
<input type="text" value={formData.custom_warranty_in_months} onChange={(e) => setFormData({ ...formData, custom_warranty_in_months: e.target.value })} disabled={isFieldDisabled('custom_warranty_in_months')} className={inputClassName} />
</div>
</div>
</div> {/* ← closes grid */}
{/* Comments Section */}
{!isNewItem && (
<div className="mt-6">
<CommentSection
referenceDoctype="Item"
referenceName={itemName || null}
title="Comments & Discussion"
pollInterval={30000}
initialLimit={5}
collapsible={true}
startCollapsed={false}
/>
</div>
)}
{/* Activity Log */}
{!isNewItem && !isDuplicating && (
<div className="mt-6">
<ActivityLog
doctype="Item"
docname={itemName || null}
creationDate={item?.creation}
createdBy={item?.owner}
compact={false}
initialVisible={5}
collapsible={true}
startCollapsed={true}
/>
</div>
)}
{/* Delete Request */}
{!isNewItem && rolesLoaded && (
<div className="mt-6 max-w-sm">
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6 border border-gray-200 dark:border-gray-700">
<h2 className="text-base font-semibold text-gray-800 dark:text-white mb-4 pb-2 border-b border-gray-200 dark:border-gray-700">
Delete Request
</h2>
<DeleteRequestButton
doctype="Item"
docname={itemName}
currentDeleteStatus={(item?.custom_delete_status ?? null) as DeleteStatus}
userRoles={userRoles}
isSystemManager={isSystemManager}
redirectOnDelete="/inventory"
onStatusChange={() => refetchItem()}
/>
</div>
</div>
)}
</div> {/* ← closes min-h-screen wrapper */}
</div>
);
};
export default ItemDetail;