feat: full IDX universe (780 ticker) — universe table + close sweep harian, dashboard foreign-flow picker dari semua ticker, rotateDepth incremental fill 10/cylcle, strip .JK, backfill cmd/universe-sweep
CI / go test + web typecheck (push) Canceled after 0s
CI / build + deploy (Nix) — flowsight (push) Canceled after 0s

This commit is contained in:
asepharyana
2026-09-16 01:06:42 +07:00
parent 07ece10474
commit 6fd89b6c19
8 changed files with 275 additions and 14 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ async function req<T>(path: string, init?: RequestInit): Promise<T> {
}
export interface Citation { endpoint: string; snapshot_at: string; ticker?: string; stale?: boolean }
export interface Health { last_cycle_at: string; credits_today: number; scheduler_ok: boolean; stale_flags: string[] }
export interface FlowSummary { date: string; foreign_net_total: number; top_accumulation: { ticker: string; net_sum: number; brokers: number }[]; closes: { ticker: string; close: number; date: string }[]; citations: Citation[] }
export interface FlowSummary { date: string; foreign_net_total: number; top_accumulation: { ticker: string; net_sum: number; brokers: number }[]; closes: { ticker: string; close: number; date: string }[]; citations: Citation[]; foreign_tickers?: string[] }
export interface ScreenRow { symbol: string; name: string; composite: number; breakdown: Record<string, unknown>; citations: Citation[] }
export interface Routine { id: number; user_key: string; type: string; schedule_cron: string; channels_json?: string; channels?: string[]; enabled: boolean; last_run?: unknown }
export interface AlertItem { id: number; user_key: string; name: string; rule_json: string; channels_json: string; last_fired: string }
+18 -2
View File
@@ -1,4 +1,4 @@
import { createResource, createSignal, createMemo, For, Show } from "solid-js";
import { createResource, createSignal, createEffect, For, Show } from "solid-js";
import { A, useNavigate } from "@solidjs/router";
import { api } from "../lib/api";
import { useAuth } from "../components/auth";
@@ -22,6 +22,13 @@ export default function Dashboard() {
const [events] = createResource(me, (user) => (user ? api.alertEvents("2000-01-01").then((r) => r.events.slice(0, 5)).catch(() => []) : []));
const [chartTiker, setChartTiker] = createSignal("BBCA");
const [foreign] = createResource(chartTiker, (t) => api.flowForeign(t).catch(() => null));
// Full picker list = every ticker that has foreign-flow rows.
const fkTickers = () => flow()?.foreign_tickers || [];
// Keep the current chart ticker valid even when the list changes.
createEffect(() => {
const list = fkTickers();
if (list.length && !list.includes(chartTiker())) setChartTiker(list[0]);
});
const ringkasan = () => {
const n = briefing()?.narasi?.trim();
@@ -124,7 +131,16 @@ export default function Dashboard() {
<CardDescription><Term kata="foreign flow" /> harian — naik = asing beli, turun = asing jual. Pilih saham:</CardDescription>
</CardHeader>
<CardContent>
<div class="mb-3 flex flex-wrap gap-2">
<div class="mb-3 flex flex-wrap items-center gap-2">
<label class="sr-only" for="fk-ticker">Pilih saham</label>
<select id="fk-ticker" class="h-9 rounded-md border bg-background px-2 text-sm"
value={chartTiker()} onChange={(e) => setChartTiker((e.currentTarget as HTMLSelectElement).value)}>
<For each={fkTickers()}>
{(t) => <option value={t}>{t}</option>}
</For>
</select>
<span class="text-xs text-muted-foreground">{fkTickers().length} saham tersedia</span>
<span class="flex-1" />
<For each={(flow()?.top_accumulation || []).map((t) => t.ticker)}>{(t) =>
<Button size="sm" variant={chartTiker() === t ? "default" : "outline"} onClick={() => setChartTiker(t)}>{t}</Button>
}</For>