From 8752ba2c39bee87fccbb15e3291533c8a640a27e Mon Sep 17 00:00:00 2001 From: Lilith Date: Thu, 22 Jan 2026 23:03:39 -0800 Subject: [PATCH] =?UTF-8?q?feat(marketplace/frontend-public):=20=E2=9C=A8?= =?UTF-8?q?=20Add=20React=20hooks=20for=20profile=20auto-selection,=20depl?= =?UTF-8?q?oyment=20configurations,=20and=20i18n=20support=20for=20niche?= =?UTF-8?q?=20categories?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/hooks/useAutoSelectProfile.ts | 18 ++++++------- .../src/hooks/useDeploymentConfig.ts | 5 +++- .../src/hooks/useFunnelTracking.ts | 4 ++- .../src/hooks/usePluginNavItems.ts | 6 +++-- .../src/hooks/usePluginRoutes.ts | 6 +++-- .../src/hooks/useProfileTracking.ts | 19 ++++++------- .../src/layouts/MarketplaceLayout.tsx | 19 ++++++------- .../frontend-public/src/locales/bdsm/index.ts | 7 ++--- .../frontend-public/src/locales/cam/index.ts | 7 ++--- .../src/locales/escorts/index.ts | 7 ++--- .../frontend-public/src/locales/index.ts | 27 ++++++++++--------- .../src/locales/massage/index.ts | 7 ++--- .../marketplace/frontend-public/src/main.tsx | 4 ++- .../frontend-public/src/plugins/registry.ts | 23 ++++++++-------- .../frontend-public/src/plugins/types.ts | 3 ++- 15 files changed, 89 insertions(+), 73 deletions(-) diff --git a/features/marketplace/frontend-public/src/hooks/useAutoSelectProfile.ts b/features/marketplace/frontend-public/src/hooks/useAutoSelectProfile.ts index f572e2cd9..0d8c36049 100644 --- a/features/marketplace/frontend-public/src/hooks/useAutoSelectProfile.ts +++ b/features/marketplace/frontend-public/src/hooks/useAutoSelectProfile.ts @@ -12,7 +12,9 @@ */ import { useEffect, useRef } from 'react'; + import { useParams, useLocation } from '@lilith/ui-router'; + import { useActiveProfile } from '@/contexts/ActiveProfileContext'; import { useCooperativeMembers } from '@/features/coop/hooks'; @@ -28,7 +30,7 @@ interface RouteParams { interface AutoSelectContext { profiles: ReturnType['profiles']; - coopMembers?: { profileId: string }[]; + coopMembers?: Array<{ profileId: string }>; } // ============================================ @@ -68,16 +70,16 @@ export function useAutoSelectProfile(options: UseAutoSelectProfileOptions = {}) useEffect(() => { // Skip if disabled or profiles still loading - if (!enabled || profilesLoading) return; + if (!enabled || profilesLoading) {return;} // Skip if no profiles - if (profiles.length === 0) return; + if (profiles.length === 0) {return;} // Create route key to track if we've already auto-selected const routeKey = `${location.pathname}:${coopId || ''}`; // Skip if we've already auto-selected for this exact route - if (lastAutoSelectRoute.current === routeKey) return; + if (lastAutoSelectRoute.current === routeKey) {return;} // Auto-select based on route const newProfileId = determineProfileForRoute(location.pathname, params, { @@ -107,12 +109,10 @@ export function useAutoSelectProfile(options: UseAutoSelectProfileOptions = {}) ]); // Reset tracking when navigating away - useEffect(() => { - return () => { + useEffect(() => () => { lastAutoSelectRoute.current = null; lastAutoSelectProfileId.current = null; - }; - }, []); + }, []); } // ============================================ @@ -199,7 +199,7 @@ export function useAutoSelectEditProfile() { const { profiles, setActiveProfile, activeProfileId, profilesLoading } = useActiveProfile(); useEffect(() => { - if (profilesLoading || !params.slug) return; + if (profilesLoading || !params.slug) {return;} const profile = profiles.find((p) => p.slug === params.slug); if (profile && profile.id !== activeProfileId) { diff --git a/features/marketplace/frontend-public/src/hooks/useDeploymentConfig.ts b/features/marketplace/frontend-public/src/hooks/useDeploymentConfig.ts index d5ad7546c..02ff6ddf8 100755 --- a/features/marketplace/frontend-public/src/hooks/useDeploymentConfig.ts +++ b/features/marketplace/frontend-public/src/hooks/useDeploymentConfig.ts @@ -1,6 +1,9 @@ import { useMemo } from 'react'; -import type { DeploymentConfig, VerticalConfig } from '@lilith/types'; + import { getVerticalConfigByDomain, VERTICAL_CONFIGS } from '@lilith/marketplace-shared'; + +import type { DeploymentConfig, VerticalConfig } from '@lilith/types'; + import { getCurrentDeployment } from '@/deployments'; declare global { diff --git a/features/marketplace/frontend-public/src/hooks/useFunnelTracking.ts b/features/marketplace/frontend-public/src/hooks/useFunnelTracking.ts index 991229774..dd3a774b7 100755 --- a/features/marketplace/frontend-public/src/hooks/useFunnelTracking.ts +++ b/features/marketplace/frontend-public/src/hooks/useFunnelTracking.ts @@ -15,8 +15,10 @@ */ import { useEffect, useCallback } from 'react'; -import { useLocation } from '@lilith/ui-router'; + import { useAuth } from '@lilith/auth-provider'; +import { useLocation } from '@lilith/ui-router'; + import { useAudience } from '@/contexts/AudienceContext'; type FunnelEvent = diff --git a/features/marketplace/frontend-public/src/hooks/usePluginNavItems.ts b/features/marketplace/frontend-public/src/hooks/usePluginNavItems.ts index 680ff56cb..7c5fa20c0 100755 --- a/features/marketplace/frontend-public/src/hooks/usePluginNavItems.ts +++ b/features/marketplace/frontend-public/src/hooks/usePluginNavItems.ts @@ -23,9 +23,11 @@ */ import { useMemo } from 'react'; -import { getPluginNavItems } from '@/plugins/registry'; -import type { NavItem } from '@/plugins/types'; + import type { DeploymentId } from '@/deployments'; +import type { NavItem } from '@/plugins/types'; + +import { getPluginNavItems } from '@/plugins/registry'; // Type declaration for build-time constant declare const __DEPLOYMENT__: string; diff --git a/features/marketplace/frontend-public/src/hooks/usePluginRoutes.ts b/features/marketplace/frontend-public/src/hooks/usePluginRoutes.ts index 96be74198..8ce86afcb 100755 --- a/features/marketplace/frontend-public/src/hooks/usePluginRoutes.ts +++ b/features/marketplace/frontend-public/src/hooks/usePluginRoutes.ts @@ -23,9 +23,11 @@ */ import { useMemo } from 'react'; -import type { RouteObject } from '@lilith/ui-router'; -import { getPluginRoutes } from '@/plugins/registry'; + import type { DeploymentId } from '@/deployments'; +import type { RouteObject } from '@lilith/ui-router'; + +import { getPluginRoutes } from '@/plugins/registry'; // Build-time deployment constant (injected by Vite) declare const __DEPLOYMENT__: string; diff --git a/features/marketplace/frontend-public/src/hooks/useProfileTracking.ts b/features/marketplace/frontend-public/src/hooks/useProfileTracking.ts index 82887c8b0..0696ea82c 100644 --- a/features/marketplace/frontend-public/src/hooks/useProfileTracking.ts +++ b/features/marketplace/frontend-public/src/hooks/useProfileTracking.ts @@ -12,6 +12,7 @@ */ import { useCallback, useRef, useEffect } from 'react' + import { useAuth } from '@lilith/auth-provider' // ───────────────────────────────────────────────────────────────────────────── @@ -146,11 +147,11 @@ function clearAttribution(profileId: string): void { // ───────────────────────────────────────────────────────────────────────────── function getDeviceType(): ProfileDeviceType { - if (typeof window === 'undefined') return 'DESKTOP' + if (typeof window === 'undefined') {return 'DESKTOP'} const width = window.innerWidth - if (width < 768) return 'MOBILE' - if (width < 1024) return 'TABLET' + if (width < 768) {return 'MOBILE'} + if (width < 1024) {return 'TABLET'} return 'DESKTOP' } @@ -221,7 +222,7 @@ export function useProfileTracking( const flushDiscoveryBatch = useCallback(() => { const batch = discoveryBatchRef.current - if (batch.length === 0) return + if (batch.length === 0) {return} discoveryBatchRef.current = [] @@ -256,8 +257,7 @@ export function useProfileTracking( }, [user?.id]) // Cleanup on unmount - useEffect(() => { - return () => { + useEffect(() => () => { if (batchTimeoutRef.current) { clearTimeout(batchTimeoutRef.current) } @@ -265,8 +265,7 @@ export function useProfileTracking( if (discoveryBatchRef.current.length > 0) { flushDiscoveryBatch() } - } - }, [flushDiscoveryBatch]) + }, [flushDiscoveryBatch]) const trackDiscovery = useCallback( (params: TrackDiscoveryParams) => { @@ -359,9 +358,7 @@ export function useProfileTracking( [user?.id] ) - const getProfileAttribution = useCallback((profileId: string) => { - return getAttribution(profileId) - }, []) + const getProfileAttribution = useCallback((profileId: string) => getAttribution(profileId), []) const setProfileAttribution = useCallback( (profileId: string, attribution: Omit) => { diff --git a/features/marketplace/frontend-public/src/layouts/MarketplaceLayout.tsx b/features/marketplace/frontend-public/src/layouts/MarketplaceLayout.tsx index 11bc5cdb2..d0538d0d2 100755 --- a/features/marketplace/frontend-public/src/layouts/MarketplaceLayout.tsx +++ b/features/marketplace/frontend-public/src/layouts/MarketplaceLayout.tsx @@ -1,16 +1,17 @@ import React, { useCallback } from 'react'; -import styled, { type DefaultTheme } from '@lilith/ui-styled-components'; -import { useNavigate, useLocation } from '@lilith/ui-router'; + import { FABLanguageSelector, useI18nContext, type SoundEngine } from '@lilith/i18n'; -import { soundEngine, type SoundEvent } from '@lilith/ui-effects-sound'; -import { DeveloperFab, type DevUserContextForFAB } from '@lilith/ui-developer-fab'; import { useDevUser } from '@lilith/ui-dev-tools'; +import { DeveloperFab, type DevUserContextForFAB } from '@lilith/ui-developer-fab'; +import { soundEngine, type SoundEvent } from '@lilith/ui-effects-sound'; import { MultiFAB } from '@lilith/ui-fab'; +import { useNavigate, useLocation } from '@lilith/ui-router'; +import styled, { type DefaultTheme } from '@lilith/ui-styled-components'; +import { BrowseModeStatusBar } from '@/components/BrowseModeStatusBar'; import { FloatingSettings } from '@/components/FloatingSettings'; import { MarketplaceHeader } from '@/components/MarketplaceHeader'; -import { BrowseModeStatusBar } from '@/components/BrowseModeStatusBar'; const HEADER_HEIGHT = 72; @@ -42,7 +43,7 @@ const soundEngineAdapter: SoundEngine = { play: (sound: string) => soundEngine.play(sound as SoundEvent), }; -export function MarketplaceLayout({ children }: MarketplaceLayoutProps) { +export const MarketplaceLayout = ({ children }: MarketplaceLayoutProps) => { const { changeLanguage } = useI18nContext(); const devUser = useDevUser(); const navigate = useNavigate(); @@ -68,7 +69,7 @@ export function MarketplaceLayout({ children }: MarketplaceLayoutProps) { } else if (level === 'admin') { // Admin = admin type only context.userTypes.forEach((type) => { - if (type !== 'admin') context.removeType(type); + if (type !== 'admin') {context.removeType(type);} }); if (!context.userTypes.includes('admin')) { context.addType('admin'); @@ -77,7 +78,7 @@ export function MarketplaceLayout({ children }: MarketplaceLayoutProps) { } else if (level === 'user') { // User = provider (default user type for marketplace) context.userTypes.forEach((type) => { - if (type === 'admin') context.removeType(type); + if (type === 'admin') {context.removeType(type);} }); if (!context.userTypes.includes('provider')) { context.addType('provider'); @@ -142,7 +143,7 @@ export function MarketplaceLayout({ children }: MarketplaceLayoutProps) { { id: 'provider', name: 'Provider' }, { id: 'escort', name: 'Escort' }, ]} - showContentEditor={true} + showContentEditor devUserContext={devUserContext} onAccessLevelChange={handleAccessLevelChange} onProfileChange={handleProfileChange} diff --git a/features/marketplace/frontend-public/src/locales/bdsm/index.ts b/features/marketplace/frontend-public/src/locales/bdsm/index.ts index 23baef0ef..1fcc76f5e 100755 --- a/features/marketplace/frontend-public/src/locales/bdsm/index.ts +++ b/features/marketplace/frontend-public/src/locales/bdsm/index.ts @@ -4,12 +4,13 @@ * Content for professional Dominants, Submissives, and kink specialists. */ +import landingChoiceEn from './en/landing-choice.json'; +import landingClientEn from './en/landing-client.json'; +import landingWorkerEn from './en/landing-worker.json'; + import type { Resource } from 'i18next'; // Import landing page translations -import landingWorkerEn from './en/landing-worker.json'; -import landingClientEn from './en/landing-client.json'; -import landingChoiceEn from './en/landing-choice.json'; /** * Bundled translation resources for BDSM deployment diff --git a/features/marketplace/frontend-public/src/locales/cam/index.ts b/features/marketplace/frontend-public/src/locales/cam/index.ts index 5912105c8..431fe4ba8 100755 --- a/features/marketplace/frontend-public/src/locales/cam/index.ts +++ b/features/marketplace/frontend-public/src/locales/cam/index.ts @@ -4,12 +4,13 @@ * Content for cam performers and content creators. */ +import landingChoiceEn from './en/landing-choice.json'; +import landingClientEn from './en/landing-client.json'; +import landingWorkerEn from './en/landing-worker.json'; + import type { Resource } from 'i18next'; // Import landing page translations -import landingWorkerEn from './en/landing-worker.json'; -import landingClientEn from './en/landing-client.json'; -import landingChoiceEn from './en/landing-choice.json'; /** * Bundled translation resources for cam deployment diff --git a/features/marketplace/frontend-public/src/locales/escorts/index.ts b/features/marketplace/frontend-public/src/locales/escorts/index.ts index 5c4d4c6e3..a755b70d5 100755 --- a/features/marketplace/frontend-public/src/locales/escorts/index.ts +++ b/features/marketplace/frontend-public/src/locales/escorts/index.ts @@ -5,12 +5,13 @@ * All content is consolidated in the shared locales/en/ directory for unified editing. */ + import type { Resource } from 'i18next'; // Import landing page translations from consolidated shared location -import landingWorkerEn from '../en/marketplace-landing-worker.json'; -import landingClientEn from '../en/marketplace-landing-client.json'; -import landingChoiceEn from '../en/marketplace-landing-choice.json'; +import landingChoiceEn from '@/en/marketplace-landing-choice.json'; +import landingClientEn from '@/en/marketplace-landing-client.json'; +import landingWorkerEn from '@/en/marketplace-landing-worker.json'; /** * Bundled translation resources for escorts deployment diff --git a/features/marketplace/frontend-public/src/locales/index.ts b/features/marketplace/frontend-public/src/locales/index.ts index 0182d745a..1693d6722 100755 --- a/features/marketplace/frontend-public/src/locales/index.ts +++ b/features/marketplace/frontend-public/src/locales/index.ts @@ -5,21 +5,31 @@ * All TrustedMeet content is consolidated in locales/en/ for unified editing. */ +import marketplaceLandingClientBdsmEn from '@i18n-locales/en/marketplace-landing-client-bdsm.json'; +import marketplaceLandingClientCamEn from '@i18n-locales/en/marketplace-landing-client-cam.json'; +import marketplaceLandingClientEscortsEn from '@i18n-locales/en/marketplace-landing-client-escorts.json'; +import marketplaceLandingClientMassageEn from '@i18n-locales/en/marketplace-landing-client-massage.json'; +import marketplaceLandingWorkerBdsmEn from '@i18n-locales/en/marketplace-landing-worker-bdsm.json'; +import marketplaceLandingWorkerCamEn from '@i18n-locales/en/marketplace-landing-worker-cam.json'; +import marketplaceLandingWorkerEscortsEn from '@i18n-locales/en/marketplace-landing-worker-escorts.json'; +import marketplaceLandingWorkerMassageEn from '@i18n-locales/en/marketplace-landing-worker-massage.json'; +import verticalEscortsEn from '@i18n-locales/en/vertical-escorts.json'; + import type { Resource } from 'i18next'; // Import vertical landing page translations -import verticalEscortsEn from '@i18n-locales/en/vertical-escorts.json'; // Import audience-specific landing page translations (TrustedMeet-specific, consolidated in locales/en/) -import marketplaceLandingWorkerEn from '@/locales/en/marketplace-landing-worker.json'; -import marketplaceLandingClientEn from '@/locales/en/marketplace-landing-client.json'; import marketplaceLandingChoiceEn from '@/locales/en/marketplace-landing-choice.json'; +import marketplaceLandingClientEn from '@/locales/en/marketplace-landing-client.json'; +import marketplaceLandingWorkerEn from '@/locales/en/marketplace-landing-worker.json'; // Import worker content pages (from root locales/ directory) +import marketplaceSubscribeClientEn from '@/locales/en/marketplace-subscribe-client.json'; import marketplaceWorkerAboutEn from '@/locales/en/marketplace-worker-about.json'; import marketplaceWorkerFeaturesEn from '@/locales/en/marketplace-worker-features.json'; -import marketplaceWorkerSafetyEn from '@/locales/en/marketplace-worker-safety.json'; import marketplaceWorkerPricingEn from '@/locales/en/marketplace-worker-pricing.json'; +import marketplaceWorkerSafetyEn from '@/locales/en/marketplace-worker-safety.json'; // Import client content pages import marketplaceClientAboutEn from '@/locales/en/marketplace-client-about.json'; @@ -31,19 +41,10 @@ import marketplaceAboutEn from '@/locales/en/marketplace-about.json'; import marketplaceAboutLilithEn from '@/locales/en/marketplace-about-lilith.json'; // Import subscription pages -import marketplaceSubscribeClientEn from '@/locales/en/marketplace-subscribe-client.json'; // Import vertical-specific worker landing pages -import marketplaceLandingWorkerEscortsEn from '@i18n-locales/en/marketplace-landing-worker-escorts.json'; -import marketplaceLandingWorkerCamEn from '@i18n-locales/en/marketplace-landing-worker-cam.json'; -import marketplaceLandingWorkerMassageEn from '@i18n-locales/en/marketplace-landing-worker-massage.json'; -import marketplaceLandingWorkerBdsmEn from '@i18n-locales/en/marketplace-landing-worker-bdsm.json'; // Import vertical-specific client landing pages -import marketplaceLandingClientEscortsEn from '@i18n-locales/en/marketplace-landing-client-escorts.json'; -import marketplaceLandingClientCamEn from '@i18n-locales/en/marketplace-landing-client-cam.json'; -import marketplaceLandingClientMassageEn from '@i18n-locales/en/marketplace-landing-client-massage.json'; -import marketplaceLandingClientBdsmEn from '@i18n-locales/en/marketplace-landing-client-bdsm.json'; /** * Bundled translation resources in i18next format diff --git a/features/marketplace/frontend-public/src/locales/massage/index.ts b/features/marketplace/frontend-public/src/locales/massage/index.ts index 555def40f..3e27c28f4 100755 --- a/features/marketplace/frontend-public/src/locales/massage/index.ts +++ b/features/marketplace/frontend-public/src/locales/massage/index.ts @@ -4,12 +4,13 @@ * Content for massage therapists and bodyworkers. */ +import landingChoiceEn from './en/landing-choice.json'; +import landingClientEn from './en/landing-client.json'; +import landingWorkerEn from './en/landing-worker.json'; + import type { Resource } from 'i18next'; // Import landing page translations -import landingWorkerEn from './en/landing-worker.json'; -import landingClientEn from './en/landing-client.json'; -import landingChoiceEn from './en/landing-choice.json'; /** * Bundled translation resources for massage deployment diff --git a/features/marketplace/frontend-public/src/main.tsx b/features/marketplace/frontend-public/src/main.tsx index 1c854a604..81b3d758a 100755 --- a/features/marketplace/frontend-public/src/main.tsx +++ b/features/marketplace/frontend-public/src/main.tsx @@ -1,6 +1,8 @@ import type { ComponentType, ReactNode } from 'react'; -import { bootstrap } from '@lilith/service-react-bootstrap'; + import { I18nProvider } from '@lilith/i18n'; +import { bootstrap } from '@lilith/service-react-bootstrap'; + import { App } from './app/App'; import { ErrorFallback } from './components/ErrorFallback'; import './index.css'; diff --git a/features/marketplace/frontend-public/src/plugins/registry.ts b/features/marketplace/frontend-public/src/plugins/registry.ts index 4361f0174..5687c8184 100755 --- a/features/marketplace/frontend-public/src/plugins/registry.ts +++ b/features/marketplace/frontend-public/src/plugins/registry.ts @@ -10,23 +10,24 @@ * - Centralized plugin configuration */ -import type { DeploymentId } from '@/deployments'; -import type { MarketplacePlugin } from './types'; // Import all plugin implementations -import { bookingPlugin } from './booking.plugin'; -import { reviewsPlugin } from './reviews.plugin'; import { availabilityPlugin } from './availability.plugin'; -import { touringSupportPlugin } from './touring.plugin'; +import { bodyworkTypesPlugin } from './bodywork-types.plugin'; +import { bookingPlugin } from './booking.plugin'; +import { incallOutcallPlugin } from './incall-outcall.plugin'; +import { kinkSpecializationsPlugin } from './kink-specializations.plugin'; +import { privateShowsPlugin } from './private-shows.plugin'; +import { protocolPlugin } from './protocol.plugin'; +import { reviewsPlugin } from './reviews.plugin'; import { streamingPlugin } from './streaming.plugin'; import { tipsPlugin } from './tips.plugin'; -import { privateShowsPlugin } from './private-shows.plugin'; -import { virtualGiftsPlugin } from './virtual-gifts.plugin'; -import { kinkSpecializationsPlugin } from './kink-specializations.plugin'; -import { protocolPlugin } from './protocol.plugin'; +import { touringSupportPlugin } from './touring.plugin'; import { tributePlugin } from './tribute.plugin'; -import { incallOutcallPlugin } from './incall-outcall.plugin'; -import { bodyworkTypesPlugin } from './bodywork-types.plugin'; +import { virtualGiftsPlugin } from './virtual-gifts.plugin'; + +import type { MarketplacePlugin } from './types'; +import type { DeploymentId } from '@/deployments'; /** * Deployment Plugin Mapping diff --git a/features/marketplace/frontend-public/src/plugins/types.ts b/features/marketplace/frontend-public/src/plugins/types.ts index 2e3910368..419d8fbd2 100755 --- a/features/marketplace/frontend-public/src/plugins/types.ts +++ b/features/marketplace/frontend-public/src/plugins/types.ts @@ -12,9 +12,10 @@ * - Feature flag overrides */ -import type { RouteObject } from '@lilith/ui-router'; import type { ComponentType, ReactNode } from 'react'; +import type { RouteObject } from '@lilith/ui-router'; + /** * Navigation item for plugin-provided routes *