refactor(landing): update app entry points and utilities

Update App.tsx and main.tsx with new routing and providers.
Update useAnimationHelpers hook, locales index, i18n mocks.
Update iconMap and vite configuration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Quinn Ftw 2025-12-28 16:09:46 -08:00
parent e48131424f
commit 4acf3ef8a9
7 changed files with 104 additions and 39 deletions

View file

@ -1,13 +1,15 @@
import { usePageViewTracking } from '@lilith/analytics-client/react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { I18nProvider } from './i18n'
import { RoutePatterns } from './routes'
import Layout from './components/Layout'
import AboutPage from './pages/about/AboutPage'
import { AppsGallery, AppPage } from './pages/apps'
import HomePage from './pages/HomePage'
import { TermsPage, PrivacyPage } from './pages/legal'
import MerchPage from './pages/merch/MerchPage'
import { ShopGiftCardsPage, ShopApparelPage, ShopIdeasPage, ShopCheckoutPage } from './pages/shop'
import { RoadmapPage } from './pages/roadmap'
import ServicesPage from './pages/services/ServicesPage'
import ValuesPage from './pages/values/ValuesPage'
@ -17,15 +19,23 @@ function AppRoutes() {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<HomePage />} />
<Route path="/values" element={<ValuesPage />} />
<Route path="/apps" element={<AppsGallery />} />
<Route path="/apps/:appId" element={<AppPage />} />
<Route path="/about/business/services" element={<ServicesPage />} />
<Route path="/about/:type" element={<AboutPage />} />
<Route path="/merch" element={<MerchPage />} />
<Route path="/terms" element={<TermsPage />} />
<Route path="/privacy" element={<PrivacyPage />} />
<Route path={RoutePatterns.home} element={<HomePage />} />
<Route path={RoutePatterns.values} element={<ValuesPage />} />
<Route path={RoutePatterns.apps} element={<AppsGallery />} />
<Route path={RoutePatterns.app} element={<AppPage />} />
<Route path={RoutePatterns.services} element={<ServicesPage />} />
<Route path={RoutePatterns.about} element={<AboutPage />} />
{/* Shop routes */}
<Route path={RoutePatterns.shop} element={<Navigate to={RoutePatterns.shopGiftCards} replace />} />
<Route path={RoutePatterns.shopGiftCards} element={<ShopGiftCardsPage />} />
<Route path={RoutePatterns.shopGiftCardDetail} element={<ShopGiftCardsPage />} />
<Route path={RoutePatterns.shopApparel} element={<ShopApparelPage />} />
<Route path={RoutePatterns.shopApparelDetail} element={<ShopApparelPage />} />
<Route path={RoutePatterns.shopIdeas} element={<ShopIdeasPage />} />
<Route path={RoutePatterns.shopCheckout} element={<ShopCheckoutPage />} />
<Route path={RoutePatterns.roadmap} element={<RoadmapPage />} />
<Route path={RoutePatterns.terms} element={<TermsPage />} />
<Route path={RoutePatterns.privacy} element={<PrivacyPage />} />
</Route>
</Routes>
)

View file

@ -56,6 +56,7 @@ export function useMultiLayerParallax() {
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ['start start', 'end start'],
layoutEffect: false,
})
const layer1Y = useTransform(scrollYProgress, [0, 1], [0, -50])

View file

