feat: add account toggle, message dedup tracking, SMTP per-user and Gmail OAuth flow plan

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Agent-Logs-Url: https://github.com/christianlouis/pop_puller_to_gmail/sessions/b77800ca-b452-4427-b0bf-63403e906916
This commit is contained in:
copilot-swe-agent[bot]
2026-03-25 11:20:22 +00:00
parent a1d258de01
commit a15216c630
6 changed files with 245 additions and 48 deletions
+39 -4
View File
@@ -4,7 +4,7 @@ import { AuthGuard } from '@/components/AuthGuard';
import { DashboardLayout } from '@/components/DashboardLayout';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { mailAccountsApi, MailAccount } from '@/lib/api';
import { Plus, Edit2, Trash2, CheckCircle, XCircle, AlertTriangle } from 'lucide-react';
import { Plus, Edit2, Trash2, CheckCircle, XCircle, AlertTriangle, Power } from 'lucide-react';
import { useState } from 'react';
import { AddMailAccountModal } from '@/components/AddMailAccountModal';
@@ -25,6 +25,13 @@ export default function AccountsPage() {
},
});
const toggleMutation = useMutation({
mutationFn: mailAccountsApi.toggle,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['mail-accounts'] });
},
});
const handleEdit = (account: MailAccount) => {
setEditingAccount(account);
setIsModalOpen(true);
@@ -40,6 +47,14 @@ export default function AccountsPage() {
}
};
const handleToggle = async (id: number) => {
try {
await toggleMutation.mutateAsync(id);
} catch {
alert('Failed to update account');
}
};
const handleCloseModal = () => {
setIsModalOpen(false);
setEditingAccount(null);
@@ -69,7 +84,9 @@ export default function AccountsPage() {
{accounts.map((account) => (
<div
key={account.id}
className="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden"
className={`bg-white rounded-lg shadow-md border overflow-hidden transition-opacity ${
account.is_enabled ? 'border-gray-200' : 'border-gray-200 opacity-60'
}`}
>
<div className="p-6">
<div className="flex items-start justify-between mb-4">
@@ -81,9 +98,15 @@ export default function AccountsPage() {
</div>
<div className="flex items-center gap-2">
{account.is_enabled ? (
<CheckCircle className="h-5 w-5 text-green-500" aria-label="Enabled" />
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-700">
<CheckCircle className="h-3 w-3" />
Enabled
</span>
) : (
<XCircle className="h-5 w-5 text-gray-400" aria-label="Disabled" />
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-500">
<XCircle className="h-3 w-3" />
Disabled
</span>
)}
</div>
</div>
@@ -129,6 +152,18 @@ export default function AccountsPage() {
)}
<div className="flex items-center gap-2 pt-4 border-t border-gray-200">
<button
onClick={() => handleToggle(account.id)}
disabled={toggleMutation.isPending}
title={account.is_enabled ? 'Disable account' : 'Enable account'}
className={`flex items-center justify-center px-3 py-2 text-sm font-medium rounded-md transition-colors disabled:opacity-50 ${
account.is_enabled
? 'text-yellow-600 bg-yellow-50 hover:bg-yellow-100'
: 'text-green-600 bg-green-50 hover:bg-green-100'
}`}
>
<Power className="h-4 w-4" />
</button>
<button
onClick={() => handleEdit(account)}
className="flex-1 flex items-center justify-center px-3 py-2 text-sm font-medium text-blue-600 bg-blue-50 rounded-md hover:bg-blue-100 transition-colors"
+5
View File
@@ -204,6 +204,11 @@ export const mailAccountsApi = {
return response.data;
},
async toggle(id: number): Promise<MailAccount> {
const response = await api.patch<MailAccount>(`/mail-accounts/${id}/toggle`);
return response.data;
},
async delete(id: number): Promise<void> {
await api.delete(`/mail-accounts/${id}`);
},