diff --git a/.gitignore b/.gitignore
index 22d7c07..cf490e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,3 +80,6 @@ __pycache__/
# Secret environment files (local overrides)
.env*.local
+
+# Shadcn UI components
+components/ui/
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..8638f02
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+auto-install-peers = false
diff --git a/app/activities/[id]/page.tsx b/app/activities/[id]/page.tsx
index 9b14389..d3687df 100644
--- a/app/activities/[id]/page.tsx
+++ b/app/activities/[id]/page.tsx
@@ -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,
@@ -21,17 +9,24 @@ import {
} from 'lucide-react';
import { DateTime } from 'luxon';
import Link from 'next/link';
+import { headers } from 'next/headers';
import { notFound } from 'next/navigation';
+import { createI18nStore, loadSSRLanguage } from '@/i18n';
+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);
@@ -47,6 +42,13 @@ export default async function EventDetailPage({
params: Promise<{ id: string }>;
}) {
const { id } = await params;
+ const headerStore = await headers();
+ const { language, languageMap } = await loadSSRLanguage({
+ cookie: headerStore.get('cookie') ?? '',
+ acceptLanguage: headerStore.get('accept-language') ?? '',
+ });
+ const { t } = createI18nStore(language, languageMap);
+
const found = await findActivity(id);
if (!found) notFound();
@@ -89,12 +91,13 @@ export default async function EventDetailPage({
@@ -109,17 +112,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')}
{t('events.loading')}
-- 公益慈善会议、竞赛和活动重要截止日期概览,不再错过参与公益事业、奉献爱心和社会服务的机会 + {t('activities_list_text_subtitle')}
@@ -228,14 +223,114 @@ export default function Home() {
- {t('events.hint')} + {t('activities_list_text_no_result_tip')}
Failed to load data: {error}
特色服务
++ {t('restaurants_detail_text_features')} +
-
- 志愿者老师的备课资料库,含教案、示范视频与音频素材 + {t('tutoring_list_text_subtitle')}
正在加载课程...
-请尝试更换关键词或标签
+{t('tutoring_list_text_empty_tip')}