agentlytics 0.0.7 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/cache.js +67 -24
- package/editors/codex.js +453 -0
- package/editors/commandcode.js +159 -0
- package/editors/index.js +3 -1
- package/index.js +1 -1
- package/package.json +3 -2
- package/server.js +60 -4
- package/ui/src/App.jsx +40 -3
- package/ui/src/components/ActivityHeatmap.jsx +11 -7
- package/ui/src/components/ChatSidebar.jsx +313 -0
- package/ui/src/components/DateRangePicker.jsx +104 -0
- package/ui/src/index.css +6 -0
- package/ui/src/lib/api.js +17 -3
- package/ui/src/lib/constants.js +16 -0
- package/ui/src/pages/ChatDetail.jsx +13 -3
- package/ui/src/pages/Dashboard.jsx +14 -14
- package/ui/src/pages/DeepAnalysis.jsx +6 -3
- package/ui/src/pages/ProjectDetail.jsx +236 -0
- package/ui/src/pages/Projects.jsx +50 -121
- package/ui/src/pages/Sessions.jsx +58 -46
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { useState, useEffect, useMemo, useRef, useCallback } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { useSearchParams } from 'react-router-dom'
|
|
3
3
|
import { Search, Filter, List, FolderOpen, ChevronDown, ChevronRight, X } from 'lucide-react'
|
|
4
4
|
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Tooltip, Legend, Filler } from 'chart.js'
|
|
5
5
|
import { Line } from 'react-chartjs-2'
|
|
6
6
|
import { fetchChats } from '../lib/api'
|
|
7
|
-
import { editorColor, editorLabel, formatDate } from '../lib/constants'
|
|
7
|
+
import { editorColor, editorLabel, formatDate, dateRangeToApiParams } from '../lib/constants'
|
|
8
8
|
import { useTheme } from '../lib/theme'
|
|
9
9
|
import EditorDot from '../components/EditorDot'
|
|
10
|
+
import DateRangePicker from '../components/DateRangePicker'
|
|
11
|
+
import ChatSidebar from '../components/ChatSidebar'
|
|
10
12
|
|
|
11
13
|
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Tooltip, Legend, Filler)
|
|
12
14
|
|
|
@@ -107,8 +109,9 @@ export default function Sessions({ overview }) {
|
|
|
107
109
|
const [loading, setLoading] = useState(true)
|
|
108
110
|
const [groupByProject, setGroupByProject] = useState(false)
|
|
109
111
|
const [collapsedProjects, setCollapsedProjects] = useState(new Set())
|
|
110
|
-
const [dateRange, setDateRange] = useState(null) // [startWeek, endWeek]
|
|
111
|
-
const
|
|
112
|
+
const [dateRange, setDateRange] = useState(null) // [startWeek, endWeek] — chart drag-select
|
|
113
|
+
const [apiDateRange, setApiDateRange] = useState(null) // { from, to } — server-side date filter
|
|
114
|
+
const [selectedChatId, setSelectedChatId] = useState(null)
|
|
112
115
|
const chartRef = useRef(null)
|
|
113
116
|
|
|
114
117
|
const onRangeSelect = useCallback((start, end) => {
|
|
@@ -117,12 +120,12 @@ export default function Sessions({ overview }) {
|
|
|
117
120
|
|
|
118
121
|
useEffect(() => {
|
|
119
122
|
setLoading(true)
|
|
120
|
-
fetchChats({ editor, limit: 1000 }).then(data => {
|
|
123
|
+
fetchChats({ editor, limit: 1000, ...dateRangeToApiParams(apiDateRange) }).then(data => {
|
|
121
124
|
setChats(data.chats)
|
|
122
125
|
setTotal(data.total)
|
|
123
126
|
setLoading(false)
|
|
124
127
|
})
|
|
125
|
-
}, [editor])
|
|
128
|
+
}, [editor, apiDateRange])
|
|
126
129
|
|
|
127
130
|
const searchFiltered = search
|
|
128
131
|
? chats.filter(c =>
|
|
@@ -212,7 +215,7 @@ export default function Sessions({ overview }) {
|
|
|
212
215
|
style={{ borderBottom: '1px solid var(--c-border)' }}
|
|
213
216
|
onMouseEnter={e => e.currentTarget.style.background = 'var(--c-bg3)'}
|
|
214
217
|
onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
|
|
215
|
-
onClick={() =>
|
|
218
|
+
onClick={() => setSelectedChatId(c.id)}
|
|
216
219
|
>
|
|
217
220
|
<td className="py-2.5 px-4">
|
|
218
221
|
<EditorDot source={c.source} showLabel size={7} />
|
|
@@ -229,6 +232,9 @@ export default function Sessions({ overview }) {
|
|
|
229
232
|
<td className="py-2.5 px-4">
|
|
230
233
|
<span className="text-xs" style={{ color: 'var(--c-text2)' }}>{c.mode}</span>
|
|
231
234
|
</td>
|
|
235
|
+
<td className="py-2.5 px-4 text-xs truncate max-w-[180px] font-mono" style={{ color: 'var(--c-text2)' }} title={c.topModel || ''}>
|
|
236
|
+
{c.topModel || ''}
|
|
237
|
+
</td>
|
|
232
238
|
<td className="py-2.5 px-4 text-xs whitespace-nowrap" style={{ color: 'var(--c-text2)' }}>
|
|
233
239
|
{formatDate(c.lastUpdatedAt || c.createdAt)}
|
|
234
240
|
</td>
|
|
@@ -283,46 +289,48 @@ export default function Sessions({ overview }) {
|
|
|
283
289
|
|
|
284
290
|
{/* Filters */}
|
|
285
291
|
<div className="flex items-center gap-3">
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
292
|
+
<div className="relative">
|
|
293
|
+
<Filter size={13} className="absolute left-3 top-1/2 -translate-y-1/2" style={{ color: 'var(--c-text3)' }} />
|
|
294
|
+
<select
|
|
295
|
+
value={editor}
|
|
296
|
+
onChange={e => setEditor(e.target.value)}
|
|
297
|
+
className="pl-8 pr-3 py-1 text-[11px] outline-none appearance-none cursor-pointer"
|
|
298
|
+
style={{ background: 'var(--c-bg3)', color: 'var(--c-text)', border: '1px solid var(--c-border)' }}
|
|
299
|
+
>
|
|
300
|
+
<option value="">all editors</option>
|
|
301
|
+
{editors.map(e => (
|
|
302
|
+
<option key={e.id} value={e.id}>{editorLabel(e.id)} ({e.count})</option>
|
|
303
|
+
))}
|
|
304
|
+
</select>
|
|
305
|
+
</div>
|
|
306
|
+
<div className="relative flex-1 max-w-sm">
|
|
307
|
+
<Search size={13} className="absolute left-3 top-1/2 -translate-y-1/2" style={{ color: 'var(--c-text3)' }} />
|
|
308
|
+
<input
|
|
309
|
+
type="text"
|
|
310
|
+
placeholder="search sessions..."
|
|
311
|
+
value={search}
|
|
312
|
+
onChange={e => setSearch(e.target.value)}
|
|
313
|
+
className="w-full pl-8 pr-3 py-1 text-[11px] outline-none"
|
|
314
|
+
style={{ background: 'var(--c-bg3)', color: 'var(--c-text)', border: '1px solid var(--c-border)' }}
|
|
315
|
+
/>
|
|
316
|
+
</div>
|
|
317
|
+
<button
|
|
318
|
+
onClick={() => setGroupByProject(!groupByProject)}
|
|
319
|
+
className="flex items-center gap-1.5 px-3 py-1 text-[11px] transition"
|
|
320
|
+
style={{
|
|
321
|
+
border: groupByProject ? '1px solid var(--c-accent)' : '1px solid var(--c-border)',
|
|
322
|
+
color: groupByProject ? 'var(--c-accent)' : 'var(--c-text2)',
|
|
323
|
+
background: groupByProject ? 'rgba(99,102,241,0.1)' : 'transparent',
|
|
324
|
+
}}
|
|
293
325
|
>
|
|
294
|
-
<
|
|
295
|
-
{
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
<input
|
|
303
|
-
type="text"
|
|
304
|
-
placeholder="search sessions..."
|
|
305
|
-
value={search}
|
|
306
|
-
onChange={e => setSearch(e.target.value)}
|
|
307
|
-
className="w-full pl-8 pr-3 py-2 text-sm outline-none"
|
|
308
|
-
style={{ background: 'var(--c-bg3)', color: 'var(--c-text)', border: '1px solid var(--c-border)' }}
|
|
309
|
-
/>
|
|
310
|
-
</div>
|
|
311
|
-
<button
|
|
312
|
-
onClick={() => setGroupByProject(!groupByProject)}
|
|
313
|
-
className="flex items-center gap-1.5 px-3 py-2 text-xs transition"
|
|
314
|
-
style={{
|
|
315
|
-
border: groupByProject ? '1px solid var(--c-accent)' : '1px solid var(--c-border)',
|
|
316
|
-
color: groupByProject ? 'var(--c-accent)' : 'var(--c-text2)',
|
|
317
|
-
background: groupByProject ? 'rgba(99,102,241,0.1)' : 'transparent',
|
|
318
|
-
}}
|
|
319
|
-
>
|
|
320
|
-
{groupByProject ? <FolderOpen size={13} /> : <List size={13} />}
|
|
321
|
-
{groupByProject ? 'grouped' : 'flat'}
|
|
322
|
-
</button>
|
|
323
|
-
<span className="text-[10px]" style={{ color: 'var(--c-text3)' }}>
|
|
324
|
-
{loading ? 'loading...' : `${filtered.length} of ${total}`}
|
|
325
|
-
</span>
|
|
326
|
+
{groupByProject ? <FolderOpen size={13} /> : <List size={13} />}
|
|
327
|
+
{groupByProject ? 'grouped' : 'flat'}
|
|
328
|
+
</button>
|
|
329
|
+
<span className="text-[10px]" style={{ color: 'var(--c-text3)' }}>
|
|
330
|
+
{loading ? 'loading...' : `${filtered.length} of ${total}`}
|
|
331
|
+
</span>
|
|
332
|
+
{/* Server-side date range filter */}
|
|
333
|
+
<div className="ml-auto"><DateRangePicker value={apiDateRange} onChange={setApiDateRange} /></div>
|
|
326
334
|
</div>
|
|
327
335
|
|
|
328
336
|
{/* Session table */}
|
|
@@ -335,6 +343,7 @@ export default function Sessions({ overview }) {
|
|
|
335
343
|
<th className="text-left py-2.5 px-4 font-medium">name</th>
|
|
336
344
|
<th className="text-left py-2.5 px-4 font-medium">project</th>
|
|
337
345
|
<th className="text-left py-2.5 px-4 font-medium">mode</th>
|
|
346
|
+
<th className="text-left py-2.5 px-4 font-medium">model</th>
|
|
338
347
|
<th className="text-left py-2.5 px-4 font-medium">updated</th>
|
|
339
348
|
</tr>
|
|
340
349
|
</thead>
|
|
@@ -383,6 +392,9 @@ export default function Sessions({ overview }) {
|
|
|
383
392
|
)}
|
|
384
393
|
</div>
|
|
385
394
|
)}
|
|
395
|
+
|
|
396
|
+
{/* Chat sidebar */}
|
|
397
|
+
<ChatSidebar chatId={selectedChatId} onClose={() => setSelectedChatId(null)} />
|
|
386
398
|
</div>
|
|
387
399
|
)
|
|
388
400
|
}
|