feat: add diagnostics page and broken link checker
All checks were successful
Deploy eolas-app / deploy (push) Successful in 1m7s

This commit is contained in:
Thomas Bishop 2025-12-22 17:08:40 +00:00
parent 7454a42f80
commit caef8ddf6b
3 changed files with 163 additions and 69 deletions

View file

@ -7,6 +7,7 @@ import TagTemplate from "./templates/TagTemplate"
import EntryTemplate from "./templates/EntryTemplate" import EntryTemplate from "./templates/EntryTemplate"
import Settings from "./pages/settings" import Settings from "./pages/settings"
import About from "./pages/about" import About from "./pages/about"
import Diagnostics from "./pages/diagnostics"
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
@ -27,6 +28,7 @@ export default function App() {
<Routes> <Routes>
<Route index element={<Home />} /> <Route index element={<Home />} />
<Route path="/settings" element={<Settings />} /> <Route path="/settings" element={<Settings />} />
<Route path="/diagnostics" element={<Diagnostics />} />
<Route path="/about" element={<About />} /> <Route path="/about" element={<About />} />
<Route path="/entries/:entry" element={<EntryTemplate />} /> <Route path="/entries/:entry" element={<EntryTemplate />} />
<Route path="/tags/:tag" element={<TagTemplate />} /> <Route path="/tags/:tag" element={<TagTemplate />} />

View file

@ -1,57 +1,72 @@
import { Info, FileText, Waypoints, SquareLibrary, Settings, ChevronRight } from "lucide-react" import {
Info,
FileText,
Waypoints,
SquareLibrary,
Settings,
ChevronRight,
TestTubeDiagonal,
TestTube2,
FlaskConical,
} from "lucide-react"
import { import {
Sidebar, Sidebar,
SidebarContent, SidebarContent,
SidebarGroup, SidebarGroup,
SidebarGroupContent, SidebarGroupContent,
SidebarMenu, SidebarMenu,
SidebarMenuButton, SidebarMenuButton,
SidebarMenuItem, SidebarMenuItem,
SidebarHeader, SidebarHeader,
SidebarFooter, SidebarFooter,
} from "@/components/ui/sidebar" } from "@/components/ui/sidebar"
import TagListSidebar from "@/components/TagListSidebar" import TagListSidebar from "@/components/TagListSidebar"
import EntriesListSidebar from "@/components/EntriesListSidebar" import EntriesListSidebar from "@/components/EntriesListSidebar"
import { Link } from "react-router" import { Link } from "react-router"
const footerMenu = [ const footerMenu = [
{ {
title: "About", title: "About",
url: "/about", url: "/about",
icon: Info, icon: Info,
}, },
{
title: "Diagnostics",
url: "/diagnostics",
icon: FlaskConical,
},
{ {
title: "Settings", title: "Settings",
url: "/settings", url: "/settings",
icon: Settings, icon: Settings,
}, },
] ]
export function AppSidebar() { export function AppSidebar() {
return ( return (
<Sidebar> <Sidebar>
<SidebarHeader className="border-b h-12"> <SidebarHeader className="border-b h-12">
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <SidebarMenuItem>
<SidebarMenuButton <SidebarMenuButton
asChild asChild
className="data-[slot=sidebar-menu-button]:!p-1.5 rounded-none" className="data-[slot=sidebar-menu-button]:!p-1.5 rounded-none"
> >
<Link to="/"> <Link to="/">
<SquareLibrary className="h-5 w-5" /> <SquareLibrary className="h-5 w-5" />
<span className="text-base font-semibold">Eólas</span> <span className="text-base font-semibold">Eólas</span>
</Link> </Link>
</SidebarMenuButton> </SidebarMenuButton>
</SidebarMenuItem> </SidebarMenuItem>
</SidebarMenu> </SidebarMenu>
</SidebarHeader> </SidebarHeader>
<SidebarContent> <SidebarContent>
<SidebarGroup> <SidebarGroup>
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu>
{/* {/*
<SidebarMenuItem key="graph"> <SidebarMenuItem key="graph">
<SidebarMenuButton asChild> <SidebarMenuButton asChild>
<a href="#"> <a href="#">
@ -62,38 +77,38 @@ export function AppSidebar() {
</SidebarMenuItem> </SidebarMenuItem>
*/} */}
<EntriesListSidebar /> <EntriesListSidebar />
<TagListSidebar /> <TagListSidebar />
</SidebarMenu> </SidebarMenu>
</SidebarGroupContent> </SidebarGroupContent>
</SidebarGroup> </SidebarGroup>
</SidebarContent> </SidebarContent>
<SidebarFooter> <SidebarFooter>
<SidebarGroup> <SidebarGroup>
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu>
{footerMenu.map((item) => ( {footerMenu.map((item) => (
<SidebarMenuItem key={item.title}> <SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild className="rounded-none"> <SidebarMenuButton asChild className="rounded-none">
<Link to={item.url}> <Link to={item.url}>
<item.icon /> <item.icon />
<span className="font-medium">{item.title}</span> <span className="font-medium">{item.title}</span>
</Link> </Link>
{/* {/*
<a href={item.url}> <a href={item.url}>
<item.icon /> <item.icon />
<span>{item.title}</span> <span>{item.title}</span>
</a> </a>
*/} */}
</SidebarMenuButton> </SidebarMenuButton>
</SidebarMenuItem> </SidebarMenuItem>
))} ))}
</SidebarMenu> </SidebarMenu>
</SidebarGroupContent> </SidebarGroupContent>
</SidebarGroup> </SidebarGroup>
</SidebarFooter> </SidebarFooter>
</Sidebar> </Sidebar>
) )
} }

