From fb3311cb7f2e4b630d34b9493b93ee289c17392b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:55:11 +0000 Subject: [PATCH 1/5] Initial plan From 105d6dcd6ecad199491a6d67d13c7dcdf098c1c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:56:23 +0000 Subject: [PATCH 2/5] Initial plan From 6afd7c871e7c694a93f119aa66f1b798c9b5b2bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 20:02:12 +0000 Subject: [PATCH 3/5] feat(dashboard): replace per-run table with per-account Mailbox Status view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove noisy "Recent Processing Runs" table from dashboard - Add per-account AccountStatusRow showing: name, email, OK/Error/Pending badge, relative last-check time, inline error message, lifetime counters - Stats cards: "Emails Forwarded Today" → "Emails Processed" (all-time), "Errors" → "Accounts with Errors" (turns gray when 0) - Remove unused processingRunsApi import (saves one API call on page load) - "View activity & history →" link to /logs for full drill-down - Update CHANGELOG.md and docs/TODO.md Agent-Logs-Url: https://github.com/christianlouis/InboxConverge/sessions/0d97741e-e999-49dd-a8f2-dceac2c1da1f Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- CHANGELOG.md | 7 + docs/TODO.md | 1 + frontend/src/app/dashboard/page.tsx | 232 ++++++++++++++-------------- 3 files changed, 124 insertions(+), 116 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a222f96..449c721 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [Unreleased] + +### Changed + +- Dashboard "Recent Processing Runs" table replaced with a per-account **Mailbox Status** view: each account now shows its last-check status (OK / Error / Pending), relative last-check time, any error message, and lifetime processed/failed counters. The noisy per-run table is gone; full activity history remains available on the Logs page. +- Stats cards updated: "Emails Forwarded Today" → "Emails Processed" (all-time total from account records); "Errors" → "Accounts with Errors" (count of accounts currently showing an error). + ## v0.3.2 (2026-03-28) ### Bug Fixes diff --git a/docs/TODO.md b/docs/TODO.md index 82bca22..2a77b4e 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -4,6 +4,7 @@ Comprehensive task breakdown for repository improvements and production readines ## ✅ Recently Completed +- [x] **Dashboard redesign**: Replaced noisy "Recent Processing Runs" table with a per-account "Mailbox Status" view showing last-check status (OK/Error/Pending), relative timestamp, error messages, and lifetime counters. Stats cards updated to show all-time processed count and accounts-with-errors count. - [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] Fixed 21 mypy type errors: `Column[T]` vs native type mismatches in `notification_service.py`, `mail_processor.py`, `auth.py`, `tasks.py`, `providers.py`, `mail_accounts.py`, and `main.py` (`lifespan` parameter rename). - [x] **Provider logos rework**: Logos now displayed as full-width banner strips at the top of each account card using `next/image fill + object-contain`. Handles all aspect ratios (1:1 square to 6:1 wordmark) without distortion. Proton Mail added. diff --git a/frontend/src/app/dashboard/page.tsx b/frontend/src/app/dashboard/page.tsx index 07b77a4..5e5f74e 100644 --- a/frontend/src/app/dashboard/page.tsx +++ b/frontend/src/app/dashboard/page.tsx @@ -3,38 +3,44 @@ import { AuthGuard } from '@/components/AuthGuard'; import { DashboardLayout } from '@/components/DashboardLayout'; import { useQuery } from '@tanstack/react-query'; -import { mailAccountsApi, processingRunsApi } from '@/lib/api'; +import { mailAccountsApi, MailAccount } from '@/lib/api'; import Link from 'next/link'; -import { - Mail, - Send, - CheckCircle, +import { + Mail, + Send, + CheckCircle, AlertCircle, - TrendingUp, - Clock + Clock, + XCircle, + AlertTriangle, + Inbox, } from 'lucide-react'; +function formatRelative(iso?: string | null): string { + if (!iso) return 'Never'; + const diff = Date.now() - new Date(iso).getTime(); + const minutes = Math.floor(diff / 60000); + if (minutes < 1) return 'Just now'; + if (minutes < 60) return `${minutes}m ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours}h ago`; + return `${Math.floor(hours / 24)}d ago`; +} + interface StatCardProps { title: string; value: string | number; icon: React.ComponentType<{ className?: string }>; iconColor: string; - trend?: string; } -function StatCard({ title, value, icon: Icon, iconColor, trend }: StatCardProps) { +function StatCard({ title, value, icon: Icon, iconColor }: StatCardProps) { return (

