feat(observabilidade): debounce search + row clickable inteira

This commit is contained in:
2026-04-23 02:01:25 +01:00
parent d2452d4402
commit 2c8525bc8a
2 changed files with 23 additions and 9 deletions
+15 -3
View File
@@ -1,4 +1,4 @@
import { useState } from 'react' import { useEffect, useState } from 'react'
export interface Filters { export interface Filters {
days: number days: number
@@ -18,6 +18,18 @@ interface Props {
export function FilterBar({ initial, projects, tools, skills, onChange }: Props) { export function FilterBar({ initial, projects, tools, skills, onChange }: Props) {
const [f, setF] = useState<Filters>(initial) const [f, setF] = useState<Filters>(initial)
const [qLocal, setQLocal] = useState<string>(initial.q)
useEffect(() => {
const t = setTimeout(() => {
if (qLocal !== f.q) {
const next = { ...f, q: qLocal }
setF(next)
onChange(next)
}
}, 300)
return () => clearTimeout(t)
}, [qLocal])
function update(partial: Partial<Filters>) { function update(partial: Partial<Filters>) {
const next = { ...f, ...partial } const next = { ...f, ...partial }
@@ -81,8 +93,8 @@ export function FilterBar({ initial, projects, tools, skills, onChange }: Props)
<input <input
type="search" type="search"
placeholder="Pesquisar no prompt inicial…" placeholder="Pesquisar no prompt inicial…"
value={f.q} value={qLocal}
onChange={(e) => update({ q: e.target.value })} onChange={(e) => setQLocal(e.target.value)}
className="flex-1 min-w-[200px] bg-slate-900 border border-white/10 rounded px-3 py-2 text-sm" className="flex-1 min-w-[200px] bg-slate-900 border border-white/10 rounded px-3 py-2 text-sm"
/> />
</div> </div>
+6 -4
View File
@@ -1,4 +1,4 @@
import { Link } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import type { SessionMeta } from '../../../api/types/session' import type { SessionMeta } from '../../../api/types/session'
function formatDuration(sec: number | null): string { function formatDuration(sec: number | null): string {
@@ -26,16 +26,18 @@ interface Props {
} }
export function SessionRow({ session }: Props) { export function SessionRow({ session }: Props) {
const navigate = useNavigate()
const when = new Date(session.started_at).toLocaleString('pt-PT', { dateStyle: 'short', timeStyle: 'short' }) const when = new Date(session.started_at).toLocaleString('pt-PT', { dateStyle: 'short', timeStyle: 'short' })
return ( return (
<tr className="border-b border-white/5 hover:bg-white/5"> <tr
className="border-b border-white/5 hover:bg-white/5 cursor-pointer"
onClick={() => navigate(`/sessions/${session.session_id}`)}
>
<td className="px-3 py-2 text-sm text-slate-300">{when}</td> <td className="px-3 py-2 text-sm text-slate-300">{when}</td>
<td className="px-3 py-2 text-sm text-slate-400">{session.project_slug}</td> <td className="px-3 py-2 text-sm text-slate-400">{session.project_slug}</td>
<td className="px-3 py-2 text-sm text-slate-200"> <td className="px-3 py-2 text-sm text-slate-200">
<Link to={`/sessions/${session.session_id}`} className="hover:underline">
{session.first_prompt?.slice(0, 80) ?? '—'} {session.first_prompt?.slice(0, 80) ?? '—'}
{(session.first_prompt?.length ?? 0) > 80 ? '…' : ''} {(session.first_prompt?.length ?? 0) > 80 ? '…' : ''}
</Link>
</td> </td>
<td className="px-3 py-2 text-sm text-slate-400">{formatDuration(session.duration_sec)}</td> <td className="px-3 py-2 text-sm text-slate-400">{formatDuration(session.duration_sec)}</td>
<td className="px-3 py-2 text-sm text-right text-slate-400">{session.event_count}</td> <td className="px-3 py-2 text-sm text-right text-slate-400">{session.event_count}</td>