SEO: server-render list pages, GET-based search, URL pagination (size=10), SSR locale fallback, and MobX i18n unification#9
Open
Copilot wants to merge 14 commits into
Open
Conversation
Copilot
AI
changed the title
[WIP] Implement SEO improvements for GoodActionHub
SEO: server-render list pages, GET-based search, and URL pagination (size=10)
Jun 6, 2026
TechQuery
requested changes
Jun 7, 2026
Copilot
AI
changed the title
SEO: server-render list pages, GET-based search, and URL pagination (size=10)
SEO: server-render list pages, GET-based search, URL pagination (size=10), and SSR locale fallback
Jun 7, 2026
[add] Traditional Chinese translation data [optimize] update Upstream packages
There was a problem hiding this comment.
Pull request overview
This PR aims to improve SEO and shareability of the three “list” surfaces by moving search/filter/pagination state into URL-driven server-rendered pages, and by adding SSR language selection/fallback for list-page UI text.
Changes:
- Reworked
/activities,/restaurants, and/tutoringlist pages to be server components with GET-based search and URL pagination (page size = 10). - Introduced SSR language loading via
mobx-i18n+ new translation maps forzh-CN,zh-TW, anden-US. - Aligned activities data fetching by adding
fetchActivitiesCatalog()and reusing it in the API route and activities pages.
Reviewed changes
Copilot reviewed 20 out of 22 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| public/locales/zh-TW/translation.json | Adds Traditional Chinese i18next translation namespace data. |
| public/locales/zh-TW/common.json | Adds Traditional Chinese i18next common namespace data. |
| package.json | Updates scripts/deps; introduces shadcn-helper install hook and dependency upgrades. |
| lib/pagination.ts | Adds shared page parsing + visible page calculation helpers. |
| lib/activities.ts | Adds fetchActivitiesCatalog() to centralize activities fetch behavior. |
| i18n/zh-TW.ts | Adds SSR translation map for Traditional Chinese. |
| i18n/zh-CN.ts | Adds SSR translation map for Simplified Chinese. |
| i18n/en-US.ts | Adds SSR translation map for English (US). |
| i18n/index.ts | Adds SSR i18n store + cookie/header/query language loading. |
| i18n/config.ts | Extends client i18next config to support zh-TW and formatting tweaks. |
| eslint.config.ts | Adds React version setting and keeps existing TS-eslint rule override. |
| components/index.ini | Declares a shadcn registry component to be installed (pager). |
| components.json | Adds a custom shadcn registry URL. |
| app/tutoring/page.tsx | Converts tutoring list to SSR with GET search + URL tag selection + pagination. |
| app/tutoring/[slug]/page.tsx | Adds SSR i18n text usage in tutoring detail page. |
| app/restaurants/page.tsx | Converts restaurants list to SSR with GET search + URL filter + pagination. |
| app/restaurants/[id]/page.tsx | Adds SSR i18n text usage in restaurant detail page. |
| app/api/data/route.ts | Uses fetchActivitiesCatalog() for consistent upstream fetch behavior. |
| app/activities/page.tsx | Converts activities list to SSR with GET search + pagination and SSR locale loading. |
| app/activities/[id]/page.tsx | Uses fetchActivitiesCatalog() + SSR i18n text usage in activity detail page. |
| .gitignore | Ignores components/ui/ (shadcn UI components). |
Comments suppressed due to low confidence (1)
app/api/data/route.ts:14
fetchActivitiesCatalog()now throws on upstream fetch errors, but the API route catches everything and returns HTTP 500. Previously, upstream fetch failures returned 502 (bad gateway), which more accurately signals an upstream dependency issue. Consider preserving 502 for fetch failures so monitoring/clients can distinguish upstream vs server errors.
export async function GET() {
try {
const externalData = await fetchActivitiesCatalog();
const data = externalData.map(transformItem);
return NextResponse.json(data);
} catch (err) {
console.error('Failed to fetch data from external API:', err);
return NextResponse.json({ error: 'Failed to load data' }, { status: 500 });
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1
to
+4
| export function parsePage(rawPage?: string): number { | ||
| const parsed = Number.parseInt(rawPage ?? '1', 10); | ||
| return Number.isFinite(parsed) && parsed > 0 ? parsed : 1; | ||
| } |
Comment on lines
+11
to
+13
| import { createI18nStore, loadSSRLanguage } from '@/i18n'; | ||
| import { Pager } from '@/components/ui/mobx-restful-shadcn/pager'; | ||
| import { parsePage } from '@/lib/pagination'; |
Comment on lines
+5
to
+9
| import { createI18nStore, loadSSRLanguage } from '@/i18n'; | ||
| import FoodAIDialog from '@/components/FoodAIDialog'; | ||
| import SafeTranslation from '@/components/SafeTranslation'; | ||
| import { Pager } from '@/components/ui/mobx-restful-shadcn/pager'; | ||
| import { fetchBitesCatalog, BitesRestaurant } from '@/lib/bitesCatalog'; | ||
| import { parsePage } from '@/lib/pagination'; |
Comment on lines
+6
to
+10
| import { createI18nStore, loadSSRLanguage } from '@/i18n'; | ||
| import { EventCard } from '@/components/EventCard'; | ||
| import { FilterBar } from '@/components/FilterBar'; | ||
| import { GitCodeIcon } from '@/components/icons/GitCodeIcon'; | ||
| import { GitHubIcon } from '@/components/icons/GitHubIcon'; | ||
|
|
||
| import { DeadlineItem, EventData } from '@/lib/data'; | ||
| import { useEventStore } from '@/lib/store'; | ||
| import Fuse from 'fuse.js'; | ||
|
|
||
| import { DateTime } from 'luxon'; | ||
| import Link from 'next/link'; | ||
| import { useEffect, useMemo } from 'react'; | ||
| import { useTranslation } from 'react-i18next'; | ||
| import { Pager } from '@/components/ui/mobx-restful-shadcn/pager'; |
Comment on lines
+161
to
+165
| <div className="bg-white/80 backdrop-blur-sm rounded-xl p-6 shadow-lg border border-white/20 mb-8"> | ||
| <FilterBar /> | ||
| <form action="/activities" method="get" className="flex gap-3"> | ||
| <input | ||
| type="text" | ||
| name="query" |
Comment on lines
13
to
15
| "start": "next start", | ||
| "test": "next typegen && lint-staged && tsc --noEmit", | ||
| "test": "next typegen && lint-staged && git add . && tsc --noEmit", | ||
| "knip": "knip", |
Comment on lines
+6
to
+8
| "registries": { | ||
| "@mobx-restful-shadcn": "https://mobx-restful-shadcn.idea2.app/r/{name}.json" | ||
| }, |
Comment on lines
+11
to
+15
| const i18nData = { | ||
| 'zh-CN': zhCN, | ||
| 'zh-TW': () => import('./zh-TW'), | ||
| 'en-US': () => import('./en-US'), | ||
| }; |
Member
|
@copilot 完全使用 |
Contributor
Author
已在 |
Copilot
AI
changed the title
SEO: server-render list pages, GET-based search, URL pagination (size=10), and SSR locale fallback
SEO: server-render list pages, GET-based search, URL pagination (size=10), SSR locale fallback, and MobX i18n unification
Jun 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR addresses SEO for the three list surfaces by moving rendering/filtering state into URL-driven server pages. It also standardizes pagination to URL parameters with a 10-item page size and visible page links at the end of each list, and fully unifies translation handling on
mobx-i18n.Scope (issue requirements)
GETform submissions.Server-rendered list pages
app/activities/page.tsxapp/restaurants/page.tsxapp/tutoring/page.tsxsearchParamson the server, compute filtered datasets, and slice results by page.mobx-i18n.URL-driven search + filtering
GETforms:/activities?keywords=.../restaurants?keywords=...&filter=.../tutoring?keywords=...&tag=...Pagination behavior
lib/pagination.ts.components/ui/mobx-restful-shadcn/pager.tsxto match the current imports and keep builds reproducible.PAGE_SIZE = 10pageIndexquery paramData access alignment
fetchActivitiesCatalog()inlib/activities.tsand reused it in activities list/detail and/api/datapath to keep fetch behavior consistent.Client i18n unification
i18next/react-i18nextclient integration with a sharedmobx-i18nhook/store.i18next-related config and dependencies.Human changes