added graph and potentialmining

This commit is contained in:
suryac 2026-08-06 15:44:09 +05:30
parent 0ada5c4735
commit 7835cd3630
5 changed files with 90 additions and 73 deletions

View File

@ -60,6 +60,7 @@ export interface CallDetailProps {
placeOrderAction?: ReactNode;
refreshKey?: number;
onBack?: () => void;
onDataLoad?: (data: Record<string, unknown>) => void;
}
export function CallDetail({
@ -69,6 +70,7 @@ export function CallDetail({
placeOrderAction,
refreshKey,
onBack,
onDataLoad,
}: CallDetailProps) {
const [data, setData] = useState<Record<string, unknown> | null>(null);
const [potentialData, setPotentialData] = useState<any[] | null>(null);
@ -84,8 +86,9 @@ export function CallDetail({
const r = await orderBookingClient.detailView(ORDER_BOOKING.detailViews.CALLS, instanceId);
if (live) {
setData(r.data || {});
if (onDataLoad) onDataLoad(r.data || {});
const storeCode = (r.data?.select_store as any)?.store_code || (r.data?.select_store as any)?.code;
const storeCode = (r.data?.select_store as any)?.store_code_2 || (r.data?.select_store as any)?.code || (r.data as any)?.store_code || (r.data as any)?.store?.store_code;
if (storeCode) {
orderBookingClient.request<{ potential: { potential: any[] } }>(
'POST',
@ -107,7 +110,7 @@ export function CallDetail({
return () => {
live = false;
};
}, [instanceId, refreshKey]);
}, [instanceId, refreshKey, onDataLoad]);
if (error) {
return (

View File

@ -52,7 +52,7 @@ export function OrderDetail({ instanceId }: WiredDetailViewProps) {
if (!data || Object.keys(data).length === 0) {
return (
<Card title={`Order #${instanceId}`}>
<Card title={`Order`}>
<EmptyState title="No details found" />
</Card>
);
@ -117,13 +117,6 @@ export function OrderDetail({ instanceId }: WiredDetailViewProps) {
return (
<div className="flex flex-col gap-6 bg-transparent">
{/* Header */}
<div className="flex items-center gap-4">
<div>
<h1 className="text-xl font-semibold tracking-tight text-slate-900">Order Detail</h1>
<p className="text-sm text-slate-500">#{orderId}</p>
</div>
</div>
{/* Status + Date ribbon */}
<div className="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-slate-200 bg-[var(--tiles-card-bg)] p-4 shadow-sm">

View File

@ -47,7 +47,7 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
: "grid grid-cols-1 lg:grid-cols-3 gap-6 w-full";
return (
<div className={gridClass}>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 w-full">
{charts.map((chart, idx) => {
const title = (chart.key || `Chart ${idx + 1}`)
.replace(/_/g, ' ')
@ -93,10 +93,20 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
chartType = 4; // Donut
} else if (lowerKey.includes('brand') || lowerKey.includes('product')) {
chartType = 5; // Table
} else if (lowerKey.includes('monthly')) {
} else if (lowerKey.includes('monthly') || lowerKey.includes('yearly')) {
chartType = 0; // Bar
}
// Determine if it should span full width
const isFullWidth = (gridCols === 1) || (!gridCols && charts.length <= 2) || hasSeries || lowerKey.includes('monthly') || lowerKey.includes('yearly') || finalData.length > 8;
const fullSpanClass = gridCols === 2 ? 'lg:col-span-2' : 'lg:col-span-3';
// Calculate a dynamic width if there's a lot of data to allow horizontal scrolling
const minChartWidth = chartType === 0 || chartType === 1 || chartType === 2
? Math.max(100, finalData.length * (hasSeries ? seriesKeys.length * 15 + 40 : 60))
: 100;
const renderChartContent = () => {
return (
<>
@ -177,8 +187,8 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
};
return (
<Card key={chart.chart_uid || idx} title={title} className="shadow-sm border border-gray-100 !bg-[var(--tiles-card-bg)]">
<div className="h-[320px] w-full mt-4">
<Card key={chart.chart_uid || idx} title={title} className={`shadow-sm border border-gray-100 !bg-[var(--tiles-card-bg)] ${isFullWidth ? fullSpanClass : ''}`}>
<div className={`h-[320px] w-full mt-4 overflow-y-hidden ${minChartWidth > 100 ? 'overflow-x-auto scrollbar-slim' : ''}`}>
{chartType === 5 ? (
<div className="w-full h-full overflow-y-auto pr-2 scrollbar-slim">
<table className="w-full text-left text-sm text-gray-600 border-collapse">
@ -203,6 +213,7 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
</table>
</div>
) : (
<div style={{ minWidth: minChartWidth > 100 ? `${minChartWidth}px` : '100%', height: '100%' }}>
<ResponsiveContainer width="100%" height="100%" className="focus:outline-none [&_.recharts-wrapper]:outline-none [&_.recharts-surface]:outline-none" style={{ outline: 'none' }}>
{chartType === 3 || chartType === 4 ? (
<PieChart margin={{ top: 10, right: 10, left: 10, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
@ -242,6 +253,7 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
</BarChart>
)}
</ResponsiveContainer>
</div>
)}
</div>
</Card>

View File

@ -15,7 +15,7 @@ export function CallsView({ onRowClick, pageSize, headerActions, rowActions, ref
headerActions={headerActions}
rowActions={rowActions}
refreshKey={refreshKey}
sortBy="instance_id"
sortBy="visit_date"
sortDir="desc"
omitColumns={['instance_id']}
initialFilters={initialFilters}

View File

@ -98,21 +98,21 @@ export function RecordView({
setPage(1);
};
const initialFiltersStr = JSON.stringify(initialFilters || {});
const initialFiltersNormalized = useMemo(() => {
const res: Record<string, string[]> = {};
if (initialFilters) {
Object.entries(initialFilters).forEach(([k, v]) => {
const parsed = JSON.parse(initialFiltersStr);
Object.entries(parsed).forEach(([k, v]) => {
if (Array.isArray(v)) {
res[k] = v;
res[k] = v as string[];
} else if (v) {
res[k] = [v];
res[k] = [v as string];
}
});
}
return res;
}, [initialFilters]);
}, [initialFiltersStr]);
const [activeFilters, setActiveFilters] = useState<Record<string, string[]>>(initialFiltersNormalized);
const [activeFilters, setActiveFilters] = useState<Record<string, string[]>>({});
const [resp, setResp] = useState<RecordViewResponse | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
@ -134,14 +134,23 @@ export function RecordView({
const filtersParam = useMemo(() => {
const parsed = JSON.parse(activeFiltersStr) as Record<string, string[]>;
return Object.entries(parsed)
// Merge user active filters on top of initial prefilters
const merged = { ...initialFiltersNormalized };
Object.entries(parsed).forEach(([k, v]) => {
if (v && v.length > 0) {
merged[k] = v;
}
});
return Object.entries(merged)
.filter(([_, vals]) => vals && vals.length > 0)
.map(([key, vals]) => ({
field_key: key,
values: vals,
data_type: 'character varying',
}));
}, [activeFiltersStr]);
}, [activeFiltersStr, initialFiltersNormalized]);
useEffect(() => {
let live = true;