krishna_sales/src/components/forms/fields/PotentialMiningGrid.tsx
2026-07-24 19:11:39 +05:30

189 lines
7.4 KiB
TypeScript

import { useMemo, useCallback } from 'react';
import type { FormScreenField } from '../../../api/types';
import { Select } from '../../reusable/Select';
import { Input } from '../../reusable/Input';
import { isCategoryColumn } from './gridUtils';
export interface PotentialMiningGridProps {
label: string;
columns: FormScreenField[];
value: Record<string, unknown>[];
onChange: (val: Record<string, unknown>[]) => void;
readOnlyCols?: string[];
}
export function PotentialMiningGrid({
label,
columns,
value = [],
onChange,
readOnlyCols = [],
}: PotentialMiningGridProps) {
const rows = value.length > 0 ? value : [{}];
const catCol = useMemo(() => columns.find(c => isCategoryColumn(c.id, c.name)), [columns]);
const visibleColumns = useMemo(() => {
return columns.filter((c) => {
const idL = c.id.toLowerCase();
const nL = (c.name || '').toLowerCase();
return !idL.includes('sku') && !nL.includes('sku') &&
!idL.includes('br_code') && !idL.includes('brcode') && !idL.includes('brand_code') &&
!nL.includes('br code') && !idL.includes('row_kgs');
});
}, [columns]);
const gridTemplateColumns = useMemo(() => {
const numCols = visibleColumns.length;
if (numCols === 4) return `1.3fr 1.5fr 0.7fr 0.8fr`;
if (numCols === 3) return `1.4fr 1.6fr 0.7fr`;
if (numCols === 2) return `1.5fr 1.5fr`;
return `repeat(${numCols}, minmax(0, 1fr))`;
}, [visibleColumns]);
const categoriesList = useMemo(() => {
if (catCol?.properties?.options && catCol.properties.options.length > 0) {
return catCol.properties.options.map((o: any) => ({
value: String(o.value ?? o.label),
label: String(o.label ?? o.value),
}));
}
return [];
}, [catCol]);
const updateRowField = useCallback((rowIdx: number, fieldId: string, val: unknown) => {
const next = rows.map((r) => ({ ...r }));
let row = { ...next[rowIdx] };
const colDef = columns.find((c) => c.id === fieldId);
const colName = colDef?.name || '';
const isCatField = isCategoryColumn(fieldId, colName);
if (isCatField) {
const catVal = String(val ?? '');
row[fieldId] = catVal;
row['category'] = catVal;
row['product_category'] = catVal;
} else {
row[fieldId] = val;
}
next[rowIdx] = row;
onChange(next);
}, [rows, columns, onChange]);
return (
<div className="max-w-full bg-[var(--tiles-card-bg)] rounded-xl border border-border-default p-6 space-y-6 shadow-sm font-sans">
<div className="flex items-center justify-between">
<p className="text-sm font-bold text-slate-700 uppercase tracking-wide">
{label || 'Potential Mining'}
</p>
</div>
<div className="border-t border-border-subtle pt-4">
<div className="grid gap-3 px-1 pb-2 text-xs font-bold text-muted uppercase tracking-wider" style={{ gridTemplateColumns }}>
{visibleColumns.map((col) => (
<span key={col.id}>{col.name}</span>
))}
</div>
<div className="space-y-3">
{rows.map((row, rowIdx) => {
const currentCatVal = String(row.category ?? row.product_category ?? (catCol ? row[catCol.id] : '') ?? '');
return (
<div key={rowIdx}>
<div className="grid gap-3 items-center" style={{ gridTemplateColumns }}>
{visibleColumns.map((col) => {
const fieldId = col.id;
const isReadOnly = readOnlyCols.some(c => c === fieldId || c.toLowerCase() === col.name?.toLowerCase());
if (fieldId === 'difference' || col.name?.toLowerCase() === 'difference') {
const diffVal = Number(row[fieldId]) || 0;
let displayStr = '0 kgs';
let textColor = 'text-slate-600';
let bgColor = 'bg-slate-50 border-slate-200';
if (diffVal < 0) {
displayStr = `${Math.abs(diffVal)} kgs shortage`;
textColor = 'text-rose-600';
bgColor = 'bg-rose-50 border-rose-200';
} else if (diffVal > 0) {
displayStr = `${diffVal} kgs surplus`;
textColor = 'text-emerald-600';
bgColor = 'bg-emerald-50 border-emerald-200';
}
return (
<div key={fieldId} className={`flex items-center justify-center h-[42px] px-3 rounded-md border text-xs font-bold w-full ${bgColor} ${textColor}`}>
{displayStr}
</div>
);
}
const isCat = isCategoryColumn(fieldId, col.name);
const isBagsOrNum = col.data_type === 'number' || fieldId.includes('bags') || fieldId.includes('quantity') || col.name?.toLowerCase().includes('bags');
if (isCat) {
return (
<Select
key={fieldId}
value={currentCatVal}
onChange={(e) => updateRowField(rowIdx, fieldId, e.target.value)}
disabled={isReadOnly}
options={[{ value: '', label: 'Category' }, ...categoriesList]}
className="w-full"
/>
);
}
if (isBagsOrNum) {
return (
<Input
key={fieldId}
type="number"
disabled={isReadOnly}
value={row[fieldId] !== undefined && row[fieldId] !== null ? String(row[fieldId]) : ''}
onChange={(e) => updateRowField(rowIdx, fieldId, e.target.value)}
placeholder="0"
className="w-full"
/>
);
}
if (col.data_type === 'select' || col.data_type === 'multiselect') {
const opts = col.properties?.options || [];
return (
<Select
key={fieldId}
value={String(row[fieldId] ?? '')}
disabled={isReadOnly}
onChange={(e) => updateRowField(rowIdx, fieldId, e.target.value)}
options={[{ value: '', label: 'Select...' }, ...opts.map((o: any) => ({ value: String(o.value), label: o.label }))]}
className="w-full"
/>
);
}
return (
<Input
key={fieldId}
type="text"
disabled={isReadOnly}
value={String(row[fieldId] ?? '')}
onChange={(e) => updateRowField(rowIdx, fieldId, e.target.value)}
placeholder={col.name ? `Enter ${col.name.toLowerCase()}...` : 'Enter details...'}
className="w-full"
/>
);
})}
</div>
</div>
);
})}
</div>
</div>
</div>
);
}