feat: default watchlist semua, login-gate fitur, CI anti-stale via /api/version

This commit is contained in:
asepharyana
2026-09-15 22:12:09 +07:00
parent 5c953289b1
commit 79f42dfefa
22 changed files with 602 additions and 84 deletions
+7 -1
View File
@@ -15,7 +15,7 @@ const TEMPLATES: { label: string; desc: string; rule: Record<string, unknown> }[
{ label: "📢 Semua gerakan mencolok", desc: "Skor gabungan di atas ambang", rule: { all: [{ field: "score", op: ">=", value: 60 }] } },
];
export default function Alerts() {
function AlertsInner() {
const [alerts, { refetch }] = createResource(() => api.alerts());
const [events, { refetch: refetchEv }] = createResource(() => api.alertEvents("2000-01-01").then((r) => r.events.slice(0, 30)));
const [dests, { refetch: refetchDests }] = createResource(() => api.destinations());
@@ -127,3 +127,9 @@ export default function Alerts() {
</div>
);
}
import { Gate } from "../index";
export default function Alerts() {
return <Gate fitur="Notifikasi">{AlertsInner()}</Gate>;
}
+7 -1
View File
@@ -9,7 +9,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../co
import { Skeleton } from "../components/ui/skeleton";
Chart.register(...registerables);
export default function Portfolio() {
function PortfolioInner() {
const [risk] = createResource(() => api.risk());
const conc = () => risk()?.concentration || [];
const betaN = () => Number(risk()?.beta ?? 1);
@@ -66,3 +66,9 @@ export default function Portfolio() {
</div>
);
}
import { Gate } from "../index";
export default function Portfolio() {
return <Gate fitur="Portofolio">{PortfolioInner()}</Gate>;
}
+7 -1
View File
@@ -10,7 +10,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card"
import { Skeleton } from "../components/ui/skeleton";
import { TextField, TextFieldInput } from "../components/ui/text-field";
export default function Report() {
function ReportInner() {
const params = useParams();
const [rep, setRep] = createSignal<ReportPayload | null>(null);
const [md, setMd] = createSignal("");
@@ -103,3 +103,9 @@ export default function Report() {
</div>
);
}
import { Gate } from "../index";
export default function Report() {
return <Gate fitur="Report saham">{<ReportInner />}</Gate>;
}
+7 -1
View File
@@ -17,7 +17,7 @@ const MANFAAT: Record<string, { judul: string; desc: string }> = {
"weekend-review": { judul: "📝 Review mingguan", desc: "Ringkasan seminggu: apa yang terjadi dan pelajaran." },
};
export default function Routines() {
function RoutinesInner() {
const [data, { refetch }] = createResource(() => api.routines());
const [runs, { refetch: refetchRuns }] = createResource(() => api.runs().then((r) => r.runs.slice(0, 20)));
const [type_, setType] = createSignal("morning-briefing");
@@ -97,3 +97,9 @@ export default function Routines() {
</div>
);
}
import { Gate } from "../index";
export default function Routines() {
return <Gate fitur="Jadwal otomatis">{RoutinesInner()}</Gate>;
}
+12 -3
View File
@@ -8,15 +8,16 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../co
import { Skeleton } from "../components/ui/skeleton";
import { useNavigate } from "@solidjs/router";
// Preset awam -> body /api/screen.
// Preset awam -> body /api/screen. PRESET_SEMUA = default auto-jalan.
export const PRESET_SEMUA = { label: "📦 Semua — urut paling menarik", desc: "Tanpa filter, ranking gabungan", body: { limit: 20 } };
const PRESETS: { label: string; desc: string; body: Record<string, unknown> }[] = [
{ label: "🔥 Yang lagi diborong bandar", desc: "Broker besar net-beli besar", body: { institutional: { broker_score_min: 5 }, limit: 20 } },
{ label: "🌍 Yang asing lagi beli", desc: "Uang luar negeri masuk", body: { institutional: { foreign_inflow: true }, limit: 20 } },
{ label: "🕵️ Yang orang dalamnya ikut beli", desc: "Insider buying terdeteksi", body: { institutional: { insider_buying: true }, limit: 20 } },
{ label: "📦 Semua — urut paling menarik", desc: "Tanpa filter, ranking gabungan", body: { limit: 20 } },
PRESET_SEMUA,
];
export default function Screener() {
function ScreenerInner() {
const navigate = useNavigate();
const [rows, setRows] = createSignal<ScreenRow[]>([]);
const [ran, setRan] = createSignal(false);
@@ -31,6 +32,8 @@ export default function Screener() {
} catch (e) { setErr(String(e)); }
finally { setBusy(false); }
}
// Default = semua: auto-jalan preset "Semua" sekali saat halaman dibuka.
if (!ran() && !busy()) void runPreset(PRESET_SEMUA);
const hasil = () => rows().map((r) => ({ row: r, ...verdictFor(r) }));
return (
<div class="space-y-4">
@@ -77,3 +80,9 @@ export default function Screener() {
</div>
);
}
import { Gate } from "../index";
export default function Screener() {
return <Gate fitur="Cari saham">{ScreenerInner()}</Gate>;
}