{title}

{value}

- {trend && ( -
- - {trend} -
- )}
@@ -44,27 +50,78 @@ function StatCard({ title, value, icon: Icon, iconColor, trend }: StatCardProps) ); } +function AccountStatusRow({ account }: { account: MailAccount }) { + const hasError = !!account.last_error_message; + const lastChecked = account.last_check_at; + + return ( +
+
+ {/* Left: name + email */} +
+ +
+

{account.name}

+

{account.email_address}

+
+
+ + {/* Right: status badge + last check */} +
+ {hasError ? ( + + + Error + + ) : lastChecked ? ( + + + OK + + ) : ( + + + Pending + + )} +

+ {formatRelative(lastChecked)} +

+
+
+ + {/* Error message */} + {hasError && ( +
+ +

{account.last_error_message}

+
+ )} + + {/* Lifetime counters (only when there's activity) */} + {(account.total_emails_processed > 0 || account.total_emails_failed > 0) && ( +
+ {account.total_emails_processed.toLocaleString()} processed + {account.total_emails_failed > 0 && ( + {account.total_emails_failed.toLocaleString()} failed + )} +
+ )} +
+ ); +} + export default function DashboardPage() { - const { data: accounts } = useQuery({ + const { data: accounts, isLoading: accountsLoading } = useQuery({ queryKey: ['mail-accounts'], queryFn: mailAccountsApi.list, }); - const { data: runs, isLoading: runsLoading } = useQuery({ - queryKey: ['processing-runs'], - queryFn: () => processingRunsApi.list({ page: 1, page_size: 10 }), - }); - const stats = { totalAccounts: accounts?.length || 0, activeAccounts: accounts?.filter((a) => a.is_enabled).length || 0, - emailsToday: runs?.items - ?.filter((r) => { - const today = new Date().toDateString(); - return new Date(r.started_at).toDateString() === today; - }) - .reduce((sum, r) => sum + r.emails_forwarded, 0) || 0, - errors: runs?.items?.filter((r) => r.emails_failed > 0).length || 0, + totalProcessed: accounts?.reduce((sum, a) => sum + a.total_emails_processed, 0) || 0, + accountsWithErrors: accounts?.filter((a) => !!a.last_error_message).length || 0, }; return ( @@ -80,8 +137,8 @@ export default function DashboardPage() { iconColor="bg-blue-500" /> @@ -92,101 +149,44 @@ export default function DashboardPage() { iconColor="bg-purple-500" /> 0 ? 'bg-red-500' : 'bg-gray-400'} />
- {/* Recent Processing Runs */} + {/* Mailbox Status Overview */}
-

Recent Processing Runs

+

Mailbox Status

- View all logs → + View activity & history →
-
- {runsLoading ? ( -
-
-
- ) : runs && runs.items && runs.items.length > 0 ? ( - - - - - - - - - - - - - {runs.items.map((run) => { - const account = accounts?.find((a) => a.id === run.mail_account_id); - return ( - - - - - - - - - ); - })} - -
- Account - - Started At - - Status - - Fetched - - Forwarded - - Errors -
- {run.account_name || account?.name || `Account ${run.mail_account_id}`} - -
- - {new Date(run.started_at).toLocaleString()} -
-
- - {run.status} - - - {run.emails_fetched} - - {run.emails_forwarded} - - {run.emails_failed > 0 ? ( - {run.emails_failed} - ) : ( - 0 - )} -
- ) : ( -
-

No processing runs yet

-
- )} -
+ + {accountsLoading ? ( +
+
+
+ ) : accounts && accounts.length > 0 ? ( +
+ {accounts.map((account) => ( + + ))} +
+ ) : ( +
+ +

No mail accounts configured yet.

+ + Add your first account → + +
+ )}
From f7caa05c8ff7b1a4a39792bbe28c5eaf18195b45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 20:03:19 +0000 Subject: [PATCH 4/5] Fix provider logos, improve Fetch button UX on mail accounts page Agent-Logs-Url: https://github.com/christianlouis/InboxConverge/sessions/058b6481-f8eb-43ed-8f5f-715c19c4a377 Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- CHANGELOG.md | 6 +++++ backend/app/api/v1/endpoints/mail_accounts.py | 1 + backend/app/models/schemas.py | 2 ++ docs/TODO.md | 3 +++ frontend/src/app/accounts/page.tsx | 26 ++++++++++++++++--- .../src/components/AddMailAccountModal.tsx | 4 ++- frontend/src/components/ProviderWizard.tsx | 2 ++ frontend/src/lib/api.ts | 2 ++ 8 files changed, 42 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a222f96..97e6a42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [Unreleased] + +### Fixed +- Provider logos now appear on the Mail Accounts page: `provider_name` is correctly saved when creating accounts via the provider wizard and propagated through backend/frontend schemas. +- Fetch-emails button now shows a text label ("Fetch"), a descriptive tooltip, a "Fetching…" loading state, and a brief green "Queued!" confirmation after the action completes. + ## v0.3.2 (2026-03-28) ### Bug Fixes diff --git a/backend/app/api/v1/endpoints/mail_accounts.py b/backend/app/api/v1/endpoints/mail_accounts.py index 3d870f4..34174a2 100644 --- a/backend/app/api/v1/endpoints/mail_accounts.py +++ b/backend/app/api/v1/endpoints/mail_accounts.py @@ -104,6 +104,7 @@ async def create_mail_account( check_interval_minutes=account_in.check_interval_minutes, max_emails_per_check=account_in.max_emails_per_check, delete_after_forward=account_in.delete_after_forward, + provider_name=account_in.provider_name, ) db.add(account) diff --git a/backend/app/models/schemas.py b/backend/app/models/schemas.py index 51d6fff..f630f60 100644 --- a/backend/app/models/schemas.py +++ b/backend/app/models/schemas.py @@ -113,6 +113,7 @@ class MailAccountBase(BaseModel): check_interval_minutes: int = Field(default=5, gt=0, le=1440) max_emails_per_check: int = Field(default=50, gt=0, le=1000) delete_after_forward: bool = True + provider_name: Optional[str] = Field(None, max_length=100) class MailAccountCreate(MailAccountBase): @@ -135,6 +136,7 @@ class MailAccountUpdate(BaseModel): check_interval_minutes: Optional[int] = Field(None, gt=0, le=1440) max_emails_per_check: Optional[int] = Field(None, gt=0, le=1000) delete_after_forward: Optional[bool] = None + provider_name: Optional[str] = Field(None, max_length=100) class MailAccountResponse(MailAccountBase): diff --git a/docs/TODO.md b/docs/TODO.md index 82bca22..a8862f8 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -4,6 +4,9 @@ Comprehensive task breakdown for repository improvements and production readines ## ✅ Recently Completed +- [x] **Provider logos now saved on account creation**: `provider_name` field added to `MailAccountCreate` and `MailAccountUpdate` schemas (backend and frontend). `ProviderWizard` now passes `provider_name` in its `onSelect` callback; `AddMailAccountModal` stores it so logos are displayed correctly on the accounts page. +- [x] **Fetch button UX improvements**: The "fetch emails" button on the accounts page now shows a "Fetch" text label for clarity, a tooltip explaining its purpose, a spinning "Fetching…" state during the API call, and a brief green "Queued!" confirmation after success. + - [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] Fixed 21 mypy type errors: `Column[T]` vs native type mismatches in `notification_service.py`, `mail_processor.py`, `auth.py`, `tasks.py`, `providers.py`, `mail_accounts.py`, and `main.py` (`lifespan` parameter rename). - [x] **Provider logos rework**: Logos now displayed as full-width banner strips at the top of each account card using `next/image fill + object-contain`. Handles all aspect ratios (1:1 square to 6:1 wordmark) without distortion. Proton Mail added. diff --git a/frontend/src/app/accounts/page.tsx b/frontend/src/app/accounts/page.tsx index 6e6aae5..714aa91 100644 --- a/frontend/src/app/accounts/page.tsx +++ b/frontend/src/app/accounts/page.tsx @@ -53,6 +53,7 @@ export default function AccountsPage() { const [isModalOpen, setIsModalOpen] = useState(false); const [editingAccount, setEditingAccount] = useState(null); const [pullingIds, setPullingIds] = useState>(new Set()); + const [successIds, setSuccessIds] = useState>(new Set()); const queryClient = useQueryClient(); const { data: accounts, isLoading } = useQuery({ @@ -101,6 +102,14 @@ export default function AccountsPage() { setPullingIds((prev) => new Set(prev).add(id)); try { await mailAccountsApi.pullNow(id); + setSuccessIds((prev) => new Set(prev).add(id)); + setTimeout(() => { + setSuccessIds((prev) => { + const next = new Set(prev); + next.delete(id); + return next; + }); + }, 2000); } catch { alert('Failed to queue pull'); } finally { @@ -227,11 +236,22 @@ export default function AccountsPage() {