feat: default watchlist semua, login-gate fitur, CI anti-stale via /api/version
This commit is contained in:
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user