Files
helix-engage/src/components/rules/source-weights-panel.tsx
saridsa2 b90740e009 feat: rules engine — priority config UI + worklist scoring
- Rules engine spec v2 (priority vs automation rules distinction)
- Priority Rules settings page with weight sliders, SLA config, campaign/source weights
- Collapsible config sections with dynamic headers
- Live worklist preview panel with client-side scoring
- AI assistant panel (collapsible) with rules-engine-specific system prompt
- Worklist panel: score display with SLA status dots, sort by score
- Scoring library (scoring.ts) for client-side preview computation
- Sidebar: Rules Engine nav item under Configuration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:20:59 +05:30

49 lines
1.9 KiB
TypeScript

import { useMemo } from 'react';
import { WeightSliderRow } from './weight-slider-row';
import { CollapsibleSection } from './collapsible-section';
import { SOURCE_LABELS } from '@/lib/scoring';
import type { PriorityConfig } from '@/lib/scoring';
interface SourceWeightsPanelProps {
config: PriorityConfig;
onChange: (config: PriorityConfig) => void;
}
const SOURCE_ORDER = ['WHATSAPP', 'PHONE', 'FACEBOOK_AD', 'GOOGLE_AD', 'INSTAGRAM', 'WEBSITE', 'REFERRAL', 'WALK_IN', 'OTHER'];
export const SourceWeightsPanel = ({ config, onChange }: SourceWeightsPanelProps) => {
const updateSource = (source: string, weight: number) => {
onChange({
...config,
sourceWeights: { ...config.sourceWeights, [source]: weight },
});
};
const badge = useMemo(() => {
const weights = SOURCE_ORDER.map(s => config.sourceWeights[s] ?? 5);
const avg = weights.reduce((a, b) => a + b, 0) / weights.length;
const highest = SOURCE_ORDER.reduce((best, s) => (config.sourceWeights[s] ?? 5) > (config.sourceWeights[best] ?? 5) ? s : best, SOURCE_ORDER[0]);
return `Avg ${avg.toFixed(1)} · Top: ${SOURCE_LABELS[highest]}`;
}, [config.sourceWeights]);
return (
<CollapsibleSection
title="Source Weights"
subtitle="Leads from higher-weighted sources get priority"
badge={badge}
defaultOpen={false}
>
<div className="divide-y divide-tertiary">
{SOURCE_ORDER.map(source => (
<WeightSliderRow
key={source}
label={SOURCE_LABELS[source] ?? source}
weight={config.sourceWeights[source] ?? 5}
onWeightChange={(w) => updateSource(source, w)}
/>
))}
</div>
</CollapsibleSection>
);
};