feat(cortex): add Intake Blocks MVP for voice/text commands
Adds 4 new intents for intake workflow: - intake_status: shows completion percentage and section checklist - intake_navigeer: navigation to specific intake tabs - risico_query: displays risk assessments with severity indicators - diagnose_query: shows primary/secondary diagnoses New components: - IntakeStatusBlock, RisicoBlock, DiagnoseBlock - Shared block components (BlockLoading, BlockError, BlockEmpty, BlockSection, BlockItem, BlockFooter) - useBlockData and useIntakeContext hooks New API routes: - GET /api/cortex/intake/status - GET /api/cortex/intake/risico - GET /api/cortex/intake/diagnose Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
128
components/cortex/shared/block-item.tsx
Normal file
128
components/cortex/shared/block-item.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* BlockItem Component
|
||||
*
|
||||
* List item for use within BlockSection.
|
||||
* Supports title, subtitle, badge, and click handling.
|
||||
*
|
||||
* Epic: E1.S2 - Block Layout
|
||||
*/
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
export type BadgeVariant = 'default' | 'success' | 'warning' | 'danger';
|
||||
|
||||
interface BlockItemProps {
|
||||
/** Hoofdtekst */
|
||||
title: string;
|
||||
/** Subtekst (datum, auteur, etc.) */
|
||||
subtitle?: string;
|
||||
/** Status badge */
|
||||
badge?: {
|
||||
label: string;
|
||||
variant: BadgeVariant;
|
||||
};
|
||||
/** Klik handler (maakt item klikbaar) */
|
||||
onClick?: () => void;
|
||||
/** Extra CSS classes */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Styling
|
||||
// ============================================================================
|
||||
|
||||
const BADGE_STYLES: Record<BadgeVariant, string> = {
|
||||
default: 'bg-slate-100 text-slate-700 border-slate-200',
|
||||
success: 'bg-green-50 text-green-700 border-green-200',
|
||||
warning: 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
danger: 'bg-red-50 text-red-700 border-red-200',
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Component
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* List item for Cortex blocks.
|
||||
* Renders as button when clickable, div otherwise.
|
||||
*/
|
||||
export function BlockItem({
|
||||
title,
|
||||
subtitle,
|
||||
badge,
|
||||
onClick,
|
||||
className,
|
||||
}: BlockItemProps) {
|
||||
const isClickable = Boolean(onClick);
|
||||
const Component = isClickable ? 'button' : 'div';
|
||||
|
||||
return (
|
||||
<Component
|
||||
type={isClickable ? 'button' : undefined}
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'flex items-center justify-between p-3 rounded-lg',
|
||||
'bg-slate-50 border border-slate-200',
|
||||
'w-full text-left',
|
||||
isClickable && [
|
||||
'cursor-pointer',
|
||||
'hover:bg-slate-100 hover:border-slate-300',
|
||||
'transition-colors',
|
||||
],
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* Content */}
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium text-slate-900 truncate">{title}</p>
|
||||
{subtitle && (
|
||||
<p className="text-xs text-slate-500 mt-0.5 truncate">{subtitle}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Badge */}
|
||||
{badge && (
|
||||
<span
|
||||
className={cn(
|
||||
'ml-3 flex-shrink-0',
|
||||
'px-2 py-0.5 rounded-full',
|
||||
'text-xs font-medium border',
|
||||
BADGE_STYLES[badge.variant]
|
||||
)}
|
||||
>
|
||||
{badge.label}
|
||||
</span>
|
||||
)}
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Helper: Get variant from risk level
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Maps risk level string to badge variant.
|
||||
* Used for consistent risk display across blocks.
|
||||
*/
|
||||
export function getRiskBadgeVariant(level: string): BadgeVariant {
|
||||
switch (level.toLowerCase()) {
|
||||
case 'laag':
|
||||
return 'success';
|
||||
case 'gemiddeld':
|
||||
case 'matig':
|
||||
return 'warning';
|
||||
case 'hoog':
|
||||
case 'zeer_hoog':
|
||||
case 'zeer hoog':
|
||||
return 'danger';
|
||||
default:
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user