77
src/pages/diagnostics.tsx Normal file
View file

@ -0,0 +1,77 @@
import { Button } from "@/components/ui/button"
import PageTemplate from "@/templates/PageTemplate"
import api from "@/api/eolas-api"
import { useQuery } from "@tanstack/react-query"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { Link } from "react-router"
const PageBody = () => {
const { data, refetch, isLoading, error } = useQuery({
queryKey: ["diagnostics_broken-link"],
queryFn: () => api.get("/diagnostics/broken-links").then((res) => res.data),
enabled: false,
})
data?.data.sort((a, b) => a.source_entry_title.localeCompare(b.source_entry_title))
return (
<div className="max-w-2xl p-4 lg:p-6">
<div className="flex flex-row justify-between">
<h3 className="font-semibold mb-4">Broken links</h3>
<Button size="sm" variant="secondary" onClick={() => refetch()}>
Find broken links
</Button>
</div>
{isLoading ? (
<p>Generating list...</p>
) : error ? (
<div className="p-4 text-sm dark:text-red-300 text-red-700">
<div className="p-2 border-2 dark:border-red-800 border-red-500 dark:bg-red-900 bg-red-300">
Error fetching broken links
</div>{" "}
</div>
) : (
data && (
<>
<p className="leading-[1.5] mb-4 not-first:mt-4">
There are <b>{data?.count}</b> broken links.
</p>
<Table>
<TableHeader>
<TableRow>
<TableHead>Source entry</TableHead>
<TableHead>Broken link</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data?.data.map((link, i) => (
<TableRow key={i}>
<TableCell>
<Link
to={`/entries/${link?.source_entry_title}`} // Fixed: to={ instead of to=
className="text-foreground underline-offset-3 text-sm underline hover:text-gray-700 dark:hover:text-green-300"
>
{link?.source_entry_title.replace(/_/g, " ")}{" "}
</Link>{" "}
</TableCell>
<TableCell>{link?.broken_link_title}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</>
)
)}
</div>
)
}
export default function Diagnostics() {
return <PageTemplate pageTitle="Diagnostics" pageBody={<PageBody />} />
}