fix(email): clarify provider capability states
This commit is contained in:
@@ -90,8 +90,9 @@ describe('CorrespondenceInboxPage', () => {
|
|||||||
expect(await screen.findByText(/backend engineer/i)).toBeInTheDocument();
|
expect(await screen.findByText(/backend engineer/i)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/2 labels/i)).toBeInTheDocument();
|
expect(screen.getByText(/2 labels/i)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/1 attachments/i)).toBeInTheDocument();
|
expect(screen.getByText(/1 attachments/i)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/Gmail: owner@gmail\.test · Read only/i)).toBeInTheDocument();
|
expect(screen.getByText(/Gmail: owner@gmail\.test · Read only; reconnect to enable send/i)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/Outlook: Not connected · Read only/i)).toBeInTheDocument();
|
expect(screen.getByText(/^Outlook: Not connected$/i)).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText(/Outlook: Not connected.*Read only/i)).not.toBeInTheDocument();
|
||||||
|
|
||||||
fireEvent.change(screen.getByLabelText(/search/i), { target: { value: 'Maria' } });
|
fireEvent.change(screen.getByLabelText(/search/i), { target: { value: 'Maria' } });
|
||||||
fireEvent.mouseDown(screen.getAllByRole('combobox')[0]);
|
fireEvent.mouseDown(screen.getAllByRole('combobox')[0]);
|
||||||
@@ -107,6 +108,20 @@ describe('CorrespondenceInboxPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('keeps saved correspondence visible when provider status is unavailable', async () => {
|
||||||
|
const original = mockedApi.get.getMockImplementation();
|
||||||
|
mockedApi.get.mockImplementation((url: string, config?: any) => {
|
||||||
|
if (url === '/email/providers') return Promise.reject(new Error('provider status unavailable'));
|
||||||
|
return original!(url, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
renderPage();
|
||||||
|
|
||||||
|
expect(await screen.findByText(/email provider status is temporarily unavailable/i)).toBeInTheDocument();
|
||||||
|
expect(await screen.findByText(/acme systems/i)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/saved jobtracker correspondence remains available/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test('opens provider-backed plain-text message detail in the hub', async () => {
|
test('opens provider-backed plain-text message detail in the hub', async () => {
|
||||||
renderPage();
|
renderPage();
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ type EmailProviderStatus = {
|
|||||||
canSend: boolean;
|
canSend: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function emailProviderStatusLabel(provider: EmailProviderStatus) {
|
||||||
|
if (!provider.connected) return `${provider.displayName}: Not connected`;
|
||||||
|
const identity = provider.address || "Connected account";
|
||||||
|
if (!provider.canRead) return `${provider.displayName}: ${identity} · Mailbox access unavailable`;
|
||||||
|
return `${provider.displayName}: ${identity}${provider.canSend ? " · Read + send" : " · Read only; reconnect to enable send"}`;
|
||||||
|
}
|
||||||
|
|
||||||
type EmailMessageDetail = {
|
type EmailMessageDetail = {
|
||||||
id: string;
|
id: string;
|
||||||
threadId: string;
|
threadId: string;
|
||||||
@@ -94,6 +101,7 @@ export default function CorrespondenceInboxPage() {
|
|||||||
const { confirm } = useConfirm();
|
const { confirm } = useConfirm();
|
||||||
const [items, setItems] = useState<CorrespondenceInboxItem[]>([]);
|
const [items, setItems] = useState<CorrespondenceInboxItem[]>([]);
|
||||||
const [providers, setProviders] = useState<EmailProviderStatus[]>([]);
|
const [providers, setProviders] = useState<EmailProviderStatus[]>([]);
|
||||||
|
const [providerStatusError, setProviderStatusError] = useState(false);
|
||||||
const [selectedMessageId, setSelectedMessageId] = useState<number | null>(null);
|
const [selectedMessageId, setSelectedMessageId] = useState<number | null>(null);
|
||||||
const [messageDetail, setMessageDetail] = useState<EmailMessageDetail | null>(null);
|
const [messageDetail, setMessageDetail] = useState<EmailMessageDetail | null>(null);
|
||||||
const [detailLoading, setDetailLoading] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
@@ -134,8 +142,14 @@ export default function CorrespondenceInboxPage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.get<EmailProviderStatus[]>("/email/providers")
|
api.get<EmailProviderStatus[]>("/email/providers")
|
||||||
.then((response) => setProviders(response.data ?? []))
|
.then((response) => {
|
||||||
.catch(() => setProviders([]));
|
setProviders(response.data ?? []);
|
||||||
|
setProviderStatusError(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setProviders([]);
|
||||||
|
setProviderStatusError(true);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const filteredSummary = useMemo(() => {
|
const filteredSummary = useMemo(() => {
|
||||||
@@ -346,10 +360,13 @@ export default function CorrespondenceInboxPage() {
|
|||||||
size="small"
|
size="small"
|
||||||
color={provider.connected ? "success" : "default"}
|
color={provider.connected ? "success" : "default"}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
label={`${provider.displayName}: ${provider.connected ? provider.address || "Connected" : "Not connected"}${provider.canSend ? " · Send access granted" : " · Read only"}`}
|
label={emailProviderStatusLabel(provider)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
{providerStatusError ? (
|
||||||
|
<Alert severity="warning" sx={{ mb: 2 }}>Email provider status is temporarily unavailable. Saved JobTracker correspondence remains available.</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{view === "review" ? <GmailReviewPage embedded /> : <>
|
{view === "review" ? <GmailReviewPage embedded /> : <>
|
||||||
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "2fr 1fr 1fr auto" }, gap: 1.25, mb: 2 }}>
|
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "2fr 1fr 1fr auto" }, gap: 1.25, mb: 2 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user