Pkgs search: add more metadata

This commit is contained in:
Johannes Kirschbauer 2024-07-18 13:57:39 +02:00 committed by mergify[bot]
parent 75ebb3c276
commit 9299c33700
7 changed files with 392 additions and 307 deletions

View File

@ -1,3 +1,4 @@
"use client";
import { FilterProvider } from "@/components/layout/filterContext";
import { Header } from "@/components/layout/header";
import { PkgsSearch } from "@/components/searchInput/pkgsSearch";
@ -16,6 +17,7 @@ export default function SearchLayout({
<Suspense fallback="query">
<FilterProvider queryUrl="pkgs">
<Header search={<PkgsSearch placeholder="Search packages" />} />
<Container
disableGutters
maxWidth="lg"

View File

@ -1,5 +1,31 @@
"use client";
import { EmptyRecordsPlaceholder } from "@/components/emptyRecordsPlaceholder";
import { useFilter } from "@/components/layout/filterContext";
import Clear from "@mui/icons-material/Clear";
import {
Box,
Button,
Chip,
Collapse,
Container,
Divider,
Icon,
IconButton,
LinearProgress,
List,
ListItem,
ListItemText,
Typography,
} from "@mui/material";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
import LinkIcon from "@mui/icons-material/Link";
import CodeIcon from "@mui/icons-material/Code";
import { ExpandLess, ExpandMore, GitHub, Mail } from "@mui/icons-material";
const getPayload = (term: string) => {
const extraWildcards = term.split(" ").map((t) => ({
wildcard: {
@ -189,18 +215,29 @@ type Hit = {
type PackageItemProps = {
item: Hit;
selected: boolean;
setSelected: (id: string) => void;
};
const PackageItem = (props: PackageItemProps) => {
const { item } = props;
const { item, selected, setSelected } = props;
const { _source: meta } = item;
console.log({ meta, item });
return (
<>
<ListItem
sx={{ px: 1, py: 2, bgcolor: "background.paper", my: 1 }}
aria-label={`item-${item._source.package_pname}`}
>
<Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: 2,
width: "100%",
}}
>
<ListItemText
primaryTypographyProps={{
variant: "h5",
@ -209,10 +246,42 @@ const PackageItem = (props: PackageItemProps) => {
secondaryTypographyProps={{
component: "div",
variant: "body1",
color: "text.primary",
color: "text.secondary",
}}
primary={<Link href={"#"}>{meta.package_attr_name}</Link>}
primary={
<Link
href={"#"}
onClick={() => {
setSelected(item._id);
}}
>
{meta.package_attr_name}
</Link>
}
secondary={
<Box
sx={{ maxWidth: "sm" }}
dangerouslySetInnerHTML={{
__html: meta.package_description,
}}
/>
}
/>
<Box
sx={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
}}
>
<ListItemText
sx={{
display: "flex",
flexDirection: "column-reverse",
}}
secondary={"name"}
primary={meta.package_pname}
/>
<ListItemText
sx={{
display: "flex",
@ -221,8 +290,6 @@ const PackageItem = (props: PackageItemProps) => {
secondary={"version"}
primary={meta.package_pversion}
/>
}
/>
<ListItemText
sx={{
display: "flex",
@ -243,35 +310,14 @@ const PackageItem = (props: PackageItemProps) => {
</div>
}
/>
<ListItemText
primaryTypographyProps={{
component: "div",
variant: "body1",
maxWidth: "40rem",
}}
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column-reverse",
flexDirection: "row",
flexWrap: "wrap",
}}
secondary="description"
primary={
<div
dangerouslySetInnerHTML={{
__html: meta.package_description,
}}
/>
}
/>
<Box sx={{ display: "flex", gap: 2, mt: 1 }}>
<Link href={meta.package_homepage?.[0] || "#"}>Homepage</Link>
<Link
href={`https://github.com/NixOS/nixpkgs/blob/nixos-24.05/${meta.package_position.replace(
":",
"#L"
)}`}
>
Source
</Link>
<ListItemText
sx={{
display: "flex",
@ -285,31 +331,177 @@ const PackageItem = (props: PackageItemProps) => {
</Link>
)
}
></ListItemText>
/>
<Button
sx={{
ml: "auto",
}}
component={Link}
href={meta.package_homepage?.[0] || "#"}
startIcon={<LinkIcon fontSize="inherit" />}
>
Homepage
</Button>
<Button
component={Link}
href={`https://github.com/NixOS/nixpkgs/blob/nixos-unstable/${meta.package_position.replace(
":",
"#L"
)}`}
startIcon={<CodeIcon fontSize="inherit" />}
>
Source
</Button>
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
mt: "-1rem",
}}
>
<IconButton onClick={() => setSelected(item._id)}>
{selected ? <ExpandLess /> : <ExpandMore />}
</IconButton>
</Box>
<Collapse in={selected}>
<Divider flexItem orientation="horizontal" />
{/* <Box
sx={{
display: "flex",
justifyContent: "center",
}}
>
<IconButton onClick={() => setSelected(item._id)}>
<ExpandLess />
</IconButton>
</Box> */}
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: 2,
width: "100%",
}}
>
<ListItemText
primaryTypographyProps={{
component: "div",
variant: "body1",
color: "text.primary",
}}
// secondaryTypographyProps={{
// }}
primary={
<Box
sx={{ width: "100%" }}
dangerouslySetInnerHTML={{
__html: meta.package_longDescription,
}}
/>
}
// secondary={
// ""
// }
/>
<ListItemText
sx={{
display: "flex",
flexDirection: "column-reverse",
}}
secondary="programs"
primary={
<Box
sx={{
display: "flex",
flexDirection: "row",
gap: "0.5rem",
color: "primary.main",
}}
>
{meta.package_programs.map((output) => (
<code key={output}>{output}</code>
))}
</Box>
}
/>
<ListItemText
sx={{
display: "flex",
flexDirection: "column-reverse",
}}
secondary="Platforms"
primary={
<Box
sx={{
display: "flex",
flexWrap: "wrap",
flexDirection: "row",
gap: "0.5rem",
color: "primary.main",
}}
>
{meta.package_platforms.map((output) => (
<code key={output}>{output}</code>
))}
</Box>
}
/>
<ListItemText
sx={{
display: "flex",
flexDirection: "column-reverse",
}}
secondary="Maintainers"
primary={
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "0.5rem",
color: "primary.main",
}}
>
{meta.package_maintainers.map((maintainer) => (
<Box
key={maintainer.github}
sx={{
display: "flex",
flexDirection: "row",
gap: "0.5rem",
}}
>
<Link href={`https://github.com/${maintainer.github}`}>
<Icon fontSize="inherit">
<GitHub fontSize="inherit" />
</Icon>
{maintainer.name}
</Link>
<Box component={"span"} sx={{ color: "text.primary" }}>
via
</Box>
<Link href={`mailto:${maintainer.email}`}>
<Icon fontSize="inherit">
<Mail fontSize="inherit" />
</Icon>
{maintainer.email}
</Link>
</Box>
))}
</Box>
}
/>
</Box>
</Collapse>
</Box>
</ListItem>
</>
);
};
import { EmptyRecordsPlaceholder } from "@/components/emptyRecordsPlaceholder";
import { useFilter } from "@/components/layout/filterContext";
import Clear from "@mui/icons-material/Clear";
import {
Box,
Chip,
Container,
IconButton,
LinearProgress,
List,
ListItem,
ListItemText,
Typography,
} from "@mui/material";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
import AppsIcon from "@mui/icons-material/Apps";
export default function Page() {
const query = useSearchParams();
@ -319,10 +511,14 @@ export default function Page() {
const [items, setItems] = useState<Hit[]>();
const [loading, setLoading] = useState<boolean>(false);
const [selected, setSelected] = useState<string | null>(null);
useEffect(() => {
if (term) {
setLoading(true);
fetch(`https://search.nixos.org/backend/latest-42-nixos-24.05/_search`, {
fetch(
`https://search.nixos.org/backend/latest-42-nixos-unstable/_search`,
{
method: "POST",
body: JSON.stringify(getPayload(term)),
headers: {
@ -330,7 +526,8 @@ export default function Page() {
Authorization:
"Basic YVdWU0FMWHBadjpYOGdQSG56TDUyd0ZFZWt1eHNmUTljU2g=",
},
})
}
)
.then((res) => res.json())
.then((data) => {
setResults(data);
@ -361,8 +558,38 @@ export default function Page() {
<Container maxWidth="lg" sx={{ mt: 2 }}>
{loading ? (
<LinearProgress sx={{ my: 2 }} />
) : !term ? (
<Box
sx={{
display: "flex",
flexDirection: "column",
mt: "3.7rem",
justifySelf: "center",
width: "100%",
px: 2,
mx: 1,
}}
>
<EmptyRecordsPlaceholder
CardProps={{
sx: { backgroundColor: "inherit" },
}}
title="Type something to search"
icon={<AppsIcon fontSize="inherit" />}
/>
<Box sx={{ marginLeft: "auto" }}>
<Link rel="canonical" href="/">
Or search functions
</Link>
</Box>
</Box>
) : (
<>
<Box
sx={{
display: "flex",
}}
>
<Typography
component="span"
variant="subtitle1"
@ -377,6 +604,22 @@ export default function Page() {
sx={{ color: "primary.contrastText" }}
/>
</Typography>
<Typography
component="span"
variant="subtitle1"
sx={{
marginLeft: "auto",
order: 1,
p: 1,
textTransform: "capitalize",
}}
>
<Chip
label={`Channel: nixos-unstable`}
color="primary"
sx={{ color: "primary.contrastText" }}
/>
</Typography>
{term && (
<Typography
component="span"
@ -404,11 +647,22 @@ export default function Page() {
/>
</Typography>
)}
</Box>
<List aria-label="basic-list" sx={{ pt: 1, width: "100%" }}>
{items?.length ? (
items.map((item, idx) => (
<React.Fragment key={`${idx}`}>
<PackageItem item={item} />
<PackageItem
item={item}
selected={item._id === selected}
setSelected={(id) => {
if (id === selected) {
setSelected(null);
} else {
setSelected(id);
}
}}
/>
</React.Fragment>
))
) : (

View File

@ -1,73 +0,0 @@
"use client";
import { ChevronRight, ExpandMore } from "@mui/icons-material";
import {
Collapse,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
} from "@mui/material";
import { ReactNode, useState } from "react";
const borderStyle = {
borderInlineStartStyle: "solid",
borderInlineStartWidth: "1px",
borderInlineStartColor: "#e4e4e7",
ml: 2,
pl: 2,
};
interface CollapseWrapperProps {
children: ReactNode;
name: string;
position: number;
}
export const CollapseWrapper = (props: CollapseWrapperProps) => {
const { children, name, position } = props;
const [expandedKeys, setExpanded] = useState<string[]>([]);
const isExpanded = expandedKeys.includes(name);
return (
<>
<ListItem
disablePadding
disableGutters
sx={{
...(position > 0 ? borderStyle : undefined),
}}
>
<ListItemButton
disableGutters
onClick={() => {
setExpanded((s) =>
isExpanded ? s.filter((n) => n !== name) : [...s, name]
);
}}
>
<ListItemText
primary={name}
primaryTypographyProps={{
sx: {
fontWeight: 700,
},
}}
/>
<ListItemIcon sx={{ minWidth: "40px" }}>
{isExpanded ? <ExpandMore /> : <ChevronRight />}
</ListItemIcon>
</ListItemButton>
</ListItem>
<Collapse in={isExpanded}>
<List
dense
disablePadding
sx={{
...(position > 0 ? borderStyle : undefined),
}}
>
{children}
</List>
</Collapse>
</>
);
};

View File

@ -1,18 +0,0 @@
"use client";
import { ListItemButton } from "@mui/material";
import { usePathname } from "next/navigation";
interface ListIndicatorProps {
currentPath: string;
children: React.ReactNode;
}
export const ListEntry = (props: ListIndicatorProps) => {
const { currentPath, children } = props;
const pathname = usePathname();
const selected = pathname === currentPath;
return (
<ListItemButton disableGutters selected={selected}>
{children}
</ListItemButton>
);
};

View File

@ -1,54 +0,0 @@
import { Group, getMdxMeta } from "@/utils";
import { ListItem, ListItemText } from "@mui/material";
import Link from "next/link";
import { CollapseWrapper } from "./ListCollapse";
import { ListEntry } from "./ListEntry";
interface ListGroupProps {
grp: Group;
position: number;
}
export const ListGroup = async (props: ListGroupProps) => {
const { grp, position } = props;
const sorted = Object.entries(grp).sort(([, v], [,]) =>
Array.isArray(v) ? 1 : -1
);
return sorted.map(async ([name, entry], idx) => {
if (Array.isArray(entry)) {
const matter = await getMdxMeta(entry);
const { frontmatter } = matter.compiled;
return (
<Link rel="canonical" key={`${idx}`} href={`/ref/${entry.join("/")}`}>
<ListItem
disablePadding
disableGutters
sx={{
ml: 2,
pl: 2,
borderInlineStartStyle: "solid",
borderInlineStartWidth: "1px",
borderInlineStartColor: "#e4e4e7",
}}
>
<ListEntry currentPath={`/ref/${entry.join("/")}`}>
<ListItemText
primary={
frontmatter.path
? frontmatter.path.join(".")
: frontmatter.title
}
/>
</ListEntry>
</ListItem>
</Link>
);
} else {
return (
<CollapseWrapper key={idx} name={name} position={position}>
<ListGroup grp={entry} position={position + 1} />
</CollapseWrapper>
);
}
});
};

View File

@ -1,33 +0,0 @@
import { Group, generateStaticSidebarEntries, set } from "@/utils";
import { Box, List, Paper, Typography } from "@mui/material";
import { ListGroup } from "./ListGroup";
export const NavSidebar = async () => {
const list = await generateStaticSidebarEntries();
const grp: Group = list.reduce((acc, item) => {
set(acc, item.id.join("."), item.id);
return acc;
}, {});
return (
<Paper sx={{ height: "100%" }}>
<Box
sx={{
display: "flex",
width: "100%",
py: 2,
}}
>
<Typography variant="subtitle1" component="h3">
References
</Typography>
</Box>
<List
sx={{ width: "100%", bgcolor: "background.paper" }}
component="nav"
disablePadding
>
<ListGroup grp={grp} position={0} />
</List>
</Paper>
);
};

View File

@ -1,10 +1,14 @@
"use client";
import { Box, LinearProgress, Link } from "@mui/material";
import { Box, Button, LinearProgress, Link } from "@mui/material";
import { Filter } from "../filter";
import { ReactNode, Suspense } from "react";
import { SocialIcons } from "./layout";
import localFont from "next/font/local";
import { styled } from "@mui/material/styles";
const HeaderLink = styled(Link)(({ theme }) => ({
color: theme.palette.primary.contrastText + "!important",
}));
const fira = localFont({
src: "../../fonts/FiraCode-VF.ttf",
@ -45,9 +49,11 @@ export const Header = (props: HeaderProps) => {
display: { xs: "none", md: "block" },
}}
>
<Link
<Button
// component={Link}
rel="canonical"
href="/"
LinkComponent={HeaderLink}
className={fira.className}
sx={{
color: "primary.contrastText",
@ -56,7 +62,7 @@ export const Header = (props: HeaderProps) => {
aria-label="Home"
>
{"Noogλe"}
</Link>
</Button>
</Box>
{/* <Box
sx={{
@ -69,7 +75,7 @@ export const Header = (props: HeaderProps) => {
<Home />
</IconButton>
</Box> */}
{search && (
<Box
sx={{
justifySelf: "center",
@ -85,6 +91,7 @@ export const Header = (props: HeaderProps) => {
>
<Suspense fallback="search input ">{search}</Suspense>
</Box>
)}
<Box
sx={{
justifySelf: "end",