Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
535e663
Initial plan
Copilot Jun 6, 2026
6667edb
feat: server render list pages with URL search pagination
Copilot Jun 6, 2026
36e2d2d
refactor: share pagination helpers and tighten list filter parsing
Copilot Jun 6, 2026
fb5d908
Merge remote-tracking branch 'origin/main' into copilot/seo-goodactio…
Copilot Jun 7, 2026
4d6b6e0
chore: address review feedback on server list pages
Copilot Jun 7, 2026
e5930b9
[refactor] simplify Source Code with MobX-i18n & MobX-RESTful-Shadcn
TechQuery Jun 8, 2026
17f423d
[migrate] replace Bun with PNPM for compatibility
TechQuery Jun 8, 2026
479c328
[fix] Parameter Name of Search Keyword
TechQuery Jun 13, 2026
2a79bfd
[fix] rollback Activity page filter
TechQuery Jun 13, 2026
d9889cb
refactor: replace i18next with mobx-i18n
Copilot Jun 13, 2026
0afcb05
fix: address i18n review follow-ups
Copilot Jun 13, 2026
649f2de
fix: tighten mobx i18n typing
Copilot Jun 13, 2026
224b5fc
fix: polish mobx i18n hook
Copilot Jun 13, 2026
822f0a4
fix: clean up mobx i18n helpers
Copilot Jun 13, 2026
9060b31
fix: restore mobx i18n context flow
Copilot Jun 14, 2026
9e8c13b
fix: address i18n review follow-ups
Copilot Jun 14, 2026
f2a5c15
fix: polish pager i18n follow-ups
Copilot Jun 14, 2026
32c7c3e
fix: tidy pager locale details
Copilot Jun 14, 2026
964b407
fix: clean up final i18n review nits
Copilot Jun 14, 2026
c5e40b7
fix: sweep remaining i18n review issues
Copilot Jun 15, 2026
839a978
fix: address final validation feedback
Copilot Jun 15, 2026
b7b622e
fix: sweep review guidance
Copilot Jun 15, 2026
7d03385
fix: finalize i18n review sweep
Copilot Jun 15, 2026
071d8c3
fix: sweep latest review feedback
Copilot Jun 15, 2026
f84af4b
fix: normalize empty activities query URLs
Copilot Jun 15, 2026
194b05c
fix: clean empty activities URLs
Copilot Jun 15, 2026
6d9c8fe
Fix remaining review patterns
Copilot Jun 15, 2026
8eb3f09
Clean package script formatting
Copilot Jun 15, 2026
7a04017
Fix remaining review patterns
Copilot Jun 15, 2026
2ebe8b9
Harden timezone loading cleanup
Copilot Jun 15, 2026
5993f8b
Address latest review sweep
Copilot Jun 15, 2026
25972e1
Align remaining review patterns
Copilot Jun 15, 2026
95cea06
Apply suggestions from code review
TechQuery Jun 15, 2026
9da085c
[fix] Server starting bugs
TechQuery Jun 15, 2026
23bca88
[remove] some useless codes
TechQuery Jun 16, 2026
5f3d912
[optimize] update i18n data & simplify i18n SSR bootstrap
TechQuery Jun 16, 2026
124c7c8
Server-render remaining app pages
Copilot Jun 16, 2026
0a425cf
Clarify recommend fetch errors
Copilot Jun 16, 2026
968a32b
Remove redundant recommend assertion
Copilot Jun 16, 2026
08feb3b
Apply review-wide component cleanup
Copilot Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .devcontainer/Dockerfile

This file was deleted.

28 changes: 0 additions & 28 deletions .devcontainer/devcontainer.json

This file was deleted.

44 changes: 9 additions & 35 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Deploy to Vercel
on:
push:
branches:
- "**"
- '**'

permissions:
contents: write
Expand All @@ -18,43 +18,17 @@ jobs:
- uses: actions/checkout@v6
if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}

- uses: oven-sh/setup-bun@v2
if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}

- name: Deploy to Vercel
id: vercel-deployment
if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}
shell: bash
run: |
set -euo pipefail

bun install vercel -g

if [[ "$GITHUB_REF" == 'refs/heads/main' ]]; then
DeployOutput=$(vercel -t "$VERCEL_TOKEN" --prod)
else
DeployOutput=$(vercel -t "$VERCEL_TOKEN")
fi
echo "$DeployOutput"

ParsedURL=$(echo "$DeployOutput" | grep -Eo 'https://[^[:space:]]*\.vercel\.app' | tail -n 1)

if [[ -z "$ParsedURL" ]]; then
echo "Failed to parse Vercel URL from deploy output"
exit 1
fi
vercel inspect "$ParsedURL" -t "$VERCEL_TOKEN" -F json > vercel-inspect.json

InspectURL=$(jq -r '.url // empty' vercel-inspect.json)

