Compare commits
No commits in common. "94d41194d1f6bbe141c98a96d2115b3a6e530608" and "819b43bd77cfb4885e9c96372a43fb3715ba1e30" have entirely different histories.
94d41194d1
...
819b43bd77
@ -271,5 +271,5 @@ export const HIDDEN_FORM_FIELDS = [
|
|||||||
'store_code_3'
|
'store_code_3'
|
||||||
];
|
];
|
||||||
|
|
||||||
export const LOOKUP_RECORD_LIMIT = 200;
|
export const LOOKUP_RECORD_LIMIT = 500;
|
||||||
export const MAX_SKUS_PER_CHUNK = 20;
|
export const MAX_SKUS_PER_CHUNK = 20;
|
||||||
|
|||||||
@ -187,8 +187,8 @@ export function StoreDetail({ instanceId, onBack, onEdit, refreshKey }: StoreDet
|
|||||||
<div className="bg-[var(--tiles-card-bg)] rounded-2xl border border-slate-200 shadow-sm overflow-hidden flex flex-col md:flex-row">
|
<div className="bg-[var(--tiles-card-bg)] rounded-2xl border border-slate-200 shadow-sm overflow-hidden flex flex-col md:flex-row">
|
||||||
{/* Image Section */}
|
{/* Image Section */}
|
||||||
{imageUrl ? (
|
{imageUrl ? (
|
||||||
<div className="md:w-1/3 lg:w-1/4 h-48 md:h-auto relative bg-slate-100 shrink-0">
|
<div className="md:w-1/3 lg:w-1/4 h-48 md:h-auto relative bg-slate-100">
|
||||||
<img src={imageUrl} alt={String(businessName)} className="absolute inset-0 w-full h-full object-cover" />
|
<img src={imageUrl} alt={String(businessName)} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="md:w-1/3 lg:w-1/4 h-48 md:h-auto flex items-center justify-center bg-slate-100 border-r border-slate-200">
|
<div className="md:w-1/3 lg:w-1/4 h-48 md:h-auto flex items-center justify-center bg-slate-100 border-r border-slate-200">
|
||||||
@ -321,32 +321,32 @@ export function StoreDetail({ instanceId, onBack, onEdit, refreshKey }: StoreDet
|
|||||||
|
|
||||||
{/* Order Potential Card */}
|
{/* Order Potential Card */}
|
||||||
{Array.isArray(potential) && potential.length > 0 && (
|
{Array.isArray(potential) && potential.length > 0 && (
|
||||||
<Card pad={false} className="overflow-hidden border-emerald-200 shadow-sm">
|
<Card pad={false} className="overflow-hidden border-slate-200 shadow-sm">
|
||||||
<div className="flex items-center gap-2 p-4 border-b border-emerald-100 bg-emerald-50">
|
<div className="flex items-center gap-2 p-4 border-b border-slate-100 bg-[var(--tiles-card-bg)]">
|
||||||
<FileText size={16} className="text-emerald-600" />
|
<FileText size={16} className="text-slate-400" />
|
||||||
<h3 className="text-xs font-bold text-emerald-800 uppercase tracking-wider m-0">Order Potential</h3>
|
<h3 className="text-xs font-bold text-slate-500 uppercase tracking-wider m-0">Order Potential</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-[var(--tiles-card-bg)]">
|
<div className="bg-[var(--tiles-card-bg)]">
|
||||||
<table className="w-full text-sm text-left">
|
<table className="w-full text-sm text-left">
|
||||||
<thead className="bg-emerald-50 border-b border-emerald-100">
|
<thead className="bg-slate-50 border-b border-slate-100">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="py-3 px-5 font-bold text-emerald-800 text-xs uppercase tracking-wider">Product Category</th>
|
<th className="py-3 px-5 font-bold text-slate-500 text-xs uppercase tracking-wider">Product Category</th>
|
||||||
<th className="py-3 px-5 font-bold text-emerald-800 text-xs uppercase tracking-wider text-right">Quantity</th>
|
<th className="py-3 px-5 font-bold text-slate-500 text-xs uppercase tracking-wider text-right">Quantity</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{potential.map((p: any, idx: number) => {
|
{potential.map((p: any, idx: number) => {
|
||||||
if (!p.product_category && !p.quantity) return null;
|
if (!p.product_category && !p.quantity) return null;
|
||||||
return (
|
return (
|
||||||
<tr key={idx} className="border-b border-emerald-50 last:border-0 hover:bg-emerald-50/50">
|
<tr key={idx} className="border-b border-slate-100 last:border-0 hover:bg-slate-50/50">
|
||||||
<td className="py-3 px-5 font-semibold text-emerald-900">{p.product_category ? String(p.product_category) : '-'}</td>
|
<td className="py-3 px-5 font-semibold text-slate-900">{p.product_category ? String(p.product_category) : '-'}</td>
|
||||||
<td className="py-3 px-5 text-emerald-700 text-right">{p.quantity ? String(p.quantity) : '0'}</td>
|
<td className="py-3 px-5 text-slate-700 text-right">{p.quantity ? String(p.quantity) : '0'}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<tr className="bg-emerald-50 border-t border-emerald-200 font-bold">
|
<tr className="bg-slate-50 border-t border-slate-200 font-bold">
|
||||||
<td className="py-3 px-5 text-emerald-900">Total</td>
|
<td className="py-3 px-5 text-slate-900">Total</td>
|
||||||
<td className="py-3 px-5 text-emerald-900 text-right">{totalPotential}</td>
|
<td className="py-3 px-5 text-slate-900 text-right">{totalPotential}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@ -174,6 +174,43 @@ 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);
|
setValues(defaultValues);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
if (res.activity_name) {
|
if (res.activity_name) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
import React, { 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, LOOKUP_RECORD_LIMIT } from '../../api/config';
|
import { ORDER_BOOKING, PIPELINE, LOOKUP_RECORD_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';
|
||||||
@ -61,9 +61,9 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
|||||||
}, [values]);
|
}, [values]);
|
||||||
|
|
||||||
const [storeOptions, setStoreOptions] = useState<{ value: string; label: string; _raw?: any }[]>([]);
|
const [storeOptions, setStoreOptions] = useState<{ value: string; label: string; _raw?: any }[]>([]);
|
||||||
const [storeSearchQuery, setStoreSearchQuery] = useState('');
|
|
||||||
const [fetchingDailyLog, setFetchingDailyLog] = useState(false);
|
const [fetchingDailyLog, setFetchingDailyLog] = useState(false);
|
||||||
const [fetchingStores, setFetchingStores] = useState(false);
|
const [fetchingStores, setFetchingStores] = useState(false);
|
||||||
|
const fetchingStoresRef = useRef(false);
|
||||||
|
|
||||||
const [chainedActivity, setChainedActivity] = useState<{
|
const [chainedActivity, setChainedActivity] = useState<{
|
||||||
activityId: string;
|
activityId: string;
|
||||||
@ -119,6 +119,40 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
|||||||
Object.assign(initialValues, res.data);
|
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 => {
|
setValues(prev => {
|
||||||
const next = { ...initialValues, ...prev };
|
const next = { ...initialValues, ...prev };
|
||||||
valuesRef.current = next;
|
valuesRef.current = next;
|
||||||
@ -136,7 +170,9 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
|||||||
}, [client, activityId, todayStr, nowTimeStr]);
|
}, [client, activityId, todayStr, nowTimeStr]);
|
||||||
|
|
||||||
// 2. Helper to fetch select_store options ONLY using updated prefilled formData
|
// 2. Helper to fetch select_store options ONLY using updated prefilled formData
|
||||||
const fetchStoreOptions = useCallback(async (formDataOverride?: Record<string, any>, search: string = '') => {
|
const fetchStoreOptions = useCallback(async (formDataOverride?: Record<string, any>) => {
|
||||||
|
if (fetchingStoresRef.current) return;
|
||||||
|
fetchingStoresRef.current = true;
|
||||||
setFetchingStores(true);
|
setFetchingStores(true);
|
||||||
setStoreError(null);
|
setStoreError(null);
|
||||||
|
|
||||||
@ -148,7 +184,6 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
|||||||
activityId,
|
activityId,
|
||||||
fieldId: 'select_store',
|
fieldId: 'select_store',
|
||||||
formData: formDataToSend,
|
formData: formDataToSend,
|
||||||
search,
|
|
||||||
limit: LOOKUP_RECORD_LIMIT,
|
limit: LOOKUP_RECORD_LIMIT,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -199,16 +234,10 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setFetchingStores(false);
|
setFetchingStores(false);
|
||||||
|
fetchingStoresRef.current = false;
|
||||||
}
|
}
|
||||||
}, [client, activityId]);
|
}, [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
|
// 3. Fetch Daily Log lookup data initially & prefill form state
|
||||||
const loadDailyLogData = useCallback(async (dateVal: string, timeVal: string) => {
|
const loadDailyLogData = useCallback(async (dateVal: string, timeVal: string) => {
|
||||||
setFetchingDailyLog(true);
|
setFetchingDailyLog(true);
|
||||||
@ -435,8 +464,6 @@ export function LogVisitForm({ client, onSuccess, onCancel, onActivityChange }:
|
|||||||
options={[{ value: '', label: `Select ${f.name}...` }, ...storeOptions]}
|
options={[{ value: '', label: `Select ${f.name}...` }, ...storeOptions]}
|
||||||
onDropdownOpen={handleStoreDropdownOpen}
|
onDropdownOpen={handleStoreDropdownOpen}
|
||||||
onChange={e => handleStoreSelect(fieldId, e.target.value)}
|
onChange={e => handleStoreSelect(fieldId, e.target.value)}
|
||||||
onSearchChange={setStoreSearchQuery}
|
|
||||||
disableLocalSearch={true}
|
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
/>
|
/>
|
||||||
{fetchingStores && (
|
{fetchingStores && (
|
||||||
|
|||||||
@ -6,16 +6,12 @@ export function SelectField({
|
|||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
onChange,
|
onChange,
|
||||||
onSearchChange,
|
|
||||||
disableLocalSearch,
|
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
value: string;
|
value: string;
|
||||||
options: any[];
|
options: any[];
|
||||||
onChange: (val: string, fullRow?: any) => void;
|
onChange: (val: string, fullRow?: any) => void;
|
||||||
onSearchChange?: (query: string) => void;
|
|
||||||
disableLocalSearch?: boolean;
|
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
@ -28,8 +24,6 @@ export function SelectField({
|
|||||||
onChange(val, opt?._raw);
|
onChange(val, opt?._raw);
|
||||||
}}
|
}}
|
||||||
options={[{ value: '', label: 'Select...' }, ...options.map((o: any) => ({ value: String(o.value), label: o.label }))]}
|
options={[{ value: '', label: 'Select...' }, ...options.map((o: any) => ({ value: String(o.value), label: o.label }))]}
|
||||||
onSearchChange={onSearchChange}
|
|
||||||
disableLocalSearch={disableLocalSearch}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,6 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
|||||||
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
|
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
|
||||||
const [records, setRecords] = useState<any[]>([]);
|
const [records, setRecords] = useState<any[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
|
||||||
|
|
||||||
const formDataStr = JSON.stringify(formData);
|
const formDataStr = JSON.stringify(formData);
|
||||||
|
|
||||||
@ -33,7 +32,6 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
|||||||
activityId,
|
activityId,
|
||||||
fieldId,
|
fieldId,
|
||||||
formData: JSON.parse(formDataStr),
|
formData: JSON.parse(formDataStr),
|
||||||
search: searchQuery,
|
|
||||||
limit: LOOKUP_RECORD_LIMIT
|
limit: LOOKUP_RECORD_LIMIT
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
@ -74,7 +72,7 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
|||||||
mounted = false;
|
mounted = false;
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
};
|
};
|
||||||
}, [client, config, activityId, fieldId, formDataStr, searchQuery]);
|
}, [client, config, activityId, fieldId, formDataStr]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectField
|
<SelectField
|
||||||
@ -86,8 +84,6 @@ export function WfLookupField({ label, required, value, onChange, client, config
|
|||||||
onChange(val, row);
|
onChange(val, row);
|
||||||
}}
|
}}
|
||||||
options={options}
|
options={options}
|
||||||
onSearchChange={setSearchQuery}
|
|
||||||
disableLocalSearch={true}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,21 +126,20 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
|
|||||||
interval={0}
|
interval={0}
|
||||||
dy={10}
|
dy={10}
|
||||||
dx={-5}
|
dx={-5}
|
||||||
height={90}
|
height={70}
|
||||||
/>
|
/>
|
||||||
<YAxis
|
<YAxis
|
||||||
axisLine={false}
|
axisLine={false}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
tick={{ fontSize: 12, fill: '#64748B' }}
|
tick={{ fontSize: 12, fill: '#64748B' }}
|
||||||
allowDecimals={false}
|
allowDecimals={false}
|
||||||
width={40}
|
width={80}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
cursor={{ fill: '#F8FAFC', stroke: '#E2E8F0', strokeWidth: 1, strokeDasharray: '3 3' }}
|
cursor={{ fill: '#F8FAFC', stroke: '#E2E8F0', strokeWidth: 1, strokeDasharray: '3 3' }}
|
||||||
contentStyle={{ borderRadius: '8px', border: '1px solid #E2E8F0', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)', fontSize: '14px', fontFamily: 'inherit' }}
|
contentStyle={{ borderRadius: '8px', border: '1px solid #E2E8F0', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)', fontSize: '14px', fontFamily: 'inherit' }}
|
||||||
labelStyle={{ fontWeight: 'bold', color: '#0F172A', marginBottom: '4px' }}
|
labelStyle={{ fontWeight: 'bold', color: '#0F172A', marginBottom: '4px' }}
|
||||||
wrapperStyle={{ zIndex: 1000 }}
|
wrapperStyle={{ zIndex: 1000 }}
|
||||||
allowEscapeViewBox={{ x: true, y: true }}
|
|
||||||
/>
|
/>
|
||||||
{hasSeries && <Legend wrapperStyle={{ paddingTop: '20px' }} />}
|
{hasSeries && <Legend wrapperStyle={{ paddingTop: '20px' }} />}
|
||||||
{seriesKeys.map((key, i) => {
|
{seriesKeys.map((key, i) => {
|
||||||
@ -190,7 +189,7 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card key={chart.chart_uid || idx} title={title} className={`shadow-sm border border-gray-100 !bg-[var(--tiles-card-bg)] ${isFullWidth ? fullSpanClass : ''} !overflow-visible`}>
|
<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 ${minChartWidth > 100 ? 'overflow-x-auto scrollbar-slim' : ''}`}>
|
<div className={`h-[320px] w-full mt-4 ${minChartWidth > 100 ? 'overflow-x-auto scrollbar-slim' : ''}`}>
|
||||||
{chartType === 5 ? (
|
{chartType === 5 ? (
|
||||||
<div className="w-full h-full overflow-y-auto pr-2 scrollbar-slim">
|
<div className="w-full h-full overflow-y-auto pr-2 scrollbar-slim">
|
||||||
@ -216,7 +215,7 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '100%', minWidth: minChartWidth > 100 ? `${minChartWidth}px` : '100%', height: '100%' }}>
|
<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' }}>
|
<ResponsiveContainer width="100%" height="100%" className="focus:outline-none [&_.recharts-wrapper]:outline-none [&_.recharts-surface]:outline-none" style={{ outline: 'none' }}>
|
||||||
{chartType === 3 || chartType === 4 ? (
|
{chartType === 3 || chartType === 4 ? (
|
||||||
<PieChart margin={{ top: 10, right: 10, left: 10, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
|
<PieChart margin={{ top: 10, right: 10, left: 10, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
|
||||||
@ -224,7 +223,6 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
|
|||||||
contentStyle={{ borderRadius: '8px', border: '1px solid #E2E8F0', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)', fontSize: '14px', fontFamily: 'inherit' }}
|
contentStyle={{ borderRadius: '8px', border: '1px solid #E2E8F0', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)', fontSize: '14px', fontFamily: 'inherit' }}
|
||||||
itemStyle={{ color: '#0F172A', fontWeight: '500' }}
|
itemStyle={{ color: '#0F172A', fontWeight: '500' }}
|
||||||
wrapperStyle={{ zIndex: 1000 }}
|
wrapperStyle={{ zIndex: 1000 }}
|
||||||
allowEscapeViewBox={{ x: true, y: true }}
|
|
||||||
/>
|
/>
|
||||||
<Legend wrapperStyle={{ paddingTop: '20px' }} />
|
<Legend wrapperStyle={{ paddingTop: '20px' }} />
|
||||||
<Pie
|
<Pie
|
||||||
@ -245,15 +243,15 @@ export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
|
|||||||
</Pie>
|
</Pie>
|
||||||
</PieChart>
|
</PieChart>
|
||||||
) : chartType === 1 ? (
|
) : chartType === 1 ? (
|
||||||
<LineChart data={finalData} margin={{ top: 10, right: 30, left: 0, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
|
<LineChart data={finalData} margin={{ top: 10, right: 10, left: 0, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
|
||||||
{renderChartContent()}
|
{renderChartContent()}
|
||||||
</LineChart>
|
</LineChart>
|
||||||
) : chartType === 2 ? (
|
) : chartType === 2 ? (
|
||||||
<AreaChart data={finalData} margin={{ top: 10, right: 30, left: 0, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
|
<AreaChart data={finalData} margin={{ top: 10, right: 10, left: 0, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
|
||||||
{renderChartContent()}
|
{renderChartContent()}
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
) : (
|
) : (
|
||||||
<BarChart data={finalData} margin={{ top: 10, right: 30, left: 0, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
|
<BarChart data={finalData} margin={{ top: 10, right: 10, left: 0, bottom: 10 }} className="focus:outline-none outline-none" style={{ outline: 'none' }}>
|
||||||
{renderChartContent()}
|
{renderChartContent()}
|
||||||
</BarChart>
|
</BarChart>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -21,8 +21,6 @@ export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|||||||
/** Disable search header filter if set to false */
|
/** Disable search header filter if set to false */
|
||||||
searchable?: boolean;
|
searchable?: boolean;
|
||||||
onDropdownOpen?: () => void;
|
onDropdownOpen?: () => void;
|
||||||
onSearchChange?: (query: string) => void;
|
|
||||||
disableLocalSearch?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Custom searchable select component using React Portal to prevent container clipping. */
|
/** Custom searchable select component using React Portal to prevent container clipping. */
|
||||||
@ -37,8 +35,6 @@ export function Select({
|
|||||||
placeholder,
|
placeholder,
|
||||||
searchable = true,
|
searchable = true,
|
||||||
onDropdownOpen,
|
onDropdownOpen,
|
||||||
onSearchChange,
|
|
||||||
disableLocalSearch,
|
|
||||||
...rest
|
...rest
|
||||||
}: SelectProps) {
|
}: SelectProps) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@ -56,9 +52,7 @@ export function Select({
|
|||||||
const selectedOption = normalizedOptions.find((o) => o.value === currentValue);
|
const selectedOption = normalizedOptions.find((o) => o.value === currentValue);
|
||||||
const selectedOptionLabel = selectedOption ? selectedOption.label : '';
|
const selectedOptionLabel = selectedOption ? selectedOption.label : '';
|
||||||
|
|
||||||
const filteredOptions = disableLocalSearch
|
const filteredOptions = normalizedOptions.filter((o) =>
|
||||||
? normalizedOptions
|
|
||||||
: normalizedOptions.filter((o) =>
|
|
||||||
o.label.toLowerCase().includes(searchQuery.toLowerCase())
|
o.label.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -186,11 +180,7 @@ export function Select({
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => {
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
const val = e.target.value;
|
|
||||||
setSearchQuery(val);
|
|
||||||
onSearchChange?.(val);
|
|
||||||
}}
|
|
||||||
placeholder="Search options..."
|
placeholder="Search options..."
|
||||||
className="w-full text-xs bg-transparent border-none outline-none text-strong placeholder:text-muted"
|
className="w-full text-xs bg-transparent border-none outline-none text-strong placeholder:text-muted"
|
||||||
autoFocus
|
autoFocus
|
||||||
@ -202,7 +192,6 @@ export function Select({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
onSearchChange?.('');
|
|
||||||
}}
|
}}
|
||||||
className="text-muted hover:text-strong p-0.5 rounded cursor-pointer"
|
className="text-muted hover:text-strong p-0.5 rounded cursor-pointer"
|
||||||
>
|
>
|
||||||
@ -281,7 +270,6 @@ export interface MultiSelectProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
/** Disable search header filter if set to false */
|
/** Disable search header filter if set to false */
|
||||||
searchable?: boolean;
|
searchable?: boolean;
|
||||||
onSearchChange?: (query: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Custom searchable multiselect component using React Portal. */
|
/** Custom searchable multiselect component using React Portal. */
|
||||||
@ -297,7 +285,6 @@ export function MultiSelect({
|
|||||||
disabled,
|
disabled,
|
||||||
placeholder,
|
placeholder,
|
||||||
searchable = true,
|
searchable = true,
|
||||||
onSearchChange,
|
|
||||||
}: MultiSelectProps) {
|
}: MultiSelectProps) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
@ -466,11 +453,7 @@ export function MultiSelect({
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => {
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
const val = e.target.value;
|
|
||||||
setSearchQuery(val);
|
|
||||||
onSearchChange?.(val);
|
|
||||||
}}
|
|
||||||
placeholder="Search options..."
|
placeholder="Search options..."
|
||||||
className="w-full text-xs bg-transparent border-none outline-none text-strong placeholder:text-muted"
|
className="w-full text-xs bg-transparent border-none outline-none text-strong placeholder:text-muted"
|
||||||
autoFocus
|
autoFocus
|
||||||
@ -482,7 +465,6 @@ export function MultiSelect({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
onSearchChange?.('');
|
|
||||||
}}
|
}}
|
||||||
className="text-muted hover:text-strong p-0.5 rounded cursor-pointer"
|
className="text-muted hover:text-strong p-0.5 rounded cursor-pointer"
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user