order details and potential mining did edit and add

This commit is contained in:
suryacp23 2026-07-27 13:41:06 +05:30
parent e03a5a483c
commit 32ddfff807
5 changed files with 129 additions and 86 deletions

View File

@ -9,7 +9,6 @@ import {
User,
ClipboardList,
TrendingUp,
Edit3,
MapPin,
Clock,
Phone,
@ -25,7 +24,6 @@ export interface CallDetailProps {
instanceId: number | string;
selectedRow?: Record<string, any>;
potentialMiningAction?: ReactNode;
placeOrderAction?: ReactNode;
refreshKey?: number;
onBack?: () => void;
onDataLoad?: (data: Record<string, unknown>) => void;
@ -35,7 +33,6 @@ export function CallDetail({
instanceId,
selectedRow,
potentialMiningAction,
placeOrderAction,
refreshKey,
onBack,
onDataLoad,
@ -56,7 +53,7 @@ export function CallDetail({
setData(r.data || {});
if (onDataLoad) onDataLoad(r.data || {});
const storeCode = (r.data?.select_store as any)?.store_code || (r.data?.select_store as any)?.code;
const storeCode = (r.data?.select_store as any)?.store_code || (r.data?.select_store as any)?.code || (r.data as any)?.store_code || (r.data as any)?.store?.store_code;
if (storeCode) {
orderBookingClient.request<{ potential: { potential: any[] } }>(
'POST',

View File

@ -1,4 +1,4 @@
import { ArrowUpRight, ArrowDownRight, Pencil, Trash2 } from 'lucide-react';
import { Pencil, Trash2 } from 'lucide-react';
import { cn } from '../../lib/cn';
export interface MiningItem {

View File

@ -16,7 +16,7 @@ export function SmartGridField({
value: Record<string, unknown>[];
onChange: (val: Record<string, unknown>[]) => void;
}) {
const [isModalOpen, setIsModalOpen] = useState(true);
const isPotentialMining = label.toLowerCase().includes('potential');
const formRef = useRef<HTMLDivElement>(null);
const [newRow, setNewRow] = useState<Record<string, unknown>>({});
const [editingIdx, setEditingIdx] = useState<number | null>(null);
@ -44,7 +44,6 @@ export function SmartGridField({
const startEdit = (idx: number) => {
setEditingIdx(idx);
setNewRow({ ...value[idx] });
setIsModalOpen(true);
};
const updateNewRowField = (fieldId: string, val: unknown) => {
@ -109,6 +108,11 @@ export function SmartGridField({
const hasAnyValue = visibleColumns.some(col => newRow[col.id] !== undefined && newRow[col.id] !== null && String(newRow[col.id]).trim() !== '');
for (const col of visibleColumns) {
if (isPotentialMining) {
const isReasonOrRemark = col.id.toLowerCase().includes('reason') || col.name.toLowerCase().includes('reason') || col.id.toLowerCase().includes('remark') || col.name.toLowerCase().includes('remark');
if (!isReasonOrRemark) continue;
}
const isCategory = col.id === 'product_category' || col.name === 'Product Category' || col.mapped_workflow_field === 'product_category' || getBaseId(col.id) === 'product_category';
const isProductName = col.id === 'product_name' || col.name === 'Product Name' || col.mapped_workflow_field === 'product_name' || getBaseId(col.id) === 'product_name';
const isBags = col.id === 'bags' || col.name === 'Bags' || col.mapped_workflow_field === 'bags' || getBaseId(col.id) === 'bags' || col.id.toLowerCase().includes('quantity') || col.name.toLowerCase().includes('quantity') || col.mapped_workflow_field?.toLowerCase().includes('quantity');
@ -173,13 +177,85 @@ export function SmartGridField({
// Only close form if we were editing an existing row. For new rows, stay open.
if (editingIdx !== null) {
setIsModalOpen(false);
// Nothing needed here anymore since we removed isModalOpen
}
setNewRow({});
setEditingIdx(null);
};
const isPotentialMining = label.toLowerCase().includes('potential');
const renderFormFields = () => {
return visibleColumns.map(col => {
const val = newRow[col.id];
const getBaseId = (id: string) => id.replace(/_\d+$/, '');
if (isPotentialMining) {
const isReasonOrRemark = col.id.toLowerCase().includes('reason') || col.name.toLowerCase().includes('reason') || col.id.toLowerCase().includes('remark') || col.name.toLowerCase().includes('remark');
if (!isReasonOrRemark) return null;
}
const catColId = columns.find(c => c.id === 'product_category' || c.name === 'Product Category' || c.mapped_workflow_field === 'product_category' || getBaseId(c.id) === 'product_category')?.id;
const isCatSelected = catColId ? !!newRow[catColId] : false;
const isProductName = col.id === 'product_name' || col.name === 'Product Name' || col.mapped_workflow_field === 'product_name' || getBaseId(col.id) === 'product_name';
const isBags = col.id === 'bags' || col.name === 'Bags' || col.mapped_workflow_field === 'bags' || getBaseId(col.id) === 'bags' || col.id.toLowerCase().includes('quantity') || col.name.toLowerCase().includes('quantity') || col.mapped_workflow_field?.toLowerCase().includes('quantity');
const isRequired = col.mandatory || (isCatSelected && (isProductName || isBags));
if (col.data_type === 'select' || col.data_type === 'multiselect') {
const allOpts = col.properties?.options || [];
let filteredOpts = allOpts;
if (col.id === 'product_name' || col.name === 'Product Name' || col.mapped_workflow_field === 'product_name') {
const catCol = columns.find(c => c.id === 'product_category' || c.name === 'Product Category' || c.mapped_workflow_field === 'product_category');
if (catCol) {
const selectedCategory = newRow[catCol.id] as string;
if (selectedCategory) {
filteredOpts = allOpts.filter(opt => {
const labelStr = String(opt.label || opt.value || '');
return labelStr.startsWith(selectedCategory);
});
}
}
} else {
filteredOpts = allOpts.filter(opt => {
if (!opt._raw) return true;
for (const [rowKey, rowVal] of Object.entries(newRow)) {
if (rowKey === col.id || rowVal == null || rowVal === '') continue;
const rawKey = Object.keys(opt._raw).find(rk => rk === rowKey || rk.replace(/_/g, '') === rowKey.replace(/_/g, ''));
if (rawKey && String(opt._raw[rawKey]) !== String(rowVal)) {
return false;
}
}
return true;
});
}
return (
<Select
key={col.id}
label={col.name}
required={isRequired}
error={errors[col.id]}
value={(val as string) ?? ''}
onChange={(e) => updateNewRowField(col.id, e.target.value)}
options={[{ value: '', label: 'Select...' }, ...filteredOpts.map((o: any) => ({ value: String(o.value), label: o.label }))]}
/>
);
}
return (
<Input
key={col.id}
label={col.name}
required={isRequired}
error={errors[col.id]}
type={col.data_type === 'number' ? 'number' : col.data_type === 'email' ? 'email' : 'text'}
value={(val as string) ?? ''}
onChange={(e) => updateNewRowField(col.id, col.data_type === 'number' ? Number(e.target.value) : e.target.value)}
/>
);
});
};
return (
<div className="flex flex-col gap-2 font-sans border border-border-default rounded-md p-4 bg-slate-50">
@ -243,6 +319,40 @@ export function SmartGridField({
}
const kgsStr = kgsNum > 0 ? ` (${kgsNum} kg)` : '';
if (editingIdx === i) {
return (
<div key={i} className="bg-white border border-indigo-200 rounded-xl p-4 shadow-sm flex flex-col gap-4">
<h4 className="text-sm font-semibold text-strong flex items-center gap-2">
<Pencil size={16} className="text-blue-600" /> Edit Row
</h4>
<div className="flex flex-col min-w-0 gap-1.5 p-3 bg-slate-50 border border-slate-100 rounded-lg mb-2">
<span className="font-bold text-slate-800 text-[14px] truncate">{productName}</span>
<div className="flex flex-wrap items-center gap-2">
{(!isPotentialMining || hasBagsCol) && (
<span className="font-extrabold text-indigo-600 text-[13px]">{bags} {isPotentialMining ? 'Kgs' : 'Bags'}{!isPotentialMining ? kgsStr : ''}</span>
)}
{isPotentialMining && diffVal !== null && !isNaN(diffVal) && diffVal !== 0 && (
<span className={`text-[11px] font-bold px-2 py-0.5 rounded-md ${diffVal > 0 ? 'bg-emerald-100 text-emerald-700' : 'bg-red-100 text-red-700'}`}>
{Math.abs(diffVal)} {diffVal > 0 ? 'Surplus' : 'Less'}
</span>
)}
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 items-end">
{renderFormFields()}
</div>
<div className="flex justify-end gap-2 mt-2 pt-3 border-t border-slate-100">
<Button type="button" variant="ghost" onClick={() => { setEditingIdx(null); setNewRow({}); }}>
Cancel
</Button>
<Button type="button" variant="primary" onClick={submitNewRow}>
Save Item
</Button>
</div>
</div>
);
}
return (
<div key={i} className="bg-slate-50 border border-slate-200 rounded-xl p-3 shadow-sm flex items-center justify-between">
<div className="flex flex-col min-w-0 pr-4 gap-1.5">
@ -270,9 +380,11 @@ export function SmartGridField({
<button type="button" onClick={() => startEdit(i)} className="text-indigo-600 bg-white border border-indigo-100 hover:bg-indigo-50 p-1.5 rounded-lg transition-colors flex items-center justify-center shadow-sm" title="Edit">
<Pencil size={14} />
</button>
{!isPotentialMining && (
<button type="button" onClick={() => removeRow(i)} className="text-red-600 bg-white border border-red-100 hover:bg-red-50 p-1.5 rounded-lg transition-colors flex items-center justify-center shadow-sm" title="Remove">
<Trash2 size={14} />
</button>
)}
</div>
</div>
);
@ -285,95 +397,29 @@ export function SmartGridField({
<div
ref={formRef}
className={`grid transition-[grid-template-rows,opacity,margin] duration-300 ease-in-out ${
isModalOpen || editingIdx !== null ? 'grid-rows-[1fr] opacity-100 mt-4' : 'grid-rows-[0fr] opacity-0 mt-0'
!isPotentialMining && editingIdx === null ? 'grid-rows-[1fr] opacity-100 mt-4' : 'grid-rows-[0fr] opacity-0 mt-0'
}`}
>
<div className="overflow-hidden">
<div className="p-4 border border-border-subtle rounded-xl bg-white shadow-sm">
<h4 className="text-sm font-semibold text-strong mb-4 flex items-center gap-2">
{editingIdx !== null ? (
<><Pencil size={16} className="text-blue-600" /> Edit Row</>
) : (
<>Add New Item</>
)}
Add New Item
</h4>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 items-end">
{visibleColumns.map(col => {
const val = newRow[col.id];
const getBaseId = (id: string) => id.replace(/_\d+$/, '');
const catColId = columns.find(c => c.id === 'product_category' || c.name === 'Product Category' || c.mapped_workflow_field === 'product_category' || getBaseId(c.id) === 'product_category')?.id;
const isCatSelected = catColId ? !!newRow[catColId] : false;
const isProductName = col.id === 'product_name' || col.name === 'Product Name' || col.mapped_workflow_field === 'product_name' || getBaseId(col.id) === 'product_name';
const isBags = col.id === 'bags' || col.name === 'Bags' || col.mapped_workflow_field === 'bags' || getBaseId(col.id) === 'bags' || col.id.toLowerCase().includes('quantity') || col.name.toLowerCase().includes('quantity') || col.mapped_workflow_field?.toLowerCase().includes('quantity');
const isRequired = col.mandatory || (isCatSelected && (isProductName || isBags));
if (col.data_type === 'select' || col.data_type === 'multiselect') {
const allOpts = col.properties?.options || [];
let filteredOpts = allOpts;
if (col.id === 'product_name' || col.name === 'Product Name' || col.mapped_workflow_field === 'product_name') {
const catCol = columns.find(c => c.id === 'product_category' || c.name === 'Product Category' || c.mapped_workflow_field === 'product_category');
if (catCol) {
const selectedCategory = newRow[catCol.id] as string;
if (selectedCategory) {
filteredOpts = allOpts.filter(opt => {
const labelStr = String(opt.label || opt.value || '');
return labelStr.startsWith(selectedCategory);
});
}
}
} else {
filteredOpts = allOpts.filter(opt => {
if (!opt._raw) return true;
for (const [rowKey, rowVal] of Object.entries(newRow)) {
if (rowKey === col.id || rowVal == null || rowVal === '') continue;
const rawKey = Object.keys(opt._raw).find(rk => rk === rowKey || rk.replace(/_/g, '') === rowKey.replace(/_/g, ''));
if (rawKey && String(opt._raw[rawKey]) !== String(rowVal)) {
return false;
}
}
return true;
});
}
return (
<Select
key={col.id}
label={col.name}
required={isRequired}
error={errors[col.id]}
value={(val as string) ?? ''}
onChange={(e) => updateNewRowField(col.id, e.target.value)}
options={[{ value: '', label: 'Select...' }, ...filteredOpts.map((o: any) => ({ value: String(o.value), label: o.label }))]}
/>
);
}
return (
<Input
key={col.id}
label={col.name}
required={isRequired}
error={errors[col.id]}
type={col.data_type === 'number' ? 'number' : col.data_type === 'email' ? 'email' : 'text'}
value={(val as string) ?? ''}
onChange={(e) => updateNewRowField(col.id, col.data_type === 'number' ? Number(e.target.value) : e.target.value)}
/>
);
})}
{editingIdx === null && renderFormFields()}
</div>
<div className="flex justify-end gap-2 sm:col-span-2 md:col-span-3 mt-2">
<Button type="button" variant="ghost" onClick={() => { setIsModalOpen(false); setEditingIdx(null); setNewRow({}); }}>
<Button type="button" variant="ghost" onClick={() => { setNewRow({}); }}>
Cancel
</Button>
<Button type="button" variant="primary" onClick={submitNewRow}>
{editingIdx !== null ? "Save Item" : "+ Add to Order"}
+ Add to Order
</Button>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -26,7 +26,7 @@ export function CallsPage() {
setMiningLoading(true);
setMiningPrefill(undefined);
try {
const storeCode = selectedRow?.store_code || selectedRow?.code || selectedRow?.store?.store_code;
const storeCode = selectedRow?.store_code || selectedRow?.code || selectedRow?.store?.store_code || selectedRow?.select_store?.store_code || selectedRow?.select_store?.code;
if (!storeCode) {
console.warn("Store code not found on selected row, proceeding anyway.");

View File

@ -28,7 +28,7 @@ export function OrdersPage() {
setMiningLoading(true);
setMiningPrefill(undefined);
try {
const storeCode = selectedRow?.store_code || selectedRow?.code || selectedRow?.store?.store_code;
const storeCode = selectedRow?.store_code || selectedRow?.code || selectedRow?.store?.store_code || selectedRow?.select_store?.store_code || selectedRow?.select_store?.code;
if (!storeCode) {
console.warn("Store code not found on selected row, proceeding anyway.");