fix: limit for select option and dsr pdf generating

This commit is contained in:
suryac 2026-08-18 11:35:22 +05:30
parent eff824a4ad
commit f5d19f562f
6 changed files with 62 additions and 30 deletions

View File

@ -253,7 +253,7 @@ export const SALES_REPORT_SO_NAMES = [
// --- Pipeline Endpoints --- // --- Pipeline Endpoints ---
export const PIPELINE = { export const PIPELINE = {
endpoints: { endpoints: {
nearestStores: '/api/papi2/nearest-stores', nearestStores: '/api/p/krishna-group/v1/nearest-stores',
dailySalesReport: '/api/papi2/daily-sales-report', dailySalesReport: '/api/papi2/daily-sales-report',
productiveCallSummary: '/api/papi2/productive-call-summary', productiveCallSummary: '/api/papi2/productive-call-summary',
salesOfficers: '/api/papi2/sales-officers' salesOfficers: '/api/papi2/sales-officers'
@ -263,3 +263,7 @@ export const PIPELINE = {
export const HIDDEN_FORM_FIELDS = [ export const HIDDEN_FORM_FIELDS = [
'store_code_3', 'store_code_3',
]; ];
export const SELECT_OPTION_LIMIT = 500;
export const PDF_MAX_SKUS_PER_CHUNK = 20;

View File

@ -17,7 +17,7 @@ import {
WfLookupField, WfLookupField,
RadioField, RadioField,
} from './fields'; } from './fields';
import { ORDER_BOOKING, STORE, DAILY_REPORTS, HIDDEN_FORM_FIELDS } from '../../api/config'; import { ORDER_BOOKING, STORE, DAILY_REPORTS, HIDDEN_FORM_FIELDS, SELECT_OPTION_LIMIT } from '../../api/config';
export interface DynamicFormProps { export interface DynamicFormProps {
client: ZinoClient; client: ZinoClient;
@ -457,7 +457,7 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
activityId: ORDER_BOOKING.activities.LOG_VISIT.uid, activityId: ORDER_BOOKING.activities.LOG_VISIT.uid,
fieldId: ORDER_BOOKING.activities.LOG_VISIT.fields.selectStore, fieldId: ORDER_BOOKING.activities.LOG_VISIT.fields.selectStore,
formData: { ...(chainedPrefillData as any || {}), ...values, ...(schema?.prefill_data as any || {}) }, formData: { ...(chainedPrefillData as any || {}), ...values, ...(schema?.prefill_data as any || {}) },
limit: 500 limit: SELECT_OPTION_LIMIT
}); });
const arr = Array.isArray(lookupRes) ? lookupRes : (lookupRes.data || lookupRes.records || []); const arr = Array.isArray(lookupRes) ? lookupRes : (lookupRes.data || lookupRes.records || []);
const row = arr.find((r: any) => String(r.instance_id || r.id) === String(storeId)); const row = arr.find((r: any) => String(r.instance_id || r.id) === String(storeId));

View File

@ -1,7 +1,7 @@
import { useState, useEffect, useCallback, useRef } from 'react'; import { useState, useEffect, useCallback, useRef } from 'react';
import type { ZinoClient } from '../../api/client'; import type { ZinoClient } from '../../api/client';
import type { FormScreenResponse } from '../../api/types'; import type { FormScreenResponse } from '../../api/types';
import { ORDER_BOOKING, HIDDEN_FORM_FIELDS } from '../../api/config'; import { ORDER_BOOKING, HIDDEN_FORM_FIELDS, SELECT_OPTION_LIMIT } from '../../api/config';
import { Button } from '../buttons/Button'; import { Button } from '../buttons/Button';
import { Select } from '../reusable/Select'; import { Select } from '../reusable/Select';
import { DateField, TimeField, FileInput, TextField } from './fields'; import { DateField, TimeField, FileInput, TextField } from './fields';
@ -148,7 +148,7 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
activityId, activityId,
fieldId: 'select_store', fieldId: 'select_store',
formData: formDataToSend, formData: formDataToSend,
limit: 200, limit: SELECT_OPTION_LIMIT,
}); });
const arr = Array.isArray(lookupRes) const arr = Array.isArray(lookupRes)
@ -215,7 +215,7 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
activityId, activityId,
fieldId: 'daily_log', fieldId: 'daily_log',
formData: payload, formData: payload,
limit: 200, limit: SELECT_OPTION_LIMIT,
}); });
const arr = Array.isArray(dailyLogLookupRes) const arr = Array.isArray(dailyLogLookupRes)

View File

