mirror of
https://github.com/warmbly/warmbly.git
synced 2026-11-06 00:15:30 +00:00
feat: show cold rests without campaigns and explain deduplicated mailbox connection reports
This commit is contained in:
committed by
Devin AI
parent
4d70aada93
commit
ed76eb5ac0
@@ -5,6 +5,7 @@ import { browseString, mailboxSort, mailboxTab, type MailboxTab } from "@/lib/br
|
||||
import { useSearchParams } from "@/hooks/useSearchParams";
|
||||
import useMailboxTagFilter from "@/hooks/useMailboxTagFilter";
|
||||
import groupMailboxes from "@/lib/groupMailboxes";
|
||||
import { mailboxColdActivity } from "@/lib/mailboxStatus";
|
||||
import { useAppStore } from "@/stores/useAppStore";
|
||||
import toast from "react-hot-toast/headless";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
@@ -1426,8 +1427,7 @@ function MailboxStatusPill({ box, status, historicalIssue, warming, compact = fa
|
||||
const error = status?.errors?.[0];
|
||||
const lifecycle = status?.send_lifecycle;
|
||||
const inCampaign = !!status?.in_campaign;
|
||||
const resting = inCampaign && !!lifecycle && lifecycle.state !== "active";
|
||||
const sending = inCampaign && !resting;
|
||||
const { resting, sending } = mailboxColdActivity(status);
|
||||
|
||||
let problem: { label: string; text: string; Icon: LucideIcon; title: string } | null = null;
|
||||
if (box.status === "revoked") problem = { label: "Reconnect", text: "text-rose-600", Icon: UnplugIcon, title: "Access was revoked at the provider. Reconnect the mailbox to send and warm again." };
|
||||
|
||||
@@ -790,6 +790,7 @@ function OverviewTab({ status, loading, mailbox }: { status?: import("@/lib/api/
|
||||
{status.errors.map((e: AccountError) => (
|
||||
<div key={e.id} className="rounded-md border border-rose-200 bg-rose-50 px-3 py-2">
|
||||
<div className="text-[12px] font-medium text-rose-800">{e.title}</div>
|
||||
<div className="text-[10px] text-rose-700/80 mt-0.5">Last reported {new Date(e.created_at).toLocaleString()}{e.operation === "send" ? " · Sending" : e.operation === "sync" ? " · Mailbox sync" : ""}</div>
|
||||
<div className="text-[11px] text-rose-700/90 mt-0.5 leading-relaxed">{e.message}</div>
|
||||
{e.action_required && <div className="text-[11px] text-rose-900 mt-1 font-medium">{e.action_required}</div>}
|
||||
{e.id === firstCredentialErrorId &&
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface AccountError {
|
||||
message: string;
|
||||
action_required?: string;
|
||||
created_at: Date;
|
||||
operation?: "send" | "sync" | "unknown";
|
||||
}
|
||||
|
||||
export interface AccountDailyUsage {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { mailboxColdActivity } from "./mailboxStatus";
|
||||
|
||||
describe("cold rotation activity", () => {
|
||||
it.each(["resting", "reserve"] as const)("shows %s without a campaign on this instance", (state) => {
|
||||
expect(mailboxColdActivity({ in_campaign: false, send_lifecycle: { state } })).toEqual({ resting: true, sending: false });
|
||||
});
|
||||
|
||||
it.each(["resting", "reserve"] as const)("never reports %s as campaign sending", (state) => {
|
||||
expect(mailboxColdActivity({ in_campaign: true, send_lifecycle: { state } })).toEqual({ resting: true, sending: false });
|
||||
});
|
||||
|
||||
it("keeps older status responses without lifecycle compatible", () => {
|
||||
expect(mailboxColdActivity({ in_campaign: true })).toEqual({ resting: false, sending: true });
|
||||
expect(mailboxColdActivity({ in_campaign: false })).toEqual({ resting: false, sending: false });
|
||||
expect(mailboxColdActivity()).toEqual({ resting: false, sending: false });
|
||||
});
|
||||
|
||||
it("does not call a mailbox sending just because it is available for cold rotation", () => {
|
||||
expect(mailboxColdActivity({ in_campaign: false, send_lifecycle: { state: "active" } })).toEqual({ resting: false, sending: false });
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,15 @@
|
||||
import type Inbox from "@/lib/api/models/app/emails/Inbox";
|
||||
import type AccountStatus from "@/lib/api/models/app/analytics/AccountStatus";
|
||||
import { diagnosticSendingAllowed, diagnosticWarmupActive } from "./diagnosticParticipation";
|
||||
|
||||
export type MailboxDisplayStatus = "healthy" | "warming" | "paused" | "inactive";
|
||||
|
||||
export function mailboxColdActivity(status?: Pick<AccountStatus, "send_lifecycle" | "in_campaign">) {
|
||||
const lifecycle = status?.send_lifecycle;
|
||||
const resting = !!lifecycle && lifecycle.state !== "active";
|
||||
return { resting, sending: !!status?.in_campaign && !resting };
|
||||
}
|
||||
|
||||
// Account activity is not proof of deliverability or reputation readiness.
|
||||
export default function mailboxDisplayStatus(box: Inbox): MailboxDisplayStatus {
|
||||
if (box.status !== "active") return "inactive";
|
||||
|
||||
Reference in New Issue
Block a user