mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
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:
83
apps/ui/src/components/ui/confirm-dialog.tsx
Normal file
83
apps/ui/src/components/ui/confirm-dialog.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user