Stijnus commited on
Commit
87057f8
·
1 Parent(s): e39f16e

Event logs bug fix

Browse files

minor improvements download logs, auto scroll, clear logs

app/components/settings/event-logs/EventLogsTab.tsx CHANGED
@@ -3,14 +3,34 @@ import { useSettings } from '~/lib/hooks/useSettings';
3
  import { toast } from 'react-toastify';
4
  import { Switch } from '~/components/ui/Switch';
5
  import { logStore, type LogEntry } from '~/lib/stores/logs';
 
6
 
7
  export default function EventLogsTab() {
8
  const {} = useSettings();
 
9
  const [logLevel, setLogLevel] = useState<LogEntry['level']>('info');
10
  const [autoScroll, setAutoScroll] = useState(true);
11
  const [searchQuery, setSearchQuery] = useState('');
12
  const [, forceUpdate] = useState({});
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  useEffect(() => {
15
  // Add some initial logs for testing
16
  logStore.logSystem('System started', { version: '1.0.0' });
@@ -18,6 +38,14 @@ export default function EventLogsTab() {
18
  logStore.logError('Failed to connect to provider', new Error('Connection timeout'), { provider: 'OpenAI' });
19
  }, []);
20
 
 
 
 
 
 
 
 
 
21
  const handleClearLogs = useCallback(() => {
22
  if (confirm('Are you sure you want to clear all logs?')) {
23
  logStore.clearLogs();
@@ -54,10 +82,6 @@ export default function EventLogsTab() {
54
  }
55
  }, []);
56
 
57
- const filteredLogs = useMemo(() => {
58
- return logStore.getFilteredLogs(logLevel, undefined, searchQuery);
59
- }, [logLevel, searchQuery]);
60
-
61
  const getLevelColor = (level: LogEntry['level']) => {
62
  switch (level) {
63
  case 'info':
@@ -76,15 +100,23 @@ export default function EventLogsTab() {
76
  return (
77
  <div className="p-4">
78
  <div className="flex flex-col space-y-4 mb-4">
79
- <div className="flex justify-between items-center">
 
80
  <h3 className="text-lg font-medium text-bolt-elements-textPrimary">Event Logs</h3>
81
- <div className="flex items-center space-x-2">
82
- <span className="text-sm text-bolt-elements-textSecondary">Auto-scroll</span>
83
- <Switch checked={autoScroll} onCheckedChange={setAutoScroll} />
 
 
 
 
 
 
84
  </div>
85
  </div>
86
 
87
- <div className="flex items-center space-x-2">
 
88
  <select
89
  value={logLevel}
90
  onChange={(e) => setLogLevel(e.target.value as LogEntry['level'])}
@@ -95,29 +127,35 @@ export default function EventLogsTab() {
95
  <option value="error">Error</option>
96
  <option value="debug">Debug</option>
97
  </select>
98
- <input
99
- type="text"
100
- placeholder="Search logs..."
101
- value={searchQuery}
102
- onChange={(e) => setSearchQuery(e.target.value)}
103
- className="bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm flex-1"
104
- />
105
- <button
106
- onClick={handleExportLogs}
107
- 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"
108
- >
109
- Export Logs
110
- </button>
111
- <button
112
- onClick={handleClearLogs}
113
- className="bg-red-500 text-white rounded-lg px-3 py-1.5 hover:bg-red-600 transition-colors duration-200 text-sm whitespace-nowrap"
114
- >
115
- Clear Logs
116
- </button>
 
 
 
 
 
 
117
  </div>
118
  </div>
119
 
120
- <div className="bg-bolt-elements-bg-depth-1 rounded-lg p-4 h-[500px] overflow-y-auto">
121
  {filteredLogs.length === 0 ? (
122
  <div className="text-center text-bolt-elements-textSecondary py-8">No logs found</div>
123
  ) : (
@@ -126,13 +164,17 @@ export default function EventLogsTab() {
126
  key={index}
127
  className="text-sm mb-3 font-mono border-b border-bolt-elements-borderColor pb-2 last:border-0"
128
  >
129
- <div className="flex items-center space-x-2 flex-wrap">
130
- <span className={`font-bold ${getLevelColor(log.level)}`}>[{log.level.toUpperCase()}]</span>
131
- <span className="text-bolt-elements-textSecondary">{new Date(log.timestamp).toLocaleString()}</span>
132
- <span className="text-bolt-elements-textPrimary">{log.message}</span>
 
 
 
 
133
  </div>
134
  {log.details && (
135
- <pre className="mt-2 text-xs text-bolt-elements-textSecondary overflow-x-auto">
136
  {JSON.stringify(log.details, null, 2)}
137
  </pre>
138
  )}
 
3
  import { toast } from 'react-toastify';
4
  import { Switch } from '~/components/ui/Switch';
5
  import { logStore, type LogEntry } from '~/lib/stores/logs';
6
+ import { useStore } from '@nanostores/react';
7
 
8
  export default function EventLogsTab() {
9
  const {} = useSettings();
10
+ const showLogs = useStore(logStore.showLogs);
11
  const [logLevel, setLogLevel] = useState<LogEntry['level']>('info');
12
  const [autoScroll, setAutoScroll] = useState(true);
13
  const [searchQuery, setSearchQuery] = useState('');
14
  const [, forceUpdate] = useState({});
15
 
16
+ const filteredLogs = useMemo(() => {
17
+ const logs = logStore.getLogs();
18
+ return logs.filter((log) => {
19
+ const matchesLevel = !logLevel || log.level === logLevel;
20
+ const matchesSearch =
21
+ !searchQuery ||
22
+ log.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
23
+ JSON.stringify(log.details).toLowerCase().includes(searchQuery.toLowerCase());
24
+
25
+ return matchesLevel && matchesSearch;
26
+ });
27
+ }, [logLevel, searchQuery]);
28
+
29
+ // Effect to initialize showLogs
30
+ useEffect(() => {
31
+ logStore.showLogs.set(true);
32
+ }, []);
33
+
34
  useEffect(() => {
35
  // Add some initial logs for testing
36
  logStore.logSystem('System started', { version: '1.0.0' });
 
38
  logStore.logError('Failed to connect to provider', new Error('Connection timeout'), { provider: 'OpenAI' });
39
  }, []);
40
 
41
+ useEffect(() => {
42
+ const container = document.querySelector('.logs-container');
43
+
44
+ if (container && autoScroll) {
45
+ container.scrollTop = container.scrollHeight;
46
+ }
47
+ }, [filteredLogs, autoScroll]);
48
+
49
  const handleClearLogs = useCallback(() => {
50
  if (confirm('Are you sure you want to clear all logs?')) {
51
  logStore.clearLogs();
 
82
  }
83
  }, []);
84
 
 
 
 
 
85
  const getLevelColor = (level: LogEntry['level']) => {
86
  switch (level) {
87
  case 'info':
 
100
  return (
101
  <div className="p-4">
102
  <div className="flex flex-col space-y-4 mb-4">
103
+ {/* Title and Toggles Row */}
104
+ <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
105
  <h3 className="text-lg font-medium text-bolt-elements-textPrimary">Event Logs</h3>
106
+ <div className="flex flex-wrap items-center gap-4">
107
+ <div className="flex items-center space-x-2">
108
+ <span className="text-sm text-bolt-elements-textSecondary whitespace-nowrap">Show Actions</span>
109
+ <Switch checked={showLogs} onCheckedChange={(checked) => logStore.showLogs.set(checked)} />
110
+ </div>
111
+ <div className="flex items-center space-x-2">
112
+ <span className="text-sm text-bolt-elements-textSecondary whitespace-nowrap">Auto-scroll</span>
113
+ <Switch checked={autoScroll} onCheckedChange={setAutoScroll} />
114
+ </div>
115
  </div>
116
  </div>
117
 
118
+ {/* Controls Row */}
119
+ <div className="flex flex-wrap items-center gap-2">
120
  <select
121
  value={logLevel}
122
  onChange={(e) => setLogLevel(e.target.value as LogEntry['level'])}
 
127
  <option value="error">Error</option>
128
  <option value="debug">Debug</option>
129
  </select>
130
+ <div className="flex-1 min-w-[200px]">
131
+ <input
132
+ type="text"
133
+ placeholder="Search logs..."
134
+ value={searchQuery}
135
+ onChange={(e) => setSearchQuery(e.target.value)}
136
+ className="w-full bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm"
137
+ />
138
+ </div>
139
+ {showLogs && (
140
+ <div className="flex items-center gap-2 flex-nowrap">
141
+ <button
142
+ onClick={handleExportLogs}
143
+ 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"
144
+ >
145
+ Export Logs
146
+ </button>
147
+ <button
148
+ onClick={handleClearLogs}
149
+ className="bg-red-500 text-white rounded-lg px-3 py-1.5 hover:bg-red-600 transition-colors duration-200 text-sm whitespace-nowrap"
150
+ >
151
+ Clear Logs
152
+ </button>
153
+ </div>
154
+ )}
155
  </div>
156
  </div>
157
 
158
+ <div className="bg-bolt-elements-bg-depth-1 rounded-lg p-4 h-[calc(100vh-250px)] min-h-[400px] overflow-y-auto logs-container">
159
  {filteredLogs.length === 0 ? (
160
  <div className="text-center text-bolt-elements-textSecondary py-8">No logs found</div>
161
  ) : (
 
164
  key={index}
165
  className="text-sm mb-3 font-mono border-b border-bolt-elements-borderColor pb-2 last:border-0"
166
  >
167
+ <div className="flex items-start space-x-2 flex-wrap">
168
+ <span className={`font-bold ${getLevelColor(log.level)} whitespace-nowrap`}>
169
+ [{log.level.toUpperCase()}]
170
+ </span>
171
+ <span className="text-bolt-elements-textSecondary whitespace-nowrap">
172
+ {new Date(log.timestamp).toLocaleString()}
173
+ </span>
174
+ <span className="text-bolt-elements-textPrimary break-all">{log.message}</span>
175
  </div>
176
  {log.details && (
177
+ <pre className="mt-2 text-xs text-bolt-elements-textSecondary overflow-x-auto whitespace-pre-wrap break-all">
178
  {JSON.stringify(log.details, null, 2)}
179
  </pre>
180
  )}
app/components/settings/providers/ProvidersTab.tsx CHANGED
@@ -51,11 +51,7 @@ export default function ProvidersTab() {
51
  >
52
  <div className="flex items-center justify-between mb-2">
53
  <div className="flex items-center gap-2">
54
- <img
55
- src={`/icons/${provider.name.toLowerCase()}.svg`}
56
- alt={`${provider.name} icon`}
57
- className="w-6 h-6 dark:invert"
58
- />
59
  <span className="text-bolt-elements-textPrimary">{provider.name}</span>
60
  </div>
61
  <Switch
 
51
  >
52
  <div className="flex items-center justify-between mb-2">
53
  <div className="flex items-center gap-2">
54
+ <img src={`/icons/${provider.name}.svg`} alt={`${provider.name} icon`} className="w-6 h-6 dark:invert" />
 
 
 
 
55
  <span className="text-bolt-elements-textPrimary">{provider.name}</span>
56
  </div>
57
  <Switch
app/lib/stores/logs.ts CHANGED
@@ -17,7 +17,7 @@ const MAX_LOGS = 1000; // Maximum number of logs to keep in memory
17
 
18
  class LogStore {
19
  private _logs = map<Record<string, LogEntry>>({});
20
- showLogs = atom(false);
21
 
22
  constructor() {
23
  // Load saved logs from cookies on initialization
 
17
 
18
  class LogStore {
19
  private _logs = map<Record<string, LogEntry>>({});
20
+ showLogs = atom(true);
21
 
22
  constructor() {
23
  // Load saved logs from cookies on initialization