mirror of
https://github.com/stackblitz/bolt.new
synced 2025-02-06 04:48:04 +00:00
Event logs bug fix
minor improvements download logs, auto scroll, clear logs
This commit is contained in:
parent
e39f16e436
commit
87057f8137
@ -3,14 +3,34 @@ import { useSettings } from '~/lib/hooks/useSettings';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Switch } from '~/components/ui/Switch';
|
||||
import { logStore, type LogEntry } from '~/lib/stores/logs';
|
||||
import { useStore } from '@nanostores/react';
|
||||
|
||||
export default function EventLogsTab() {
|
||||
const {} = useSettings();
|
||||
const showLogs = useStore(logStore.showLogs);
|
||||
const [logLevel, setLogLevel] = useState<LogEntry['level']>('info');
|
||||
const [autoScroll, setAutoScroll] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [, forceUpdate] = useState({});
|
||||
|
||||
const filteredLogs = useMemo(() => {
|
||||
const logs = logStore.getLogs();
|
||||
return logs.filter((log) => {
|
||||
const matchesLevel = !logLevel || log.level === logLevel;
|
||||
const matchesSearch =
|
||||
!searchQuery ||
|
||||
log.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
JSON.stringify(log.details).toLowerCase().includes(searchQuery.toLowerCase());
|
||||
|
||||
return matchesLevel && matchesSearch;
|
||||
});
|
||||
}, [logLevel, searchQuery]);
|
||||
|
||||
// Effect to initialize showLogs
|
||||
useEffect(() => {
|
||||
logStore.showLogs.set(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Add some initial logs for testing
|
||||
logStore.logSystem('System started', { version: '1.0.0' });
|
||||
@ -18,6 +38,14 @@ export default function EventLogsTab() {
|
||||
logStore.logError('Failed to connect to provider', new Error('Connection timeout'), { provider: 'OpenAI' });
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const container = document.querySelector('.logs-container');
|
||||
|
||||
if (container && autoScroll) {
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
}, [filteredLogs, autoScroll]);
|
||||
|
||||
const handleClearLogs = useCallback(() => {
|
||||
if (confirm('Are you sure you want to clear all logs?')) {
|
||||
logStore.clearLogs();
|
||||
@ -54,10 +82,6 @@ export default function EventLogsTab() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const filteredLogs = useMemo(() => {
|
||||
return logStore.getFilteredLogs(logLevel, undefined, searchQuery);
|
||||
}, [logLevel, searchQuery]);
|
||||
|
||||
const getLevelColor = (level: LogEntry['level']) => {
|
||||
switch (level) {
|
||||
case 'info':
|
||||
@ -76,15 +100,23 @@ export default function EventLogsTab() {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="flex flex-col space-y-4 mb-4">
|
||||
<div className="flex justify-between items-center">
|
||||
{/* Title and Toggles Row */}
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||
<h3 className="text-lg font-medium text-bolt-elements-textPrimary">Event Logs</h3>
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-sm text-bolt-elements-textSecondary">Auto-scroll</span>
|
||||
<span className="text-sm text-bolt-elements-textSecondary whitespace-nowrap">Show Actions</span>
|
||||
<Switch checked={showLogs} onCheckedChange={(checked) => logStore.showLogs.set(checked)} />
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-sm text-bolt-elements-textSecondary whitespace-nowrap">Auto-scroll</span>
|
||||
<Switch checked={autoScroll} onCheckedChange={setAutoScroll} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
{/* Controls Row */}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<select
|
||||
value={logLevel}
|
||||
onChange={(e) => setLogLevel(e.target.value as LogEntry['level'])}
|
||||
@ -95,13 +127,17 @@ export default function EventLogsTab() {
|
||||
<option value="error">Error</option>
|
||||
<option value="debug">Debug</option>
|
||||
</select>
|
||||
<div className="flex-1 min-w-[200px]">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search logs..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm flex-1"
|
||||
className="w-full bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm"
|
||||
/>
|
||||
</div>
|
||||
{showLogs && (
|
||||
<div className="flex items-center gap-2 flex-nowrap">
|
||||
<button
|
||||
onClick={handleExportLogs}
|
||||
className="bg-blue-500 text-white rounded-lg px-3 py-1.5 hover:bg-blue-600 transition-colors duration-200 text-sm whitespace-nowrap"
|
||||
@ -115,9 +151,11 @@ export default function EventLogsTab() {
|
||||
Clear Logs
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-bolt-elements-bg-depth-1 rounded-lg p-4 h-[500px] overflow-y-auto">
|
||||
<div className="bg-bolt-elements-bg-depth-1 rounded-lg p-4 h-[calc(100vh-250px)] min-h-[400px] overflow-y-auto logs-container">
|
||||
{filteredLogs.length === 0 ? (
|
||||
<div className="text-center text-bolt-elements-textSecondary py-8">No logs found</div>
|
||||
) : (
|
||||
@ -126,13 +164,17 @@ export default function EventLogsTab() {
|
||||
key={index}
|
||||
className="text-sm mb-3 font-mono border-b border-bolt-elements-borderColor pb-2 last:border-0"
|
||||
>
|
||||
<div className="flex items-center space-x-2 flex-wrap">
|
||||
<span className={`font-bold ${getLevelColor(log.level)}`}>[{log.level.toUpperCase()}]</span>
|
||||
<span className="text-bolt-elements-textSecondary">{new Date(log.timestamp).toLocaleString()}</span>
|
||||
<span className="text-bolt-elements-textPrimary">{log.message}</span>
|
||||
<div className="flex items-start space-x-2 flex-wrap">
|
||||
<span className={`font-bold ${getLevelColor(log.level)} whitespace-nowrap`}>
|
||||
[{log.level.toUpperCase()}]
|
||||
</span>
|
||||
<span className="text-bolt-elements-textSecondary whitespace-nowrap">
|
||||
{new Date(log.timestamp).toLocaleString()}
|
||||
</span>
|
||||
<span className="text-bolt-elements-textPrimary break-all">{log.message}</span>
|
||||
</div>
|
||||
{log.details && (
|
||||
<pre className="mt-2 text-xs text-bolt-elements-textSecondary overflow-x-auto">
|
||||
<pre className="mt-2 text-xs text-bolt-elements-textSecondary overflow-x-auto whitespace-pre-wrap break-all">
|
||||
{JSON.stringify(log.details, null, 2)}
|
||||
</pre>
|
||||
)}
|
||||
|
@ -51,11 +51,7 @@ export default function ProvidersTab() {
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={`/icons/${provider.name.toLowerCase()}.svg`}
|
||||
alt={`${provider.name} icon`}
|
||||
className="w-6 h-6 dark:invert"
|
||||
/>
|
||||
<img src={`/icons/${provider.name}.svg`} alt={`${provider.name} icon`} className="w-6 h-6 dark:invert" />
|
||||
<span className="text-bolt-elements-textPrimary">{provider.name}</span>
|
||||
</div>
|
||||
<Switch
|
||||
|
@ -17,7 +17,7 @@ const MAX_LOGS = 1000; // Maximum number of logs to keep in memory
|
||||
|
||||
class LogStore {
|
||||
private _logs = map<Record<string, LogEntry>>({});
|
||||
showLogs = atom(false);
|
||||
showLogs = atom(true);
|
||||
|
||||
constructor() {
|
||||
// Load saved logs from cookies on initialization
|
||||
|
Loading…
Reference in New Issue
Block a user