if [[ -z "$InspectURL" ]]; then
echo "Failed to parse inspect url from vercel-inspect.json"
exit 1
fi
if [[ "$InspectURL" != http* ]]; then
InspectURL="https://$InspectURL"
fi
echo "preview-url=$InspectURL" >> "$GITHUB_OUTPUT"
uses: amondnet/vercel-action@v42.3.0
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
working-directory: ./
vercel-args: ${{ github.ref == 'refs/heads/main' && ' --prod' || '' }}

- name: Lark notification
uses: Open-Source-Bazaar/feishu-action@v3
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,6 @@ __pycache__/

# Secret environment files (local overrides)
.env*.local

# Shadcn UI components
components/ui/*
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
auto-install-peers = false
49 changes: 23 additions & 26 deletions app/activities/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
import { ChinaMapWrapper } from '@/components/ChinaMapWrapper';
import { CommentBox } from '@/components/CommentBox';
import { TimelineItem } from '@/components/TimelineItem';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
ACTIVITIES_API_URL,
ExternalDeadlineItem,
transformItem,
} from '@/lib/activities';
import { formatTimezoneToUTC } from '@/lib/utils';
import {
ArrowLeft,
Calendar,
Expand All @@ -23,15 +11,21 @@ import { DateTime } from 'luxon';
import Link from 'next/link';
import { notFound } from 'next/navigation';

import { loadSSRI18nFromRequest } from '@/i18n/server';
import { ChinaMapWrapper } from '@/components/ChinaMapWrapper';
import { CommentBox } from '@/components/CommentBox';
import { TimelineItem } from '@/components/TimelineItem';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { fetchActivitiesCatalog, transformItem } from '@/lib/activities';
import { formatTimezoneToUTC } from '@/lib/utils';

const DATA_EDIT_URL =
'https://github.com/GoodAction-Hub/GoodAction-data/edit/main/data/activities.yml';

async function findActivity(id: string) {
const res = await fetch(ACTIVITIES_API_URL, { cache: 'force-cache' });

if (!res.ok) throw new URIError(`Failed to fetch activities: ${res.status}`);

const externalData = (await res.json()) as ExternalDeadlineItem[];
const externalData = await fetchActivitiesCatalog();

for (const raw of externalData) {
const item = transformItem(raw);
Expand All @@ -47,6 +41,8 @@ export default async function EventDetailPage({
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const { t } = await loadSSRI18nFromRequest();

const found = await findActivity(id);
if (!found) notFound();

Expand Down Expand Up @@ -89,12 +85,13 @@ export default async function EventDetailPage({
<Link href="/activities">
<Button variant="outline" size="sm" className="gap-2">
<ArrowLeft className="w-4 h-4" />
返回列表
{t('activities_detail_text_back_to_list')}
</Button>
</Link>
<a href={DATA_EDIT_URL} target="_blank" rel="noopener noreferrer">
<Button variant="outline" size="sm" className="gap-2">
<Pencil className="w-4 h-4" />在 GitHub 上编辑
<Pencil className="w-4 h-4" />
{t('activities_detail_text_edit_on_github')}
</Button>
</a>
</div>
Expand All @@ -109,17 +106,17 @@ export default async function EventDetailPage({
className={`inline-flex px-4 py-2 rounded-xl text-sm font-bold shadow-lg ${categoryStyle}`}
>
{item.category === 'conference'
? '会议'
? t('activities_detail_text_category_conference')
: item.category === 'competition'
? '竞赛'
: '活动'}
? t('activities_detail_text_category_competition')
: t('activities_detail_text_category_activity')}
</div>
<Badge variant="outline" className="text-xs">
{event.year}
</Badge>
{ended && (
<Badge variant="secondary" className="text-xs">
已结束
{t('activities_detail_text_ended')}
</Badge>
)}
</div>
Expand Down Expand Up @@ -178,7 +175,7 @@ export default async function EventDetailPage({
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm font-medium">
<Clock className="w-4 h-4" />
时间轴
{t('activities_detail_text_timeline')}
</div>
<div className="relative bg-gray-50 rounded-lg border h-16 flex items-center">
<div className="absolute left-[10%] right-[10%] h-0.5 bg-gray-300 top-1/2 -translate-y-1/2" />
Expand Down Expand Up @@ -207,7 +204,7 @@ export default async function EventDetailPage({
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<MapPin className="w-5 h-5" />
活动地点
{t('activities_detail_text_location')}
</CardTitle>
</CardHeader>
<CardContent>
Expand All @@ -225,7 +222,7 @@ export default async function EventDetailPage({
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<MessageSquare className="w-5 h-5" />
评论
{t('activities_detail_text_comments')}
</CardTitle>
</CardHeader>
<CardContent>
Expand Down
Loading
Loading