From fc7e09d4d6efb408b4c42829651cc1a4be0472d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:02:52 +0000 Subject: [PATCH] Add Pull Now button and provider logos to accounts page Agent-Logs-Url: https://github.com/christianlouis/InboxConverge/sessions/0201089a-598d-4ab0-a5a3-8d2ae1120c9e Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- CHANGELOG.md | 4 + backend/app/api/v1/endpoints/mail_accounts.py | 31 ++++++++ docs/TODO.md | 2 + frontend/package-lock.json | 4 +- frontend/src/app/accounts/page.tsx | 74 +++++++++++++++++-- frontend/src/lib/api.ts | 5 ++ 6 files changed, 111 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6afab76..b72544b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- **Pull Now**: Added a "Pull Now" button (⟳) to each mail account card on the Accounts page. Clicking it immediately queues a Celery `process_mail_account` task for that account via the new `POST /mail-accounts/{id}/pull-now` backend endpoint. The button shows a spinner while the request is in flight and is disabled for inactive accounts. +- **Provider logos**: Provider logos (Gmail, GMX, WEB.DE, Outlook, Yahoo, AOL, T-Online, IONOS, Freenet, Posteo, iCloud) are now displayed next to the account name on the Accounts page, using the existing SVG assets in `/public/providers/`. + ### Changed - **Mailbox Activity view**: The user-facing "Logs" page has been redesigned to a mailbox-centric layout (renamed "Mailbox Activity"). Each mail account is shown as a card with its last check diff --git a/backend/app/api/v1/endpoints/mail_accounts.py b/backend/app/api/v1/endpoints/mail_accounts.py index a846041..efe7f0b 100644 --- a/backend/app/api/v1/endpoints/mail_accounts.py +++ b/backend/app/api/v1/endpoints/mail_accounts.py @@ -17,6 +17,7 @@ from app.models.database_models import ( AccountStatus, SubscriptionPlan, ) +from app.workers.tasks import process_mail_account as process_mail_account_task from app.models.schemas import ( MailAccountCreate, MailAccountResponse, @@ -241,6 +242,36 @@ async def toggle_mail_account( return account +@router.post("/{account_id}/pull-now", status_code=status.HTTP_202_ACCEPTED) +async def pull_now( + account_id: int, + current_user: User = Depends(get_current_active_user), + db: AsyncSession = Depends(get_db), +): + """Immediately queue a pull for the given mail account""" + result = await db.execute( + select(MailAccount).where( + MailAccount.id == account_id, MailAccount.user_id == current_user.id + ) + ) + account = result.scalar_one_or_none() + + if not account: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Mail account not found" + ) + + if not account.is_enabled: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Account is disabled. Enable it before pulling.", + ) + + process_mail_account_task.delay(account_id) + + return {"message": "Pull queued successfully"} + + @router.post("/test", response_model=MailAccountTestResponse) async def test_mail_connection( test_request: MailAccountTestRequest, diff --git a/docs/TODO.md b/docs/TODO.md index cebd7e5..f35318c 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -4,6 +4,8 @@ Comprehensive task breakdown for repository improvements and production readines ## ✅ Recently Completed +- [x] **Pull Now**: Added "Pull Now" button on Accounts page that immediately queues a `process_mail_account` Celery task via `POST /mail-accounts/{id}/pull-now`. Button shows spinner while in flight and is disabled for inactive accounts. +- [x] **Provider logos**: Provider SVG logos (Gmail, GMX, WEB.DE, Outlook, Yahoo, AOL, T-Online, IONOS, Freenet, Posteo, iCloud) now displayed on the Accounts page next to each account name. - [x] Redesigned user-facing Logs page to mailbox-centric "Mailbox Activity" view: shows last check status per account + only successful pulls, suppressing noise from empty polling cycles. - [x] Added `has_emails` filter to `GET /processing-runs` and `GET /mail-accounts/{id}/processing-runs` API endpoints. - [x] Rename entire project to **InboxConverge**: all user-visible strings, Docker container/image names, DB defaults, monitoring, and docs updated. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 01e8177..9f39a28 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1724,7 +1724,7 @@ "version": "19.2.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2801,7 +2801,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { diff --git a/frontend/src/app/accounts/page.tsx b/frontend/src/app/accounts/page.tsx index 35e08e5..de32564 100644 --- a/frontend/src/app/accounts/page.tsx +++ b/frontend/src/app/accounts/page.tsx @@ -4,13 +4,46 @@ 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, Power } from 'lucide-react'; +import { Plus, Edit2, Trash2, CheckCircle, XCircle, AlertTriangle, Power, RefreshCw } from 'lucide-react'; import { useState } from 'react'; +import Image from 'next/image'; import { AddMailAccountModal } from '@/components/AddMailAccountModal'; +// Map provider_name (from backend) to the SVG icon filename in /public/providers/ +const PROVIDER_ICON_MAP: Record = { + 'Gmail': 'gmail', + 'GMX': 'gmx', + 'WEB.DE': 'webde', + 'Outlook / Hotmail': 'outlook', + 'Yahoo Mail': 'yahoo', + 'AOL Mail': 'aol', + 'T-Online': 'tonline', + '1&1 / IONOS': 'ionos', + 'Freenet': 'freenet', + 'Posteo': 'posteo', + 'mail.de': 'mailde', + 'iCloud Mail': 'icloud', +}; + +function ProviderLogo({ providerName }: { providerName?: string | null }) { + const icon = providerName ? PROVIDER_ICON_MAP[providerName] : undefined; + if (!icon) return null; + return ( + {`${providerName} {/* silently skip missing icons */}} + /> + ); +} + export default function AccountsPage() { const [isModalOpen, setIsModalOpen] = useState(false); const [editingAccount, setEditingAccount] = useState(null); + const [pullingIds, setPullingIds] = useState>(new Set()); const queryClient = useQueryClient(); const { data: accounts, isLoading } = useQuery({ @@ -55,6 +88,21 @@ export default function AccountsPage() { } }; + const handlePullNow = async (id: number) => { + setPullingIds((prev) => new Set(prev).add(id)); + try { + await mailAccountsApi.pullNow(id); + } catch { + alert('Failed to queue pull'); + } finally { + setPullingIds((prev) => { + const next = new Set(prev); + next.delete(id); + return next; + }); + } + }; + const handleCloseModal = () => { setIsModalOpen(false); setEditingAccount(null); @@ -90,13 +138,16 @@ export default function AccountsPage() { >
-
-

- {account.name} -

-

{account.email_address}

+
+ +
+

+ {account.name} +

+

{account.email_address}

+
-
+
{account.is_enabled ? ( @@ -164,6 +215,15 @@ export default function AccountsPage() { > +