From 6fc1a5b93cb3b18ab95da216c3779ca67c35e7f5 Mon Sep 17 00:00:00 2001 From: abingyyds Date: Sat, 21 Mar 2026 17:18:33 +0900 Subject: [PATCH] feat: enhance themes with react-bits components & add Claude theme - Clean theme: added ShinyText badge, BlurText hero, SpotlightCard feature cards, StarBorder dashboard button - Corporate theme: added GradientText hero, ShinyText badge, SpotlightCard feature cards and pricing cards - New 'claude' theme: warm terracotta/coral (#D97757) color scheme with cream backgrounds, GradientText, BlurText, ShinyText, SpotlightCard, StarBorder - Claude's signature design language - All themes include full nav with topup, LanguageSwitch, mobile menu Co-Authored-By: Claude Sonnet 4.6 --- src/context/ThemeContext.jsx | 4 + src/themes/claude/Home.jsx | 289 ++++++++++++++++++++++++++++++++++ src/themes/claude/Layout.jsx | 148 +++++++++++++++++ src/themes/clean/Home.jsx | 129 +++++++++------ src/themes/corporate/Home.jsx | 137 +++++++++------- 5 files changed, 602 insertions(+), 105 deletions(-) create mode 100644 src/themes/claude/Home.jsx create mode 100644 src/themes/claude/Layout.jsx diff --git a/src/context/ThemeContext.jsx b/src/context/ThemeContext.jsx index d1e4511..ba0d733 100644 --- a/src/context/ThemeContext.jsx +++ b/src/context/ThemeContext.jsx @@ -29,6 +29,10 @@ const themeRegistry = { Home: React.lazy(() => import('../themes/corporate/Home')), Layout: React.lazy(() => import('../themes/corporate/Layout')), }, + claude: { + Home: React.lazy(() => import('../themes/claude/Home')), + Layout: React.lazy(() => import('../themes/claude/Layout')), + }, }; // Full-screen loading spinner shown while site info is being fetched diff --git a/src/themes/claude/Home.jsx b/src/themes/claude/Home.jsx new file mode 100644 index 0000000..ae2de0f --- /dev/null +++ b/src/themes/claude/Home.jsx @@ -0,0 +1,289 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { useAuth } from '../../context/AuthContext'; +import { useSite } from '../../context/SiteContext'; +import { getSiteModels, getSitePackages } from '../../api'; +import CountUp from '../../components/bits/CountUp'; +import FadeContent from '../../components/bits/FadeContent'; +import GradientText from '../../components/bits/GradientText'; +import BlurText from '../../components/bits/BlurText'; +import ShinyText from '../../components/bits/ShinyText'; +import SpotlightCard from '../../components/bits/SpotlightCard'; +import StarBorder from '../../components/bits/StarBorder'; + +export default function ClaudeHome() { + const { t } = useTranslation(); + const { user } = useAuth(); + const { site } = useSite(); + const [models, setModels] = useState([]); + const [packages, setPackages] = useState([]); + + useEffect(() => { + getSiteModels().then(r => { if (r.data.success) setModels(r.data.data || []); }).catch(() => {}); + getSitePackages().then(r => { if (r.data.success) setPackages(r.data.data || []); }).catch(() => {}); + }, []); + + const enabledModels = models.filter(m => m.enabled !== false); + + return ( +
+ {/* Hero */} +
+ {/* Warm gradient background */} +
+
+
+ +
+ +
+ {/* Badge */} +
+ + +
+ + {/* Title */} +

+ + {site?.name || t('home.defaultHeroTitle')} + +

+ + {/* Subtitle */} +
+ +
+ + {/* CTA Buttons */} +
+ {user ? ( + + + {t('home.goToDashboard')} → + + + ) : ( + <> + + {t('home.getStarted')} → + + + {t('home.viewPricing')} + + + )} +
+
+
+ + {/* Stats */} + +
+
+
+ + +
+

{t('home.aiModels')}

+
+
+
+ % +
+

{t('home.uptime')}

+
+
+
+ <ms +
+

{t('home.latency')}

+
+
+
+
+
+ + {/* Features */} +
+
+ +
+

{t('home.whyChooseUs')}

+

{t('home.whyChooseUsDesc')}

+
+ +
+ +
+
+ + + +
+

{t('home.lightningFast')}

+

{t('home.lightningFastDesc')}

+
+
+ + +
+
+ + + +
+

{t('home.securePrivate')}

+

{t('home.securePrivateDesc')}

+
+
+ + +
+
+ + + +
+

{t('home.payAsYouGo')}

+

{t('home.payAsYouGoDesc')}

+
+
+
+
+
+
+ + {/* Models */} + {enabledModels.length > 0 && ( +
+ +
+

{t('home.availableModels')}

+

{t('home.availableModelsDesc', { count: enabledModels.length })}

+
+ +
+ {enabledModels.slice(0, 12).map((m, i) => ( +
+
+ + {m.display_name || m.model_name} + +
+ ))} +
+ + {enabledModels.length > 12 && ( +
+ + {t('home.viewAllModels', { count: enabledModels.length })} → + +
+ )} + +
+ )} + + {/* Packages */} + {packages.length > 0 && ( +
+
+ +
+

{t('home.plansPackages')}

+

{t('home.choosePlan')}

+
+ +
+ {packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => ( + +
+ {i === 1 && ( + + + Popular + + )} +

{pkg.name}

+ {pkg.description &&

{pkg.description}

} +
+
+ ${pkg.price} + {pkg.original_price > pkg.price && ( + ${pkg.original_price} + )} +
+ {pkg.duration > 0 &&

{t('home.days', { count: pkg.duration })}

} +
+ + {user ? t('home.subscribe') : t('home.getStarted')} + +
+
+ ))} +
+
+
+
+ )} + + {/* CTA */} +
+ +
+ {/* Decorative elements */} +
+
+ +
+

