onClick?.(opportunity)}
+ className={cn(
+ 'cursor-grab rounded-lg border bg-white p-3 shadow-sm transition-all',
+ 'hover:shadow-md hover:border-gray-300',
+ 'active:cursor-grabbing active:shadow-lg'
+ )}
+ >
+ {/* Opportunity Name */}
+
+
{opportunity.name}
+ {opportunity.partnerName && (
+
{opportunity.partnerName}
+ )}
+
+
+ {/* Expected Revenue */}
+
+
+
+ ${formatCurrency(opportunity.expectedRevenue)}
+
+
+
+ {/* Footer: Date and Avatar */}
+
+ {/* Expected Close Date */}
+ {opportunity.expectedCloseDate ? (
+
+
+ {formatDate(opportunity.expectedCloseDate)}
+
+ ) : (
+
+
+ Sin fecha
+
+ )}
+
+ {/* Assigned User Avatar */}
+ {opportunity.assignedTo ? (
+
+ ) : (
+
+ )}
+
+
+ {/* Probability indicator */}
+
+
+ Probabilidad
+ {opportunity.probability}%
+
+
+
= 70 ? 'bg-green-500' :
+ opportunity.probability >= 40 ? 'bg-yellow-500' : 'bg-red-500'
+ )}
+ style={{ width: `${opportunity.probability}%` }}
+ />
+
+
+
+ );
+}
+
+export default KanbanCard;
diff --git a/src/shared/components/crm/KanbanColumn.tsx b/src/shared/components/crm/KanbanColumn.tsx
new file mode 100644
index 0000000..1ec9a31
--- /dev/null
+++ b/src/shared/components/crm/KanbanColumn.tsx
@@ -0,0 +1,154 @@
+import { DragEvent, useState } from 'react';
+import { DollarSign } from 'lucide-react';
+import { KanbanCard, type KanbanOpportunity } from './KanbanCard';
+import { formatNumber } from '@utils/formatters';
+import { cn } from '@utils/cn';
+
+export type StageKey = 'new' | 'qualified' | 'proposition' | 'won' | 'lost';
+
+export interface KanbanColumnProps {
+ title: string;
+ stage: StageKey;
+ items: KanbanOpportunity[];
+ onDrop: (opportunity: KanbanOpportunity, targetStage: StageKey) => void;
+ onCardClick?: (opportunity: KanbanOpportunity) => void;
+}
+
+const stageColors: Record
= {
+ new: {
+ bg: 'bg-blue-50',
+ border: 'border-blue-200',
+ header: 'bg-blue-100',
+ text: 'text-blue-700',
+ },
+ qualified: {
+ bg: 'bg-indigo-50',
+ border: 'border-indigo-200',
+ header: 'bg-indigo-100',
+ text: 'text-indigo-700',
+ },
+ proposition: {
+ bg: 'bg-amber-50',
+ border: 'border-amber-200',
+ header: 'bg-amber-100',
+ text: 'text-amber-700',
+ },
+ won: {
+ bg: 'bg-green-50',
+ border: 'border-green-200',
+ header: 'bg-green-100',
+ text: 'text-green-700',
+ },
+ lost: {
+ bg: 'bg-red-50',
+ border: 'border-red-200',
+ header: 'bg-red-100',
+ text: 'text-red-700',
+ },
+};
+
+const formatCurrency = (value: number): string => {
+ return formatNumber(value, 'es-MX', { minimumFractionDigits: 0, maximumFractionDigits: 0 });
+};
+
+export function KanbanColumn({ title, stage, items, onDrop, onCardClick }: KanbanColumnProps) {
+ const [isDragOver, setIsDragOver] = useState(false);
+
+ const colors = stageColors[stage];
+
+ // Calculate totals
+ const totalCount = items.length;
+ const totalValue = items.reduce((sum, item) => sum + (item.expectedRevenue || 0), 0);
+
+ // Pass-through for drag start - actual data transfer happens in KanbanCard
+ const handleDragStart = (_e: DragEvent, _opportunity: KanbanOpportunity) => {
+ // Data transfer is handled by KanbanCard
+ };
+
+ const handleDragOver = (e: DragEvent) => {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'move';
+ setIsDragOver(true);
+ };
+
+ const handleDragLeave = (e: DragEvent) => {
+ e.preventDefault();
+ setIsDragOver(false);
+ };
+
+ const handleDrop = (e: DragEvent) => {
+ e.preventDefault();
+ setIsDragOver(false);
+
+ try {
+ const data = e.dataTransfer.getData('application/json');
+ const opportunity: KanbanOpportunity = JSON.parse(data);
+ onDrop(opportunity, stage);
+ } catch (err) {
+ console.error('Error parsing dropped data:', err);
+ }
+ };
+
+ return (
+
+ {/* Column Header */}
+
+
+
{title}
+
+ {totalCount}
+
+
+
+
+
+ ${formatCurrency(totalValue)}
+
+
+
+
+ {/* Cards Container */}
+
+ {items.length === 0 ? (
+
+ {isDragOver ? 'Soltar aqui' : 'Sin oportunidades'}
+
+ ) : (
+ items.map((opportunity) => (
+
+ ))
+ )}
+
+
+ );
+}
+
+export default KanbanColumn;
diff --git a/src/shared/components/crm/index.ts b/src/shared/components/crm/index.ts
new file mode 100644
index 0000000..fa1b249
--- /dev/null
+++ b/src/shared/components/crm/index.ts
@@ -0,0 +1,3 @@
+// CRM Components
+export { KanbanCard, type KanbanOpportunity, type KanbanCardProps } from './KanbanCard';
+export { KanbanColumn, type StageKey, type KanbanColumnProps } from './KanbanColumn';