knapsack scheduling algorithm

This commit is contained in:
2026-07-12 14:19:58 -04:00
parent 031da48ddd
commit 9e100d60d0
56 changed files with 1936 additions and 1286 deletions
+51 -7
View File
@@ -1,10 +1,12 @@
import { useRef, useEffect, useState } from 'react'
const STATUS_STYLES = {
pending: 'bg-gray-600/60 text-gray-200 border-gray-500',
completed: 'bg-green-800/60 text-green-200 border-green-600',
rerun: 'bg-red-800/60 text-red-200 border-red-600',
}
const DEVICE_ACCENT_CLASSES = [
export const DEVICE_ACCENT_CLASSES = [
'bg-sky-400',
'bg-emerald-400',
'bg-amber-400',
@@ -15,7 +17,7 @@ const DEVICE_ACCENT_CLASSES = [
'bg-fuchsia-400',
]
function getDeviceAccentClass(device) {
export function getDeviceAccentClass(device) {
if (!device) return 'bg-gray-500'
let hash = 0
@@ -47,9 +49,46 @@ function formatConfig(config) {
export default function TestCard({ test }) {
const style = STATUS_STYLES[test.status] ?? STATUS_STYLES.pending
const deviceAccentClass = getDeviceAccentClass(test.device)
const cardRef = useRef(null)
const [tooltipPos, setTooltipPos] = useState({ top: '0px', left: '0px' })
const [isHovered, setIsHovered] = useState(false)
useEffect(() => {
const cardElement = cardRef.current
if (!cardElement) return
const handleMouseMove = (e) => {
// Position tooltip near the cursor, with 10px offset
const top = e.clientY + 10
const left = e.clientX + 10
setTooltipPos({
top: `${top}px`,
left: `${left}px`,
})
}
const handleMouseEnter = () => {
setIsHovered(true)
}
const handleMouseLeave = () => {
setIsHovered(false)
}
cardElement.addEventListener('mouseenter', handleMouseEnter)
cardElement.addEventListener('mouseleave', handleMouseLeave)
cardElement.addEventListener('mousemove', handleMouseMove)
return () => {
cardElement.removeEventListener('mouseenter', handleMouseEnter)
cardElement.removeEventListener('mouseleave', handleMouseLeave)
cardElement.removeEventListener('mousemove', handleMouseMove)
}
}, [])
return (
<div className="relative group">
<div ref={cardRef} className="relative">
<div
className={`relative px-1.5 py-0.5 pl-3 rounded border text-xs font-mono truncate cursor-default select-none ${style}`}
style={{ maxWidth: '100%' }}
@@ -57,9 +96,14 @@ export default function TestCard({ test }) {
<span className={`absolute inset-y-0 left-0 w-1 rounded-l ${deviceAccentClass}`} aria-hidden="true" />
{test.test_id}
</div>
{/* Hover tooltip */}
<div className="absolute z-50 bottom-full left-0 mb-1 hidden group-hover:block min-w-max">
<div className="bg-gray-800 border border-gray-600 rounded-md px-2.5 py-1.5 text-xs text-gray-200 shadow-lg max-w-md">
{/* Hover tooltip — follows cursor */}
{isHovered && (
<div className="fixed z-50 bg-gray-800 border border-gray-600 rounded-md px-2.5 py-1.5 text-xs text-gray-200 shadow-lg max-w-md pointer-events-none"
style={{
top: tooltipPos.top,
left: tooltipPos.left,
}}
>
<p><span className="text-gray-400">Type:</span> {test.test_type ?? '—'}</p>
<p><span className="text-gray-400">Rotation:</span> {test.rotation ?? '—'}</p>
<p className="flex items-center gap-1.5">
@@ -69,7 +113,7 @@ export default function TestCard({ test }) {
</p>
<p className="mt-1 pt-1 border-t border-gray-600"><span className="text-gray-400">Config:</span> {formatConfig(test.config)}</p>
</div>
</div>
)}
</div>
)
}