feat(ui): watchlist empty-state + chat error styling + hint
CI / go test + web typecheck (push) Canceled after 0s
CI / build + deploy (Nix) — flowsight (push) Canceled after 0s

- WatchlistDrawer: shows helpful hint ('Ketik kode saham lalu Add') when empty
- ChatSidebar: errors in text-destructive, empty state explains how chat works
- styled-components/timestamp formatting and company-name display already in previous commit
This commit is contained in:
asepharyana
2026-09-16 15:39:33 +07:00
parent ffdc0fd9e8
commit ae0d5d813f
+18 -9
View File
@@ -36,7 +36,9 @@ export function WatchlistDrawer() {
</div> </div>
<Show when={err()}><p class="text-xs text-destructive">{err()}</p></Show> <Show when={err()}><p class="text-xs text-destructive">{err()}</p></Show>
<ul class="flex flex-wrap gap-1.5"> <ul class="flex flex-wrap gap-1.5">
<For each={wl()?.watchlist || []}>{(t) => <li class="flex items-center gap-1"><a href={`/report/${encodeURIComponent(t)}`}><Badge variant="secondary">{t}</Badge></a> <button class="text-xs text-muted-foreground hover:text-foreground" aria-label={`Hapus ${t} dari watchlist`} onClick={() => del(t)}>×</button></li>}</For> <Show when={(wl()?.watchlist || []).length} fallback={<li class="text-xs text-muted-foreground">Ketik kode saham lalu Add — mis. BBCA, TLKM, BBRI.</li>}>
<For each={wl()?.watchlist || []}>{(t) => <li class="flex items-center gap-1"><a href={`/report/${encodeURIComponent(t)}`}><Badge variant="secondary">{t}</Badge></a> <button class="text-xs text-muted-foreground hover:text-foreground" aria-label={`Hapus ${t} dari watchlist`} onClick={() => del(t)}>×</button></li>}</For>
</Show>
</ul> </ul>
</CardContent> </CardContent>
</Card> </Card>
@@ -45,7 +47,7 @@ export function WatchlistDrawer() {
export function ChatSidebar() { export function ChatSidebar() {
const [msg, setMsg] = createSignal(""); const [msg, setMsg] = createSignal("");
const [log, setLog] = createSignal<{ q: string; a: string }[]>([]); const [log, setLog] = createSignal<{ q: string; a: string; err?: boolean }[]>([]);
async function send() { async function send() {
const q = msg().trim(); const q = msg().trim();
if (!q) return; if (!q) return;
@@ -54,18 +56,25 @@ export function ChatSidebar() {
const r = await api.chat(q); const r = await api.chat(q);
setLog((p) => [...p, { q, a: r.answer }].slice(-10)); setLog((p) => [...p, { q, a: r.answer }].slice(-10));
} catch (e) { } catch (e) {
setLog((p) => [...p, { q, a: String(e) }].slice(-10)); setLog((p) => [...p, { q, a: String(e), err: true }].slice(-10));
} }
} }
return ( return (
<Card> <Card>
<CardHeader class="pb-2"><CardTitle class="text-base">AI Chat</CardTitle></CardHeader> <CardHeader class="pb-2"><CardTitle class="text-base">AI Chat</CardTitle></CardHeader>
<CardContent class="space-y-2"> <CardContent class="space-y-2">
<div class="max-h-64 space-y-2 overflow-y-auto"> <Show when={log().length} fallback={<p class="text-xs text-muted-foreground">Tanya soal watchlist-mu — sistem jawab dari data yang tersimpan (bukan karangan).</p>}>
<For each={log()}> <div class="max-h-64 space-y-2 overflow-y-auto">
{(m) => <div class="text-sm"><p><strong>You:</strong> {m.q}</p><p class="text-muted-foreground">{m.a.slice(0, 500)}</p></div>} <For each={log()}>
</For> {(m) => (
</div> <div class="text-sm">
<p><strong>You:</strong> {m.q}</p>
<p classList={{ "text-muted-foreground": !m.err, "text-destructive": !!m.err }}>{m.a.slice(0, 500)}</p>
</div>
)}
</For>
</div>
</Show>
<div class="flex gap-2"> <div class="flex gap-2">
<label class="sr-only" for="chat-msg">Tanya AI</label> <label class="sr-only" for="chat-msg">Tanya AI</label>
<TextField class="flex-1"><TextFieldInput id="chat-msg" placeholder="Ask about your watchlist…" value={msg()} onInput={(e) => setMsg(e.currentTarget.value)} onKeyDown={(e: KeyboardEvent) => { if (e.key === "Enter") send(); }} /></TextField> <TextField class="flex-1"><TextFieldInput id="chat-msg" placeholder="Ask about your watchlist…" value={msg()} onInput={(e) => setMsg(e.currentTarget.value)} onKeyDown={(e: KeyboardEvent) => { if (e.key === "Enter") send(); }} /></TextField>
@@ -74,4 +83,4 @@ export function ChatSidebar() {
</CardContent> </CardContent>
</Card> </Card>
); );
} }