feat: Add ConfirmDialog component and integrate into GitHubIssuesView

- Introduced a new ConfirmDialog component for user confirmation prompts.
- Integrated ConfirmDialog into GitHubIssuesView to confirm re-validation of issues, enhancing user interaction and decision-making.
- Updated handleValidateIssue function to support re-validation options, improving flexibility in issue validation handling.
This commit is contained in:
Kacper
2025-12-24 01:53:40 +01:00
parent 1ff617703c
commit 7007a8aa66
2 changed files with 114 additions and 5 deletions

View File

@@ -0,0 +1,83 @@
import type { ReactNode } from 'react';
import { LucideIcon } from 'lucide-react';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { HotkeyButton } from '@/components/ui/hotkey-button';
interface ConfirmDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onConfirm: () => void;
title: string;
description: string;
/** Optional icon to show in the title */
icon?: LucideIcon;
/** Icon color class. Defaults to "text-primary" */
iconClassName?: string;
/** Optional content to show between description and buttons */
children?: ReactNode;
/** Text for the confirm button. Defaults to "Confirm" */
confirmText?: string;
/** Text for the cancel button. Defaults to "Cancel" */
cancelText?: string;
/** Variant for the confirm button. Defaults to "default" */
confirmVariant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
}
export function ConfirmDialog({
open,
onOpenChange,
onConfirm,
title,
description,
icon: Icon,
iconClassName = 'text-primary',
children,
confirmText = 'Confirm',
cancelText = 'Cancel',
confirmVariant = 'default',
}: ConfirmDialogProps) {
const handleConfirm = () => {
onConfirm();
onOpenChange(false);
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="bg-popover border-border max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
{Icon && <Icon className={`w-5 h-5 ${iconClassName}`} />}
{title}
</DialogTitle>
<DialogDescription className="text-muted-foreground">{description}</DialogDescription>
</DialogHeader>
{children}
<DialogFooter className="gap-2 sm:gap-2 pt-4">
<Button variant="ghost" onClick={() => onOpenChange(false)} className="px-4">
{cancelText}
</Button>
<HotkeyButton
variant={confirmVariant}
onClick={handleConfirm}
hotkey={{ key: 'Enter', cmdCtrl: true }}
hotkeyActive={open}
className="px-4"
>
{Icon && <Icon className="w-4 h-4 mr-2" />}
{confirmText}
</HotkeyButton>
</DialogFooter>
</DialogContent>
</Dialog>
);
}

View File

@@ -42,6 +42,7 @@ function getFeaturePriority(complexity: IssueComplexity | undefined): number {
import { useAppStore } from '@/store/app-store';
import { Button } from '@/components/ui/button';
import { Markdown } from '@/components/ui/markdown';
import { ConfirmDialog } from '@/components/ui/confirm-dialog';
import { cn } from '@/lib/utils';
import { toast } from 'sonner';
import { ValidationDialog } from './github-issues-view/validation-dialog';
@@ -60,6 +61,8 @@ export function GitHubIssuesView() {
const [cachedValidations, setCachedValidations] = useState<Map<number, StoredValidation>>(
new Map()
);
// Track revalidation confirmation dialog
const [showRevalidateConfirm, setShowRevalidateConfirm] = useState(false);
const audioRef = useRef<HTMLAudioElement | null>(null);
const { currentProject, validationModel, muteDoneSound } = useAppStore();
@@ -246,7 +249,12 @@ export function GitHubIssuesView() {
}, []);
const handleValidateIssue = useCallback(
async (issue: GitHubIssue, showDialog = true) => {
async (
issue: GitHubIssue,
options: { showDialog?: boolean; forceRevalidate?: boolean } = {}
) => {
const { showDialog = true, forceRevalidate = false } = options;
if (!currentProject?.path) {
toast.error('No project selected');
return;
@@ -258,9 +266,9 @@ export function GitHubIssuesView() {
return;
}
// Check for cached result - if fresh, show it directly
// Check for cached result - if fresh, show it directly (unless force revalidate)
const cached = cachedValidations.get(issue.number);
if (cached && showDialog) {
if (cached && showDialog && !forceRevalidate) {
// Check if validation is stale (older than 24 hours)
const validatedAt = new Date(cached.validatedAt);
const hoursSinceValidation = (Date.now() - validatedAt.getTime()) / (1000 * 60 * 60);
@@ -568,7 +576,7 @@ export function GitHubIssuesView() {
<Button
variant="ghost"
size="sm"
onClick={() => handleValidateIssue(selectedIssue)}
onClick={() => setShowRevalidateConfirm(true)}
title="Re-validate"
>
<RefreshCw className="h-4 w-4" />
@@ -591,7 +599,9 @@ export function GitHubIssuesView() {
<Button
variant="default"
size="sm"
onClick={() => handleValidateIssue(selectedIssue)}
onClick={() =>
handleValidateIssue(selectedIssue, { forceRevalidate: true })
}
>
<Wand2 className="h-4 w-4 mr-1" />
Re-validate
@@ -766,6 +776,22 @@ export function GitHubIssuesView() {
isValidating={selectedIssue ? validatingIssues.has(selectedIssue.number) : false}
onConvertToTask={handleConvertToTask}
/>
{/* Revalidate Confirmation Dialog */}
<ConfirmDialog
open={showRevalidateConfirm}
onOpenChange={setShowRevalidateConfirm}
title="Re-validate Issue"
description={`Are you sure you want to re-validate issue #${selectedIssue?.number}? This will run a new AI analysis and replace the existing validation result.`}
icon={RefreshCw}
iconClassName="text-primary"
confirmText="Re-validate"
onConfirm={() => {
if (selectedIssue) {
handleValidateIssue(selectedIssue, { forceRevalidate: true });
}
}}
/>
</div>
);
}