Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions frontend/src/components/ChatHistoryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
DropdownMenuTrigger
} from "@/components/ui/dropdown-menu";
import { RenameChatDialog } from "@/components/RenameChatDialog";
import { DeleteChatDialog } from "@/components/DeleteChatDialog";
import { useOpenAI } from "@/ai/useOpenAi";
import { useOpenSecret } from "@opensecret/react";
import { useRouter } from "@tanstack/react-router";
Expand Down Expand Up @@ -47,6 +48,7 @@ export function ChatHistoryList({
const queryClient = useQueryClient();
const localState = useContext(LocalStateContext);
const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [selectedChat, setSelectedChat] = useState<{ id: string; title: string } | null>(null);
const [isArchivedExpanded, setIsArchivedExpanded] = useState(false);

Expand Down Expand Up @@ -330,6 +332,17 @@ export function ChatHistoryList({
setIsRenameDialogOpen(true);
}, []);

const handleOpenDeleteDialog = useCallback((conv: Conversation) => {
const title = conv.metadata?.title || "Untitled Chat";
setSelectedChat({ id: conv.id, title });
setIsDeleteDialogOpen(true);
}, []);

const handleOpenDeleteDialogArchived = useCallback((chat: ArchivedChat) => {
setSelectedChat({ id: chat.id, title: chat.title });
setIsDeleteDialogOpen(true);
}, []);

// Handle conversation renaming via API
const handleRenameConversation = useCallback(
async (conversationId: string, newTitle: string) => {
Expand Down Expand Up @@ -467,7 +480,7 @@ export function ChatHistoryList({
<Pencil className="mr-2 h-4 w-4" />
<span>Rename Chat</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleDeleteConversation(conv.id)}>
<DropdownMenuItem onClick={() => handleOpenDeleteDialog(conv)}>
<Trash className="mr-2 h-4 w-4" />
<span>Delete Chat</span>
</DropdownMenuItem>
Expand Down Expand Up @@ -544,7 +557,7 @@ export function ChatHistoryList({
<Pencil className="mr-2 h-4 w-4" />
<span>Rename Chat</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleDeleteConversation(chat.id)}>
<DropdownMenuItem onClick={() => handleOpenDeleteDialogArchived(chat)}>
<Trash className="mr-2 h-4 w-4" />
<span>Delete Chat</span>
</DropdownMenuItem>
Expand All @@ -560,13 +573,21 @@ export function ChatHistoryList({
)}

{selectedChat && (
<RenameChatDialog
open={isRenameDialogOpen}
onOpenChange={setIsRenameDialogOpen}
chatId={selectedChat.id}
currentTitle={selectedChat.title}
onRename={handleRenameConversation}
/>
<>
<RenameChatDialog
open={isRenameDialogOpen}
onOpenChange={setIsRenameDialogOpen}
chatId={selectedChat.id}
currentTitle={selectedChat.title}
onRename={handleRenameConversation}
/>
<DeleteChatDialog
open={isDeleteDialogOpen}
onOpenChange={setIsDeleteDialogOpen}
chatTitle={selectedChat.title}
onConfirm={() => handleDeleteConversation(selectedChat.id)}
/>
</>
)}
</>
);
Expand Down
8 changes: 3 additions & 5 deletions frontend/src/components/ComparisonChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,11 @@ export function ComparisonChart() {
{/* Header Row */}
<div
className="grid border-b border-[hsl(var(--marketing-card-border))]"
style={{
style={{
gridTemplateColumns: "minmax(200px, 1.5fr) repeat(6, minmax(80px, 1fr))"
}}
>
<div className="p-3 font-medium text-foreground bg-[hsl(var(--marketing-card-highlight))]/30 text-sm">

</div>
<div className="p-3 font-medium text-foreground bg-[hsl(var(--marketing-card-highlight))]/30 text-sm"></div>
{products.map((product) => (
<div
key={product.key}
Expand All @@ -197,7 +195,7 @@ export function ComparisonChart() {
className={`grid border-b border-[hsl(var(--marketing-card-border))] ${
index % 2 === 0 ? "bg-[hsl(var(--marketing-card-highlight))]/20" : ""
}`}
style={{
style={{
gridTemplateColumns: "minmax(200px, 1.5fr) repeat(6, minmax(80px, 1fr))"
}}
>
Expand Down
52 changes: 52 additions & 0 deletions frontend/src/components/DeleteChatDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle
} from "@/components/ui/alert-dialog";

interface DeleteChatDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onConfirm: () => void;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

鈿狅笍 Potential issue | 馃煚 Major

Update type definition for async operation.

The onConfirm callback is typed as () => void, but deletion operations are typically asynchronous (API calls, database updates). This type mismatch prevents proper error handling and causes the dialog to close before the operation completes.

Apply this diff to fix the type definition:

 interface DeleteChatDialogProps {
   open: boolean;
   onOpenChange: (open: boolean) => void;
-  onConfirm: () => void;
+  onConfirm: () => Promise<void>;
   chatTitle: string;
 }
馃摑 Committable suggestion

鈥硷笍 IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onConfirm: () => void;
interface DeleteChatDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onConfirm: () => Promise<void>;
chatTitle: string;
}
馃 Prompt for AI Agents
In frontend/src/components/DeleteChatDialog.tsx around line 15, the onConfirm
prop is currently typed as () => void but should be async-aware; change its type
to () => Promise<void> so callers can return a promise and the component can
await completion. Update the component usage to await onConfirm before closing
the dialog and adjust any parent callers to return a Promise (make handlers
async and propagate errors) so the dialog only closes after the deletion
completes or an error is handled.

chatTitle: string;
}

export function DeleteChatDialog({
open,
onOpenChange,
onConfirm,
chatTitle
}: DeleteChatDialogProps) {
const handleConfirm = (e: React.MouseEvent) => {
e.preventDefault();
onConfirm();
onOpenChange(false);
};

return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure you want to delete this chat?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete "{chatTitle}". This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={handleConfirm}
className="bg-destructive text-white hover:bg-destructive/90"
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
Loading