mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-05 09:33:07 +00:00
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:
@@ -6,7 +6,6 @@ import {
|
|||||||
RotateCcw,
|
RotateCcw,
|
||||||
StopCircle,
|
StopCircle,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
GitCommit,
|
|
||||||
FileText,
|
FileText,
|
||||||
Eye,
|
Eye,
|
||||||
Wand2,
|
Wand2,
|
||||||
@@ -25,7 +24,6 @@ interface CardActionsProps {
|
|||||||
onForceStop?: () => void;
|
onForceStop?: () => void;
|
||||||
onManualVerify?: () => void;
|
onManualVerify?: () => void;
|
||||||
onFollowUp?: () => void;
|
onFollowUp?: () => void;
|
||||||
onCommit?: () => void;
|
|
||||||
onImplement?: () => void;
|
onImplement?: () => void;
|
||||||
onComplete?: () => void;
|
onComplete?: () => void;
|
||||||
onViewPlan?: () => void;
|
onViewPlan?: () => void;
|
||||||
@@ -44,7 +42,6 @@ export function CardActions({
|
|||||||
onForceStop,
|
onForceStop,
|
||||||
onManualVerify,
|
onManualVerify,
|
||||||
onFollowUp,
|
onFollowUp,
|
||||||
onCommit,
|
|
||||||
onImplement,
|
onImplement,
|
||||||
onComplete,
|
onComplete,
|
||||||
onViewPlan,
|
onViewPlan,
|
||||||
@@ -251,7 +248,7 @@ export function CardActions({
|
|||||||
<span className="truncate">Refine</span>
|
<span className="truncate">Refine</span>
|
||||||
</Button>
|
</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 ? (
|
{feature.prUrl && onManualVerify ? (
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
@@ -267,20 +264,20 @@ export function CardActions({
|
|||||||
<CheckCircle2 className="w-3 h-3 mr-1" />
|
<CheckCircle2 className="w-3 h-3 mr-1" />
|
||||||
Verify
|
Verify
|
||||||
</Button>
|
</Button>
|
||||||
) : onCommit ? (
|
) : onManualVerify ? (
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="flex-1 h-7 text-[11px]"
|
className="flex-1 h-7 text-[11px]"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onCommit();
|
onManualVerify();
|
||||||
}}
|
}}
|
||||||
onPointerDown={(e) => e.stopPropagation()}
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
data-testid={`commit-${feature.id}`}
|
data-testid={`mark-as-verified-${feature.id}`}
|
||||||
>
|
>
|
||||||
<GitCommit className="w-3 h-3 mr-1" />
|
<CheckCircle2 className="w-3 h-3 mr-1" />
|
||||||
Commit
|
Mark as Verified
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
@@ -338,4 +335,3 @@ export function CardActions({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,16 +7,46 @@ import {
|
|||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} 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";
|
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 {
|
interface CardBadgesProps {
|
||||||
feature: Feature;
|
feature: Feature;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CardBadges({ feature }: CardBadgesProps) {
|
export function CardBadges({ feature }: CardBadgesProps) {
|
||||||
const { enableDependencyBlocking, features } = useAppStore();
|
const { enableDependencyBlocking, features } = useAppStore();
|
||||||
const [currentTime, setCurrentTime] = useState(() => Date.now());
|
|
||||||
|
|
||||||
// Calculate blocking dependencies (if feature is in backlog and has incomplete dependencies)
|
// Calculate blocking dependencies (if feature is in backlog and has incomplete dependencies)
|
||||||
const blockingDependencies = useMemo(() => {
|
const blockingDependencies = useMemo(() => {
|
||||||
@@ -26,50 +56,13 @@ export function CardBadges({ feature }: CardBadgesProps) {
|
|||||||
return getBlockingDependencies(feature, features);
|
return getBlockingDependencies(feature, features);
|
||||||
}, [enableDependencyBlocking, feature, features]);
|
}, [enableDependencyBlocking, feature, features]);
|
||||||
|
|
||||||
const isJustFinished = useMemo(() => {
|
// Status badges row (error, blocked)
|
||||||
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)
|
|
||||||
const showStatusBadges =
|
const showStatusBadges =
|
||||||
feature.error ||
|
feature.error ||
|
||||||
(blockingDependencies.length > 0 &&
|
(blockingDependencies.length > 0 &&
|
||||||
!feature.error &&
|
!feature.error &&
|
||||||
!feature.skipTests &&
|
!feature.skipTests &&
|
||||||
feature.status === "backlog") ||
|
feature.status === "backlog");
|
||||||
isJustFinished;
|
|
||||||
|
|
||||||
if (!showStatusBadges) {
|
if (!showStatusBadges) {
|
||||||
return null;
|
return null;
|
||||||
@@ -117,13 +110,12 @@ export function CardBadges({ feature }: CardBadgesProps) {
|
|||||||
<Lock className="w-3 h-3" />
|
<Lock className="w-3 h-3" />
|
||||||
</div>
|
</div>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent
|
<TooltipContent side="bottom" className="text-xs max-w-[250px]">
|
||||||
side="bottom"
|
|
||||||
className="text-xs max-w-[250px]"
|
|
||||||
>
|
|
||||||
<p className="font-medium mb-1">
|
<p className="font-medium mb-1">
|
||||||
Blocked by {blockingDependencies.length} incomplete{" "}
|
Blocked by {blockingDependencies.length} incomplete{" "}
|
||||||
{blockingDependencies.length === 1 ? "dependency" : "dependencies"}
|
{blockingDependencies.length === 1
|
||||||
|
? "dependency"
|
||||||
|
: "dependencies"}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
{blockingDependencies
|
{blockingDependencies
|
||||||
@@ -137,21 +129,6 @@ export function CardBadges({ feature }: CardBadgesProps) {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -161,11 +138,49 @@ interface PriorityBadgesProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PriorityBadges({ feature }: 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 =
|
const showPriorityBadges =
|
||||||
feature.priority ||
|
feature.priority ||
|
||||||
(feature.skipTests &&
|
(feature.skipTests && !feature.error && feature.status === "backlog") ||
|
||||||
!feature.error &&
|
isJustFinished;
|
||||||
feature.status === "backlog");
|
|
||||||
|
|
||||||
if (!showPriorityBadges) {
|
if (!showPriorityBadges) {
|
||||||
return null;
|
return null;
|
||||||
@@ -178,24 +193,32 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
|
|||||||
<TooltipProvider delayDuration={200}>
|
<TooltipProvider delayDuration={200}>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div
|
<CardBadge
|
||||||
className={cn(
|
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 &&
|
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 &&
|
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 &&
|
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}`}
|
data-testid={`priority-badge-${feature.id}`}
|
||||||
>
|
>
|
||||||
{feature.priority === 1
|
{feature.priority === 1 ? (
|
||||||
? "H"
|
<span className="font-bold text-xs flex items-center gap-0.5">
|
||||||
: feature.priority === 2
|
H
|
||||||
? "M"
|
</span>
|
||||||
: "L"}
|
) : feature.priority === 2 ? (
|
||||||
</div>
|
<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>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="bottom" className="text-xs">
|
<TooltipContent side="bottom" className="text-xs">
|
||||||
<p>
|
<p>
|
||||||
@@ -210,21 +233,16 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
|
|||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
)}
|
)}
|
||||||
{/* Manual verification badge */}
|
{/* Manual verification badge */}
|
||||||
{feature.skipTests &&
|
{feature.skipTests && !feature.error && feature.status === "backlog" && (
|
||||||
!feature.error &&
|
|
||||||
feature.status === "backlog" && (
|
|
||||||
<TooltipProvider delayDuration={200}>
|
<TooltipProvider delayDuration={200}>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div
|
<CardBadge
|
||||||
className={cn(
|
className="bg-[var(--status-warning-bg)] border-[var(--status-warning)]/40 text-[var(--status-warning)]"
|
||||||
"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}`}
|
data-testid={`skip-tests-badge-${feature.id}`}
|
||||||
>
|
>
|
||||||
<Hand className="w-3 h-3" />
|
<Hand className="w-3 h-3" />
|
||||||
</div>
|
</CardBadge>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="bottom" className="text-xs">
|
<TooltipContent side="bottom" className="text-xs">
|
||||||
<p>Manual verification required</p>
|
<p>Manual verification required</p>
|
||||||
@@ -232,7 +250,17 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ interface KanbanCardProps {
|
|||||||
onManualVerify?: () => void;
|
onManualVerify?: () => void;
|
||||||
onMoveBackToInProgress?: () => void;
|
onMoveBackToInProgress?: () => void;
|
||||||
onFollowUp?: () => void;
|
onFollowUp?: () => void;
|
||||||
onCommit?: () => void;
|
|
||||||
onImplement?: () => void;
|
onImplement?: () => void;
|
||||||
onComplete?: () => void;
|
onComplete?: () => void;
|
||||||
onViewPlan?: () => void;
|
onViewPlan?: () => void;
|
||||||
@@ -48,7 +47,6 @@ export const KanbanCard = memo(function KanbanCard({
|
|||||||
onManualVerify,
|
onManualVerify,
|
||||||
onMoveBackToInProgress: _onMoveBackToInProgress,
|
onMoveBackToInProgress: _onMoveBackToInProgress,
|
||||||
onFollowUp,
|
onFollowUp,
|
||||||
onCommit,
|
|
||||||
onImplement,
|
onImplement,
|
||||||
onComplete,
|
onComplete,
|
||||||
onViewPlan,
|
onViewPlan,
|
||||||
@@ -198,7 +196,6 @@ export const KanbanCard = memo(function KanbanCard({
|
|||||||
onForceStop={onForceStop}
|
onForceStop={onForceStop}
|
||||||
onManualVerify={onManualVerify}
|
onManualVerify={onManualVerify}
|
||||||
onFollowUp={onFollowUp}
|
onFollowUp={onFollowUp}
|
||||||
onCommit={onCommit}
|
|
||||||
onImplement={onImplement}
|
onImplement={onImplement}
|
||||||
onComplete={onComplete}
|
onComplete={onComplete}
|
||||||
onViewPlan={onViewPlan}
|
onViewPlan={onViewPlan}
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ export function KanbanBoard({
|
|||||||
onMoveBackToInProgress(feature)
|
onMoveBackToInProgress(feature)
|
||||||
}
|
}
|
||||||
onFollowUp={() => onFollowUp(feature)}
|
onFollowUp={() => onFollowUp(feature)}
|
||||||
onCommit={() => onCommit(feature)}
|
|
||||||
onComplete={() => onComplete(feature)}
|
onComplete={() => onComplete(feature)}
|
||||||
onImplement={() => onImplement(feature)}
|
onImplement={() => onImplement(feature)}
|
||||||
onViewPlan={() => onViewPlan(feature)}
|
onViewPlan={() => onViewPlan(feature)}
|
||||||
|
|||||||
Reference in New Issue
Block a user