feat(ui): watchlist empty-state + chat error styling + hint
- 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:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user