diff --git a/app/api/courrier/account/route.ts b/app/api/courrier/account/route.ts index 37eae771..3f76545b 100644 --- a/app/api/courrier/account/route.ts +++ b/app/api/courrier/account/route.ts @@ -156,4 +156,85 @@ export async function POST(request: Request) { { status: 500 } ); } -} \ No newline at end of file +} + +export async function DELETE(request: Request) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + const { searchParams } = new URL(request.url); + const accountId = searchParams.get('accountId'); + if (!accountId) { + return NextResponse.json({ error: 'Missing accountId' }, { status: 400 }); + } + // Find the account + const account = await prisma.mailCredentials.findFirst({ + where: { id: accountId, userId: session.user.id }, + }); + if (!account) { + return NextResponse.json({ error: 'Account not found' }, { status: 404 }); + } + // Delete from database + await prisma.mailCredentials.delete({ where: { id: accountId } }); + // Invalidate cache + await invalidateFolderCache(session.user.id, account.email, '*'); + return NextResponse.json({ success: true, message: 'Account deleted' }); + } catch (error) { + console.error('Error deleting account:', error); + return NextResponse.json({ error: 'Failed to delete account', details: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 }); + } +} + +export async function PATCH(request: Request) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + const body = await request.json(); + const { accountId, newPassword } = body; + if (!accountId || !newPassword) { + return NextResponse.json({ error: 'Missing accountId or newPassword' }, { status: 400 }); + } + // Find the account + const account = await prisma.mailCredentials.findFirst({ + where: { id: accountId, userId: session.user.id }, + }); + if (!account) { + return NextResponse.json({ error: 'Account not found' }, { status: 404 }); + } + // Test new credentials + const testResult = await testEmailConnection({ + email: account.email, + password: newPassword, + host: account.host, + port: account.port, + secure: typeof (account as any).secure === 'boolean' ? (account as any).secure : true, + }); + if (!testResult.imap) { + return NextResponse.json({ error: `Connection test failed: ${testResult.error || 'Could not connect to IMAP server'}` }, { status: 400 }); + } + // Update password in database + await prisma.mailCredentials.update({ + where: { id: accountId }, + data: { password: newPassword }, + }); + // Invalidate cache + await invalidateFolderCache(session.user.id, account.email, '*'); + return NextResponse.json({ success: true, message: 'Password updated' }); + } catch (error) { + console.error('Error updating account password:', error); + return NextResponse.json({ error: 'Failed to update password', details: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 }); + } +} + +const handleAddAccount = async (accountData: AccountData) => { + // ... account creation logic ... + // setAccounts(prev => [...prev, newAccount]); + // setVisibleFolders(prev => ({ + // ...prev, + // [newAccount.id]: newAccount.folders + // })); +}; \ No newline at end of file diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 94fcf7bd..6b2dcda1 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -31,6 +31,7 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'; import { Checkbox } from '@/components/ui/checkbox'; import { Label } from '@/components/ui/label'; import { toast } from '@/components/ui/use-toast'; +import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@/components/ui/dropdown-menu'; // Import components import EmailSidebar from '@/components/email/EmailSidebar'; @@ -163,6 +164,15 @@ export default function CourrierPage() { // Track folder visibility per account const [visibleFolders, setVisibleFolders] = useState>({}); + // Add state for modals/dialogs + const [showEditModal, setShowEditModal] = useState(false); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [accountToEdit, setAccountToEdit] = useState(null); + const [accountToDelete, setAccountToDelete] = useState(null); + const [newPassword, setNewPassword] = useState(''); + const [editLoading, setEditLoading] = useState(false); + const [deleteLoading, setDeleteLoading] = useState(false); + // Debug accounts state useEffect(() => { console.log('Current accounts state:', accounts); @@ -1024,17 +1034,24 @@ export default function CourrierPage() {
{account.name} + {/* More options button */} + {account.id !== 'all-accounts' && ( + + + + + + { e.stopPropagation(); setAccountToEdit(account); setShowEditModal(true); }}>Edit + { e.stopPropagation(); setAccountToDelete(account); setShowDeleteDialog(true); }}>Delete + + + )} {account.id !== 'all-accounts' && ( { - e.stopPropagation(); - // Toggle this specific account's expanded state - setExpandedAccounts(prev => ({ - ...prev, - [account.id]: !prev[account.id] - })); - }} + className="ml-1 text-gray-400 hover:text-gray-600 cursor-pointer" + onClick={e => { e.stopPropagation(); setExpandedAccounts(prev => ({ ...prev, [account.id]: !prev[account.id] })); }} > {expandedAccounts[account.id] ? : } @@ -1259,6 +1276,52 @@ export default function CourrierPage() { /> + + {/* Edit Password Modal */} + { if (!open) setShowEditModal(false); }}> + + Edit Account Password +
{ + e.preventDefault(); + if (!accountToEdit) return; + setEditLoading(true); + try { + const res = await fetch('/api/courrier/account', { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ accountId: accountToEdit.id, newPassword }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Failed to update password'); + toast({ title: 'Password updated', description: 'Password changed successfully.' }); + setShowEditModal(false); + setNewPassword(''); + // Refresh accounts (re-fetch or reload page) + window.location.reload(); + } catch (err) { + toast({ title: 'Error', description: err instanceof Error ? err.message : 'Failed to update password', variant: 'destructive' }); + } finally { + setEditLoading(false); + } + }}> +
+ + setNewPassword(e.target.value)} required className="mt-1" /> +
+
+ +
+
+
+
+ + {/* Delete Account Dialog */} + { if (!open) setShowDeleteDialog(false); }}> + + + Delete Account + + Are you sure you want to delete this account? This action cannot be undone. ); } \ No newline at end of file