Refine v5 release: UI consistency and logging cleanup

This commit is contained in:
rcourtman
2025-12-23 11:58:23 +00:00
parent 62ac999466
commit 2dbd32a16b
13 changed files with 54 additions and 36 deletions
+2 -2
View File
@@ -272,7 +272,7 @@ export class AIAPI {
try {
for (; ;) {
if (Date.now() - lastEventTime > STREAM_TIMEOUT_MS) {
console.warn('[AI] Alert investigation stream timeout');
logger.warn('[AI] Alert investigation stream timeout');
break;
}
@@ -310,7 +310,7 @@ export class AIAPI {
const data = JSON.parse(jsonStr);
onEvent(data as AIStreamEvent);
} catch (e) {
console.error('[AI] Failed to parse investigation event:', e);
logger.error('[AI] Failed to parse investigation event:', e);
}
}
}
+2 -1
View File
@@ -4,6 +4,7 @@
*/
import { apiFetchJSON } from '@/utils/apiClient';
import { logger } from '@/utils/logger';
export type FindingSeverity = 'info' | 'watch' | 'warning' | 'critical';
export type FindingCategory = 'performance' | 'capacity' | 'reliability' | 'backup' | 'security' | 'general';
@@ -339,7 +340,7 @@ export function subscribeToPatrolStream(
const data = JSON.parse(event.data) as PatrolStreamEvent;
onEvent(data);
} catch (e) {
console.error('Failed to parse patrol stream event:', e);
logger.error('Failed to parse patrol stream event:', e);
}
};
@@ -1,6 +1,7 @@
import { Component, createSignal, createEffect, For, Show, createMemo } from 'solid-js';
import { notificationStore } from '@/stores/notifications';
import { apiFetch } from '@/utils/apiClient';
import { logger } from '@/utils/logger';
interface Note {
id: string;
@@ -146,7 +147,7 @@ export const GuestNotes: Component<GuestNotesProps> = (props) => {
}
}
} catch (error) {
console.error('Failed to load guest knowledge:', error);
logger.error('Failed to load guest knowledge:', error);
} finally {
setIsLoading(false);
}
@@ -169,7 +170,7 @@ export const GuestNotes: Component<GuestNotesProps> = (props) => {
notificationStore.error('Failed to save guest URL');
}
} catch (error) {
console.error('Failed to save guest URL:', error);
logger.error('Failed to save guest URL:', error);
notificationStore.error('Failed to save guest URL');
} finally {
setIsSavingUrl(false);
@@ -207,7 +208,7 @@ export const GuestNotes: Component<GuestNotesProps> = (props) => {
notificationStore.error('Failed to save note');
}
} catch (error) {
console.error('Failed to save note:', error);
logger.error('Failed to save note:', error);
notificationStore.error('Failed to save note');
}
};
@@ -231,7 +232,7 @@ export const GuestNotes: Component<GuestNotesProps> = (props) => {
notificationStore.error('Failed to delete note');
}
} catch (error) {
console.error('Failed to delete note:', error);
logger.error('Failed to delete note:', error);
notificationStore.error('Failed to delete note');
}
};
@@ -256,7 +257,7 @@ export const GuestNotes: Component<GuestNotesProps> = (props) => {
notificationStore.error('Failed to export notes');
}
} catch (error) {
console.error('Failed to export notes:', error);
logger.error('Failed to export notes:', error);
notificationStore.error('Failed to export notes');
}
};
@@ -296,7 +297,7 @@ export const GuestNotes: Component<GuestNotesProps> = (props) => {
notificationStore.error('Import failed: ' + errorText);
}
} catch (error) {
console.error('Failed to import notes:', error);
logger.error('Failed to import notes:', error);
notificationStore.error('Failed to parse import file');
} finally {
setIsImporting(false);
@@ -328,7 +329,7 @@ export const GuestNotes: Component<GuestNotesProps> = (props) => {
notificationStore.error('Failed to clear notes');
}
} catch (error) {
console.error('Failed to clear notes:', error);
logger.error('Failed to clear notes:', error);
notificationStore.error('Failed to clear notes');
}
};
@@ -1,5 +1,6 @@
import { Component, createSignal, Show, onMount } from 'solid-js';
import { showSuccess, showError } from '@/utils/toast';
import { logger } from '@/utils/logger';
import { copyToClipboard } from '@/utils/clipboard';
import { clearAuth as clearApiClientAuth, setApiToken as setApiClientToken } from '@/utils/apiClient';
import { getPulseBaseUrl } from '@/utils/url';
@@ -83,7 +84,7 @@ export const FirstRunSetup: Component<{ force?: boolean; showLegacyBanner?: bool
}
}
} catch (error) {
console.error('Failed to fetch bootstrap token path:', error);
logger.error('Failed to fetch bootstrap token path:', error);
}
});
@@ -736,10 +736,7 @@ export const UnifiedAgents: Component = () => {
<p class="text-xs text-gray-600 dark:text-gray-400">
Run this command on any host to remove the Pulse agent:
</p>
<div class="flex items-center gap-2">
<code class="flex-1 break-all rounded bg-gray-900 px-3 py-2 font-mono text-xs text-red-400 dark:bg-gray-950">
{getUninstallCommand()}
</code>
<div class="relative">
<button
type="button"
onClick={async () => {
@@ -748,10 +745,17 @@ export const UnifiedAgents: Component = () => {
window.showToast(success ? 'success' : 'error', success ? 'Copied!' : 'Failed');
}
}}
class="rounded-lg bg-red-50 px-3 py-1.5 text-xs font-medium text-red-700 transition-colors hover:bg-red-100 dark:bg-red-900/30 dark:text-red-300 dark:hover:bg-red-900/50"
class="absolute top-2 right-2 p-1.5 text-gray-400 hover:text-gray-200 bg-gray-700/50 hover:bg-gray-700 rounded-md transition-colors"
title="Copy command"
>
Copy
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
</svg>
</button>
<pre class="overflow-x-auto rounded-md bg-gray-950 p-3 pr-12 font-mono text-xs text-red-400">
<code>{getUninstallCommand()}</code>
</pre>
</div>
</div>
</div>
@@ -782,10 +786,7 @@ export const UnifiedAgents: Component = () => {
<p class="text-xs text-amber-600 dark:text-amber-400">
Run this command on each legacy host to upgrade:
</p>
<div class="flex items-center gap-2">
<code class="flex-1 break-all rounded bg-amber-100 px-3 py-2 font-mono text-xs text-amber-900 dark:bg-amber-900/40 dark:text-amber-100">
{getUpgradeCommand('')}
</code>
<div class="relative">
<button
type="button"
onClick={async () => {
@@ -794,10 +795,17 @@ export const UnifiedAgents: Component = () => {
window.showToast(success ? 'success' : 'error', success ? 'Copied!' : 'Failed to copy');
}
}}
class="rounded-lg bg-amber-200 px-3 py-1.5 text-xs font-medium text-amber-800 transition-colors hover:bg-amber-300 dark:bg-amber-800 dark:text-amber-100 dark:hover:bg-amber-700"
class="absolute top-2 right-2 p-1.5 text-amber-700/60 hover:text-amber-900 bg-amber-200/50 hover:bg-amber-300/50 rounded-md transition-colors dark:text-amber-400 dark:hover:text-amber-200 dark:bg-amber-800/50 dark:hover:bg-amber-700/50"
title="Copy command"
>
Copy
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
</svg>
</button>
<pre class="overflow-x-auto rounded-md bg-amber-100 px-3 py-3 pr-12 font-mono text-xs text-amber-900 dark:bg-amber-900/40 dark:text-amber-100">
<code>{getUpgradeCommand('')}</code>
</pre>
</div>
</div>
</div>
@@ -1,6 +1,7 @@
import { Component, createSignal, createEffect, onCleanup, Show, For } from 'solid-js';
// Note: For is still used for connectedAgents list
import { copyToClipboard } from '@/utils/clipboard';
import { logger } from '@/utils/logger';
import { getPulseBaseUrl } from '@/utils/url';
import { SecurityAPI } from '@/api/security';
import { ProxmoxIcon } from '@/components/icons/ProxmoxIcon';
@@ -108,7 +109,7 @@ export const CompleteStep: Component<CompleteStepProps> = (props) => {
previousCount = totalAgents;
}
} catch (error) {
console.error('Failed to check for agents:', error);
logger.error('Failed to check for agents:', error);
}
};
@@ -138,7 +139,7 @@ export const CompleteStep: Component<CompleteStepProps> = (props) => {
setCurrentInstallToken(result.token);
}
} catch (error) {
console.error('Failed to generate new token:', error);
logger.error('Failed to generate new token:', error);
} finally {
setGeneratingToken(false);
}
@@ -1,5 +1,6 @@
import { Component, createEffect, createSignal, For, Show } from 'solid-js';
import { AIAPI } from '@/api/ai';
import { logger } from '@/utils/logger';
import type { RemediationRecord, RemediationStats } from '@/types/aiIntelligence';
const DEFAULT_UPGRADE_URL = 'https://pulserelay.pro';
@@ -24,7 +25,7 @@ export const AIImpactTimelinePanel: Component<{ hours?: number; showWhenEmpty?:
setStats(response.stats || null);
setRemediations(response.remediations || []);
} catch (e) {
console.error('Failed to load AI impact timeline:', e);
logger.error('Failed to load AI impact timeline:', e);
setError('Failed to load AI impact timeline.');
} finally {
setLoading(false);
@@ -1,5 +1,6 @@
import { Component, createSignal, createEffect, Show, For } from 'solid-js';
import { AIAPI } from '@/api/ai';
import { logger } from '@/utils/logger';
import type { FailurePrediction, ResourceCorrelation } from '@/types/aiIntelligence';
/**
@@ -40,7 +41,7 @@ export const AIInsightsPanel: Component<{ resourceId?: string; showWhenEmpty?: b
setCorrelations(corrResp.correlations || []);
}
} catch (e) {
console.error('Failed to load AI insights:', e);
logger.error('Failed to load AI insights:', e);
setError('Failed to load AI insights.');
} finally {
setLoading(false);
@@ -1,5 +1,6 @@
import { Component, createEffect, createSignal, For, Show } from 'solid-js';
import { AIAPI } from '@/api/ai';
import { logger } from '@/utils/logger';
import type { FailurePrediction, InfrastructureChange, RemediationRecord, RemediationStats, AnomalyReport } from '@/types/aiIntelligence';
const DEFAULT_UPGRADE_URL = 'https://pulserelay.pro';
@@ -110,7 +111,7 @@ export const AIOverviewTable: Component<{ showWhenEmpty?: boolean }> = (props) =
predResp.upgrade_url || corrResp.upgrade_url || remResp.upgrade_url || changesResp.upgrade_url || DEFAULT_UPGRADE_URL
);
} catch (e) {
console.error('Failed to load AI overview data:', e);
logger.error('Failed to load AI overview data:', e);
setError('Failed to load AI overview data.');
} finally {
setLoading(false);
@@ -1,5 +1,6 @@
import { Component, createEffect, createSignal, For, Show } from 'solid-js';
import { AIAPI } from '@/api/ai';
import { logger } from '@/utils/logger';
import type { InfrastructureChange } from '@/types/aiIntelligence';
const DEFAULT_UPGRADE_URL = 'https://pulserelay.pro';
@@ -30,7 +31,7 @@ export const AIRecentChangesPanel: Component<{ hours?: number; showWhenEmpty?: b
setChanges(response.changes || []);
}
} catch (e) {
console.error('Failed to load AI change history:', e);
logger.error('Failed to load AI change history:', e);
setError('Failed to load recent changes.');
} finally {
setLoading(false);
+3 -3
View File
@@ -2357,7 +2357,7 @@ function OverviewTab(props: {
},
() => {
// Error - just log it
console.error('Patrol stream error');
logger.error('Patrol stream error');
}
);
} else if ((!isExpanded || !isRunning) && liveStreamUnsubscribe) {
@@ -2444,7 +2444,7 @@ function OverviewTab(props: {
const response = await AIAPI.getRemediations({ findingId, limit: 3 });
setRemediationsByFinding((prev) => ({ ...prev, [findingId]: response.remediations || [] }));
} catch (err) {
console.error('Failed to load remediation history', err);
logger.error('Failed to load remediation history', err);
} finally {
setRemediationLoadingByFinding((prev) => ({ ...prev, [findingId]: false }));
}
@@ -2467,7 +2467,7 @@ function OverviewTab(props: {
setForcePatrolLoading(false);
}, 2000);
} catch (e) {
console.error('Force patrol error:', e);
logger.error('Force patrol error:', e);
showError('Failed to start patrol: ' + (e instanceof Error ? e.message : 'Unknown error'));
setForcePatrolLoading(false);
}
+3 -2
View File
@@ -1,4 +1,5 @@
import { createSignal } from 'solid-js';
import { logger } from '@/utils/logger';
interface AIChatContext {
targetType?: string;
@@ -49,7 +50,7 @@ const loadMessagesFromStorage = (): Message[] => {
timestamp: new Date(m.timestamp)
}));
} catch (e) {
console.error('Failed to load chat history:', e);
logger.error('Failed to load chat history:', e);
return [];
}
};
@@ -106,7 +107,7 @@ export const aiChatStore = {
try {
localStorage.setItem(HISTORY_STORAGE_KEY, JSON.stringify(msgs));
} catch (e) {
console.error('Failed to save chat history:', e);
logger.error('Failed to save chat history:', e);
}
},
@@ -7,6 +7,7 @@
*/
import { createSignal } from 'solid-js';
import { logger } from '@/utils/logger';
import { STORAGE_KEYS } from '@/utils/localStorage';
import { seedFromBackend, resetSeedingState } from './metricsHistory';
import type { TimeRange } from '@/api/charts';
@@ -87,7 +88,7 @@ export function setMetricsViewModePreference(mode: MetricsViewMode): void {
localStorage.setItem(STORAGE_KEYS.METRICS_VIEW_MODE, mode);
} catch (err) {
// Ignore localStorage errors
console.warn('Failed to save metrics view mode preference', err);
logger.warn('Failed to save metrics view mode preference', err);
}
}
@@ -112,7 +113,7 @@ export function setMetricsTimeRangePreference(range: TimeRange): void {
localStorage.setItem(STORAGE_KEYS.METRICS_TIME_RANGE, range);
} catch (err) {
// Ignore localStorage errors
console.warn('Failed to save metrics time range preference', err);
logger.warn('Failed to save metrics time range preference', err);
}
}