{t('home.readyToStart')}

+

{t('home.readyToStartDesc')}

+
+ {user ? ( + + {t('home.goToDashboard')} + + ) : ( + <> + + {t('home.createFreeAccount')} + + + {t('home.signIn')} + + + )} +
+
+
+ +
+
+ ); +} diff --git a/src/themes/claude/Layout.jsx b/src/themes/claude/Layout.jsx new file mode 100644 index 0000000..c19ac95 --- /dev/null +++ b/src/themes/claude/Layout.jsx @@ -0,0 +1,148 @@ +import React, { useState } from 'react'; +import { Outlet, Link, useNavigate, useLocation } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { useAuth } from '../../context/AuthContext'; +import { useSite } from '../../context/SiteContext'; +import LanguageSwitch from '../../components/LanguageSwitch'; + +export default function ClaudeLayout() { + const { t } = useTranslation(); + const { user, logout } = useAuth(); + const { site } = useSite(); + const navigate = useNavigate(); + const location = useLocation(); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + + const siteName = site?.name || 'AI Platform'; + + const navItems = [ + { to: '/', label: t('nav.home'), auth: false }, + { to: '/pricing', label: t('nav.pricing'), auth: false }, + { to: '/packages', label: t('nav.packages'), auth: false }, + { to: '/dashboard', label: t('nav.dashboard'), auth: true }, + { to: '/tokens', label: t('nav.apiKeys'), auth: true }, + ...(site?.enable_topup ? [{ to: '/topup', label: t('nav.topup'), auth: true }] : []), + ]; + + const visibleNavItems = navItems.filter((n) => !n.auth || user); + + return ( +
+ {/* Announcement */} + {site?.announcement && ( +
+

{site.announcement}

+
+ )} + + {/* Header */} +
+
+ + {site?.logo ? ( + {siteName} + ) : ( +
+ {siteName.charAt(0)} +
+ )} + + {siteName} + + + + + +
+ + + {user ? ( +
+ {user.display_name || user.username} + +
+ ) : ( + <> + + {t('nav.login')} + + + {t('nav.signUp')} + + + )} + + +
+
+ + {mobileMenuOpen && ( +
+ +
+ )} +
+ +
+ + +
+ ); +} diff --git a/src/themes/clean/Home.jsx b/src/themes/clean/Home.jsx index 52c6933..5ffcaa6 100644 --- a/src/themes/clean/Home.jsx +++ b/src/themes/clean/Home.jsx @@ -6,6 +6,10 @@ import { useSite } from '../../context/SiteContext'; import { getSiteModels, getSitePackages } from '../../api'; import CountUp from '../../components/bits/CountUp'; import FadeContent from '../../components/bits/FadeContent'; +import ShinyText from '../../components/bits/ShinyText'; +import BlurText from '../../components/bits/BlurText'; +import SpotlightCard from '../../components/bits/SpotlightCard'; +import StarBorder from '../../components/bits/StarBorder'; export default function CleanHome() { const { t } = useTranslation(); @@ -28,12 +32,23 @@ export default function CleanHome() {
- - {t('home.lightningFast')} + +

- {site?.name || t('home.defaultHeroTitle')} +

@@ -42,8 +57,10 @@ export default function CleanHome() {

{user ? ( - - {t('home.goToDashboard')} → + + + {t('home.goToDashboard')} → + ) : ( <> @@ -94,35 +111,41 @@ export default function CleanHome() {
-
-
- - - + +
+
+ + + +
+

{t('home.lightningFast')}

+

{t('home.lightningFastDesc')}

-

{t('home.lightningFast')}

-

{t('home.lightningFastDesc')}

-
+ -
-
- - - + +
+
+ + + +
+

{t('home.securePrivate')}

+

{t('home.securePrivateDesc')}

-

{t('home.securePrivate')}

-

{t('home.securePrivateDesc')}

-
+ -
-
- - - + +
+
+ + + +
+

{t('home.payAsYouGo')}

+

{t('home.payAsYouGoDesc')}

-

{t('home.payAsYouGo')}

-

{t('home.payAsYouGoDesc')}

-
+
@@ -171,29 +194,35 @@ export default function CleanHome() {
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => ( -
- {i === 1 && Popular} -

{pkg.name}

- {pkg.description &&

{pkg.description}

} -
-
- ${pkg.price} - {pkg.original_price > pkg.price && ( - ${pkg.original_price} - )} + +
+ {i === 1 && Popular} +

{pkg.name}

+ {pkg.description &&

{pkg.description}

} +
+
+ ${pkg.price} + {pkg.original_price > pkg.price && ( + ${pkg.original_price} + )} +
+ {pkg.duration > 0 &&

{t('home.days', { count: pkg.duration })}

}
- {pkg.duration > 0 &&

{t('home.days', { count: pkg.duration })}

} + + {user ? t('home.subscribe') : t('home.getStarted')} +
- - {user ? t('home.subscribe') : t('home.getStarted')} - -
+ ))}
diff --git a/src/themes/corporate/Home.jsx b/src/themes/corporate/Home.jsx index 75afe86..cef7023 100644 --- a/src/themes/corporate/Home.jsx +++ b/src/themes/corporate/Home.jsx @@ -6,6 +6,9 @@ import { useSite } from '../../context/SiteContext'; import { getSiteModels, getSitePackages } from '../../api'; import CountUp from '../../components/bits/CountUp'; import FadeContent from '../../components/bits/FadeContent'; +import GradientText from '../../components/bits/GradientText'; +import SpotlightCard from '../../components/bits/SpotlightCard'; +import ShinyText from '../../components/bits/ShinyText'; export default function CorporateHome() { const { t } = useTranslation(); @@ -32,11 +35,23 @@ export default function CorporateHome() {
- {t('home.lightningFast')} +
-

- {site?.name || t('home.defaultHeroTitle')} +

+ + {site?.name || t('home.defaultHeroTitle')} +

@@ -100,35 +115,41 @@ export default function CorporateHome() {

-
-
- - - + +
+
+ + + +
+

{t('home.lightningFast')}

+

{t('home.lightningFastDesc')}

-

{t('home.lightningFast')}

-

{t('home.lightningFastDesc')}

-
+ -
-
- - - + +
+
+ + + +
+

{t('home.securePrivate')}

+

{t('home.securePrivateDesc')}

-

{t('home.securePrivate')}

-

{t('home.securePrivateDesc')}

-
+ -
-
- - - + +
+
+ + + +
+

{t('home.payAsYouGo')}

+

{t('home.payAsYouGoDesc')}

-

{t('home.payAsYouGo')}

-

{t('home.payAsYouGoDesc')}

-
+
@@ -178,37 +199,43 @@ export default function CorporateHome() {
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => ( -
- {i === 1 && ( -
- Popular -
- )} -
-

{pkg.name}

- {pkg.description &&

{pkg.description}

} -
-
- ${pkg.price} - {pkg.original_price > pkg.price && ( - ${pkg.original_price} - )} + +
+ {i === 1 && ( +
+ Popular
- {pkg.duration > 0 &&

{t('home.days', { count: pkg.duration })}

} + )} +
+

{pkg.name}

+ {pkg.description &&

{pkg.description}

} +
+
+ ${pkg.price} + {pkg.original_price > pkg.price && ( + ${pkg.original_price} + )} +
+ {pkg.duration > 0 &&

{t('home.days', { count: pkg.duration })}

} +
+
+
+ + {user ? t('home.subscribe') : t('home.getStarted')} +
-
- - {user ? t('home.subscribe') : t('home.getStarted')} - -
-
+ ))}