agent-ecosystem/src/renderer/components/team/review/ReviewToolbar.tsx
iliya 1c6b7c4ef1 feat: implement soft delete and retrieval of deleted tasks in team management
- Added functionality for soft-deleting tasks, allowing tasks to be marked as deleted without permanent removal.
- Introduced new IPC channels for soft-deleting tasks and retrieving deleted tasks, enhancing task management capabilities.
- Updated TeamDataService and TeamTaskWriter to handle soft delete operations and maintain task integrity.
- Enhanced the UI components to support viewing and managing deleted tasks, improving user experience and task visibility.
- Implemented a TrashDialog for displaying deleted tasks, allowing users to review and manage their deleted items effectively.
2026-02-25 19:30:27 +02:00

194 lines
6.8 KiB
TypeScript

import React from 'react';
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
import { cn } from '@renderer/lib/utils';
import { Check, Eye, EyeOff, GitMerge, Loader2, Pencil, X } from 'lucide-react';
import type { ChangeStats } from '@shared/types';
interface ReviewToolbarProps {
stats: { pending: number; accepted: number; rejected: number };
changeStats: ChangeStats;
collapseUnchanged: boolean;
applying: boolean;
autoViewed: boolean;
instantApply?: boolean;
onAutoViewedChange: (auto: boolean) => void;
onAcceptAll: () => void;
onRejectAll: () => void;
onApply: () => void;
onCollapseUnchangedChange: (collapse: boolean) => void;
editedCount?: number;
}
export const ReviewToolbar = ({
stats,
changeStats,
collapseUnchanged: _collapseUnchanged,
applying,
autoViewed,
onAutoViewedChange,
onAcceptAll,
onRejectAll,
onApply,
onCollapseUnchangedChange: _onCollapseUnchangedChange,
instantApply = false,
editedCount = 0,
}: ReviewToolbarProps): React.ReactElement => {
const hasRejected = stats.rejected > 0;
const canApply = hasRejected && !applying;
const totalChanges = stats.pending + stats.accepted + stats.rejected;
const reviewedCount = stats.accepted + stats.rejected;
return (
<div className="flex items-center gap-3 border-b border-border bg-surface-sidebar px-4 py-2">
{/* Decision stats */}
<div className="flex items-center gap-2 text-xs">
{stats.pending > 0 && (
<span className="inline-flex items-center gap-1 rounded-full bg-zinc-500/20 px-2 py-0.5 text-zinc-400">
{stats.pending} pending
</span>
)}
{stats.accepted > 0 && (
<span className="inline-flex items-center gap-1 rounded-full bg-green-500/20 px-2 py-0.5 text-green-400">
<Check className="size-3" />
{stats.accepted} accepted
</span>
)}
{stats.rejected > 0 && (
<span className="inline-flex items-center gap-1 rounded-full bg-red-500/20 px-2 py-0.5 text-red-400">
<X className="size-3" />
{stats.rejected} rejected
</span>
)}
</div>
{/* Change stats */}
<div className="flex items-center gap-1 text-xs text-text-muted">
<span className="text-green-400">+{changeStats.linesAdded}</span>
<span className="text-red-400">-{changeStats.linesRemoved}</span>
<span className="ml-1">across {changeStats.filesChanged} files</span>
</div>
{/* Review progress */}
{totalChanges > 0 && (
<div className="flex items-center gap-2 text-xs">
<div className="h-1.5 w-20 overflow-hidden rounded-full bg-zinc-700/50">
<div
className="h-full rounded-full bg-blue-500/70 transition-all duration-300"
style={{ width: `${(reviewedCount / totalChanges) * 100}%` }}
/>
</div>
<span className="text-text-muted">
{reviewedCount}/{totalChanges}
</span>
</div>
)}
<div className="flex-1" />
{/* <Tooltip>
<TooltipTrigger asChild>
<button
onClick={() => onCollapseUnchangedChange(!collapseUnchanged)}
className={cn(
'flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors',
collapseUnchanged ? 'bg-surface-raised text-text' : 'text-text-muted hover:text-text'
)}
>
{collapseUnchanged ? (
<FoldVertical className="size-3.5" />
) : (
<UnfoldVertical className="size-3.5" />
)}
</button>
</TooltipTrigger>
<TooltipContent side="bottom">
{collapseUnchanged ? 'Show all lines' : 'Collapse unchanged regions'}
</TooltipContent>
</Tooltip> */}
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={() => onAutoViewedChange(!autoViewed)}
className={cn(
'flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors',
autoViewed ? 'bg-surface-raised text-text' : 'text-text-muted hover:text-text'
)}
>
{autoViewed ? <Eye className="size-3.5" /> : <EyeOff className="size-3.5" />}
<span className="text-[10px]">Auto</span>
</button>
</TooltipTrigger>
<TooltipContent side="bottom">
{autoViewed
? 'Auto-mark files as viewed when scrolled to end (ON)'
: 'Auto-mark files as viewed when scrolled to end (OFF)'}
</TooltipContent>
</Tooltip>
<div className="h-4 w-px bg-border" />
{editedCount > 0 && (
<span className="inline-flex items-center gap-1 rounded-full bg-amber-500/20 px-2 py-0.5 text-xs text-amber-400">
<Pencil className="size-3" /> {editedCount} edited
</span>
)}
{editedCount > 0 && <div className="h-4 w-px bg-border" />}
{/* Actions */}
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={onAcceptAll}
className="flex items-center gap-1 rounded bg-green-500/15 px-2.5 py-1 text-xs text-green-400 transition-colors hover:bg-green-500/25"
>
<Check className="size-3" />
Accept All
</button>
</TooltipTrigger>
<TooltipContent side="bottom">Accept all changes across all files</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={onRejectAll}
className="flex items-center gap-1 rounded bg-red-500/15 px-2.5 py-1 text-xs text-red-400 transition-colors hover:bg-red-500/25"
>
<X className="size-3" />
Reject All
</button>
</TooltipTrigger>
<TooltipContent side="bottom">Reject all changes across all files</TooltipContent>
</Tooltip>
{!instantApply && (
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={onApply}
disabled={!canApply}
className={cn(
'flex items-center gap-1 rounded px-3 py-1 text-xs font-medium transition-colors',
canApply
? 'bg-blue-500/20 text-blue-400 hover:bg-blue-500/30'
: 'cursor-not-allowed bg-zinc-500/10 text-zinc-600'
)}
>
{applying ? (
<Loader2 className="size-3 animate-spin" />
) : (
<GitMerge className="size-3" />
)}
{applying ? 'Applying...' : 'Apply All Changes'}
</button>
</TooltipTrigger>
<TooltipContent side="bottom">Apply review decisions across all files</TooltipContent>
</Tooltip>
)}
</div>
);
};