refactor: Remove commit actions and update badge logic in Kanban components

- Removed the onCommit action from KanbanBoard and related components to streamline functionality.
- Updated CardActions to replace the Commit button with a Mark as Verified button, enhancing clarity in user interactions.
- Introduced a new CardBadge component for consistent styling of badges across KanbanCard, improving code reusability and maintainability.
- Refactored badge rendering logic to include a Just Finished badge, ensuring accurate representation of feature status.
This commit is contained in:
Test User
2025-12-20 12:45:51 -05:00
parent 01d78be748
commit 723274523d
4 changed files with 132 additions and 112 deletions

View File

@@ -6,7 +6,6 @@ import {
RotateCcw,
StopCircle,
CheckCircle2,
GitCommit,
FileText,
Eye,
Wand2,
@@ -25,7 +24,6 @@ interface CardActionsProps {
onForceStop?: () => void;
onManualVerify?: () => void;
onFollowUp?: () => void;
onCommit?: () => void;
onImplement?: () => void;
onComplete?: () => void;
onViewPlan?: () => void;
@@ -44,7 +42,6 @@ export function CardActions({
onForceStop,
onManualVerify,
onFollowUp,
onCommit,
onImplement,
onComplete,
onViewPlan,
@@ -251,7 +248,7 @@ export function CardActions({
<span className="truncate">Refine</span>
</Button>
)}
{/* Show Verify button if PR was created (changes are committed), otherwise show Commit button */}
{/* Show Verify button if PR was created (changes are committed), otherwise show Mark as Verified button */}
{feature.prUrl && onManualVerify ? (
<Button
variant="default"
@@ -267,20 +264,20 @@ export function CardActions({
<CheckCircle2 className="w-3 h-3 mr-1" />
Verify
</Button>
) : onCommit ? (
) : onManualVerify ? (
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-[11px]"
onClick={(e) => {
e.stopPropagation();
onCommit();
onManualVerify();
}}
onPointerDown={(e) => e.stopPropagation()}
data-testid={`commit-${feature.id}`}
data-testid={`mark-as-verified-${feature.id}`}
>
<GitCommit className="w-3 h-3 mr-1" />
Commit
<CheckCircle2 className="w-3 h-3 mr-1" />
Mark as Verified
</Button>
) : null}
</>
@@ -338,4 +335,3 @@ export function CardActions({
</div>
);
}

View File

@@ -7,16 +7,46 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { AlertCircle, Lock, Sparkles, Hand } from "lucide-react";
import { AlertCircle, Lock, Hand, Sparkles } from "lucide-react";
import { getBlockingDependencies } from "@/lib/dependency-resolver";
interface CardBadgeProps {
children: React.ReactNode;
className?: string;
"data-testid"?: string;
title?: string;
}
/**
* Shared badge component matching the "Just Finished" badge style
* Used for priority badges and other card badges
*/
function CardBadge({
children,
className,
"data-testid": dataTestId,
title,
}: CardBadgeProps) {
return (
<div
className={cn(
"inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium",
className
)}
data-testid={dataTestId}
title={title}
>
{children}
</div>
);
}
interface CardBadgesProps {
feature: Feature;
}
export function CardBadges({ feature }: CardBadgesProps) {
const { enableDependencyBlocking, features } = useAppStore();
const [currentTime, setCurrentTime] = useState(() => Date.now());
// Calculate blocking dependencies (if feature is in backlog and has incomplete dependencies)
const blockingDependencies = useMemo(() => {
@@ -26,50 +56,13 @@ export function CardBadges({ feature }: CardBadgesProps) {
return getBlockingDependencies(feature, features);
}, [enableDependencyBlocking, feature, features]);
const isJustFinished = useMemo(() => {
if (
!feature.justFinishedAt ||
feature.status !== "waiting_approval" ||
feature.error
) {
return false;
}
const finishedTime = new Date(feature.justFinishedAt).getTime();
const twoMinutes = 2 * 60 * 1000;
return currentTime - finishedTime < twoMinutes;
}, [feature.justFinishedAt, feature.status, feature.error, currentTime]);
useEffect(() => {
if (!feature.justFinishedAt || feature.status !== "waiting_approval") {
return;
}
const finishedTime = new Date(feature.justFinishedAt).getTime();
const twoMinutes = 2 * 60 * 1000;
const timeRemaining = twoMinutes - (currentTime - finishedTime);
if (timeRemaining <= 0) {
return;
}
// eslint-disable-next-line no-undef
const interval = setInterval(() => {
setCurrentTime(Date.now());
}, 1000);
return () => {
// eslint-disable-next-line no-undef
clearInterval(interval);
};
}, [feature.justFinishedAt, feature.status, currentTime]);
// Status badges row (error, blocked, just finished)
// Status badges row (error, blocked)
const showStatusBadges =
feature.error ||
(blockingDependencies.length > 0 &&
!feature.error &&
!feature.skipTests &&
feature.status === "backlog") ||
isJustFinished;
feature.status === "backlog");
if (!showStatusBadges) {
return null;
@@ -117,13 +110,12 @@ export function CardBadges({ feature }: CardBadgesProps) {
<Lock className="w-3 h-3" />
</div>
</TooltipTrigger>
<TooltipContent
side="bottom"
className="text-xs max-w-[250px]"
>
<TooltipContent side="bottom" className="text-xs max-w-[250px]">
<p className="font-medium mb-1">
Blocked by {blockingDependencies.length} incomplete{" "}
{blockingDependencies.length === 1 ? "dependency" : "dependencies"}
{blockingDependencies.length === 1
? "dependency"
: "dependencies"}
</p>
<p className="text-muted-foreground">
{blockingDependencies
@@ -137,21 +129,6 @@ export function CardBadges({ feature }: CardBadgesProps) {
</Tooltip>
</TooltipProvider>
)}
{/* Just Finished badge */}
{isJustFinished && (
<div
className={cn(
"inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium",
"bg-[var(--status-success-bg)] border-[var(--status-success)]/40 text-[var(--status-success)]",
"animate-pulse"
)}
data-testid={`just-finished-badge-${feature.id}`}
title="Agent just finished working on this feature"
>
<Sparkles className="w-3 h-3" />
</div>
)}
</div>
);
}
@@ -161,11 +138,49 @@ interface PriorityBadgesProps {
}
export function PriorityBadges({ feature }: PriorityBadgesProps) {
const [currentTime, setCurrentTime] = useState(() => Date.now());
const isJustFinished = useMemo(() => {
if (
!feature.justFinishedAt ||
feature.status !== "waiting_approval" ||
feature.error
) {
return false;
}
const finishedTime = new Date(feature.justFinishedAt).getTime();
const twoMinutes = 2 * 60 * 1000;
return currentTime - finishedTime < twoMinutes;
}, [feature.justFinishedAt, feature.status, feature.error, currentTime]);
useEffect(() => {
if (!feature.justFinishedAt || feature.status !== "waiting_approval") {
return;
}
const finishedTime = new Date(feature.justFinishedAt).getTime();
const twoMinutes = 2 * 60 * 1000;
const timeRemaining = twoMinutes - (currentTime - finishedTime);
if (timeRemaining <= 0) {
return;
}
// eslint-disable-next-line no-undef
const interval = setInterval(() => {
setCurrentTime(Date.now());
}, 1000);
return () => {
// eslint-disable-next-line no-undef
clearInterval(interval);
};
}, [feature.justFinishedAt, feature.status, currentTime]);
const showPriorityBadges =
feature.priority ||
(feature.skipTests &&
!feature.error &&
feature.status === "backlog");
(feature.skipTests && !feature.error && feature.status === "backlog") ||
isJustFinished;
if (!showPriorityBadges) {
return null;
@@ -178,24 +193,32 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div
<CardBadge
className={cn(
"inline-flex items-center justify-center gap-1 rounded-full border-2 px-1.5 py-0.5 text-[10px] font-bold",
"bg-opacity-90 border rounded-[6px] px-1.5 py-0.5 flex items-center justify-center border-[1.5px] w-5 h-5", // badge style from example
feature.priority === 1 &&
"bg-red-500/20 text-red-500 border-red-500/50",
"bg-[var(--status-error-bg)] border-[var(--status-error)]/40 text-[var(--status-error)]",
feature.priority === 2 &&
"bg-yellow-500/20 text-yellow-500 border-yellow-500/50",
"bg-[var(--status-warning-bg)] border-[var(--status-warning)]/40 text-[var(--status-warning)]",
feature.priority === 3 &&
"bg-blue-500/20 text-blue-500 border-blue-500/50"
"bg-[var(--status-info-bg)] border-[var(--status-info)]/40 text-[var(--status-info)]"
)}
data-testid={`priority-badge-${feature.id}`}
>
{feature.priority === 1
? "H"
: feature.priority === 2
? "M"
: "L"}
</div>
{feature.priority === 1 ? (
<span className="font-bold text-xs flex items-center gap-0.5">
H
</span>
) : feature.priority === 2 ? (
<span className="font-bold text-xs flex items-center gap-0.5">
M
</span>
) : (
<span className="font-bold text-xs flex items-center gap-0.5">
L
</span>
)}
</CardBadge>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs">
<p>
@@ -210,29 +233,34 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
</TooltipProvider>
)}
{/* Manual verification badge */}
{feature.skipTests &&
!feature.error &&
feature.status === "backlog" && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
"inline-flex items-center gap-1 rounded-full border-2 px-1.5 py-0.5 text-[10px] font-bold",
"bg-[var(--status-warning-bg)] border-[var(--status-warning)]/50 text-[var(--status-warning)]"
)}
data-testid={`skip-tests-badge-${feature.id}`}
>
<Hand className="w-3 h-3" />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs">
<p>Manual verification required</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{feature.skipTests && !feature.error && feature.status === "backlog" && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<CardBadge
className="bg-[var(--status-warning-bg)] border-[var(--status-warning)]/40 text-[var(--status-warning)]"
data-testid={`skip-tests-badge-${feature.id}`}
>
<Hand className="w-3 h-3" />
</CardBadge>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs">
<p>Manual verification required</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{/* Just Finished badge */}
{isJustFinished && (
<CardBadge
className="bg-[var(--status-success-bg)] border-[var(--status-success)]/40 text-[var(--status-success)] animate-pulse"
data-testid={`just-finished-badge-${feature.id}`}
title="Agent just finished working on this feature"
>
<Sparkles className="w-3 h-3" />
</CardBadge>
)}
</div>
);
}

View File

@@ -21,7 +21,6 @@ interface KanbanCardProps {
onManualVerify?: () => void;
onMoveBackToInProgress?: () => void;
onFollowUp?: () => void;
onCommit?: () => void;
onImplement?: () => void;
onComplete?: () => void;
onViewPlan?: () => void;
@@ -48,7 +47,6 @@ export const KanbanCard = memo(function KanbanCard({
onManualVerify,
onMoveBackToInProgress: _onMoveBackToInProgress,
onFollowUp,
onCommit,
onImplement,
onComplete,
onViewPlan,
@@ -198,7 +196,6 @@ export const KanbanCard = memo(function KanbanCard({
onForceStop={onForceStop}
onManualVerify={onManualVerify}
onFollowUp={onFollowUp}
onCommit={onCommit}
onImplement={onImplement}
onComplete={onComplete}
onViewPlan={onViewPlan}

View File

@@ -194,7 +194,6 @@ export function KanbanBoard({
onMoveBackToInProgress(feature)
}
onFollowUp={() => onFollowUp(feature)}
onCommit={() => onCommit(feature)}
onComplete={() => onComplete(feature)}
onImplement={() => onImplement(feature)}
onViewPlan={() => onViewPlan(feature)}