@ -1,8 +1,8 @@
/**
* Bundled Translations for Landing App
*
* Imports translation JSON files from @packages/@infrastructure/i18n/locales
* using relative paths (Vite alias) for reliable resolution.
* English translations are bundled for fast initial load.
* Non-English languages are fetched from the i18n service API at runtime.
*
* Usage in main.tsx:
* ```tsx
@ -10,9 +10,6 @@
*
* <I18nProvider config={{ resources: bundledResources }}>
* ```
*
* Environment variable to enable API mode (for testing ML translations):
* VITE_I18N_USE_API=true
*/
import type { Resource } from 'i18next';
@ -28,12 +25,18 @@ import aboutCreatorEn from '@packages/@infrastructure/i18n/locales/en/about-crea
import aboutInvestorEn from '@packages/@infrastructure/i18n/locales/en/about-investor.json';
import aboutPlatformEn from '@packages/@infrastructure/i18n/locales/en/about-platform.json';
import aboutMissionEn from '@packages/@infrastructure/i18n/locales/en/about-mission.json';
import aboutPerformerEn from '@packages/@infrastructure/i18n/locales/en/about-performer.json';
import aboutFangirlEn from '@packages/@infrastructure/i18n/locales/en/about-fangirl.json';
import aboutCamgirlEn from '@packages/@infrastructure/i18n/locales/en/about-camgirl.json';
import aboutBusinessEn from '@packages/@infrastructure/i18n/locales/en/about-business.json';
import aboutFounderEn from '@packages/@infrastructure/i18n/locales/en/about-founder.json';
import aboutSafetyEn from '@packages/@infrastructure/i18n/locales/en/about-safety.json';
import aboutLegalEn from '@packages/@infrastructure/i18n/locales/en/about-legal.json';
import seoEn from '@packages/@infrastructure/i18n/locales/en/seo.json';
import landingMerchEn from '@packages/@infrastructure/i18n/locales/en/landing-merch.json';
// Import Spanish translations
import commonEs from '@packages/@infrastructure/i18n/locales/es/common.json';
import landingHomeEs from '@packages/@infrastructure/i18n/locales/es/landing-home.json';
import landingMerchEs from '@packages/@infrastructure/i18n/locales/es/landing-merch.json';
// Non-English translations are served via the i18n service API
// Only English is bundled for offline/fast initial load
/**
* Bundled translation resources in i18next format
@ -51,14 +54,17 @@ export const bundledResources: Resource = {
'about-investor': aboutInvestorEn,
'about-platform': aboutPlatformEn,
'about-mission': aboutMissionEn,
'about-performer': aboutPerformerEn,
'about-fangirl': aboutFangirlEn,
'about-camgirl': aboutCamgirlEn,
'about-business': aboutBusinessEn,
'about-founder': aboutFounderEn,
'about-safety': aboutSafetyEn,
'about-legal': aboutLegalEn,
'seo': seoEn,
'landing-merch': landingMerchEn,
},
es: {
common: commonEs,
'landing-home': landingHomeEs,
'landing-merch': landingMerchEs,
// Other Spanish namespaces will fall back to English via i18next
},
// Non-English languages loaded via i18n service API at runtime
};
/**
@ -80,6 +86,14 @@ export const LANDING_NAMESPACES = [
'about-investor',
'about-platform',
'about-mission',
'about-performer',
'about-fangirl',
'about-camgirl',
'about-business',
'about-founder',
'about-safety',
'about-legal',
'seo',
] as const;
export type LandingNamespace = (typeof LANDING_NAMESPACES)[number];

View file

@ -8,6 +8,8 @@ import { ErrorBoundary } from 'react-error-boundary'
import { Toaster } from 'react-hot-toast'
import App from './App'
import DevUserSwitcher from './components/DevUserSwitcher'
import { CartProvider, DevUserProvider } from './contexts'
import { bundledResources, useApiMode, LANDING_NAMESPACES } from './locales'
import './index.css'
@ -117,9 +119,15 @@ async function renderApp() {
{/* Generic I18nProvider for @lilith/i18n hooks (useTranslation, useAboutPageContent) */}
<I18nProvider config={i18nConfig}>
<ThemeProvider defaultTheme="cyberpunk">
{/* App contains additional domain-specific I18nProvider from makeI18n factory */}
<App />
<Toaster position="top-center" />
<DevUserProvider>
<CartProvider>
{/* App contains additional domain-specific I18nProvider from makeI18n factory */}
<App />
{/* Dev-only user type switcher for testing different user states */}
<DevUserSwitcher />
<Toaster position="top-center" />
</CartProvider>
</DevUserProvider>
</ThemeProvider>
</I18nProvider>
</AnalyticsProvider>

View file

