Files
helix-engage/src/components/rules/campaign-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

56 lines
2.1 KiB
TypeScript

import { useMemo } from 'react';
import { WeightSliderRow } from './weight-slider-row';
import { CollapsibleSection } from './collapsible-section';
import { useData } from '@/providers/data-provider';
import type { PriorityConfig } from '@/lib/scoring';
interface CampaignWeightsPanelProps {
config: PriorityConfig;
onChange: (config: PriorityConfig) => void;
}
export const CampaignWeightsPanel = ({ config, onChange }: CampaignWeightsPanelProps) => {
const { campaigns } = useData();
const updateCampaign = (campaignId: string, weight: number) => {
onChange({
...config,
campaignWeights: { ...config.campaignWeights, [campaignId]: weight },
});
};
const badge = useMemo(() => {
if (!campaigns || campaigns.length === 0) return 'No campaigns';
const configured = campaigns.filter(c => config.campaignWeights[c.id] != null).length;
return `${campaigns.length} campaigns · ${configured} configured`;
}, [campaigns, config.campaignWeights]);
if (!campaigns || campaigns.length === 0) {
return (
<CollapsibleSection title="Campaign Weights" badge="No campaigns" defaultOpen={false}>
<p className="text-xs text-tertiary py-2">Campaign weights will apply once campaigns are created.</p>
</CollapsibleSection>
);
}
return (
<CollapsibleSection
title="Campaign Weights"
subtitle="Higher-weighted campaigns get their leads called first"
badge={badge}
defaultOpen={false}
>
<div className="divide-y divide-tertiary">
{campaigns.map(campaign => (
<WeightSliderRow
key={campaign.id}
label={campaign.campaignName ?? 'Untitled Campaign'}
weight={config.campaignWeights[campaign.id] ?? 5}
onWeightChange={(w) => updateCampaign(campaign.id, w)}
/>
))}
</div>
</CollapsibleSection>
);
};