Skip to content

Commit a220db5

Browse files
authored
Minor session improvements (#356)
* redirect to new chat session when created * delete modal for deleting chat sessions
1 parent 5682bfb commit a220db5

File tree

6 files changed

+197
-81
lines changed

6 files changed

+197
-81
lines changed

src/app/(logged-in)/projects/components/delete-project-dialog.tsx

Lines changed: 73 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
'use client';
22

3-
import { AlertCircle, Loader2, Trash } from 'lucide-react';
3+
import { AlertCircle, AlertTriangle, Loader2, Trash } from 'lucide-react';
44
import { useEffect, useRef, useState } from 'react';
55

66
import { Button } from '@/components/ui/button';
77
import { Checkbox } from '@/components/ui/checkbox';
88
import {
99
Dialog,
1010
DialogContent,
11-
DialogDescription,
1211
DialogFooter,
1312
DialogHeader,
1413
DialogTitle,
@@ -124,15 +123,17 @@ export default function DeleteProjectDialog({
124123
}
125124
};
126125

127-
const isRepoConfirmationValid =
128-
!deleteRepo || repoConfirmationText === project.name;
126+
const isRepoConfirmationValid = !deleteRepo || repoConfirmationText === project.name;
129127

130128
return (
131-
<Dialog open={open} onOpenChange={(value) => {
132-
// Prevent closing while operation is in progress
133-
if (isPending || isCompleting) return;
134-
onOpenChange(value);
135-
}}>
129+
<Dialog
130+
open={open}
131+
onOpenChange={value => {
132+
// Prevent closing while operation is in progress
133+
if (isPending || isCompleting) return;
134+
onOpenChange(value);
135+
}}
136+
>
136137
<DialogContent className="sm:max-w-[425px] border border-border bg-card">
137138
<DialogHeader>
138139
<div className="flex items-center gap-2">
@@ -142,63 +143,74 @@ export default function DeleteProjectDialog({
142143
</DialogHeader>
143144

144145
<div className="space-y-4">
145-
<DialogDescription>
146-
{!isPending && !isSuccess ? (
147-
<>Are you sure you want to delete &quot;{project.name}&quot;? This action cannot be undone and all project files will be permanently removed.</>
148-
) : (
149-
<>Deleting project files. This may take a moment, please don&apos;t close this window.</>
150-
)}
151-
</DialogDescription>
152-
153-
{!isPending && !isSuccess && (
146+
{!isPending && !isSuccess ? (
154147
<>
148+
<p className="text-sm text-foreground">
149+
Are you sure you want to delete project{' '}
150+
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-sm">
151+
{project.name}
152+
</code>
153+
?
154+
</p>
155+
156+
<div className="flex gap-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20 p-3">
157+
<AlertTriangle className="h-5 w-5 text-yellow-600 dark:text-yellow-500 shrink-0 mt-0.5" />
158+
<p className="text-sm text-muted-foreground">
159+
This action cannot be undone. All project files will be permanently removed.
160+
</p>
161+
</div>
162+
155163
{!project.isImported && (
156-
<div className="space-y-3">
157-
<div className="flex items-start gap-3">
158-
<Checkbox
159-
id="delete-repo"
160-
checked={deleteRepo}
161-
onCheckedChange={v => {
162-
setDeleteRepo(Boolean(v));
163-
if (!v) setRepoConfirmationText('');
164-
}}
165-
className="mt-1"
166-
/>
167-
<Label
168-
htmlFor="delete-repo"
169-
className="text-sm font-medium cursor-pointer"
170-
>
171-
Also delete GitHub repository
172-
</Label>
173-
</div>
164+
<div className="space-y-3 pt-2">
165+
<div className="flex items-start gap-3">
166+
<Checkbox
167+
id="delete-repo"
168+
checked={deleteRepo}
169+
onCheckedChange={v => {
170+
setDeleteRepo(Boolean(v));
171+
if (!v) setRepoConfirmationText('');
172+
}}
173+
className="mt-1"
174+
/>
175+
<Label htmlFor="delete-repo" className="text-sm font-medium cursor-pointer">
176+
Also delete GitHub repository
177+
</Label>
178+
</div>
174179

175-
{deleteRepo && (
176-
<div className="ml-6 space-y-3">
177-
<div className="bg-destructive/5 rounded p-3 border border-destructive/20">
178-
<p className="text-xs text-destructive flex items-start gap-1.5">
179-
<AlertCircle className="h-3.5 w-3.5 shrink-0 mt-0.5" />
180-
<span>The GitHub repository will also be permanently deleted and cannot be undone.</span>
181-
</p>
182-
</div>
183-
184-
<div className="space-y-2">
185-
<Label htmlFor="repo-confirm" className="text-xs font-medium">
186-
Type project name to confirm
187-
</Label>
188-
<Input
189-
id="repo-confirm"
190-
placeholder={project.name}
191-
value={repoConfirmationText}
192-
onChange={(e) => setRepoConfirmationText(e.target.value)}
193-
className="text-sm"
194-
autoFocus
195-
/>
196-
</div>
180+
{deleteRepo && (
181+
<div className="ml-6 space-y-3">
182+
<div className="bg-destructive/5 rounded-lg p-3 border border-destructive/20">
183+
<p className="text-xs text-destructive flex items-start gap-1.5">
184+
<AlertCircle className="h-3.5 w-3.5 shrink-0 mt-0.5" />
185+
<span>
186+
The GitHub repository will also be permanently deleted and cannot be
187+
undone.
188+
</span>
189+
</p>
197190
</div>
198-
)}
199-
</div>
200-
)}
191+
192+
<div className="space-y-2">
193+
<Label htmlFor="repo-confirm" className="text-xs font-medium">
194+
Type project name to confirm
195+
</Label>
196+
<Input
197+
id="repo-confirm"
198+
placeholder={project.name}
199+
value={repoConfirmationText}
200+
onChange={e => setRepoConfirmationText(e.target.value)}
201+
className="text-sm"
202+
autoFocus
203+
/>
204+
</div>
205+
</div>
206+
)}
207+
</div>
208+
)}
201209
</>
210+
) : (
211+
<p className="text-sm text-muted-foreground">
212+
Deleting project files. This may take a moment, please don&apos;t close this window.
213+
</p>
202214
)}
203215

204216
{(isPending || isSuccess) && (

src/app/(project-workspace)/projects/[id]/components/chat/chat-sidebar.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ScrollArea } from '@/components/ui/scroll-area';
1818
import type { ChatSessionStatus } from '@/lib/types/chat-sessions';
1919
import { cn } from '@/lib/utils';
2020
import { ChatSessionItem } from './chat-session-item';
21+
import { DeleteChatSessionDialog } from './delete-chat-session-dialog';
2122
import { NewChatDialog } from './new-chat-dialog';
2223
import { RenameSessionDialog } from './rename-session-dialog';
2324

@@ -46,21 +47,25 @@ export default function ChatSidebar({
4647
statusFilter,
4748
isNewChatModalOpen,
4849
editingSession,
50+
deletingSession,
4951
newChatTitle,
5052

5153
// Actions
5254
setIsNewChatModalOpen,
5355
setEditingSession,
56+
setDeletingSession,
5457
setNewChatTitle,
5558
setStatusFilter,
5659
handleCreateChat,
5760
handleUpdateSession,
5861
handleDeleteSession,
62+
confirmDeleteSession,
5963
handleDuplicateSession,
6064
handleViewGitHubBranch,
6165

6266
// Loading states
6367
isCreating,
68+
isDeleting,
6469
} = useChatSidebar({
6570
projectId,
6671
onChatSessionChange,
@@ -159,6 +164,14 @@ export default function ChatSidebar({
159164
onOpenChange={open => !open && setEditingSession(null)}
160165
onRename={(session, title) => handleUpdateSession(session, { title })}
161166
/>
167+
168+
<DeleteChatSessionDialog
169+
session={deletingSession}
170+
open={!!deletingSession}
171+
onOpenChange={open => !open && setDeletingSession(null)}
172+
onConfirm={confirmDeleteSession}
173+
isDeleting={isDeleting}
174+
/>
162175
</div>
163176
);
164177
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use client';
2+
3+
import { AlertTriangle, Loader2, Trash2 } from 'lucide-react';
4+
5+
import { Button } from '@/components/ui/button';
6+
import {
7+
Dialog,
8+
DialogContent,
9+
DialogFooter,
10+
DialogHeader,
11+
DialogTitle,
12+
} from '@/components/ui/dialog';
13+
import type { ChatSession } from '@/lib/types';
14+
15+
interface DeleteChatSessionDialogProps {
16+
session: ChatSession | null;
17+
open: boolean;
18+
onOpenChange: (open: boolean) => void;
19+
onConfirm: (session: ChatSession) => void | Promise<void>;
20+
isDeleting: boolean;
21+
}
22+
23+
export function DeleteChatSessionDialog({
24+
session,
25+
open,
26+
onOpenChange,
27+
onConfirm,
28+
isDeleting,
29+
}: DeleteChatSessionDialogProps) {
30+
const handleDelete = async () => {
31+
if (!session) return;
32+
await onConfirm(session);
33+
};
34+
35+
return (
36+
<Dialog
37+
open={open}
38+
onOpenChange={value => {
39+
// Prevent closing while deletion is in progress
40+
if (isDeleting) return;
41+
onOpenChange(value);
42+
}}
43+
>
44+
<DialogContent className="sm:max-w-[425px] border border-border bg-card">
45+
<DialogHeader>
46+
<div className="flex items-center gap-2">
47+
<Trash2 className="h-5 w-5 text-destructive" />
48+
<DialogTitle>Delete Chat Session</DialogTitle>
49+
</div>
50+
</DialogHeader>
51+
52+
<div className="space-y-4">
53+
<p className="text-sm text-foreground">
54+
Are you sure you want to delete chat session{' '}
55+
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-sm">
56+
{session?.title}
57+
</code>
58+
?
59+
</p>
60+
61+
<div className="flex gap-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20 p-3">
62+
<AlertTriangle className="h-5 w-5 text-yellow-600 dark:text-yellow-500 shrink-0 mt-0.5" />
63+
<p className="text-sm text-muted-foreground">
64+
This action cannot be undone. All messages in this session will be permanently
65+
removed.
66+
</p>
67+
</div>
68+
</div>
69+
70+
<DialogFooter className="mt-6">
71+
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isDeleting}>
72+
Cancel
73+
</Button>
74+
<Button variant="destructive" onClick={handleDelete} disabled={isDeleting}>
75+
{isDeleting ? (
76+
<>
77+
<Loader2 className="h-4 w-4 animate-spin mr-2" />
78+
<span>Deleting...</span>
79+
</>
80+
) : (
81+
'Delete Session'
82+
)}
83+
</Button>
84+
</DialogFooter>
85+
</DialogContent>
86+
</Dialog>
87+
);
88+
}

src/app/(project-workspace)/projects/[id]/page.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,10 @@ export default function ProjectPage({ params }: ProjectPageProps) {
174174

175175
// Handle session selection and URL updates
176176
const handleSessionSelect = (sessionId: string) => {
177-
const session = sessions.find(s => s.id === sessionId);
178-
if (session) {
179-
setActiveChatSessionId(sessionId);
180-
setShowSidebar(false); // Switch to chat interface
181-
// Update URL to reflect selected session using query params
182-
router.push(`/projects/${projectId}?session=${session.id}`, { scroll: false });
183-
}
177+
setActiveChatSessionId(sessionId);
178+
setShowSidebar(false); // Switch to chat interface
179+
// Update URL to reflect selected session using query params
180+
router.push(`/projects/${projectId}?session=${sessionId}`, { scroll: false });
184181
};
185182

186183
// Get current session information

src/hooks/use-chat-sidebar.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export function useChatSidebar({
2121
// State
2222
const [isNewChatModalOpen, setIsNewChatModalOpen] = useState(false);
2323
const [editingSession, setEditingSession] = useState<ChatSession | null>(null);
24+
const [deletingSession, setDeletingSession] = useState<ChatSession | null>(null);
2425
const [newChatTitle, setNewChatTitle] = useState('');
2526
const [statusFilter, setStatusFilter] = useState<ChatSessionStatus[]>(['active']);
2627

@@ -81,7 +82,7 @@ export function useChatSidebar({
8182
setNewChatTitle('');
8283
setIsNewChatModalOpen(false);
8384

84-
// Redirect/select the newly created session in parent (updates URL ?session=...)
85+
// Redirect/select the newly created session in parent
8586
if (onChatSessionChange) {
8687
onChatSessionChange(newSession.session.id);
8788
}
@@ -103,18 +104,17 @@ export function useChatSidebar({
103104
[updateChatSession]
104105
);
105106

106-
// Handle session deletion
107-
const handleDeleteSession = useCallback(
108-
async (session: ChatSession) => {
109-
if (session.isDefault) return; // Prevent deletion of default session
110-
111-
const confirmed = window.confirm(
112-
`Are you sure you want to delete "${session.title}"? This action cannot be undone.`
113-
);
107+
// Handle session deletion - opens the confirmation dialog
108+
const handleDeleteSession = useCallback((session: ChatSession) => {
109+
if (session.isDefault) return; // Prevent deletion of default session
110+
setDeletingSession(session);
111+
}, []);
114112

115-
if (confirmed) {
116-
await deleteChatSession.mutateAsync(session.id);
117-
}
113+
// Confirm and execute session deletion
114+
const confirmDeleteSession = useCallback(
115+
async (session: ChatSession) => {
116+
await deleteChatSession.mutateAsync(session.id);
117+
setDeletingSession(null);
118118
},
119119
[deleteChatSession]
120120
);
@@ -150,16 +150,19 @@ export function useChatSidebar({
150150
statusFilter,
151151
isNewChatModalOpen,
152152
editingSession,
153+
deletingSession,
153154
newChatTitle,
154155

155156
// Actions
156157
setIsNewChatModalOpen,
157158
setEditingSession,
159+
setDeletingSession,
158160
setNewChatTitle,
159161
setStatusFilter,
160162
handleCreateChat,
161163
handleUpdateSession,
162164
handleDeleteSession,
165+
confirmDeleteSession,
163166
handleDuplicateSession,
164167
handleViewGitHubBranch,
165168

0 commit comments

Comments
 (0)