wulf-pulse/components/analyzer/provider-toggle.tsx
lorentz 1112a06afe feat: RMM Overshell, IT Glue audit/write-back, LogLift, link-aware bundles, dashboard overhaul
- RMM Overshell (migration 077): admin page, dispatch UI, executor/worker, target
  resolver, script registry (AD/DHCP/DNS/event-log/services/software/network/loglift)
- LogLift evidence pipeline (migration 078): upload webhook, B2 storage client,
  receiver/matcher, EventLogCollector PowerShell script
- IT Glue audit + write-back (migrations 075, 076): asset-audit runner, ticket
  xrefs, applications/configurations browse pages + apply/revert/audit endpoints
- Link-aware analyzer bundles (migration 073) + provider toggle (migration 074):
  link-discovery service, OpenRouter LLM provider, related-tickets/itglue-suggestion
  panels, analyze-bundle endpoint
- Endpoint data model + device-link reconciliation (migrations 079, 080): conflicts
  admin page, reconciler service, resolve endpoints
- Dashboard overhaul: integration-health service + alerts, overview/health endpoints
- Permissions: add itglue + rmm scopes; middleware: public /api/rmm/loglift route

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 07:13:18 -04:00

75 lines
1.8 KiB
TypeScript

'use client';
import { Sparkles, Zap } from 'lucide-react';
export type AnalyzerProvider = 'anthropic' | 'openrouter';
interface ProviderToggleProps {
value: AnalyzerProvider;
onChange: (next: AnalyzerProvider) => void;
disabled?: boolean;
size?: 'sm' | 'md';
}
const OPTIONS: Array<{
value: AnalyzerProvider;
label: string;
hint: string;
icon: typeof Sparkles;
}> = [
{
value: 'anthropic',
label: 'Claude',
hint: 'Haiku → Sonnet → Opus',
icon: Sparkles,
},
{
value: 'openrouter',
label: 'DeepSeek',
hint: 'V4 Flash → V4 Pro → R1',
icon: Zap,
},
];
export function ProviderToggle({
value,
onChange,
disabled = false,
size = 'md',
}: ProviderToggleProps) {
const padding = size === 'sm' ? 'px-2 py-1 text-xs' : 'px-3 py-1.5 text-sm';
return (
<div
className="inline-flex rounded-md border bg-muted/40 p-0.5"
role="radiogroup"
aria-label="LLM provider"
>
{OPTIONS.map((opt) => {
const Icon = opt.icon;
const active = opt.value === value;
return (
<button
key={opt.value}
type="button"
role="radio"
aria-checked={active}
disabled={disabled}
onClick={() => onChange(opt.value)}
title={opt.hint}
className={[
padding,
'rounded-sm flex items-center gap-1.5 transition-colors',
active
? 'bg-background shadow-sm font-medium'
: 'text-muted-foreground hover:text-foreground',
disabled ? 'opacity-50 cursor-not-allowed' : '',
].join(' ')}
>
<Icon className={size === 'sm' ? 'w-3 h-3' : 'w-3.5 h-3.5'} />
{opt.label}
</button>
);
})}
</div>
);
}