mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-01 16:12:02 +00:00
fix: hide the next heartbeat and consent date in admin usage stats while heartbeats are off
This commit is contained in:
@@ -81,4 +81,30 @@ describe('admin telemetry page', () => {
|
||||
expect.objectContaining({ url: '/api/admin/telemetry', body: { action: 'set-consent', consent: 'on' } }),
|
||||
]));
|
||||
});
|
||||
|
||||
it('shows no next send and no consent date while heartbeats are off', async () => {
|
||||
routes['/api/admin/telemetry'] = {
|
||||
...(routes['/api/admin/telemetry'] as object),
|
||||
consent: 'off', consentedAt: '2026-10-01T10:00:00Z', lastSentAt: '2026-10-02T10:00:00Z',
|
||||
nextScheduledAt: new Date(Date.now() - 7 * 3600_000).toISOString(),
|
||||
};
|
||||
render(<TelemetryPage />);
|
||||
await screen.findByRole('switch', { name: 'Send anonymous heartbeats' });
|
||||
expect(screen.queryByText('Next scheduled')).toBeNull();
|
||||
expect(screen.queryByText('Consented at')).toBeNull();
|
||||
expect(screen.queryByText(/hours ago/)).toBeNull();
|
||||
expect(screen.getByText('Last sent')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows a past next send as due, not as "hours ago", while heartbeats are on', async () => {
|
||||
routes['/api/admin/telemetry'] = {
|
||||
...(routes['/api/admin/telemetry'] as object),
|
||||
consent: 'on', consentedAt: '2026-10-01T10:00:00Z',
|
||||
nextScheduledAt: new Date(Date.now() - 7 * 3600_000).toISOString(),
|
||||
};
|
||||
render(<TelemetryPage />);
|
||||
await screen.findByText('Next scheduled');
|
||||
expect(screen.getByText('Due now')).toBeTruthy();
|
||||
expect(screen.getByText('Consented at')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,6 +40,13 @@ function timeAgo(iso: string | null): string {
|
||||
return `${days} days ago`;
|
||||
}
|
||||
|
||||
/** When the next heartbeat goes out; a time already past means it is due. */
|
||||
function nextRun(iso: string | null): string {
|
||||
if (!iso) return '-';
|
||||
const at = new Date(iso);
|
||||
return at.getTime() <= Date.now() ? 'Due now' : at.toLocaleString();
|
||||
}
|
||||
|
||||
export function TelemetryPage() {
|
||||
const [status, setStatus] = useState<TelemetryStatus | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -177,12 +184,18 @@ export function TelemetryPage() {
|
||||
<SettingItem label="Last sent">
|
||||
<span className="text-sm text-foreground">{timeAgo(status.lastSentAt)}</span>
|
||||
</SettingItem>
|
||||
<SettingItem label="Next scheduled">
|
||||
<span className="text-sm text-foreground">{timeAgo(status.nextScheduledAt)}</span>
|
||||
</SettingItem>
|
||||
<SettingItem label="Consented at">
|
||||
<span className="text-sm text-foreground">{status.consentedAt ? new Date(status.consentedAt).toLocaleString() : '-'}</span>
|
||||
</SettingItem>
|
||||
{/* Only a running schedule has a next send and a consent date; with
|
||||
heartbeats off they were stale ("Next scheduled: 7 hours ago"). */}
|
||||
{isOn && (
|
||||
<>
|
||||
<SettingItem label="Next scheduled">
|
||||
<span className="text-sm text-foreground">{nextRun(status.nextScheduledAt)}</span>
|
||||
</SettingItem>
|
||||
<SettingItem label="Consented at">
|
||||
<span className="text-sm text-foreground">{status.consentedAt ? new Date(status.consentedAt).toLocaleString() : '-'}</span>
|
||||
</SettingItem>
|
||||
</>
|
||||
)}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
|
||||
Reference in New Issue
Block a user