mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-11 18:28:15 +00:00
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>
This commit is contained in:
55
src/components/rules/campaign-weights-panel.tsx
Normal file
55
src/components/rules/campaign-weights-panel.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user