added required for potential and order details
This commit is contained in:
parent
bcd169e194
commit
e0d79ffbc1
@ -20,6 +20,8 @@ export function SmartGridField({
|
|||||||
const formRef = useRef<HTMLDivElement>(null);
|
const formRef = useRef<HTMLDivElement>(null);
|
||||||
const [newRow, setNewRow] = useState<Record<string, unknown>>({});
|
const [newRow, setNewRow] = useState<Record<string, unknown>>({});
|
||||||
const [editingIdx, setEditingIdx] = useState<number | null>(null);
|
const [editingIdx, setEditingIdx] = useState<number | null>(null);
|
||||||
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
|
|
||||||
const visibleColumns = columns.filter(c => {
|
const visibleColumns = columns.filter(c => {
|
||||||
const isOrderDetails = label.toLowerCase().includes('order');
|
const isOrderDetails = label.toLowerCase().includes('order');
|
||||||
@ -56,6 +58,8 @@ export function SmartGridField({
|
|||||||
|
|
||||||
const updateNewRowField = (fieldId: string, val: unknown) => {
|
const updateNewRowField = (fieldId: string, val: unknown) => {
|
||||||
let row = { ...newRow, [fieldId]: val };
|
let row = { ...newRow, [fieldId]: val };
|
||||||
|
setErrors(prev => ({ ...prev, [fieldId]: '' }));
|
||||||
|
|
||||||
|
|
||||||
const colDef = columns.find(c => c.id === fieldId);
|
const colDef = columns.find(c => c.id === fieldId);
|
||||||
|
|
||||||
@ -105,6 +109,26 @@ export function SmartGridField({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const submitNewRow = () => {
|
const submitNewRow = () => {
|
||||||
|
const newErrors: Record<string, string> = {};
|
||||||
|
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;
|
||||||
|
|
||||||
|
for (const col of visibleColumns) {
|
||||||
|
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 (isRequired && !newRow[col.id]) {
|
||||||
|
newErrors[col.id] = 'This field is required';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(newErrors).length > 0) {
|
||||||
|
setErrors(newErrors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (editingIdx !== null) {
|
if (editingIdx !== null) {
|
||||||
const next = [...value];
|
const next = [...value];
|
||||||
next[editingIdx] = newRow;
|
next[editingIdx] = newRow;
|
||||||
@ -206,7 +230,7 @@ export function SmartGridField({
|
|||||||
<span className="font-bold text-slate-800 text-[14px] truncate">{productName}</span>
|
<span className="font-bold text-slate-800 text-[14px] truncate">{productName}</span>
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
{(!isPotentialMining || hasBagsCol) && (
|
{(!isPotentialMining || hasBagsCol) && (
|
||||||
<span className="font-extrabold text-indigo-600 text-[13px]">{bags} Bags</span>
|
<span className="font-extrabold text-indigo-600 text-[13px]">{bags} {isPotentialMining ? 'Kgs' : 'Bags'}</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isPotentialMining && diffVal !== null && !isNaN(diffVal) && diffVal !== 0 && (
|
{isPotentialMining && diffVal !== null && !isNaN(diffVal) && diffVal !== 0 && (
|
||||||
@ -268,6 +292,14 @@ export function SmartGridField({
|
|||||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 items-end">
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 items-end">
|
||||||
{visibleColumns.map(col => {
|
{visibleColumns.map(col => {
|
||||||
const val = newRow[col.id];
|
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') {
|
if (col.data_type === 'select' || col.data_type === 'multiselect') {
|
||||||
const allOpts = col.properties?.options || [];
|
const allOpts = col.properties?.options || [];
|
||||||
let filteredOpts = allOpts;
|
let filteredOpts = allOpts;
|
||||||
@ -301,7 +333,8 @@ export function SmartGridField({
|
|||||||
<Select
|
<Select
|
||||||
key={col.id}
|
key={col.id}
|
||||||
label={col.name}
|
label={col.name}
|
||||||
required={col.mandatory}
|
required={isRequired}
|
||||||
|
error={errors[col.id]}
|
||||||
value={(val as string) ?? ''}
|
value={(val as string) ?? ''}
|
||||||
onChange={(e) => updateNewRowField(col.id, e.target.value)}
|
onChange={(e) => updateNewRowField(col.id, e.target.value)}
|
||||||
options={[{ value: '', label: 'Select...' }, ...filteredOpts.map((o: any) => ({ value: String(o.value), label: o.label }))]}
|
options={[{ value: '', label: 'Select...' }, ...filteredOpts.map((o: any) => ({ value: String(o.value), label: o.label }))]}
|
||||||
@ -312,7 +345,8 @@ export function SmartGridField({
|
|||||||
<Input
|
<Input
|
||||||
key={col.id}
|
key={col.id}
|
||||||
label={col.name}
|
label={col.name}
|
||||||
required={col.mandatory}
|
required={isRequired}
|
||||||
|
error={errors[col.id]}
|
||||||
type={col.data_type === 'number' ? 'number' : col.data_type === 'email' ? 'email' : 'text'}
|
type={col.data_type === 'number' ? 'number' : col.data_type === 'email' ? 'email' : 'text'}
|
||||||
value={(val as string) ?? ''}
|
value={(val as string) ?? ''}
|
||||||
onChange={(e) => updateNewRowField(col.id, col.data_type === 'number' ? Number(e.target.value) : e.target.value)}
|
onChange={(e) => updateNewRowField(col.id, col.data_type === 'number' ? Number(e.target.value) : e.target.value)}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ export interface SelectOption {
|
|||||||
export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
||||||
label?: string;
|
label?: string;
|
||||||
hint?: string;
|
hint?: string;
|
||||||
|
error?: string;
|
||||||
/** Either strings or {value,label} objects. */
|
/** Either strings or {value,label} objects. */
|
||||||
options?: Array<string | SelectOption>;
|
options?: Array<string | SelectOption>;
|
||||||
/** Class for the outer label wrapper. */
|
/** Class for the outer label wrapper. */
|
||||||
@ -17,7 +18,7 @@ export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Labeled native select styled to match Input. */
|
/** Labeled native select styled to match Input. */
|
||||||
export function Select({ label, hint, options = [], required, className, ...rest }: SelectProps) {
|
export function Select({ label, hint, error, options = [], required, className, ...rest }: SelectProps) {
|
||||||
return (
|
return (
|
||||||
<label className={cn('flex flex-col gap-1.5 font-sans', className)}>
|
<label className={cn('flex flex-col gap-1.5 font-sans', className)}>
|
||||||
{label && (
|
{label && (
|
||||||
@ -26,7 +27,7 @@ export function Select({ label, hint, options = [], required, className, ...rest
|
|||||||
{required && <span className="text-ruby-600"> *</span>}
|
{required && <span className="text-ruby-600"> *</span>}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<div className="relative bg-card rounded-md h-[42px] border border-border-default transition-[border-color,box-shadow] duration-150 focus-ring">
|
<div className={cn("relative bg-card rounded-md h-[42px] border transition-[border-color,box-shadow] duration-150 focus-ring", error ? "border-ruby-600" : "border-border-default")}>
|
||||||
<select
|
<select
|
||||||
required={required}
|
required={required}
|
||||||
className="w-full h-full border-none outline-none bg-transparent appearance-none pl-3 pr-9 font-sans text-base text-strong cursor-pointer"
|
className="w-full h-full border-none outline-none bg-transparent appearance-none pl-3 pr-9 font-sans text-base text-strong cursor-pointer"
|
||||||
@ -44,7 +45,9 @@ export function Select({ label, hint, options = [], required, className, ...rest
|
|||||||
</select>
|
</select>
|
||||||
<ChevronDown size={15} className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-faint" />
|
<ChevronDown size={15} className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-faint" />
|
||||||
</div>
|
</div>
|
||||||
{hint && <span className="text-xs text-faint">{hint}</span>}
|
{(hint || error) && (
|
||||||
|
<span className={cn('text-xs', error ? 'text-ruby-600' : 'text-faint')}>{error || hint}</span>
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -174,28 +174,24 @@ export function DailySalesReportPage() {
|
|||||||
type="date"
|
type="date"
|
||||||
value={date}
|
value={date}
|
||||||
onChange={(e) => setDate(e.target.value)}
|
onChange={(e) => setDate(e.target.value)}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Route"
|
label="Route"
|
||||||
value={route}
|
value={route}
|
||||||
onChange={(e) => setRoute(e.target.value)}
|
onChange={(e) => setRoute(e.target.value)}
|
||||||
options={[{ value: '', label: 'Select Route' }, ...SALES_REPORT_ROUTES]}
|
options={[{ value: '', label: 'Select Route' }, ...SALES_REPORT_ROUTES]}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Distributor"
|
label="Distributor"
|
||||||
value={distributor}
|
value={distributor}
|
||||||
onChange={(e) => setDistributor(e.target.value)}
|
onChange={(e) => setDistributor(e.target.value)}
|
||||||
options={[{ value: '', label: 'Select Distributor' }, ...SALES_REPORT_DISTRIBUTORS]}
|
options={[{ value: '', label: 'Select Distributor' }, ...SALES_REPORT_DISTRIBUTORS]}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="SO Name"
|
label="SO Name"
|
||||||
value={soName}
|
value={soName}
|
||||||
onChange={(e) => setSoName(e.target.value)}
|
onChange={(e) => setSoName(e.target.value)}
|
||||||
options={[{ value: '', label: 'Select SO Name' }, ...SALES_REPORT_SO_NAMES]}
|
options={[{ value: '', label: 'Select SO Name' }, ...SALES_REPORT_SO_NAMES]}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{error && <div className="text-xs text-ruby-600 font-medium">{error}</div>}
|
{error && <div className="text-xs text-ruby-600 font-medium">{error}</div>}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user