feat/search api is added
This commit is contained in:
parent
060ec00d8d
commit
caf15840aa
@ -271,5 +271,5 @@ export const HIDDEN_FORM_FIELDS = [
|
||||
'store_code_3'
|
||||
];
|
||||
|
||||
export const LOOKUP_RECORD_LIMIT = 500;
|
||||
export const LOOKUP_RECORD_LIMIT = 200;
|
||||
export const MAX_SKUS_PER_CHUNK = 20;
|
||||
|
||||
@ -174,43 +174,6 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
||||
}
|
||||
}
|
||||
|
||||
if (currentActivityId === ORDER_BOOKING.activities.LOG_VISIT.uid) {
|
||||
const user = client.currentUser();
|
||||
const userMobile = user?.mobile || localStorage.getItem('krishna_sales_user_mobile') || user?.email;
|
||||
if (userMobile) {
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
'templateid': '192',
|
||||
'x-pipeline-version': 'latest',
|
||||
'orgid': '57',
|
||||
'groupid': '25'
|
||||
};
|
||||
|
||||
const summaryRes = await client.request<any>(
|
||||
'POST',
|
||||
PIPELINE.endpoints.productiveCallSummary,
|
||||
{ mobile: userMobile },
|
||||
headers
|
||||
);
|
||||
let summaryData = summaryRes.data || summaryRes;
|
||||
if (Array.isArray(summaryData)) {
|
||||
summaryData = summaryData[0] || {};
|
||||
}
|
||||
|
||||
if (summaryData.productive_call !== undefined) {
|
||||
summaryData.total_productive_calls = summaryData.productive_call;
|
||||
}
|
||||
if (summaryData.non_productive_call !== undefined) {
|
||||
summaryData.total_non_productive_calls = summaryData.non_productive_call;
|
||||
}
|
||||
|
||||
mapPrefillData(summaryData);
|
||||
} catch (e) {
|
||||
console.warn('Failed to load productive call summary for Log Visit', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setValues(defaultValues);
|
||||
setLoading(false);
|
||||
if (res.activity_name) {
|
||||
|
||||
@ -61,9 +61,9 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
||||
}, [values]);
|
||||
|
||||
const [storeOptions, setStoreOptions] = useState<{ value: string; label: string; _raw?: any }[]>([]);
|
||||
const [storeSearchQuery, setStoreSearchQuery] = useState('');
|
||||
const [fetchingDailyLog, setFetchingDailyLog] = useState(false);
|
||||
const [fetchingStores, setFetchingStores] = useState(false);
|
||||
const fetchingStoresRef = useRef(false);
|
||||
|
||||
const [chainedActivity, setChainedActivity] = useState<{
|
||||
activityId: string;
|
||||
@ -119,40 +119,6 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
||||
Object.assign(initialValues, res.data);
|
||||
}
|
||||
|
||||
// Execute mobile pipeline prefill
|
||||
const user = client.currentUser();
|
||||
const userMobile = user?.mobile || localStorage.getItem('krishna_sales_user_mobile') || user?.email;
|
||||
if (userMobile) {
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
'templateid': '192',
|
||||
'x-pipeline-version': 'latest',
|
||||
'orgid': '57',
|
||||
'groupid': '25'
|
||||
};
|
||||
|
||||
const summaryRes = await client.request<any>(
|
||||
'POST',
|
||||
PIPELINE.endpoints.productiveCallSummary,
|
||||
{ mobile: userMobile },
|
||||
headers
|
||||
);
|
||||
let summaryData = summaryRes.data || summaryRes;
|
||||
if (Array.isArray(summaryData)) {
|
||||
summaryData = summaryData[0] || {};
|
||||
}
|
||||
if (summaryData.productive_call !== undefined) {
|
||||
summaryData.total_productive_calls = summaryData.productive_call;
|
||||
}
|
||||
if (summaryData.non_productive_call !== undefined) {
|
||||
summaryData.total_non_productive_calls = summaryData.non_productive_call;
|
||||
}
|
||||
Object.assign(initialValues, summaryData);
|
||||
} catch (e) {
|
||||
console.warn('Failed to load productive call summary for Log Visit', e);
|
||||
}
|
||||
}
|
||||
|
||||
setValues(prev => {
|
||||
const next = { ...initialValues, ...prev };
|
||||
valuesRef.current = next;
|
||||
@ -170,9 +136,7 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
||||
}, [client, activityId, todayStr, nowTimeStr]);
|
||||
|
||||
// 2. Helper to fetch select_store options ONLY using updated prefilled formData
|
||||
const fetchStoreOptions = useCallback(async (formDataOverride?: Record<string, any>) => {
|
||||
if (fetchingStoresRef.current) return;
|
||||
fetchingStoresRef.current = true;
|
||||
const fetchStoreOptions = useCallback(async (formDataOverride?: Record<string, any>, search: string = '') => {
|
||||
setFetchingStores(true);
|
||||
setStoreError(null);
|
||||
|
||||
@ -184,6 +148,7 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
||||
activityId,
|
||||
fieldId: 'select_store',
|
||||
formData: formDataToSend,
|
||||
search,
|
||||
limit: LOOKUP_RECORD_LIMIT,
|
||||
});
|
||||
|
||||
@ -234,10 +199,16 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
||||
}
|
||||
} finally {
|
||||
setFetchingStores(false);
|
||||
fetchingStoresRef.current = false;
|
||||
}
|
||||
}, [client, activityId]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
fetchStoreOptions(undefined, storeSearchQuery);
|
||||
}, 300);
|
||||
return () => clearTimeout(timer);
|
||||
}, [storeSearchQuery, fetchStoreOptions]);
|
||||
|
||||
// 3. Fetch Daily Log lookup data initially & prefill form state
|
||||
const loadDailyLogData = useCallback(async (dateVal: string, timeVal: string) => {
|
||||
setFetchingDailyLog(true);
|
||||
@ -464,6 +435,8 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
||||
options={[{ value: '', label: `Select ${f.name}...` }, ...storeOptions]}
|
||||
onDropdownOpen={handleStoreDropdownOpen}
|
||||
onChange={e => handleStoreSelect(fieldId, e.target.value)}
|
||||
onSearchChange={setStoreSearchQuery}
|
||||
disableLocalSearch={true}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
{fetchingStores && (
|
||||
|
||||
@ -6,12 +6,16 @@ export function SelectField({
|
||||
value,
|
||||
options,
|
||||
onChange,
|
||||
onSearchChange,
|
||||
disableLocalSearch,
|
||||
}: {
|
||||
label: string;
|
||||
required?: boolean;
|
||||
value: string;
|
||||
options: any[];
|
||||
onChange: (val: string, fullRow?: any) => void;
|
||||
onSearchChange?: (query: string) => void;
|
||||
disableLocalSearch?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Select
|
||||
@ -24,6 +28,8 @@ export function SelectField({
|
||||
onChange(val, opt?._raw);
|
||||
}}
|
||||
options={[{ value: '', label: 'Select...' }, ...options.map((o: any) => ({ value: String(o.value), label: o.label }))]}
|
||||
onSearchChange={onSearchChange}
|
||||
disableLocalSearch={disableLocalSearch}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
||||
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [records, setRecords] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const formDataStr = JSON.stringify(formData);
|
||||
|
||||
@ -32,6 +33,7 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
||||
activityId,
|
||||
fieldId,
|
||||
formData: JSON.parse(formDataStr),
|
||||
search: searchQuery,
|
||||
limit: LOOKUP_RECORD_LIMIT
|
||||
})
|
||||
.then(res => {
|
||||
@ -72,7 +74,7 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
||||
mounted = false;
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [client, config, activityId, fieldId, formDataStr]);
|
||||
}, [client, config, activityId, fieldId, formDataStr, searchQuery]);
|
||||
|
||||
return (
|
||||
<SelectField
|
||||
@ -84,6 +86,8 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
||||
onChange(val, row);
|
||||
}}
|
||||
options={options}
|
||||
onSearchChange={setSearchQuery}
|
||||
disableLocalSearch={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -21,6 +21,8 @@ export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
||||
/** Disable search header filter if set to false */
|
||||
searchable?: boolean;
|
||||
onDropdownOpen?: () => void;
|
||||
onSearchChange?: (query: string) => void;
|
||||
disableLocalSearch?: boolean;
|
||||
}
|
||||
|
||||
/** Custom searchable select component using React Portal to prevent container clipping. */
|
||||
@ -35,6 +37,8 @@ export function Select({
|
||||
placeholder,
|
||||
searchable = true,
|
||||
onDropdownOpen,
|
||||
onSearchChange,
|
||||
disableLocalSearch,
|
||||
...rest
|
||||
}: SelectProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@ -52,7 +56,9 @@ export function Select({
|
||||
const selectedOption = normalizedOptions.find((o) => o.value === currentValue);
|
||||
const selectedOptionLabel = selectedOption ? selectedOption.label : '';
|
||||
|
||||
const filteredOptions = normalizedOptions.filter((o) =>
|
||||
const filteredOptions = disableLocalSearch
|
||||
? normalizedOptions
|
||||
: normalizedOptions.filter((o) =>
|
||||
o.label.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
@ -180,7 +186,11 @@ export function Select({
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setSearchQuery(val);
|
||||
onSearchChange?.(val);
|
||||
}}
|
||||
placeholder="Search options..."
|
||||
className="w-full text-xs bg-transparent border-none outline-none text-strong placeholder:text-muted"
|
||||
autoFocus
|
||||
@ -192,6 +202,7 @@ export function Select({
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setSearchQuery('');
|
||||
onSearchChange?.('');
|
||||
}}
|
||||
className="text-muted hover:text-strong p-0.5 rounded cursor-pointer"
|
||||
>
|
||||
@ -270,6 +281,7 @@ export interface MultiSelectProps {
|
||||
className?: string;
|
||||
/** Disable search header filter if set to false */
|
||||
searchable?: boolean;
|
||||
onSearchChange?: (query: string) => void;
|
||||
}
|
||||
|
||||
/** Custom searchable multiselect component using React Portal. */
|
||||
@ -285,6 +297,7 @@ export function MultiSelect({
|
||||
disabled,
|
||||
placeholder,
|
||||
searchable = true,
|
||||
onSearchChange,
|
||||
}: MultiSelectProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
@ -453,7 +466,11 @@ export function MultiSelect({
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setSearchQuery(val);
|
||||
onSearchChange?.(val);
|
||||
}}
|
||||
placeholder="Search options..."
|
||||
className="w-full text-xs bg-transparent border-none outline-none text-strong placeholder:text-muted"
|
||||
autoFocus
|
||||
@ -465,6 +482,7 @@ export function MultiSelect({
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setSearchQuery('');
|
||||
onSearchChange?.('');
|
||||
}}
|
||||
className="text-muted hover:text-strong p-0.5 rounded cursor-pointer"
|
||||
>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user