Repository navigation
feat: Add public list pages with search, filters and pagination - #891
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
React Doctor found 2 new issues in 2 files · 2 warnings · score 83 / 100 (Needs work) · 0 fixed · vs 2 warnings
Reviewed by React Doctor for commit |
…imitives - Content Engine: `publicApi.listFields` now shapes the list route's response schema (`schemas.publicListSelectObject`) and `ContentPublicListRow`, so list types and OpenAPI match the runtime projection. - `@vitnode/core/tanstack/content`: `defineContentList`, `contentListQueryOptions`, `contentListPageHead`, `ContentListResults`, `ContentListPagination` and `useContentListSearchInput`. - `@vitnode/core/content/server`: export `resolveContentRequestLocale`, `contentLocaleHeaders`, `rememberContentPublicRead` and the page cache tag helpers; the public routes use the shared locale helper. - Plugin route robots accept `noindex, follow` for list variants. - Blog: `/blog` with search, category filter, sort and numbered pagination in the URL, cards from a lightweight `listFields` projection, and a public `GET /categories` summary route cached per locale. - Docs: public lists guide, embedding guide, API reference and blog guide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B89NT73f4bRH7pk5MU6XiU
- `listFields` narrows `ContentPublicListRow`, `publicListSelectObject` and the list route's OpenAPI schema while the detail keeps every field. - `defineContentList` URL normalization, page reset, API query and href; `contentListQueryOptions` key; `contentListPageHead` robots and alternates. - `ContentListResults` states and `useContentListSearchInput` debouncing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B89NT73f4bRH7pk5MU6XiU
…ublic routes
- `fetcher` / `fetcher/server`: a JSON body is typed with Hono's `JSONParsed`,
the same transform `c.json()` applies, so a `z.date()` field reads as its
ISO `string` instead of a `Date` the wire never carries.
- Content Engine public list, detail and preview routes answer with the typed
`publicListSelect` / `publicSelect` schemas instead of the erased
`ZodObject<ZodRawShape>`, so `fetcher({ module: "content/blog", path: "/" })`
infers each row from `publicApi.listFields` rather than `Record<string, unknown>`.
- Blog: drop the hand-written zod copies of the article, card and category
shapes; their types come from the routes.
- Forum: the approval notice reads only the fields it renders, which fits the
edit route's JSON answer.
- Docs: fetcher response typing and the content list examples.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B89NT73f4bRH7pk5MU6XiU
- A `z.date()` field in a JSON response reads as `string`, nested and nullable ones too. - A content type's public list route infers rows from `listFields`, while the detail route keeps every exposed field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B89NT73f4bRH7pk5MU6XiU
1e218b5 to
b426eb7
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b426eb7239
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ...blogPostsQueryOptions({ locale, search }), | ||
| meta: LOADER_DATA_QUERY_META, | ||
| }) | ||
| .catch(() => null), |
There was a problem hiding this comment.
Prevent failed list loads from entering the page cache
When the initial posts request fails during a production cache miss, this catch converts the loader failure into null, so /blog still returns a cacheable 200 error screen. Since the route caches /blog for 300 seconds plus 60 seconds of stale-while-revalidate and the failed API read contributes no content tags, a transient outage can make the error screen persist for every visitor after the API recovers. Let the shared render fail or otherwise mark this degraded response as non-cacheable; the categories fallback below has the same problem.
Useful? React with 👍 / 👎.
| if (committed !== value) { | ||
| setCommitted(value); | ||
| if ((value ?? "") !== draft.trim()) setDraft(value ?? ""); | ||
| } |
There was a problem hiding this comment.
Cancel pending search commits after URL changes
When value changes while a debounce is pending—for example, a reader edits an existing query and then clicks Clear filters or navigates Back before 500 ms—this synchronization updates the displayed draft but leaves the discarded term scheduled. The callback later invokes onCommit with that stale term and navigates back to the search the reader just left, so clearing or history navigation does not stick. Cancel the pending callback when an external value change is adopted.
Useful? React with 👍 / 👎.
… pages - Implemented `public-posts.ts` for handling published post conditions and translations. - Created `authors.module.ts` to manage authors of blog posts with caching. - Developed blog category pages with sidebar navigation and chips for better UX. - Introduced skeleton loading states for blog pages to improve user experience during data fetching. - Added transitions for blog categories and articles to enhance visual feedback. - Implemented legacy post redirection for improved SEO and user navigation.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 158662519d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| routePaths: { | ||
| pl: { | ||
| '/blog/:slug': '/wpisy/:slug', | ||
| '/blog/article/:slug': '/blog/artykul/:slug', |
There was a problem hiding this comment.
Preserve the retired Polish article route
When a previously published Polish URL such as /pl/wpisy/my-post is requested, the checked web app no longer rewrites it to the new legacy handler: this replacement removes the old /blog/:slug → /wpisy/:slug mapping, while the redirect page added in plugins/blog/src/routes.ts is still registered only at the internal /blog/:slug path. Those existing localized links therefore become 404s instead of receiving the promised permanent redirect; retain a Polish mapping for the legacy route in both the web and API i18n configurations.
Useful? React with 👍 / 👎.
| inArray(blog_posts_author_id.itemId, postIds), | ||
| publishedPostCondition(), |
There was a problem hiding this comment.
Gate author lookups on localized publication
When an article's base row is published but all of its translation rows are draft or unpublished, this anonymous endpoint still returns the article ID and its authors because publishedPostCondition() checks only the base table. The normal localized public list additionally requires a published translation, so callers can enumerate IDs here and disclose authorship for an article that has no public page; apply the localized publication predicate, either for a requested locale or for at least one enabled locale.
Useful? React with 👍 / 👎.
| return parseSearch({ | ||
| ...current, | ||
| ...patch, | ||
| page: resetsPage ? undefined : (patch.page ?? current.page), |
There was a problem hiding this comment.
Honor explicit page clearing in
withSearch
When a consumer calls withSearch(current, { page: undefined }) to return from a later page to the first page, the nullish coalescing expression restores current.page, so the URL remains on the old page. This contradicts the helper's documented rule that undefined clears a value; distinguish an absent page property from an explicitly supplied undefined instead of using ??.
Useful? React with 👍 / 👎.
|
|
||
| if (!selected) { | ||
| const translated = categories.items.find(category => | ||
| Object.values(category.slugs).includes(slug), |
There was a problem hiding this comment.
React Doctor · react-doctor/js-set-map-lookups (warning)
This scales poorly because array.includes() inside a loop scans the whole list every time. Use a Set for constant-time lookups.
Fix → Use a Set or Map when you check for the same items over and over. Array.includes/find scans the whole list each time
| <Empty className="border"> | ||
| <EmptyHeader> | ||
| <EmptyMedia variant="icon">{icon}</EmptyMedia> | ||
| <EmptyTitle render={<h2 />}>{title}</EmptyTitle> |
There was a problem hiding this comment.
React Doctor · react-doctor/heading-has-content (warning)
Blind users can't use this heading to navigate because screen readers skip it empty, so add text, aria-label, or aria-labelledby.
Fix → Put readable text in every heading.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e32015f067
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const overlay = await loadContentPublicPeopleOverlay( | ||
| c, | ||
| definition, | ||
| people, | ||
| rows, | ||
| ); |
There was a problem hiding this comment.
Hydrate collection-backed people in live previews
When publicApi.people names a to-many user field and a preview token has revisionId: 0 (the supported path for records that predate editorial revisions), readPreviewRow obtains a live row through contentPublicSelection, which deliberately omits junction-backed fields. This call therefore receives no IDs and replaces the field with an empty array, so previews silently lose authors even though ordinary detail and list reads load them through advanced.loadMany; the live-preview path needs the collection-aware hydrator before resolving people.
Useful? React with 👍 / 👎.
Description
What?
Implements a complete public list system for the Content Engine, enabling plugins to create searchable, filterable, paginated list pages (like
/blog) with URL-based state management. This includes:defineContentList,contentListQueryOptions,contentListPageHead): Framework-agnostic helpers for declaring sorts, filters, page size and building normalized URL stateContentListResults,ContentListPagination,useContentListSearchInput): Reusable React components for rendering lists with proper accessibility and pagination/blogpage implementation with search, category filtering, sort options and paginationlistFieldsconfiguration to optimize list row payloadsKey features:
index, followwith canonical links; filtered/paginated views arenoindex, followWhy?
The Content Engine needed a reference implementation for public list pages. This enables:
listFieldsto avoid sending unnecessary data to list viewsThe blog plugin demonstrates the full pattern: declaring sorts/filters, fetching paginated data, rendering cards, and handling search/filtering in the UI.
https://claude.ai/code/session_01B89NT73f4bRH7pk5MU6XiU