Merge branch 'main' into copilot/update-dashboard-status-view
This commit is contained in:
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- 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.
|
- 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).
|
- 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).
|
||||||
|
### 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)
|
## v0.3.2 (2026-03-28)
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ async def create_mail_account(
|
|||||||
check_interval_minutes=account_in.check_interval_minutes,
|
check_interval_minutes=account_in.check_interval_minutes,
|
||||||
max_emails_per_check=account_in.max_emails_per_check,
|
max_emails_per_check=account_in.max_emails_per_check,
|
||||||
delete_after_forward=account_in.delete_after_forward,
|
delete_after_forward=account_in.delete_after_forward,
|
||||||
|
provider_name=account_in.provider_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
db.add(account)
|
db.add(account)
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ class MailAccountBase(BaseModel):
|
|||||||
check_interval_minutes: int = Field(default=5, gt=0, le=1440)
|
check_interval_minutes: int = Field(default=5, gt=0, le=1440)
|
||||||
max_emails_per_check: int = Field(default=50, gt=0, le=1000)
|
max_emails_per_check: int = Field(default=50, gt=0, le=1000)
|
||||||
delete_after_forward: bool = True
|
delete_after_forward: bool = True
|
||||||
|
provider_name: Optional[str] = Field(None, max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class MailAccountCreate(MailAccountBase):
|
class MailAccountCreate(MailAccountBase):
|
||||||
@@ -135,6 +136,7 @@ class MailAccountUpdate(BaseModel):
|
|||||||
check_interval_minutes: Optional[int] = Field(None, gt=0, le=1440)
|
check_interval_minutes: Optional[int] = Field(None, gt=0, le=1440)
|
||||||
max_emails_per_check: Optional[int] = Field(None, gt=0, le=1000)
|
max_emails_per_check: Optional[int] = Field(None, gt=0, le=1000)
|
||||||
delete_after_forward: Optional[bool] = None
|
delete_after_forward: Optional[bool] = None
|
||||||
|
provider_name: Optional[str] = Field(None, max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class MailAccountResponse(MailAccountBase):
|
class MailAccountResponse(MailAccountBase):
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ Comprehensive task breakdown for repository improvements and production readines
|
|||||||
## ✅ Recently Completed
|
## ✅ 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] **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] **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.
|
||||||
- [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] 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.
|
- [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.
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export default function AccountsPage() {
|
|||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [editingAccount, setEditingAccount] = useState<MailAccount | null>(null);
|
const [editingAccount, setEditingAccount] = useState<MailAccount | null>(null);
|
||||||
const [pullingIds, setPullingIds] = useState<Set<number>>(new Set());
|
const [pullingIds, setPullingIds] = useState<Set<number>>(new Set());
|
||||||
|
const [successIds, setSuccessIds] = useState<Set<number>>(new Set());
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const { data: accounts, isLoading } = useQuery({
|
const { data: accounts, isLoading } = useQuery({
|
||||||
@@ -101,6 +102,14 @@ export default function AccountsPage() {
|
|||||||
setPullingIds((prev) => new Set(prev).add(id));
|
setPullingIds((prev) => new Set(prev).add(id));
|
||||||
try {
|
try {
|
||||||
await mailAccountsApi.pullNow(id);
|
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 {
|
} catch {
|
||||||
alert('Failed to queue pull');
|
alert('Failed to queue pull');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -227,11 +236,22 @@ export default function AccountsPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => handlePullNow(account.id)}
|
onClick={() => handlePullNow(account.id)}
|
||||||
disabled={!account.is_enabled || pullingIds.has(account.id)}
|
disabled={!account.is_enabled || pullingIds.has(account.id)}
|
||||||
title="Pull emails now"
|
title="Fetch new emails from this account now"
|
||||||
aria-label="Pull emails now"
|
aria-label="Fetch emails now"
|
||||||
className="flex items-center justify-center px-3 py-2 text-sm font-medium text-indigo-600 bg-indigo-50 rounded-md hover:bg-indigo-100 transition-colors disabled:opacity-50"
|
className={`flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium rounded-md transition-colors disabled:opacity-50 ${
|
||||||
|
successIds.has(account.id)
|
||||||
|
? 'text-green-600 bg-green-50 hover:bg-green-100'
|
||||||
|
: 'text-indigo-600 bg-indigo-50 hover:bg-indigo-100'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<RefreshCw className={`h-4 w-4 ${pullingIds.has(account.id) ? 'animate-spin' : ''}`} />
|
<RefreshCw className={`h-4 w-4 ${pullingIds.has(account.id) ? 'animate-spin' : ''}`} />
|
||||||
|
<span>
|
||||||
|
{pullingIds.has(account.id)
|
||||||
|
? 'Fetching…'
|
||||||
|
: successIds.has(account.id)
|
||||||
|
? 'Queued!'
|
||||||
|
: 'Fetch'}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleEdit(account)}
|
onClick={() => handleEdit(account)}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export function AddMailAccountModal({ account, onClose }: AddMailAccountModalPro
|
|||||||
check_interval_minutes: account?.check_interval_minutes || 5,
|
check_interval_minutes: account?.check_interval_minutes || 5,
|
||||||
max_emails_per_check: account?.max_emails_per_check || 50,
|
max_emails_per_check: account?.max_emails_per_check || 50,
|
||||||
delete_after_forward: account?.delete_after_forward ?? true,
|
delete_after_forward: account?.delete_after_forward ?? true,
|
||||||
|
provider_name: account?.provider_name ?? null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onMutationSuccess = () => {
|
const onMutationSuccess = () => {
|
||||||
@@ -75,7 +76,7 @@ export function AddMailAccountModal({ account, onClose }: AddMailAccountModalPro
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProviderSelect = (config: { name: string; protocol: string; host: string; port: number; use_ssl: boolean }) => {
|
const handleProviderSelect = (config: { name: string; provider_name: string; protocol: string; host: string; port: number; use_ssl: boolean }) => {
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
name: config.name,
|
name: config.name,
|
||||||
@@ -83,6 +84,7 @@ export function AddMailAccountModal({ account, onClose }: AddMailAccountModalPro
|
|||||||
host: config.host,
|
host: config.host,
|
||||||
port: config.port,
|
port: config.port,
|
||||||
use_ssl: config.use_ssl,
|
use_ssl: config.use_ssl,
|
||||||
|
provider_name: config.provider_name,
|
||||||
}));
|
}));
|
||||||
setWizardStep('form');
|
setWizardStep('form');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface ProviderPreset {
|
|||||||
|
|
||||||
interface ProviderConfig {
|
interface ProviderConfig {
|
||||||
name: string;
|
name: string;
|
||||||
|
provider_name: string;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
@@ -160,6 +161,7 @@ export function ProviderWizard({ onSelect, onManual }: ProviderWizardProps) {
|
|||||||
|
|
||||||
onSelect({
|
onSelect({
|
||||||
name: selectedProvider.name,
|
name: selectedProvider.name,
|
||||||
|
provider_name: selectedProvider.name,
|
||||||
protocol: selectedProtocol === 'imap_ssl' ? 'imap_ssl' : 'pop3_ssl',
|
protocol: selectedProtocol === 'imap_ssl' ? 'imap_ssl' : 'pop3_ssl',
|
||||||
host: config.host,
|
host: config.host,
|
||||||
port: config.port,
|
port: config.port,
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ export interface MailAccountCreate {
|
|||||||
check_interval_minutes?: number;
|
check_interval_minutes?: number;
|
||||||
max_emails_per_check?: number;
|
max_emails_per_check?: number;
|
||||||
delete_after_forward?: boolean;
|
delete_after_forward?: boolean;
|
||||||
|
provider_name?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MailAccountUpdate {
|
export interface MailAccountUpdate {
|
||||||
@@ -113,6 +114,7 @@ export interface MailAccountUpdate {
|
|||||||
check_interval_minutes?: number;
|
check_interval_minutes?: number;
|
||||||
max_emails_per_check?: number;
|
max_emails_per_check?: number;
|
||||||
delete_after_forward?: boolean;
|
delete_after_forward?: boolean;
|
||||||
|
provider_name?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProcessingRun {
|
export interface ProcessingRun {
|
||||||
|
|||||||
Reference in New Issue
Block a user