Skip to content

Commit bd7b7d9

Browse files
leopu00claude
andcommitted
feat(hooks): pagina /hooks — lista hook attivi con EventBadge, source, status, discovery da workspace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent de6c82d commit bd7b7d9

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

web/app/hooks/page.tsx

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
'use client'
2+
3+
import Link from 'next/link'
4+
import { useEffect, useState, useCallback } from 'react'
5+
6+
type Hook = { name: string; description: string; source: string; events: string[]; enabled: boolean }
7+
8+
function EventBadge({ event }: { event: string }) {
9+
const [type, action] = event.split(':')
10+
return (
11+
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-[9px] font-mono"
12+
style={{ background: 'rgba(59,130,246,0.08)', color: 'var(--color-blue)', border: '1px solid rgba(59,130,246,0.2)' }}>
13+
<span className="font-bold">{type}</span>{action && <span className="text-[var(--color-dim)]">:{action}</span>}
14+
</span>
15+
)
16+
}
17+
18+
function HookCard({ hook }: { hook: Hook }) {
19+
return (
20+
<div className="flex items-start gap-4 px-5 py-4 border-b border-[var(--color-border)] hover:bg-[var(--color-row)] transition-colors">
21+
<div className="flex-shrink-0 mt-1">
22+
<span className="inline-block w-2 h-2 rounded-full" style={{ background: hook.enabled ? 'var(--color-green)' : 'var(--color-dim)' }} />
23+
</div>
24+
<div className="flex-1 min-w-0">
25+
<div className="flex items-center gap-2 mb-1">
26+
<span className="text-[12px] font-semibold font-mono text-[var(--color-bright)]">{hook.name}</span>
27+
<span className="badge text-[8px]" style={{ color: 'var(--color-dim)', border: '1px solid var(--color-border)' }}>{hook.source}</span>
28+
<span className="badge text-[8px]" style={{
29+
color: hook.enabled ? 'var(--color-green)' : 'var(--color-dim)',
30+
background: hook.enabled ? 'rgba(0,200,83,0.08)' : 'transparent',
31+
border: `1px solid ${hook.enabled ? 'rgba(0,200,83,0.3)' : 'var(--color-border)'}`,
32+
}}>{hook.enabled ? 'attivo' : 'disattivo'}</span>
33+
</div>
34+
{hook.description && <p className="text-[10px] text-[var(--color-muted)] mb-1.5">{hook.description}</p>}
35+
<div className="flex flex-wrap gap-1">
36+
{hook.events.map(e => <EventBadge key={e} event={e} />)}
37+
{hook.events.length === 0 && <span className="text-[9px] text-[var(--color-dim)]">nessun evento</span>}
38+
</div>
39+
</div>
40+
</div>
41+
)
42+
}
43+
44+
export default function HooksPage() {
45+
const [hooks, setHooks] = useState<Hook[]>([])
46+
const [total, setTotal] = useState(0)
47+
const [hooksDir, setHooksDir] = useState('')
48+
const [error, setError] = useState('')
49+
50+
const fetchHooks = useCallback(async () => {
51+
const res = await fetch('/api/hooks').catch(() => null)
52+
if (!res?.ok) return
53+
const data = await res.json()
54+
setHooks(data.hooks ?? [])
55+
setTotal(data.total ?? 0)
56+
setHooksDir(data.hooksDir ?? '')
57+
setError(data.error ?? '')
58+
}, [])
59+
60+
useEffect(() => { fetchHooks() }, [fetchHooks])
61+
62+
const activeCount = hooks.filter(h => h.enabled).length
63+
const totalEvents = hooks.reduce((s, h) => s + h.events.length, 0)
64+
65+
return (
66+
<div style={{ animation: 'fade-in 0.35s ease both' }}>
67+
<div className="mb-8 pb-6 border-b border-[var(--color-border)]">
68+
<div className="flex items-center gap-2 mb-1">
69+
<Link href="/dashboard" className="text-[10px] text-[var(--color-dim)] hover:text-[var(--color-muted)] no-underline transition-colors">Dashboard</Link>
70+
<span className="text-[var(--color-border)]">/</span>
71+
<span className="text-[10px] text-[var(--color-muted)]">Hooks</span>
72+
</div>
73+
<h1 className="text-2xl font-bold tracking-tight text-[var(--color-white)] mt-3">Hooks</h1>
74+
<p className="text-[var(--color-muted)] text-[11px] mt-1">
75+
{activeCount}/{total} attivi · {totalEvents} eventi registrati
76+
{hooksDir && <span className="ml-2 font-mono text-[var(--color-dim)]">{hooksDir}</span>}
77+
</p>
78+
{error && <p className="text-[10px] text-[var(--color-yellow)] mt-1">{error}</p>}
79+
</div>
80+
81+
<div className="border border-[var(--color-border)] rounded-lg overflow-hidden bg-[var(--color-panel)]">
82+
{hooks.length === 0
83+
? <div className="py-16 text-center">
84+
<p className="text-[var(--color-dim)] text-[12px]">Nessun hook trovato.</p>
85+
<p className="text-[var(--color-dim)] text-[10px] mt-1">Crea una cartella hooks/ nel workspace con HOOK.md e handler.ts</p>
86+
</div>
87+
: hooks.map(h => <HookCard key={h.name} hook={h} />)
88+
}
89+
</div>
90+
</div>
91+
)
92+
}

0 commit comments

Comments
 (0)