// ======================================== // SpecCard Component // ======================================== // Spec card with readMode badge, keywords, priority indicator and action menu import * as React from 'react'; import { useIntl } from 'react-intl'; import { cn } from '@/lib/utils'; import { Card, CardContent } from '@/components/ui/Card'; import { Badge } from '@/components/ui/Badge'; import { Button } from '@/components/ui/Button'; import { Switch } from '@/components/ui/Switch'; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, } from '@/components/ui/DropdownMenu'; import { MoreVertical, Edit, Trash2, FileText, Tag, } from 'lucide-react'; // ========== Types ========== /** * Spec dimension type */ export type SpecDimension = 'specs' | 'personal'; /** * Spec read mode type */ export type SpecReadMode = 'required' | 'optional'; /** * Spec priority type */ export type SpecPriority = 'critical' | 'high' | 'medium' | 'low'; /** * Spec data structure */ export interface Spec { /** Unique spec identifier */ id: string; /** Spec title (display name) */ title: string; /** Spec file path */ file: string; /** Spec dimension/category */ dimension: SpecDimension; /** Read mode: required (always inject) or optional (keyword match) */ readMode: SpecReadMode; /** Priority level */ priority: SpecPriority; /** Keywords for matching (optional specs) */ keywords: string[]; /** Whether spec is enabled */ enabled: boolean; /** Optional description */ description?: string; } /** * SpecCard component props */ export interface SpecCardProps { /** Spec data */ spec: Spec; /** Called when edit action is triggered */ onEdit?: (spec: Spec) => void; /** Called when delete action is triggered */ onDelete?: (specId: string) => void; /** Called when toggle enabled is triggered */ onToggle?: (specId: string, enabled: boolean) => void; /** Optional className */ className?: string; /** Show actions dropdown */ showActions?: boolean; /** Disabled state for actions */ actionsDisabled?: boolean; } // ========== Configuration ========== // Read mode badge configuration const readModeConfig: Record< SpecReadMode, { variant: 'default' | 'secondary'; labelKey: string } > = { required: { variant: 'default', labelKey: 'specs.readMode.required' }, optional: { variant: 'secondary', labelKey: 'specs.readMode.optional' }, }; // Priority badge configuration const priorityConfig: Record< SpecPriority, { variant: 'destructive' | 'warning' | 'info' | 'secondary'; labelKey: string } > = { critical: { variant: 'destructive', labelKey: 'specs.priority.critical' }, high: { variant: 'warning', labelKey: 'specs.priority.high' }, medium: { variant: 'info', labelKey: 'specs.priority.medium' }, low: { variant: 'secondary', labelKey: 'specs.priority.low' }, }; // ========== Component ========== /** * SpecCard component for displaying spec information */ export function SpecCard({ spec, onEdit, onDelete, onToggle, className, showActions = true, actionsDisabled = false, }: SpecCardProps) { const { formatMessage } = useIntl(); const readMode = readModeConfig[spec.readMode]; const priority = priorityConfig[spec.priority]; const handleToggle = (enabled: boolean) => { onToggle?.(spec.id, enabled); }; const handleAction = (e: React.MouseEvent, action: 'edit' | 'delete') => { e.stopPropagation(); switch (action) { case 'edit': onEdit?.(spec); break; case 'delete': onDelete?.(spec.id); break; } }; return ( {/* Header */} {spec.title} {spec.file} {showActions && ( e.stopPropagation()} disabled={actionsDisabled} > {formatMessage({ id: 'common.aria.actions' })} handleAction(e, 'edit')}> {formatMessage({ id: 'specs.actions.edit' })} handleAction(e, 'delete')} className="text-destructive focus:text-destructive" > {formatMessage({ id: 'specs.actions.delete' })} )} {/* Badges */} {formatMessage({ id: readMode.labelKey })} {formatMessage({ id: priority.labelKey })} {/* Description */} {spec.description && ( {spec.description} )} {/* Keywords */} {spec.keywords.length > 0 && ( {spec.keywords.slice(0, 4).map((keyword) => ( {keyword} ))} {spec.keywords.length > 4 && ( +{spec.keywords.length - 4} )} )} {/* Footer with toggle */} {formatMessage({ id: spec.enabled ? 'specs.status.enabled' : 'specs.status.disabled' })} ); } /** * Skeleton loader for SpecCard */ export function SpecCardSkeleton({ className }: { className?: string }) { return ( ); } export default SpecCard;
{spec.file}
{spec.description}