@ -1,5 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import type { ZinoClient } from '../../../api/client'; import type { ZinoClient } from '../../../api/client';
import { SELECT_OPTION_LIMIT } from '../../../api/config';
import { SelectField } from './SelectField'; import { SelectField } from './SelectField';
export interface WfLookupFieldProps { export interface WfLookupFieldProps {
@ -38,7 +39,7 @@ export function WfLookupField({ label, required, value, onChange, client, config
activityId, activityId,
fieldId, fieldId,
formData, formData,
limit: 200 limit: SELECT_OPTION_LIMIT
}) })
.then(res => { .then(res => {
if (!mounted) return; if (!mounted) return;

View File

@ -64,11 +64,7 @@ export function NearestStoresMap() {
method: 'POST', method: 'POST',
headers: { headers: {
'accept': 'application/json, text/plain, */*', 'accept': 'application/json, text/plain, */*',
'content-type': 'application/json', 'content-type': 'application/json'
'groupid': '25',
'orgid': '57',
'templateid': '189',
'x-pipeline-version': 'latest'
}, },
body: JSON.stringify({ body: JSON.stringify({
latitude: lat, latitude: lat,

View File

@ -5,7 +5,8 @@ import {
SALES_REPORT_ROUTES, SALES_REPORT_ROUTES,
ROUTE_WISE_DISTRIBUTORS, ROUTE_WISE_DISTRIBUTORS,
BASE_URL, BASE_URL,
PIPELINE PIPELINE,
PDF_MAX_SKUS_PER_CHUNK
} from '../api/config'; } from '../api/config';
import { Download, Printer } from 'lucide-react'; import { Download, Printer } from 'lucide-react';
import jsPDF from 'jspdf'; import jsPDF from 'jspdf';
@ -73,28 +74,56 @@ export function DailySalesReportPage() {
// Meta Info // Meta Info
doc.setFontSize(10); doc.setFontSize(10);
doc.text(`Distributor Name : ${distributor}`, 14, 40);
doc.text(`Date : ${date}`, rightMargin, 40, { align: 'right' }); let leftY = 40;
const selectedSo = soOptions.find(o => o.value === soEmail); if (distributor) {
doc.text(`SO Name : ${selectedSo ? selectedSo.label : soEmail}`, rightMargin, 45, { align: 'right' }); doc.text(`Distributor Name : ${distributor}`, 14, leftY);
leftY += 5;
}
let rightY = 40;
if (date) {
doc.text(`Date : ${date}`, rightMargin, rightY, { align: 'right' });
rightY += 5;
}
if (soEmail) {
const selectedSo = soOptions.find(o => o.value === soEmail);
doc.text(`SO Name : ${selectedSo ? selectedSo.label : soEmail}`, rightMargin, rightY, { align: 'right' });
rightY += 5;
}
}; };
// Split headers into chunks to avoid jspdf-autotable's horizontal page break bugs // Split headers into chunks of exactly PDF_MAX_SKUS_PER_CHUNK, even if it means splitting a brand.
// This allows us to keep the 2-tier header perfectly intact and break at heading boundaries.
const MAX_SKUS_PER_CHUNK = 14;
const headerChunks: any[][] = []; const headerChunks: any[][] = [];
let currentChunk: any[] = []; let currentChunk: any[] = [];
let currentSkuCount = 0; let currentSkuCount = 0;
reportData?.response?.headers?.forEach((h: any) => { reportData?.response?.headers?.forEach((h: any) => {
const skus = h.skus || []; const allSkus = h.skus || [];
if (currentSkuCount + skus.length > MAX_SKUS_PER_CHUNK && currentSkuCount > 0) { if (allSkus.length === 0) return;
headerChunks.push(currentChunk);
currentChunk = []; let remainingSkus = [...allSkus];
currentSkuCount = 0;
while (remainingSkus.length > 0) {
const availableSpace = PDF_MAX_SKUS_PER_CHUNK - currentSkuCount;
// Take as many SKUs as we can fit in the current chunk
const skusToTake = remainingSkus.splice(0, availableSpace);
currentChunk.push({
...h,
skus: skusToTake
});
currentSkuCount += skusToTake.length;
if (currentSkuCount >= PDF_MAX_SKUS_PER_CHUNK) {
headerChunks.push(currentChunk);
currentChunk = [];
currentSkuCount = 0;
}
} }
currentChunk.push(h);
currentSkuCount += skus.length;
}); });
if (currentChunk.length > 0) { if (currentChunk.length > 0) {
headerChunks.push(currentChunk); headerChunks.push(currentChunk);
@ -299,10 +328,12 @@ export function DailySalesReportPage() {
</div> </div>
<div className="flex justify-between items-start text-sm font-bold text-strong mb-2"> <div className="flex justify-between items-start text-sm font-bold text-strong mb-2">
<div>Distributor Name : {distributor}</div> <div className="flex flex-col gap-1.5">
{distributor && <div>Distributor Name : {distributor}</div>}
</div>
<div className="text-right flex flex-col gap-1.5"> <div className="text-right flex flex-col gap-1.5">
<div>Date : {date}</div> {date && <div>Date : {date}</div>}
<div>SO Name : {soOptions.find(o => o.value === soEmail)?.label || soEmail}</div> {soEmail && <div>SO Name : {soOptions.find(o => o.value === soEmail)?.label || soEmail}</div>}
</div> </div>
</div> </div>