Merge pull request #126 from christianlouis/copilot/add-logos-to-mail-accounts
Add domain-based logo fallback for mail accounts without provider_name
This commit is contained in:
@@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Domain-based logo fallback for mail accounts**: `ProviderLogoBanner` now shows provider logos even for accounts that have no `provider_name` set, by extracting the domain from the email address and matching it against a new `DOMAIN_ICON_MAP`. Covers Gmail, GMX, WEB.DE, Yahoo Mail, AOL, T-Online, Outlook/Hotmail, IONOS, Freenet, iCloud, Posteo, and Proton Mail.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Convert `frontend/jest.config.js` to `jest.config.mjs` using ES module `import`/`export` syntax to resolve ESLint `@typescript-eslint/no-require-imports` error.
|
- Convert `frontend/jest.config.js` to `jest.config.mjs` using ES module `import`/`export` syntax to resolve ESLint `@typescript-eslint/no-require-imports` error.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ Comprehensive task breakdown for repository improvements and production readines
|
|||||||
- [x] Fixed worker `send_user_notification` using rolled-back DB session causing `greenlet_spawn has not been called` errors; status/`last_check_at` now always committed before sending notifications via a fresh session.
|
- [x] Fixed worker `send_user_notification` using rolled-back DB session causing `greenlet_spawn has not been called` errors; status/`last_check_at` now always committed before sending notifications via a fresh session.
|
||||||
- [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] **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] **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] **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] **Domain-based logo fallback**: `ProviderLogoBanner` now falls back to email-domain matching when `provider_name` is absent, so all known providers (GMX, WEB.DE, T-Online, etc.) show their logo even on legacy accounts.
|
||||||
- [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] **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] **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.
|
||||||
|
|||||||
Generated
+2
-2
@@ -2819,7 +2819,7 @@
|
|||||||
"version": "19.2.10",
|
"version": "19.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
|
||||||
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
|
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
@@ -4256,7 +4256,7 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/damerau-levenshtein": {
|
"node_modules/damerau-levenshtein": {
|
||||||
|
|||||||
@@ -26,20 +26,60 @@ const PROVIDER_ICON_MAP: Record<string, string> = {
|
|||||||
'Proton Mail': 'protonmail',
|
'Proton Mail': 'protonmail',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fallback: map email domains to SVG icon filenames for accounts without a provider_name
|
||||||
|
const DOMAIN_ICON_MAP: Record<string, string> = {
|
||||||
|
// Gmail
|
||||||
|
'gmail.com': 'gmail', 'googlemail.com': 'gmail',
|
||||||
|
// GMX
|
||||||
|
'gmx.de': 'gmx', 'gmx.net': 'gmx', 'gmx.at': 'gmx', 'gmx.ch': 'gmx', 'gmx.com': 'gmx',
|
||||||
|
// WEB.DE
|
||||||
|
'web.de': 'webde',
|
||||||
|
// Outlook / Hotmail
|
||||||
|
'outlook.com': 'outlook', 'hotmail.com': 'outlook', 'live.com': 'outlook',
|
||||||
|
'msn.com': 'outlook', 'outlook.de': 'outlook',
|
||||||
|
// Yahoo Mail
|
||||||
|
'yahoo.com': 'yahoo', 'yahoo.de': 'yahoo', 'yahoo.co.uk': 'yahoo', 'ymail.com': 'yahoo',
|
||||||
|
// AOL Mail
|
||||||
|
'aol.com': 'aol', 'aim.com': 'aol',
|
||||||
|
// T-Online
|
||||||
|
't-online.de': 'tonline',
|
||||||
|
// 1&1 / IONOS
|
||||||
|
'online.de': 'ionos', 'onlinehome.de': 'ionos', '1und1.de': 'ionos',
|
||||||
|
// Freenet
|
||||||
|
'freenet.de': 'freenet',
|
||||||
|
// iCloud Mail
|
||||||
|
'icloud.com': 'icloud', 'me.com': 'icloud', 'mac.com': 'icloud',
|
||||||
|
// Posteo
|
||||||
|
'posteo.de': 'posteo', 'posteo.net': 'posteo',
|
||||||
|
// Proton Mail
|
||||||
|
'proton.me': 'protonmail', 'protonmail.com': 'protonmail',
|
||||||
|
'protonmail.ch': 'protonmail', 'pm.me': 'protonmail',
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full-width logo banner rendered at the top of a card.
|
* Full-width logo banner rendered at the top of a card.
|
||||||
* Uses next/image fill + object-contain so every logo – regardless of its
|
* Uses next/image fill + object-contain so every logo – regardless of its
|
||||||
* native aspect ratio (1:1 square up to ~6:1 wordmark) – fits correctly
|
* native aspect ratio (1:1 square up to ~6:1 wordmark) – fits correctly
|
||||||
* inside the fixed-height strip without distortion.
|
* inside the fixed-height strip without distortion.
|
||||||
|
*
|
||||||
|
* Resolves the icon by first checking provider_name, then falling back to
|
||||||
|
* the email domain so that accounts created without a provider_name still
|
||||||
|
* show the correct logo.
|
||||||
*/
|
*/
|
||||||
function ProviderLogoBanner({ providerName }: { providerName?: string | null }) {
|
function ProviderLogoBanner({ providerName, email }: { providerName?: string | null; email?: string | null }) {
|
||||||
const icon = providerName ? PROVIDER_ICON_MAP[providerName] : undefined;
|
let icon = providerName ? PROVIDER_ICON_MAP[providerName] : undefined;
|
||||||
|
if (!icon && email) {
|
||||||
|
const atIndex = email.lastIndexOf('@');
|
||||||
|
const domain = atIndex !== -1 ? email.slice(atIndex + 1).toLowerCase() : undefined;
|
||||||
|
if (domain) icon = DOMAIN_ICON_MAP[domain];
|
||||||
|
}
|
||||||
if (!icon) return null;
|
if (!icon) return null;
|
||||||
|
const label = providerName ?? email?.split('@')[1] ?? 'provider';
|
||||||
return (
|
return (
|
||||||
<div className="relative h-16 w-full bg-gray-50 border-b border-gray-100 overflow-hidden">
|
<div className="relative h-16 w-full bg-gray-50 border-b border-gray-100 overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
src={`/providers/${icon}.svg`}
|
src={`/providers/${icon}.svg`}
|
||||||
alt={`${providerName} logo`}
|
alt={`${label} logo`}
|
||||||
fill
|
fill
|
||||||
unoptimized
|
unoptimized
|
||||||
sizes="(max-width: 768px) 100vw, 33vw"
|
sizes="(max-width: 768px) 100vw, 33vw"
|
||||||
@@ -155,7 +195,7 @@ export default function AccountsPage() {
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{/* Provider logo banner – full-width strip that accommodates any aspect ratio */}
|
{/* Provider logo banner – full-width strip that accommodates any aspect ratio */}
|
||||||
<ProviderLogoBanner providerName={account.provider_name} />
|
<ProviderLogoBanner providerName={account.provider_name} email={account.email_address} />
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<div className="flex items-start justify-between mb-4">
|
<div className="flex items-start justify-between mb-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user