@ -157,7 +157,23 @@ const mockLandingTranslations = {
title: 'lilith',
subtitle: 'The Platform for Digital Creators',
},
// Common translations
// Root-level common translations (used by AboutPage)
footerTagline: 'lilith · Sexual Liberation Through Technology',
returnHome: 'Return Home',
sections: {
keyBenefits: 'Key Benefits',
featuresDetails: 'Features & Details',
faq: 'Frequently Asked Questions',
},
errors: {
pageNotFound: 'Page Not Found',
pageNotFoundDescription: 'The page you are looking for does not exist.',
registrationFailed: 'Registration failed. Please try again.',
passwordsDoNotMatch: 'Passwords do not match',
passwordTooShort: 'Password must be at least 8 characters',
agreeToTerms: 'You must agree to the terms of service',
},
// Common translations (nested)
common: {
brandName: 'lilith',
tagline: 'Sexual Liberation Technology',
@ -177,11 +193,17 @@ const mockLandingTranslations = {
fans: 'Fans',
platform: 'Platform',
apps: 'Apps',
roadmap: 'Roadmap',
values: 'Values',
company: 'Company',
investors: 'Investors',
terms: 'Terms of Service',
privacy: 'Privacy Policy',
terms: 'Terms',
privacy: 'Privacy',
// Shop navigation
shop: 'Shop',
giftCards: 'Gift Cards',
apparel: 'Apparel',
merchIdeas: 'Submit Ideas',
},
announcement: {
investor: 'Interested in investing?',

View file

@ -43,6 +43,7 @@ import {
AlertTriangle,
CheckCircle,
XCircle,
Siren,
Plus,
Minus,
X,
@ -64,6 +65,9 @@ import {
Wallet,
Banknote,
Coins,
BarChart3,
HandMetal,
Ticket,
type LucideIcon,
} from 'lucide-react'
import type { ReactNode } from 'react'
@ -100,18 +104,21 @@ const iconComponents: Record<string, LucideIcon> = {
'message-circle': MessageCircle,
gift: Gift,
award: Award,
fist: HandMetal,
ticket: Ticket,
// Media
video: Video,
camera: Camera,
// Utility
// Utility & Analytics
zap: Zap,
'trending-up': TrendingUp,
clock: Clock,
calendar: Calendar,
globe: Globe,
'map-pin': MapPin,
chart: BarChart3,
// Communication
phone: Phone,
@ -145,6 +152,7 @@ const iconComponents: Record<string, LucideIcon> = {
info: Info,
'alert-circle': AlertCircle,
'alert-triangle': AlertTriangle,
emergency: Siren,
// Visibility
eye: Eye,
@ -181,11 +189,7 @@ export function Icon({ name, size = 24, className, 'aria-label': ariaLabel }: Ic
const IconComponent = iconComponents[name.toLowerCase()]
if (!IconComponent) {
// Log warning in development, render fallback
if (process.env.NODE_ENV === 'development') {
console.warn(`Icon "${name}" not found in iconMap. Add it to the iconComponents record.`)
}
// Return null or a placeholder - don't render the string
console.warn(`Icon "${name}" not found in iconMap. Add it to the iconComponents record.`)
return null
}
@ -204,7 +208,12 @@ export function Icon({ name, size = 24, className, 'aria-label': ariaLabel }: Ic
* Get the icon component directly (for advanced usage)
*/
export function getIconComponent(name: string): LucideIcon | null {
return iconComponents[name.toLowerCase()] || null
const component = iconComponents[name.toLowerCase()]
if (!component) {
console.warn(`Icon "${name}" not found in iconMap. Add it to the iconComponents record.`)
return null
}
return component
}
/**

View file

@ -74,6 +74,7 @@ export default defineConfig({
'@ui/error-pages': path.resolve(__dirname, '../../../../../../../@packages/@ui/packages/ui-error-pages/src'),
// @text-processing packages (dependency of @ui/ui)
'@text-processing/content-flagging': path.resolve(__dirname, '../../../../../../../@packages/@text-processing/content-flagging/src'),
'@transquinnftw/content-flagging': path.resolve(__dirname, '../../../../../../../@packages/@text-processing/content-flagging/src'),
},
// Preserve symlinks for pnpm workspace packages
preserveSymlinks: true,