PageSourceSearch

https://ridedna.ai/assets/HandlingAnalyses-PC17cBpy.js

js ridedna.ai collected 2026-10-05 14:39:24 UTC 5,484 bytes, 30 lines download raw bytes

1import { d as j, r as y, e as N, J as b, y as v, K as k, j as e, B as d, C, a$ as A, b0 as $, by as w, a4 as R, a5 as c, a8 as E, ac as M } from "./index-B9miRpwC.js";
2import { R as p } from "./refresh-cw-D2_WynW5.js";
3import { A as S } from "./arrow-right-BqRLgtOU.js";
4function f(a) {
5  if (!a) return "never";
6  const n = new Date(a).getTime();
7  if (!Number.isFinite(n)) return "never";
8  const i = Math.round((Date.now() - n) / 6e4);
9  if (i < 1) return "just now";
10  if (i < 60) return `${i}m ago`;
11  const s = Math.round(i / 60);
12  return s < 48 ? `${s}h ago` : new Date(a).toLocaleDateString();
13}
14function P({ kind: a }) {
15  return a === "contrast" ? e.jsx(c, { variant: "secondary", className: "text-xs", children: "Contrast" }) : e.jsx(c, { variant: "outline", className: "text-xs", children: "Single Bike" });
16}
17const l = 25;
18function D() {
19  const { toast: a } = j(), [n, i] = y.useState(0), { data: s, isLoading: o, refetch: g } = N({ queryKey: ["/api/admin/handling-analyses", n], queryFn: async () => {
20    const t = await fetch(`/api/admin/handling-analyses?limit=${l}&offset=${n * l}`, { credentials: "include" });
21    if (!t.ok) throw new Error("Failed to fetch");
22    return t.json();
23  } }), m = b({ mutationFn: async (t) => (await k("POST", `/api/admin/handling-analyses/${t}/resubmit-index`, {})).json(), onSuccess: (t) => {
24    a({ title: t.submitted ? "Resubmitted to IndexNow" : "Already indexed recently", description: t.url }), v.invalidateQueries({ queryKey: ["/api/admin/handling-analyses"] });
25  }, onError: () => {
26    a({ title: "Resubmission failed", variant: "destructive" });
27  } }), x = s?.rows ?? [], u = s?.total ?? 0, r = Math.ceil(u / l);
28  return e.jsxs("div", { className: "p-6 space-y-6", children: [e.jsxs("div", { className: "flex items-center justify-between gap-4 flex-wrap", children: [e.jsxs("div", { children: [e.jsx("h1", { className: "text-2xl font-semibold", "data-testid": "text-page-title", children: "Handling AI Analyses" }), e.jsx("p", { className: "text-muted-foreground text-sm mt-1", children: "All persisted analyses \u2014 single-bike (saved to geometry record) and contrast (two-bike head-to-head). Each generates a public SEO page." })] }), e.jsxs(d, { variant: "outline", onClick: () => g(), "data-testid": "button-refresh", children: [e.jsx(p, { className: "w-4 h-4 mr-2" }), "Refresh"] })] }), e.jsxs(C, { children: [e.jsxs(A, { children: [e.jsx($, { children: "Saved Analyses" }), e.jsx(w, { children: o ? "Loading\u2026" : `${u} total records` })] }), e.jsxs(R, { children: [o ? e.jsx("div", { className: "space-y-3", children: Array.from({ length: 5 }).map((t, h) => e.jsx("div", { className: "h-16 bg-muted rounded animate-pulse" }, h)) }) : x.length === 0 ? e.jsx("p", { className: "text-muted-foreground text-sm py-8 text-center", children: "No analyses saved yet. Generate a contrast or single-bike handling analysis in the Handling Index to create one." }) : e.jsx("div", { className: "space-y-3", children: x.map((t) => e.jsxs("div", { className: "flex items-start justify-between gap-3 p-3 border rounded-md flex-wrap", "data-testid": `card-analysis-${t.id}`, children: [e.jsxs("div", { className: "min-w-0 flex-1", children: [e.jsxs("div", { className: "flex items-center gap-2 flex-wrap mb-1", children: [e.jsx(P, { kind: t.kind }), t.kind === "contrast" ? e.jsxs(e.Fragment, { children: [e.jsx("span", { className: "text-sm font-medium truncate", "data-testid": `text-slug-a-${t.id}`, children: t.slugA }), e.jsx("span", { className: "text-muted-foreground text-xs", children: "vs" }), e.jsx("span", { className: "text-sm font-medium truncate", "data-testid": `text-slug-b-${t.id}`, children: t.slugB })] }) : e.jsx("span", { className: "text-sm font-medium truncate", "data-testid": `text-label-${t.id}`, children: t.label })] }), e.jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [e.jsxs("span", { className: "text-xs text-muted-foreground", children: ["Updated ", f(t.updatedAt)] }), e.jsxs("span", { className: "text-xs text-muted-foreground", children: ["IndexNow: ", f(t.lastIndexNow)] }), t.kind === "contrast" && e.jsx(c, { variant: "outline", className: "text-xs", children: t.language.toUpperCase() })] }), e.jsx("div", { className: "mt-1", children: e.jsxs("a", { href: t.publicUrl, target: "_blank", rel: "noopener noreferrer", className: "text-xs text-primary hover:underline inline-flex items-center gap-1", "data-testid": `link-public-${t.id}`, children: [t.publicUrl.replace("https://ridedna.ai", ""), e.jsx(E, { className: "w-3 h-3" })] }) })] }), e.jsxs(d, { variant: "outline", size: "sm", onClick: () => m.mutate(t.id), disabled: m.isPending, "data-testid": `button-resubmit-${t.id}`, children: [e.jsx(p, { className: "w-3 h-3 mr-1" }), "Reindex"] })] }, t.id)) }), r > 1 && e.jsxs("div", { className: "flex items-center justify-between mt-4", children: [e.jsxs(d, { variant: "outline", size: "sm", onClick: () => i((t) => Math.max(0, t - 1)), disabled: n === 0, "data-testid": "button-prev-page", children: [e.jsx(M, { className: "w-4 h-4 mr-1" }), "Previous"] }), e.jsxs("span", { className: "text-sm text-muted-foreground", "data-testid": "text-page-info", children: ["Page ", n + 1, " of ", r] }), e.jsxs(d, { variant: "outline", size: "sm", onClick: () => i((t) => Math.min(r - 1, t + 1)), disabled: n >= r - 1, "data-testid": "button-next-page", children: ["Next", e.jsx(S, { className: "w-4 h-4 ml-1" })] })] })] })] })] });
29}
30export { D as default };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.