potential mining api error fixed
This commit is contained in:
parent
2754484155
commit
1357935ead
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect, 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 { Button } from '../buttons/Button';
|
import { Button } from '../buttons/Button';
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
WfLookupField,
|
WfLookupField,
|
||||||
RadioField,
|
RadioField,
|
||||||
} from './fields';
|
} from './fields';
|
||||||
import { ORDER_BOOKING } from '../../api/config';
|
import { ORDER_BOOKING, STORE } from '../../api/config';
|
||||||
|
|
||||||
export interface DynamicFormProps {
|
export interface DynamicFormProps {
|
||||||
client: ZinoClient;
|
client: ZinoClient;
|
||||||
@ -122,9 +122,14 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [submitError, setSubmitError] = useState<string | null>(null);
|
const [submitError, setSubmitError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleFieldChange = (fieldId: string, newVal: unknown) => {
|
const clickedActionRef = useRef<string | null>(null);
|
||||||
|
|
||||||
|
const handleFieldChange = (fieldId: string, newVal: unknown, fullRow?: any) => {
|
||||||
setValues(prev => {
|
setValues(prev => {
|
||||||
const next = { ...prev, [fieldId]: newVal };
|
const next = { ...prev, [fieldId]: newVal };
|
||||||
|
if (fullRow) {
|
||||||
|
next[`${fieldId}_row`] = fullRow;
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-calculate order_details totals
|
// Auto-calculate order_details totals
|
||||||
const getBaseIdForField = (id: string) => id.replace(/_\d+$/, '');
|
const getBaseIdForField = (id: string) => id.replace(/_\d+$/, '');
|
||||||
@ -174,15 +179,22 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
// Filter out disabled fields (usually server-generated IDs)
|
// Filter out disabled fields (usually server-generated IDs)
|
||||||
const fields = schema.fields.filter(f => !f.properties?.disabled);
|
const fields = schema.fields.filter(f => !f.properties?.disabled);
|
||||||
|
|
||||||
|
const actionField = fields.find(f => f.name.toLowerCase() === 'action' && f.data_type === 'radio');
|
||||||
|
const normalFields = fields.filter(f => f !== actionField);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
setSubmitError(null);
|
setSubmitError(null);
|
||||||
try {
|
try {
|
||||||
const payload: Record<string, unknown> = {};
|
const payload: Record<string, unknown> = {};
|
||||||
|
const finalValues = { ...values };
|
||||||
|
if (actionField && clickedActionRef.current) {
|
||||||
|
finalValues[actionField.id] = clickedActionRef.current;
|
||||||
|
}
|
||||||
|
|
||||||
for (const f of fields) {
|
for (const f of fields) {
|
||||||
const val = values[f.id];
|
const val = finalValues[f.id];
|
||||||
if (val == null) continue;
|
if (val == null) continue;
|
||||||
|
|
||||||
if (f.data_type === 'phone' && typeof val === 'string') {
|
if (f.data_type === 'phone' && typeof val === 'string') {
|
||||||
@ -243,11 +255,52 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
if (nextActivity) {
|
if (nextActivity) {
|
||||||
let nextPrefillData = undefined;
|
let nextPrefillData = undefined;
|
||||||
if (nextActivity.activity_uid === ORDER_BOOKING.activities.POTENTIAL_MINING.uid) {
|
if (nextActivity.activity_uid === ORDER_BOOKING.activities.POTENTIAL_MINING.uid) {
|
||||||
|
let storeCodeToSend = String((values['select_store_row'] as any)?.store_code || (chainedPrefillData?.['select_store_row'] as any)?.store_code || '');
|
||||||
|
|
||||||
|
if (!storeCodeToSend) {
|
||||||
|
let storeId = values['select_store'] || chainedPrefillData?.['select_store'] || (schema?.prefill_data as any)?.select_store || (schema?.data as any)?.select_store;
|
||||||
|
if (typeof storeId === 'object' && storeId !== null) {
|
||||||
|
storeCodeToSend = (storeId as any).store_code || storeCodeToSend;
|
||||||
|
storeId = (storeId as any).value || (storeId as any).instance_id || String(storeId);
|
||||||
|
}
|
||||||
|
if (!storeCodeToSend && storeId) {
|
||||||
|
try {
|
||||||
|
const detailRes = await client.detailView(STORE.detailViews.STORE, storeId);
|
||||||
|
if (detailRes.data && detailRes.data.store_code) {
|
||||||
|
storeCodeToSend = String(detailRes.data.store_code);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
try {
|
||||||
|
const lookupRes = await client.wfLookupRecords({
|
||||||
|
activityId: ORDER_BOOKING.activities.LOG_VISIT.uid,
|
||||||
|
fieldId: ORDER_BOOKING.activities.LOG_VISIT.fields.selectStore,
|
||||||
|
formData: { ...(chainedPrefillData as any || {}), ...values, ...(schema?.prefill_data as any || {}) },
|
||||||
|
limit: 500
|
||||||
|
});
|
||||||
|
const arr = Array.isArray(lookupRes) ? lookupRes : (lookupRes.data || lookupRes.records || []);
|
||||||
|
const row = arr.find((r: any) => String(r.instance_id || r.id) === String(storeId));
|
||||||
|
if (row && row.store_code) {
|
||||||
|
storeCodeToSend = String(row.store_code);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to fetch store code:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!storeCodeToSend) {
|
||||||
|
storeCodeToSend = String(values['select_store'] || chainedPrefillData?.['select_store'] || (schema?.prefill_data as any)?.select_store || '');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pmRes = await client.request<{ potential: { potential: any[] } }>(
|
const pmRes = await client.request<{ potential: { potential: any[] } }>(
|
||||||
'POST',
|
'POST',
|
||||||
'/api/papi2/potential-mining',
|
'/api/papi2/potential-mining',
|
||||||
{ instance_id: String(res.instance_id ?? currentInstanceId) },
|
{
|
||||||
|
instance_id: String(res.instance_id ?? currentInstanceId),
|
||||||
|
store_code: storeCodeToSend
|
||||||
|
},
|
||||||
{ 'TemplateID': '146' }
|
{ 'TemplateID': '146' }
|
||||||
);
|
);
|
||||||
const rawPotential = pmRes.potential?.potential || [];
|
const rawPotential = pmRes.potential?.potential || [];
|
||||||
@ -268,11 +321,11 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextPrefillData) {
|
setChainedPrefillData(prev => ({
|
||||||
setChainedPrefillData(nextPrefillData);
|
...prev,
|
||||||
} else {
|
...values,
|
||||||
setChainedPrefillData(undefined);
|
...(nextPrefillData || {})
|
||||||
}
|
}));
|
||||||
|
|
||||||
setChainQueue(pending);
|
setChainQueue(pending);
|
||||||
setCurrentActivityId(nextActivity.activity_uid);
|
setCurrentActivityId(nextActivity.activity_uid);
|
||||||
@ -290,7 +343,7 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
||||||
{fields.map(f => {
|
{normalFields.map(f => {
|
||||||
const type = f.data_type;
|
const type = f.data_type;
|
||||||
const val = values[f.id];
|
const val = values[f.id];
|
||||||
const isDisabled = schema.field_defaults?.[f.id]?.disabled;
|
const isDisabled = schema.field_defaults?.[f.id]?.disabled;
|
||||||
@ -308,7 +361,7 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
activityId={currentActivityId}
|
activityId={currentActivityId}
|
||||||
fieldId={f.id}
|
fieldId={f.id}
|
||||||
formData={values}
|
formData={values}
|
||||||
onChange={(newVal) => handleFieldChange(f.id, newVal)}
|
onChange={(newVal, fullRow) => handleFieldChange(f.id, newVal, fullRow)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -456,9 +509,23 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{actionField && actionField.properties?.options ? (
|
||||||
|
actionField.properties.options.map((opt: any) => (
|
||||||
|
<Button
|
||||||
|
key={opt.value}
|
||||||
|
type="submit"
|
||||||
|
variant="primary"
|
||||||
|
disabled={submitting}
|
||||||
|
onClick={() => { clickedActionRef.current = opt.value; }}
|
||||||
|
>
|
||||||
|
{submitting && clickedActionRef.current === opt.value ? 'Submitting...' : opt.label}
|
||||||
|
</Button>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
<Button type="submit" variant="primary" disabled={submitting}>
|
<Button type="submit" variant="primary" disabled={submitting}>
|
||||||
{submitting ? 'Submitting...' : 'Submit'}
|
{submitting ? 'Submitting...' : 'Submit'}
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,7 +6,7 @@ export interface WfLookupFieldProps {
|
|||||||
label: string;
|
label: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
onChange: (val: string) => void;
|
onChange: (val: string, fullRow?: any) => void;
|
||||||
client: ZinoClient;
|
client: ZinoClient;
|
||||||
config: any; // wf_lookup_config
|
config: any; // wf_lookup_config
|
||||||
properties?: any; // parent field properties
|
properties?: any; // parent field properties
|
||||||
@ -17,14 +17,13 @@ export interface WfLookupFieldProps {
|
|||||||
|
|
||||||
export function WfLookupField({ label, required, value, onChange, client, config, activityId, fieldId, formData }: WfLookupFieldProps) {
|
export function WfLookupField({ label, required, value, onChange, client, config, activityId, fieldId, formData }: WfLookupFieldProps) {
|
||||||
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
|
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [records, setRecords] = useState<any[]>([]);
|
||||||
|
|
||||||
const formDataStr = JSON.stringify(formData);
|
const formDataStr = JSON.stringify(formData);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let mounted = true;
|
let mounted = true;
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
client.wfLookupRecords({
|
client.wfLookupRecords({
|
||||||
activityId,
|
activityId,
|
||||||
@ -36,6 +35,7 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
// The API might return { data: [...] } or { records: [...] } or just an array
|
// The API might return { data: [...] } or { records: [...] } or just an array
|
||||||
const arr = Array.isArray(res) ? res : (res.data || res.records || []);
|
const arr = Array.isArray(res) ? res : (res.data || res.records || []);
|
||||||
|
setRecords(arr);
|
||||||
|
|
||||||
const displayFields = config?.display_fields || [];
|
const displayFields = config?.display_fields || [];
|
||||||
|
|
||||||
@ -59,9 +59,6 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
|||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error("Failed to load wf_lookup records:", err);
|
console.error("Failed to load wf_lookup records:", err);
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
if (mounted) setLoading(false);
|
|
||||||
});
|
});
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
@ -76,7 +73,10 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
|||||||
label={label}
|
label={label}
|
||||||
required={required}
|
required={required}
|
||||||
value={String(value || '')}
|
value={String(value || '')}
|
||||||
onChange={onChange}
|
onChange={(val) => {
|
||||||
|
const row = records.find(r => String(r.instance_id || r.id) === val);
|
||||||
|
onChange(val, row);
|
||||||
|
}}
|
||||||
options={options}
|
options={options}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -86,6 +86,8 @@ export function AnalyticsChart({ data }: AnalyticsChartProps) {
|
|||||||
chartType = 4; // Donut
|
chartType = 4; // Donut
|
||||||
} else if (lowerKey.includes('brand') || lowerKey.includes('product')) {
|
} else if (lowerKey.includes('brand') || lowerKey.includes('product')) {
|
||||||
chartType = 5; // Table
|
chartType = 5; // Table
|
||||||
|
} else if (lowerKey.includes('monthly')) {
|
||||||
|
chartType = 0; // Bar
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderChartContent = () => {
|
const renderChartContent = () => {
|
||||||
|
|||||||
@ -26,10 +26,22 @@ export function formatValue(value: unknown, fieldKey?: string): string {
|
|||||||
const isTimeOnly = lowerKey.includes('time');
|
const isTimeOnly = lowerKey.includes('time');
|
||||||
const isDateOnly = lowerKey.includes('date');
|
const isDateOnly = lowerKey.includes('date');
|
||||||
|
|
||||||
|
const isYearZero = typeof value === 'string' && value.startsWith('0000-01-01');
|
||||||
|
|
||||||
if (isTimeOnly && !isDateOnly) {
|
if (isTimeOnly && !isDateOnly) {
|
||||||
return date.toLocaleTimeString('en-IN', { hour: 'numeric', minute: '2-digit', hour12: true });
|
return date.toLocaleTimeString('en-IN', {
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: true,
|
||||||
|
...(isYearZero && { timeZone: 'UTC' })
|
||||||
|
});
|
||||||
} else if (isDateOnly && !isTimeOnly) {
|
} else if (isDateOnly && !isTimeOnly) {
|
||||||
return date.toLocaleDateString('en-IN', { day: 'numeric', month: 'short', year: 'numeric' });
|
return date.toLocaleDateString('en-IN', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
year: 'numeric',
|
||||||
|
...(isYearZero && { timeZone: 'UTC' })
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return date.toLocaleString('en-IN', {
|
return date.toLocaleString('en-IN', {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
@ -37,7 +49,8 @@ export function formatValue(value: unknown, fieldKey?: string): string {
|
|||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
hour12: true
|
hour12: true,
|
||||||
|
...(isYearZero && { timeZone: 'UTC' })
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { useEffect, useState } from 'react';
|
|||||||
import { Button } from '../components/buttons/Button';
|
import { Button } from '../components/buttons/Button';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { DynamicForm } from '../components/forms/DynamicForm';
|
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||||
import { formatActivityName } from '../lib/format';
|
|
||||||
import { ORDER_BOOKING } from '../api/config';
|
import { ORDER_BOOKING } from '../api/config';
|
||||||
import { orderBookingClient } from '../api/clients';
|
import { orderBookingClient } from '../api/clients';
|
||||||
|
|
||||||
@ -139,7 +138,9 @@ export function CallsPage() {
|
|||||||
placeOrderAction={
|
placeOrderAction={
|
||||||
(() => {
|
(() => {
|
||||||
const stateName = String(selectedRow.current_state_name || selectedRow.current_state_name_ || selectedRow.current_state || selectedRow.status || '').toLowerCase();
|
const stateName = String(selectedRow.current_state_name || selectedRow.current_state_name_ || selectedRow.current_state || selectedRow.status || '').toLowerCase();
|
||||||
if (stateName.includes('ordered') || stateName === 'ordered' || stateName.includes('order')) {
|
if (stateName === 'no order' || stateName === 'no_order') {
|
||||||
|
return null;
|
||||||
|
} else if (stateName.includes('ordered') || stateName === 'ordered' || stateName.includes('order')) {
|
||||||
return (
|
return (
|
||||||
<Button onClick={() => setActiveActivity({ id: ORDER_BOOKING.activities.EDIT_ORDER.uid, name: 'Edit Order' })}>
|
<Button onClick={() => setActiveActivity({ id: ORDER_BOOKING.activities.EDIT_ORDER.uid, name: 'Edit Order' })}>
|
||||||
Edit Order
|
Edit Order
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { useState } from 'react';
|
|||||||
import { Button } from '../components/buttons/Button';
|
import { Button } from '../components/buttons/Button';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { DynamicForm } from '../components/forms/DynamicForm';
|
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||||
import { formatActivityName } from '../lib/format';
|
|
||||||
import { DAILY_REPORTS } from '../api/config';
|
import { DAILY_REPORTS } from '../api/config';
|
||||||
import { dailyReportsClient } from '../api/clients';
|
import { dailyReportsClient } from '../api/clients';
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { useState } from 'react';
|
|||||||
import { Button } from '../components/buttons/Button';
|
import { Button } from '../components/buttons/Button';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { DynamicForm } from '../components/forms/DynamicForm';
|
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||||
import { formatActivityName } from '../lib/format';
|
|
||||||
import { ORDER_BOOKING } from '../api/config';
|
import { ORDER_BOOKING } from '../api/config';
|
||||||
import { orderBookingClient } from '../api/clients';
|
import { orderBookingClient } from '../api/clients';
|
||||||
import { useAuth } from '../auth/context';
|
import { useAuth } from '../auth/context';
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { useState } from 'react';
|
|||||||
import { Button } from '../components/buttons/Button';
|
import { Button } from '../components/buttons/Button';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { DynamicForm } from '../components/forms/DynamicForm';
|
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||||
import { formatActivityName } from '../lib/format';
|
|
||||||
import { ORDER_BOOKING } from '../api/config';
|
import { ORDER_BOOKING } from '../api/config';
|
||||||
import { orderBookingClient } from '../api/clients';
|
import { orderBookingClient } from '../api/clients';
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { StoreDetail } from '../components/dv';
|
|||||||
import { Button } from '../components/buttons/Button';
|
import { Button } from '../components/buttons/Button';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { DynamicForm } from '../components/forms/DynamicForm';
|
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||||
import { formatActivityName } from '../lib/format';
|
|
||||||
import { STORE } from '../api/config';
|
import { STORE } from '../api/config';
|
||||||
import { storeClient } from '../api/clients';
|
import { storeClient } from '../api/clients';
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user