New
This commit is contained in:
parent
32fa617f32
commit
8c9a41c575
1
dist/assets/index-ButmEv-s.css
vendored
1
dist/assets/index-ButmEv-s.css
vendored
File diff suppressed because one or more lines are too long
379
dist/assets/index-z66hXP0q.js
vendored
379
dist/assets/index-z66hXP0q.js
vendored
File diff suppressed because one or more lines are too long
38
dist/index.html
vendored
38
dist/index.html
vendored
@ -1,20 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||
<title>QuantFortune | Long-Term Investment Strategies</title>
|
||||
<meta name="description" content="Simple, disciplined investment strategies for salaried investors. Build wealth steadily with our proven long-term approaches." />
|
||||
<meta property="og:title" content="QuantFortune | Long-Term Investment Strategies" />
|
||||
<meta property="og:description" content="Simple, disciplined investment strategies for salaried investors." />
|
||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Fira+Code:wght@300..700&family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Lora:ital,wght@0,400..700;1,400..700&family=Merriweather:ital,opsz,wght@0,18..144,300..900;1,18..144,300..900&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Outfit:wght@100..900&family=Oxanium:wght@200..800&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100..900;1,100..900&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||
<script type="module" crossorigin src="/assets/index-z66hXP0q.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-ButmEv-s.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||
<title>QuantFortune | Long-Term Investment Strategies</title>
|
||||
<meta name="description" content="Simple, disciplined investment strategies for salaried investors. Build wealth steadily with our proven long-term approaches." />
|
||||
<meta property="og:title" content="QuantFortune | Long-Term Investment Strategies" />
|
||||
<meta property="og:description" content="Simple, disciplined investment strategies for salaried investors." />
|
||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Fira+Code:wght@300..700&family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Lora:ital,wght@0,400..700;1,400..700&family=Merriweather:ital,opsz,wght@0,18..144,300..900;1,18..144,300..900&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Outfit:wght@100..900&family=Oxanium:wght@200..800&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100..900;1,100..900&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||
<script type="module" crossorigin src="/assets/index-Dko5e3BG.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Dq1pAiY6.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@ -45,7 +45,7 @@ export default function AuthDialogs({ layout = "desktop" }: AuthDialogsProps) {
|
||||
});
|
||||
|
||||
useQuery<SessionUser | null>({
|
||||
queryKey: ["/api/me"],
|
||||
queryKey: ["/me"],
|
||||
queryFn: getQueryFn<SessionUser | null>({ on401: "returnNull" }),
|
||||
});
|
||||
|
||||
@ -53,8 +53,8 @@ export default function AuthDialogs({ layout = "desktop" }: AuthDialogsProps) {
|
||||
user: SessionUser,
|
||||
action: "signup" | "login",
|
||||
) => {
|
||||
queryClient.setQueryData(["/api/me"], user);
|
||||
queryClient.invalidateQueries({ queryKey: ["/api/broker/status"] });
|
||||
queryClient.setQueryData(["/me"], user);
|
||||
queryClient.invalidateQueries({ queryKey: ["/broker/status"] });
|
||||
toast({
|
||||
title: action === "signup" ? "Account created" : "Signed in",
|
||||
description:
|
||||
@ -68,7 +68,7 @@ export default function AuthDialogs({ layout = "desktop" }: AuthDialogsProps) {
|
||||
|
||||
const signupMutation = useMutation<SessionUser, Error, AuthPayload>({
|
||||
mutationFn: async (payload) => {
|
||||
const res = await apiRequest("POST", "/api/signup", {
|
||||
const res = await apiRequest("POST", "/signup", {
|
||||
username: payload.email,
|
||||
password: payload.password,
|
||||
});
|
||||
@ -85,7 +85,7 @@ export default function AuthDialogs({ layout = "desktop" }: AuthDialogsProps) {
|
||||
|
||||
const loginMutation = useMutation<SessionUser, Error, AuthPayload>({
|
||||
mutationFn: async (payload) => {
|
||||
const res = await apiRequest("POST", "/api/login", payload);
|
||||
const res = await apiRequest("POST", "/login", payload);
|
||||
return res.json();
|
||||
},
|
||||
onSuccess: (user) => handleAuthSuccess(user, "login"),
|
||||
@ -109,7 +109,7 @@ export default function AuthDialogs({ layout = "desktop" }: AuthDialogsProps) {
|
||||
|
||||
const resetRequestMutation = useMutation({
|
||||
mutationFn: async (email: string) => {
|
||||
const res = await apiRequest("POST", "/api/password-reset/request", { email });
|
||||
const res = await apiRequest("POST", "/password-reset/request", { email });
|
||||
return res.json();
|
||||
},
|
||||
onSuccess: () => {
|
||||
@ -123,7 +123,7 @@ export default function AuthDialogs({ layout = "desktop" }: AuthDialogsProps) {
|
||||
|
||||
const resetConfirmMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const res = await apiRequest("POST", "/api/password-reset/confirm", {
|
||||
const res = await apiRequest("POST", "/password-reset/confirm", {
|
||||
email: resetEmail.trim(),
|
||||
otp: resetOtp.trim(),
|
||||
new_password: resetPassword,
|
||||
|
||||
@ -36,13 +36,13 @@ export default function AuthSection() {
|
||||
});
|
||||
|
||||
const meQuery = useQuery<SessionUser | null>({
|
||||
queryKey: ["/api/me"],
|
||||
queryKey: ["/me"],
|
||||
queryFn: getQueryFn<SessionUser | null>({ on401: "returnNull" }),
|
||||
});
|
||||
|
||||
const handleAuthSuccess = (user: SessionUser, action: "signup" | "login") => {
|
||||
queryClient.setQueryData(["/api/me"], user);
|
||||
queryClient.invalidateQueries({ queryKey: ["/api/broker/status"] });
|
||||
queryClient.setQueryData(["/me"], user);
|
||||
queryClient.invalidateQueries({ queryKey: ["/broker/status"] });
|
||||
toast({
|
||||
title: action === "signup" ? "Account created" : "Signed in",
|
||||
description: `Welcome ${user.username}`,
|
||||
@ -51,7 +51,7 @@ export default function AuthSection() {
|
||||
|
||||
const signupMutation = useMutation<SessionUser, Error, AuthPayload>({
|
||||
mutationFn: async (payload) => {
|
||||
const res = await apiRequest("POST", "/api/signup", {
|
||||
const res = await apiRequest("POST", "/signup", {
|
||||
username: payload.email,
|
||||
password: payload.password,
|
||||
});
|
||||
@ -68,7 +68,7 @@ export default function AuthSection() {
|
||||
|
||||
const loginMutation = useMutation<SessionUser, Error, AuthPayload>({
|
||||
mutationFn: async (payload) => {
|
||||
const res = await apiRequest("POST", "/api/login", payload);
|
||||
const res = await apiRequest("POST", "/login", payload);
|
||||
return res.json();
|
||||
},
|
||||
onSuccess: (user) => handleAuthSuccess(user, "login"),
|
||||
@ -82,11 +82,11 @@ export default function AuthSection() {
|
||||
|
||||
const logoutMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
await apiRequest("POST", "/api/logout");
|
||||
await apiRequest("POST", "/logout");
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.setQueryData(["/api/me"], null);
|
||||
queryClient.removeQueries({ queryKey: ["/api/broker/status"] });
|
||||
queryClient.setQueryData(["/me"], null);
|
||||
queryClient.removeQueries({ queryKey: ["/broker/status"] });
|
||||
toast({ title: "Signed out" });
|
||||
window.location.assign("/");
|
||||
},
|
||||
|
||||
@ -62,12 +62,12 @@ export default function BrokerConnectDialog({
|
||||
const [holdings, setHoldings] = useState<any[]>([]);
|
||||
|
||||
const { data: sessionUser } = useQuery<SessionUser | null>({
|
||||
queryKey: ["/api/me"],
|
||||
queryKey: ["/me"],
|
||||
queryFn: getQueryFn<SessionUser | null>({ on401: "returnNull" }),
|
||||
});
|
||||
|
||||
const { data: brokerStatus, refetch: refetchStatus } = useQuery<BrokerStatusResponse | null>({
|
||||
queryKey: ["/api/broker/status"],
|
||||
queryKey: ["/broker/status"],
|
||||
queryFn: getQueryFn<BrokerStatusResponse>({ on401: "returnNull" }),
|
||||
staleTime: 0,
|
||||
refetchOnMount: "always",
|
||||
@ -82,7 +82,7 @@ export default function BrokerConnectDialog({
|
||||
throw new Error("API secret is required");
|
||||
}
|
||||
const redirectUrl = `${window.location.origin}/login`;
|
||||
const res = await apiRequest("POST", "/api/broker/zerodha/login", {
|
||||
const res = await apiRequest("POST", "/broker/zerodha/login", {
|
||||
apiKey,
|
||||
apiSecret,
|
||||
redirectUrl,
|
||||
@ -102,7 +102,7 @@ export default function BrokerConnectDialog({
|
||||
|
||||
const holdingsMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/zerodha/holdings");
|
||||
const res = await apiRequest("GET", "/zerodha/holdings");
|
||||
return res.json() as Promise<{ holdings: any[] }>;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
@ -118,7 +118,7 @@ export default function BrokerConnectDialog({
|
||||
|
||||
const disconnectMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const res = await apiRequest("POST", "/api/broker/disconnect");
|
||||
const res = await apiRequest("POST", "/broker/disconnect");
|
||||
return res.json() as Promise<{ connected: boolean }>;
|
||||
},
|
||||
onSuccess: () => {
|
||||
|
||||
@ -20,17 +20,17 @@ export default function Navigation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { data: sessionUser } = useQuery<SessionUser | null>({
|
||||
queryKey: ["/api/me"],
|
||||
queryKey: ["/me"],
|
||||
queryFn: getQueryFn<SessionUser | null>({ on401: "returnNull" }),
|
||||
});
|
||||
|
||||
const logoutMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
await apiRequest("POST", "/api/logout");
|
||||
await apiRequest("POST", "/logout");
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.setQueryData(["/api/me"], null);
|
||||
queryClient.removeQueries({ queryKey: ["/api/broker/status"] });
|
||||
queryClient.setQueryData(["/me"], null);
|
||||
queryClient.removeQueries({ queryKey: ["/broker/status"] });
|
||||
window.location.assign("/");
|
||||
},
|
||||
});
|
||||
|
||||
@ -183,23 +183,23 @@ export default function PortfolioSection() {
|
||||
isFetching: brokerStatusLoading,
|
||||
refetch: refetchBrokerStatus,
|
||||
} = useQuery<BrokerStatusResponse | null>({
|
||||
queryKey: ["/api/broker/status"],
|
||||
queryKey: ["/broker/status"],
|
||||
queryFn: getQueryFn<BrokerStatusResponse>({ on401: "returnNull" }),
|
||||
staleTime: 0,
|
||||
refetchOnMount: "always",
|
||||
});
|
||||
const { data: sessionUser } = useQuery<SessionUser | null>({
|
||||
queryKey: ["/api/me"],
|
||||
queryKey: ["/me"],
|
||||
queryFn: getQueryFn<SessionUser | null>({ on401: "returnNull" }),
|
||||
});
|
||||
const systemStatusQuery = useQuery<SystemStatusResponse>({
|
||||
queryKey: ["/api/system/status"],
|
||||
queryKey: ["/system/status"],
|
||||
queryFn: getQueryFn<SystemStatusResponse>({ on401: "throw" }),
|
||||
refetchInterval: 15000,
|
||||
});
|
||||
const armMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const res = await fetch("/api/system/arm", {
|
||||
const res = await fetch("/system/arm", {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
});
|
||||
@ -211,7 +211,7 @@ export default function PortfolioSection() {
|
||||
const redirect =
|
||||
payload?.detail?.redirect_url ||
|
||||
payload?.redirect_url ||
|
||||
"/api/broker/login";
|
||||
"/broker/login";
|
||||
window.location.assign(redirect);
|
||||
return null;
|
||||
}
|
||||
@ -241,9 +241,9 @@ export default function PortfolioSection() {
|
||||
});
|
||||
|
||||
const holdingsQuery = useQuery<HoldingsResponse>({
|
||||
queryKey: ["/api/zerodha/holdings"],
|
||||
queryKey: ["/zerodha/holdings"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/zerodha/holdings");
|
||||
const res = await apiRequest("GET", "/zerodha/holdings");
|
||||
return res.json();
|
||||
},
|
||||
enabled: !!brokerStatus?.connected,
|
||||
@ -262,9 +262,9 @@ export default function PortfolioSection() {
|
||||
});
|
||||
|
||||
const fundsQuery = useQuery<FundsResponse>({
|
||||
queryKey: ["/api/zerodha/funds"],
|
||||
queryKey: ["/zerodha/funds"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/zerodha/funds");
|
||||
const res = await apiRequest("GET", "/zerodha/funds");
|
||||
return res.json();
|
||||
},
|
||||
enabled: !!brokerStatus?.connected,
|
||||
@ -325,7 +325,7 @@ export default function PortfolioSection() {
|
||||
useEffect(() => {
|
||||
const fetchStatus = async () => {
|
||||
try {
|
||||
const res = await fetch("/api/engine/status");
|
||||
const res = await fetch("/engine/status");
|
||||
const data = await res.json();
|
||||
setEngineStatus(data);
|
||||
} catch {
|
||||
@ -341,7 +341,7 @@ export default function PortfolioSection() {
|
||||
useEffect(() => {
|
||||
const fetchMarketStatus = async () => {
|
||||
try {
|
||||
const res = await fetch("/api/market/status");
|
||||
const res = await fetch("/market/status");
|
||||
const data = await res.json();
|
||||
setMarketStatus(data);
|
||||
} catch {
|
||||
@ -403,11 +403,11 @@ export default function PortfolioSection() {
|
||||
}, [prefersReducedMotion]);
|
||||
|
||||
const equityCurveQuery = useQuery<EquityCurveResponse>({
|
||||
queryKey: ["/api/zerodha/equity-curve", startDate],
|
||||
queryKey: ["/zerodha/equity-curve", startDate],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest(
|
||||
"GET",
|
||||
`/api/zerodha/equity-curve${startDate ? `?from=${startDate}` : ""}`,
|
||||
`/zerodha/equity-curve${startDate ? `?from=${startDate}` : ""}`,
|
||||
);
|
||||
return res.json();
|
||||
},
|
||||
|
||||
@ -138,25 +138,25 @@ function PaperTradingPortfolio() {
|
||||
const skipFirstPnlPointRef = useRef(true);
|
||||
|
||||
const fundsQuery = useQuery<PaperFundsResponse>({
|
||||
queryKey: ["/api/paper/funds"],
|
||||
queryKey: ["/paper/funds"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/paper/funds");
|
||||
const res = await apiRequest("GET", "/paper/funds");
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
const positionsQuery = useQuery<PaperPositionsResponse>({
|
||||
queryKey: ["/api/paper/positions"],
|
||||
queryKey: ["/paper/positions"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/paper/positions");
|
||||
const res = await apiRequest("GET", "/paper/positions");
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
const ordersQuery = useQuery<PaperOrdersResponse>({
|
||||
queryKey: ["/api/paper/orders"],
|
||||
queryKey: ["/paper/orders"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/paper/orders");
|
||||
const res = await apiRequest("GET", "/paper/orders");
|
||||
return res.json();
|
||||
},
|
||||
refetchInterval: 10000,
|
||||
@ -246,7 +246,7 @@ function PaperTradingPortfolio() {
|
||||
let timer: number;
|
||||
const poll = async () => {
|
||||
try {
|
||||
const res = await fetch("/api/paper/mtm", {
|
||||
const res = await fetch("/paper/mtm", {
|
||||
credentials: "include",
|
||||
});
|
||||
if (!res.ok) {
|
||||
@ -307,7 +307,7 @@ function PaperTradingPortfolio() {
|
||||
useEffect(() => {
|
||||
const fetchMarketStatus = async () => {
|
||||
try {
|
||||
const res = await fetch("/api/market/status", {
|
||||
const res = await fetch("/market/status", {
|
||||
credentials: "include",
|
||||
});
|
||||
if (!res.ok) {
|
||||
@ -340,7 +340,7 @@ function PaperTradingPortfolio() {
|
||||
|
||||
setIsResetting(true);
|
||||
try {
|
||||
const res = await fetch("/api/paper/reset", { method: "POST" });
|
||||
const res = await fetch("/paper/reset", { method: "POST" });
|
||||
if (!res.ok) {
|
||||
throw new Error("Reset failed");
|
||||
}
|
||||
@ -474,7 +474,7 @@ function PaperTradingPortfolio() {
|
||||
|
||||
setIsAddingCash(true);
|
||||
try {
|
||||
await apiRequest("POST", "/api/paper/add-cash", { amount: addCashAmount });
|
||||
await apiRequest("POST", "/paper/add-cash", { amount: addCashAmount });
|
||||
setMtmCash((prev) =>
|
||||
typeof prev === "number" ? prev + addCashAmount : addCashAmount
|
||||
);
|
||||
|
||||
@ -32,7 +32,7 @@ export default function SupportPage() {
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [checking, setChecking] = useState(false);
|
||||
const { data: sessionUser } = useQuery<SessionUser | null>({
|
||||
queryKey: ["/api/me"],
|
||||
queryKey: ["/me"],
|
||||
queryFn: getQueryFn<SessionUser | null>({ on401: "returnNull" }),
|
||||
});
|
||||
const sessionEmail = sessionUser?.username?.trim() || "";
|
||||
@ -52,7 +52,7 @@ export default function SupportPage() {
|
||||
}
|
||||
setSubmitting(true);
|
||||
try {
|
||||
const res = await apiRequest("POST", "/api/support/ticket", {
|
||||
const res = await apiRequest("POST", "/support/ticket", {
|
||||
...form,
|
||||
email: effectiveEmail,
|
||||
});
|
||||
@ -75,7 +75,7 @@ export default function SupportPage() {
|
||||
}
|
||||
setChecking(true);
|
||||
try {
|
||||
const res = await apiRequest("POST", `/api/support/ticket/status/${ticketId.trim()}`, {
|
||||
const res = await apiRequest("POST", `/support/ticket/status/${ticketId.trim()}`, {
|
||||
email: effectiveEmail,
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
@ -48,7 +48,7 @@ export default function ZerodhaCallback() {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = `/api/broker/zerodha/callback?request_token=${encodeURIComponent(params.requestToken)}`;
|
||||
const url = `/broker/zerodha/callback?request_token=${encodeURIComponent(params.requestToken)}`;
|
||||
apiRequest("GET", url)
|
||||
.then(() => finalize("success"))
|
||||
.catch((err: any) => finalize("error", err?.message || "Unable to complete the login."));
|
||||
|
||||
@ -4,9 +4,9 @@ import type { InvariantsResponse } from "./types";
|
||||
|
||||
export default function AdminInvariants() {
|
||||
const invariantsQuery = useQuery<InvariantsResponse>({
|
||||
queryKey: ["/api/admin/health/invariants"],
|
||||
queryKey: ["admin/health/invariants"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/admin/health/invariants");
|
||||
const res = await apiRequest("GET", "admin/health/invariants");
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
@ -13,9 +13,9 @@ const StatCard = ({ label, value }: { label: string; value: number | string }) =
|
||||
|
||||
export default function AdminOverview() {
|
||||
const overviewQuery = useQuery<OverviewResponse>({
|
||||
queryKey: ["/api/admin/overview"],
|
||||
queryKey: ["admin/overview"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/admin/overview");
|
||||
const res = await apiRequest("GET", "admin/overview");
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
@ -31,7 +31,7 @@ export default function AdminPage() {
|
||||
|
||||
const checkAccess = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch("/api/admin/overview", { credentials: "include" });
|
||||
const res = await fetch("admin/overview", { credentials: "include" });
|
||||
if (res.status === 401) {
|
||||
setAccessState("unauthenticated");
|
||||
return;
|
||||
@ -63,7 +63,7 @@ export default function AdminPage() {
|
||||
setLoginPending(true);
|
||||
setLoginError(null);
|
||||
try {
|
||||
await apiRequest("POST", "/api/login", {
|
||||
await apiRequest("POST", "login", {
|
||||
email: loginForm.email.trim(),
|
||||
password: loginForm.password,
|
||||
});
|
||||
|
||||
@ -5,9 +5,9 @@ import type { RunDetailResponse } from "./types";
|
||||
|
||||
export default function AdminRunDetail({ runId }: { runId: string }) {
|
||||
const detailQuery = useQuery<RunDetailResponse>({
|
||||
queryKey: ["/api/admin/runs", runId],
|
||||
queryKey: ["admin/runs", runId],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", `/api/admin/runs/${runId}`);
|
||||
const res = await apiRequest("GET", `admin/runs/${runId}`);
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
@ -24,9 +24,9 @@ export default function AdminRuns() {
|
||||
}, [page, pageSize, status, mode, userId]);
|
||||
|
||||
const runsQuery = useQuery<RunsResponse>({
|
||||
queryKey: ["/api/admin/runs", page, status, mode, userId],
|
||||
queryKey: ["admin/runs", page, status, mode, userId],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", `/api/admin/runs?${queryString}`);
|
||||
const res = await apiRequest("GET", `admin/runs?${queryString}`);
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
@ -26,7 +26,7 @@ type TicketsResponse = {
|
||||
export default function AdminSupportTickets() {
|
||||
const queryClient = useQueryClient();
|
||||
const { data, isLoading } = useQuery<TicketsResponse>({
|
||||
queryKey: ["/api/admin/support-tickets"],
|
||||
queryKey: ["admin/support-tickets"],
|
||||
queryFn: getQueryFn<TicketsResponse>({ on401: "throw" }),
|
||||
});
|
||||
|
||||
@ -34,9 +34,9 @@ export default function AdminSupportTickets() {
|
||||
const confirmed = window.confirm("Delete this ticket? This cannot be undone.");
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
await apiRequest("DELETE", `/api/admin/support-tickets/${ticketId}`);
|
||||
await apiRequest("DELETE", `admin/support-tickets/${ticketId}`);
|
||||
toast({ title: "Ticket deleted" });
|
||||
queryClient.invalidateQueries({ queryKey: ["/api/admin/support-tickets"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["admin/support-tickets"] });
|
||||
} catch (err: any) {
|
||||
toast({ title: "Delete failed", description: err?.message || "Try again." });
|
||||
}
|
||||
|
||||
@ -5,9 +5,9 @@ import type { UserDetailResponse } from "./types";
|
||||
|
||||
export default function AdminUserDetail({ userId }: { userId: string }) {
|
||||
const detailQuery = useQuery<UserDetailResponse>({
|
||||
queryKey: ["/api/admin/users", userId],
|
||||
queryKey: ["admin/users", userId],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", `/api/admin/users/${userId}`);
|
||||
const res = await apiRequest("GET", `admin/users/${userId}`);
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
@ -42,15 +42,15 @@ export default function AdminUsers() {
|
||||
}, [page, pageSize, query]);
|
||||
|
||||
const usersQuery = useQuery<UsersResponse>({
|
||||
queryKey: ["/api/admin/users", page, query],
|
||||
queryKey: ["admin/users", page, query],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", `/api/admin/users?${queryString}`);
|
||||
const res = await apiRequest("GET", `admin/users?${queryString}`);
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
|
||||
const meQuery = useQuery<{ id: string; username: string; role?: string } | null>({
|
||||
queryKey: ["/api/me"],
|
||||
queryKey: ["me"],
|
||||
queryFn: getQueryFn({ on401: "returnNull" }),
|
||||
});
|
||||
|
||||
@ -66,13 +66,13 @@ export default function AdminUsers() {
|
||||
try {
|
||||
const res = await apiRequest(
|
||||
"DELETE",
|
||||
`/api/admin/users/${deleteTarget.user_id}?hard=true`,
|
||||
`admin/users/${deleteTarget.user_id}?hard=true`,
|
||||
);
|
||||
(await res.json()) as DeleteUserResponse;
|
||||
setDeleteTarget(null);
|
||||
setConfirmChecked(false);
|
||||
await queryClient.invalidateQueries({ queryKey: ["/api/admin/users"] });
|
||||
await queryClient.invalidateQueries({ queryKey: ["/api/admin/overview"] });
|
||||
await queryClient.invalidateQueries({ queryKey: ["admin/users"] });
|
||||
await queryClient.invalidateQueries({ queryKey: ["admin/overview"] });
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Delete failed";
|
||||
setDeleteError(message);
|
||||
@ -90,13 +90,13 @@ export default function AdminUsers() {
|
||||
try {
|
||||
const res = await apiRequest(
|
||||
"POST",
|
||||
`/api/admin/users/${resetTarget.user_id}/hard-reset`,
|
||||
`admin/users/${resetTarget.user_id}/hard-reset`,
|
||||
);
|
||||
(await res.json()) as HardResetResponse;
|
||||
setResetTarget(null);
|
||||
setResetChecked(false);
|
||||
await queryClient.invalidateQueries({ queryKey: ["/api/admin/users"] });
|
||||
await queryClient.invalidateQueries({ queryKey: ["/api/admin/overview"] });
|
||||
await queryClient.invalidateQueries({ queryKey: ["admin/users"] });
|
||||
await queryClient.invalidateQueries({ queryKey: ["admin/overview"] });
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Reset failed";
|
||||
setResetError(message);
|
||||
@ -180,8 +180,8 @@ export default function AdminUsers() {
|
||||
isSelf={user.user_id === currentUserId}
|
||||
isSuperAdmin={isSuperAdmin}
|
||||
onUpdated={() => {
|
||||
queryClient.invalidateQueries({ queryKey: ["/api/admin/users"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["/api/admin/overview"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["admin/users"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["admin/overview"] });
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@ -58,7 +58,7 @@ export default function RoleActions({ target, isSelf, isSuperAdmin, onUpdated }:
|
||||
try {
|
||||
await apiRequest(
|
||||
"POST",
|
||||
`/api/admin/users/${target.user_id}/${actionEndpoint[action]}`,
|
||||
`admin/users/${target.user_id}/${actionEndpoint[action]}`,
|
||||
);
|
||||
onUpdated();
|
||||
setAction(null);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user