Kraft102's picture
fix: sql.js Docker/Alpine compatibility layer for PatternMemory and FailureMemory
5a81b95
import { ReactNode } from 'react';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { GripVertical, X, Maximize2, Minimize2, Square } from 'lucide-react';
import { cn } from '@/lib/utils';
import { DashboardWidget } from '@/pages/Dashboard';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
interface DraggableWidgetProps {
widget: DashboardWidget;
children: ReactNode;
onRemove: () => void;
onResize: (size: 'small' | 'medium' | 'large') => void;
isDragging?: boolean;
}
const DraggableWidget = ({
widget,
children,
onRemove,
onResize,
isDragging
}: DraggableWidgetProps) => {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({ id: widget.id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
const sizeClasses = {
small: '',
medium: 'md:col-span-1',
large: 'md:col-span-2',
};
return (
<div
ref={setNodeRef}
style={style}
className={cn(
"relative bg-card/80 backdrop-blur-sm border border-border/50 overflow-hidden transition-all duration-200 group",
sizeClasses[widget.size],
isDragging && "opacity-50 scale-105 border-primary shadow-lg shadow-primary/20"
)}
>
{/* Header with drag handle */}
<div className="flex items-center gap-2 px-3 py-2 border-b border-border/50 bg-secondary/30">
<button
{...attributes}
{...listeners}
className="cursor-grab active:cursor-grabbing p-1 hover:bg-secondary/50 rounded transition-colors"
>
<GripVertical className="w-4 h-4 text-muted-foreground" />
</button>
<div className="w-2 h-2 rounded-full bg-primary animate-pulse" />
<span className="font-display text-xs uppercase tracking-wider text-primary flex-1">
{widget.name}
</span>
{/* Size controls */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="h-6 w-6 p-0 opacity-0 group-hover:opacity-100 transition-opacity">
{widget.size === 'small' && <Minimize2 className="w-3 h-3" />}
{widget.size === 'medium' && <Square className="w-3 h-3" />}
{widget.size === 'large' && <Maximize2 className="w-3 h-3" />}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="bg-card border-border z-50">
<DropdownMenuItem onClick={() => onResize('small')} className="cursor-pointer">
<Minimize2 className="w-4 h-4 mr-2" />
Lille
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onResize('medium')} className="cursor-pointer">
<Square className="w-4 h-4 mr-2" />
Medium
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onResize('large')} className="cursor-pointer">
<Maximize2 className="w-4 h-4 mr-2" />
Stor
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{/* Remove button */}
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0 opacity-0 group-hover:opacity-100 transition-opacity text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={onRemove}
>
<X className="w-3 h-3" />
</Button>
</div>
{/* Widget content */}
<div className="p-4">
{children}
</div>
{/* Corner accents */}
<div className="absolute top-0 left-0 w-3 h-3 border-l-2 border-t-2 border-primary/50" />
<div className="absolute top-0 right-0 w-3 h-3 border-r-2 border-t-2 border-primary/50" />
<div className="absolute bottom-0 left-0 w-3 h-3 border-l-2 border-b-2 border-primary/50" />
<div className="absolute bottom-0 right-0 w-3 h-3 border-r-2 border-b-2 border-primary/50" />
</div>
);
};
export default DraggableWidget;