mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-24 12:13:28 +00:00
Refine v5 release: UI consistency and logging cleanup
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user