diff --git a/README.md b/README.md index 6ffcdbb..3aad24d 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,5 @@ bugs / open questions -- [ ] how should languages be encoded in urls (queries with special characters like `portuguese (brazil)` are currently also broken/unreliable) +- [ ] how should languages be encoded in urls (queries with special characters like + `portuguese (brazil)` are currently also broken/unreliable) diff --git a/app/languages/[language]/page.tsx b/app/languages/[language]/page.tsx index 86cef32..6f20a94 100644 --- a/app/languages/[language]/page.tsx +++ b/app/languages/[language]/page.tsx @@ -1,5 +1,17 @@ -import { FacetedListing, type FacetedListingProps } from "@/components/faceted-listing"; +import { SimpleListing } from "@/components/simple-listing"; -export default function LanguagesPage(props: FacetedListingProps) { - return ; +interface LanguagesPageProps { + params?: { + language: string; + }; +} + +export default function LanguagesPage(props: LanguagesPageProps) { + return ( + + ); } diff --git a/app/languages/page.tsx b/app/languages/page.tsx new file mode 100644 index 0000000..8696087 --- /dev/null +++ b/app/languages/page.tsx @@ -0,0 +1,5 @@ +import LanguagesPage from "./[language]/page"; + +export default function BlankLanguagesPage() { + return ; +} diff --git a/app/translators/[id]/page.tsx b/app/translators/[id]/page.tsx new file mode 100644 index 0000000..14011bc --- /dev/null +++ b/app/translators/[id]/page.tsx @@ -0,0 +1,18 @@ +import { SimpleListing } from "@/components/simple-listing"; + +interface TranslatorsPageProps { + params?: { + id: string; + }; +} + +export default function TranslatorsPage(props: TranslatorsPageProps) { + return ( + + ); +} diff --git a/app/translators/page.tsx b/app/translators/page.tsx index 313609c..fa924f4 100644 --- a/app/translators/page.tsx +++ b/app/translators/page.tsx @@ -1,5 +1,5 @@ -import { FacetedListing, type FacetedListingProps } from "@/components/faceted-listing"; +import TranslatorsPage from "./[id]/page"; -export default function TranslatorPage(props: FacetedListingProps) { - return ; +export default function BlankTranslatorsPage() { + return ; } diff --git a/app/works/[...args]/page.tsx b/app/works/[...args]/page.tsx deleted file mode 100644 index 1cacde7..0000000 --- a/app/works/[...args]/page.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -"use client"; -import { useTranslations } from "next-intl"; - -import { AppNavLink } from "@/components/app-nav-link"; -import { MainContent } from "@/components/main-content"; -import { ClickablePublicationThumbnail } from "@/components/publication-cover"; -import { PublicationGrid } from "@/components/publication-grid"; -import { getPublications } from "@/lib/data"; -import { type Category, otherCategories } from "@/lib/model"; - -interface WorkPageProps { - params: { - args: [Category?, string?]; - }; -} - -// eslint-disable-next-line @next/next/no-async-client-component -export default async function WorkPage(props: WorkPageProps) { - const catt = useTranslations("BernhardCategories"); - const _t = useTranslations("WorkPage"); - - const category = props.params.args[0]; - - const publications = await getPublications({ q: "*", filter: { erstpublikation: true } }); - - return ( - -
- {otherCategories.map((c) => { - return ( - - {catt(c)} - - ); - })} -
- -
- {category} -
    -
    - - {publications.map((p) => { - return ; - })} - -
    - ); -} diff --git a/app/works/[category]/[work]/page.tsx b/app/works/[category]/[work]/page.tsx new file mode 100644 index 0000000..bcccbd2 --- /dev/null +++ b/app/works/[category]/[work]/page.tsx @@ -0,0 +1,56 @@ +// import { useTranslations } from "next-intl"; + +import { AppNavLink } from "@/components/app-nav-link"; +import { SimpleListing } from "@/components/simple-listing"; +import { otherCategories, proseCategories } from "@/lib/model"; + +interface WorksPageProps { + params?: { + category: string; + work?: string; + }; +} + +export default function WorksPage(props: WorksPageProps) { + // const catt = useTranslations("BernhardCategories"); + // const _t = useTranslations("WorkPage"); + // const catt = (x) => { + // return x; + // }; + return ( + <> +
    + {otherCategories.map((c) => { + return ( + + {c} + + ); + })} +
    +
    + {props.params?.category === "prose" || + (proseCategories as unknown as Array).includes( + props.params?.category as unknown as string, + ) + ? proseCategories.map((c) => { + return ( + + {c} + + ); + }) + : null} +
    + + + ); +} diff --git a/app/works/[category]/page.tsx b/app/works/[category]/page.tsx new file mode 100644 index 0000000..e4896a2 --- /dev/null +++ b/app/works/[category]/page.tsx @@ -0,0 +1,11 @@ +import WorksPage from "./[work]/page"; + +interface BlankWorksPageProps { + params: { + category: string; + }; +} + +export default function BlankWorksPage(props: BlankWorksPageProps) { + return ; +} diff --git a/components/app-header.tsx b/components/app-header.tsx index 490bb31..555dd80 100644 --- a/components/app-header.tsx +++ b/components/app-header.tsx @@ -12,7 +12,7 @@ export function AppHeader(): ReactNode { const links = { home: { href: createHref({ pathname: "/" }), label: t("links.home") }, works: { - href: createHref({ pathname: "/works/drama" }), + href: createHref({ pathname: "/works/poetry" }), label: t("links.works"), }, languages: { diff --git a/components/bernhard-links.tsx b/components/bernhard-links.tsx index ee8c9f9..a9372d2 100644 --- a/components/bernhard-links.tsx +++ b/components/bernhard-links.tsx @@ -1,4 +1,4 @@ -import type { BernhardWork } from "@/types/model"; +import type { BernhardWork } from "@/lib/model"; interface BernhardWorkProps { work: BernhardWork; diff --git a/components/faceted-listing.tsx b/components/faceted-listing.tsx index 49e5a5a..485f8b3 100644 --- a/components/faceted-listing.tsx +++ b/components/faceted-listing.tsx @@ -43,7 +43,7 @@ export async function FacetedListing(props: FacetedListingProps) { facetValue: searchParams.get(props.facet), }); - const data = await getFaceted([props.facet], safeParams.facetValue, safeParams.page); + const data = await getFaceted(props.facet, safeParams.facetValue); const publications = data.hits?.map((h) => { return h.document; }); diff --git a/components/instantsearch.tsx b/components/instantsearch.tsx new file mode 100644 index 0000000..2b1ceec --- /dev/null +++ b/components/instantsearch.tsx @@ -0,0 +1,87 @@ +"use client"; + +import type { ReactNode } from "react"; +import { Configure, Hits, Pagination, SortBy, Stats } from "react-instantsearch"; +import { InstantSearchNext } from "react-instantsearch-nextjs"; +import TypesenseInstantSearchAdapter, { type SearchClient } from "typesense-instantsearch-adapter"; + +import { ClickablePublicationThumbnail } from "@/components/publication-cover"; +import { collectionName } from "@/lib/data"; +import type { Publication } from "@/lib/model"; + +import type { SimpleListingProps } from "./simple-listing"; + +interface InstantSearchProps { + faceting: SimpleListingProps; + // children: ReactNode; +} + +const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({ + server: { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + apiKey: process.env.NEXT_PUBLIC_TYPESENSE_API_KEY!, + nodes: [ + { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + host: process.env.NEXT_PUBLIC_TYPESENSE_HOST!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + port: Number(process.env.NEXT_PUBLIC_TYPESENSE_PORT!), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + protocol: process.env.NEXT_PUBLIC_TYPESENSE_PROTOCOL!, + }, + ], + }, + additionalSearchParameters: { + query_by: "title", + }, +}); + +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +const searchClient = typesenseInstantsearchAdapter.searchClient as unknown as SearchClient; + +export function InstantSearch(props: InstantSearchProps): ReactNode { + // const { children } = props; + return ( + + +
    + sort by{" "} + +
    + { + return ; + }} + /> +
    + + +
    +
    + ); +} diff --git a/components/publication-cover.tsx b/components/publication-cover.tsx index 486e5aa..ea1947c 100644 --- a/components/publication-cover.tsx +++ b/components/publication-cover.tsx @@ -2,7 +2,7 @@ import Image from "next/image"; import { useTranslations } from "next-intl"; import type { ReactNode } from "react"; -import type { Publication } from "@/types/model"; +import type { Publication } from "@/lib/model"; import { AppLink } from "./app-link"; diff --git a/components/simple-listing.tsx b/components/simple-listing.tsx new file mode 100644 index 0000000..1f7b9c8 --- /dev/null +++ b/components/simple-listing.tsx @@ -0,0 +1,33 @@ +import { getFaceted } from "@/lib/data"; + +import { AppNavLink } from "./app-nav-link"; +import { InstantSearch } from "./instantsearch"; +import { MainContent } from "./main-content"; + +export interface SimpleListingProps { + path: string; + filter_by?: string; + facetingField: string; + facetingValue?: string; +} + +export async function SimpleListing(props: SimpleListingProps) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const counts = (await getFaceted(props.facetingField, props.filter_by)).facet_counts?.[0]!.counts; + return ( + +
    + {counts?.map((c) => { + return ( +
  • + + {c.value} ({c.count}) + +
  • + ); + })} +
    +
    {props.facetingValue ? : null}
    +
    + ); +} diff --git a/lib/data.ts b/lib/data.ts index 62a2cc7..def5238 100644 --- a/lib/data.ts +++ b/lib/data.ts @@ -3,8 +3,6 @@ import type { SearchResponse } from "typesense/lib/Typesense/Documents"; import type { Publication } from "@/lib/model"; -const perPage = 16; - export const client = new Client({ nodes: [ { @@ -26,35 +24,18 @@ export const collectionName = "thomas-bernhard"; const collection = client.collections(collectionName); -const searchDefaults = { - q: "*", - filter: {} as Partial, - query_by: "title, contains.title", - page: 1, - per_page: 12, - sort_by: "title:desc", //"_rand:asc", // requires typesense 0.28 ! -}; - -type SearchArgs = Partial; - +// TODO rename 'getcounts' as this is only used in SimpleListing export async function getFaceted( - facetFields: Array, - search = "*", - page = 1, - sortAlphabetic = false, + facetField: string, + filter_by?: string, ): Promise> { return collection.documents().search({ - q: search, + q: "*", query_by: "*", - facet_by: facetFields - .map((name) => { - return sortAlphabetic ? `${name}(sort_by: _alpha:asc)` : name; - }) - .join(), - // filter_by: facetValue && `${facet}: \`${facetValue}\``, - max_facet_values: 100, - page: page, - per_page: perPage, + facet_by: `${facetField}(sort_by: _alpha:asc)`, + filter_by: filter_by, + max_facet_values: 500, + per_page: 1, // don't really need the data }) as unknown as Promise>; } @@ -62,6 +43,16 @@ export async function getPublication(id: string) { return collection.documents(id).retrieve() as Promise; } +const searchDefaults = { + q: "*", + filter: {} as Partial, + query_by: "title, contains.title", + page: 1, + per_page: 12, + sort_by: "title:desc", //"_rand:asc", // requires typesense 0.28 ! +}; + +type SearchArgs = Partial; export async function getPublications( args: SearchArgs = searchDefaults, ): Promise> {