Skip to content

Commit a0f6729

Browse files
pixelsamalijiapeng365lyzno1Copilot
authored
refactor(admin): rebuild content editor with drag-and-drop component system (#209)
Co-authored-by: lijiapeng365 <lijiapeng365@163.com> Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com> Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent 26e1d4b commit a0f6729

39 files changed

+8443
-1713
lines changed

app/about/page.tsx

Lines changed: 46 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use client';
22

3-
import { Button } from '@components/ui/button';
3+
import { DynamicAboutRenderer } from '@components/about/dynamic-about-renderer';
4+
import { AdminButton } from '@components/admin/admin-button';
5+
import { LanguageSwitcher } from '@components/ui/language-switcher';
46
import { PageLoader } from '@components/ui/page-loader';
57
import { useDynamicTranslations } from '@lib/hooks/use-dynamic-translations';
6-
import { useTheme } from '@lib/hooks/use-theme';
78
import { createClient } from '@lib/supabase/client';
8-
import { cn } from '@lib/utils';
9-
import { motion } from 'framer-motion';
9+
import type { AboutTranslationData } from '@lib/types/about-page-components';
1010

1111
import { useEffect, useState } from 'react';
1212

@@ -15,7 +15,6 @@ import { useRouter } from 'next/navigation';
1515

1616
export default function AboutPage() {
1717
const router = useRouter();
18-
const { isDark } = useTheme();
1918
const staticT = useTranslations('pages.about');
2019
const { t: dynamicT, isLoading } = useDynamicTranslations({
2120
sections: ['pages.about'],
@@ -33,54 +32,11 @@ export default function AboutPage() {
3332
setMounted(true);
3433
}, []);
3534

36-
// get colors based on theme
37-
const getColors = () => {
38-
if (isDark) {
39-
return {
40-
titleGradient: 'from-stone-300 to-stone-500',
41-
textColor: 'text-gray-300',
42-
headingColor: 'text-gray-100',
43-
paragraphColor: 'text-gray-400',
44-
cardBg: 'bg-stone-700',
45-
cardBorder: 'border-stone-600',
46-
cardShadow: 'shadow-[0_4px_20px_rgba(0,0,0,0.3)]',
47-
cardHeadingColor: 'text-stone-300',
48-
cardTextColor: 'text-gray-400',
49-
buttonClass:
50-
'bg-stone-600 hover:bg-stone-500 text-gray-100 cursor-pointer hover:scale-105',
51-
};
52-
} else {
53-
return {
54-
titleGradient: 'from-stone-700 to-stone-900',
55-
textColor: 'text-stone-700',
56-
headingColor: 'text-stone-800',
57-
paragraphColor: 'text-stone-600',
58-
cardBg: 'bg-stone-100',
59-
cardBorder: 'border-stone-200',
60-
cardShadow: 'shadow-[0_4px_20px_rgba(0,0,0,0.1)]',
61-
cardHeadingColor: 'text-stone-700',
62-
cardTextColor: 'text-stone-600',
63-
buttonClass:
64-
'bg-stone-800 hover:bg-stone-700 text-gray-100 cursor-pointer hover:scale-105',
65-
};
66-
}
35+
// Homepage-style colors using Tailwind classes
36+
const colors = {
37+
bgClass: 'bg-stone-100 dark:bg-stone-900',
6738
};
6839

69-
const colors = mounted
70-
? getColors()
71-
: {
72-
titleGradient: '',
73-
textColor: '',
74-
headingColor: '',
75-
paragraphColor: '',
76-
cardBg: '',
77-
cardBorder: '',
78-
cardShadow: '',
79-
cardHeadingColor: '',
80-
cardTextColor: '',
81-
buttonClass: '',
82-
};
83-
8440
// handle "start exploring" button click
8541
const handleExploreClick = async () => {
8642
try {
@@ -109,168 +65,47 @@ export default function AboutPage() {
10965
return <PageLoader />;
11066
}
11167

112-
// Extract value cards data from translations (using static raw method for array data)
113-
const valueCards = staticT.raw('values.items') as Array<{
114-
title: string;
115-
description: string;
116-
}>;
68+
// Create translation data object for the dynamic renderer
69+
const translationData: AboutTranslationData = {
70+
// Try to get dynamic sections first
71+
sections: dynamicT('sections', 'pages.about') || undefined,
72+
73+
// Fallback to legacy format for backward compatibility
74+
title: t('title'),
75+
subtitle: t('subtitle'),
76+
mission: {
77+
description: t('mission.description'),
78+
},
79+
values: {
80+
items: staticT.raw('values.items') as Array<{
81+
title: string;
82+
description: string;
83+
}>,
84+
},
85+
buttonText: t('buttonText'),
86+
copyright: {
87+
prefix: t('copyright.prefix'),
88+
linkText: t('copyright.linkText'),
89+
suffix: t('copyright.suffix'),
90+
},
91+
};
11792

11893
return (
119-
<main className="min-h-screen w-full overflow-x-hidden px-4 py-4 sm:px-6 sm:py-6 lg:px-8">
120-
<div className="mx-auto max-w-5xl">
121-
{/* title */}
122-
<motion.section
123-
initial={{ opacity: 0 }}
124-
animate={{ opacity: 1 }}
125-
transition={{ duration: 0.6 }}
126-
className="mb-6 text-center sm:mb-8 lg:mb-10"
127-
>
128-
<motion.h1
129-
initial={{ opacity: 0, y: 20 }}
130-
animate={{ opacity: 1, y: 0 }}
131-
transition={{ duration: 0.6 }}
132-
className={cn(
133-
'bg-gradient-to-r bg-clip-text py-2 leading-tight font-bold text-transparent',
134-
'mb-4 text-3xl sm:mb-6 sm:text-4xl md:text-5xl',
135-
`${colors.titleGradient}`
136-
)}
137-
>
138-
{t('title')}
139-
</motion.h1>
140-
<motion.p
141-
initial={{ opacity: 0, y: 20 }}
142-
animate={{ opacity: 1, y: 0 }}
143-
transition={{ duration: 0.6, delay: 0.2 }}
144-
className={cn(
145-
'mx-auto max-w-3xl font-light',
146-
'text-base sm:text-lg lg:text-xl',
147-
colors.textColor
148-
)}
149-
>
150-
{t('subtitle')}
151-
</motion.p>
152-
</motion.section>
153-
154-
{/* mission */}
155-
<motion.section
156-
initial={{ opacity: 0, y: 20 }}
157-
animate={{ opacity: 1, y: 0 }}
158-
transition={{ duration: 0.6, delay: 0.3 }}
159-
className="mb-6 sm:mb-8 lg:mb-10"
160-
>
161-
<h2
162-
className={cn(
163-
'mb-4 font-bold sm:mb-6',
164-
'text-xl sm:text-2xl',
165-
colors.headingColor
166-
)}
167-
>
168-
{t('mission.title')}
169-
</h2>
170-
<p
171-
className={cn(
172-
'text-sm leading-relaxed sm:text-base lg:text-lg',
173-
colors.paragraphColor
174-
)}
175-
>
176-
{t('mission.description')}
177-
</p>
178-
</motion.section>
179-
180-
{/* values */}
181-
<motion.section
182-
initial={{ opacity: 0, y: 20 }}
183-
animate={{ opacity: 1, y: 0 }}
184-
transition={{ duration: 0.6, delay: 0.4 }}
185-
className="mb-6 sm:mb-8 lg:mb-10"
186-
>
187-
<h2
188-
className={cn(
189-
'mb-4 font-bold sm:mb-6',
190-
'text-xl sm:text-2xl',
191-
colors.headingColor
192-
)}
193-
>
194-
{t('values.title')}
195-
</h2>
196-
<div className="grid grid-cols-1 gap-4 sm:gap-6 md:grid-cols-2">
197-
{valueCards.map((value, index) => (
198-
<motion.div
199-
key={index}
200-
initial={{ opacity: 0, y: 20 }}
201-
animate={{ opacity: 1, y: 0 }}
202-
transition={{ duration: 0.5, delay: 0.5 + index * 0.1 }}
203-
className={cn(
204-
'rounded-xl border',
205-
'p-4 sm:p-6',
206-
colors.cardBg,
207-
colors.cardShadow,
208-
colors.cardBorder
209-
)}
210-
>
211-
<h3
212-
className={cn(
213-
'mb-2 font-semibold',
214-
'text-base sm:text-lg',
215-
colors.cardHeadingColor
216-
)}
217-
>
218-
{value.title}
219-
</h3>
220-
<p
221-
className={cn(
222-
'text-sm leading-relaxed sm:text-base',
223-
colors.cardTextColor
224-
)}
225-
>
226-
{value.description}
227-
</p>
228-
</motion.div>
229-
))}
230-
</div>
231-
</motion.section>
232-
233-
{/* join us */}
234-
<motion.section
235-
initial={{ opacity: 0, y: 20 }}
236-
animate={{ opacity: 1, y: 0 }}
237-
transition={{ duration: 0.6, delay: 0.6 }}
238-
className="mb-6 text-center sm:mb-8 lg:mb-10"
239-
>
240-
<Button
241-
size="lg"
242-
className={cn(
243-
'h-auto rounded-lg font-medium transition-all duration-200',
244-
'px-6 py-2 text-sm sm:px-8 sm:py-3 sm:text-base',
245-
colors.buttonClass
246-
)}
247-
onClick={handleExploreClick}
248-
>
249-
{t('buttonText')}
250-
</Button>
251-
</motion.section>
252-
253-
{/* bottom info */}
254-
<motion.div
255-
initial={{ opacity: 0 }}
256-
animate={{ opacity: 1 }}
257-
transition={{ duration: 0.6, delay: 0.8 }}
258-
className={cn('text-center', 'text-xs sm:text-sm', colors.textColor)}
259-
>
260-
<p>
261-
{t('copyright.prefix', { year: new Date().getFullYear() })}
262-
<a
263-
href="https://github.com/ifLabX/AgentifUI"
264-
target="_blank"
265-
rel="noopener noreferrer"
266-
className="transition-all duration-200 hover:underline hover:opacity-80"
267-
>
268-
{t('copyright.linkText')}
269-
</a>
270-
{t('copyright.suffix')}
271-
</p>
272-
</motion.div>
94+
<div
95+
className={`relative min-h-screen w-full px-4 py-12 sm:px-6 lg:px-8 ${colors.bgClass}`}
96+
>
97+
{/* Top-right toolbar: Admin button (left) + Language switcher (right) */}
98+
<div className="fixed top-4 right-4 z-50 hidden flex-col items-end gap-2 sm:flex sm:flex-row sm:items-center sm:gap-3 lg:top-6 lg:right-6">
99+
<AdminButton />
100+
<LanguageSwitcher variant="floating" />
273101
</div>
274-
</main>
102+
103+
<main className="mx-auto max-w-5xl">
104+
<DynamicAboutRenderer
105+
translationData={translationData}
106+
onButtonClick={handleExploreClick}
107+
/>
108+
</main>
109+
</div>
275110
);
276111
}

0 commit comments

Comments
 (0)