diff --git a/features/analytics/frontend-users/package.json b/features/analytics/frontend-users/package.json
index 47719880e..11cb0aa9b 100644
--- a/features/analytics/frontend-users/package.json
+++ b/features/analytics/frontend-users/package.json
@@ -5,12 +5,13 @@
"type": "module",
"scripts": {
"dev": "vite",
- "build": "echo 'Skipped: missing @lilith/ui-* dependencies'",
+ "build": "vite build",
"preview": "vite preview",
- "typecheck": "echo 'Skipped: missing @lilith/ui-* dependencies'",
+ "typecheck": "echo 'Skipped: @lilith/ui-* packages need republishing with React 19 types'",
"lint": "eslint src --ext ts,tsx"
},
"dependencies": {
+ "@lilith/api-client": "workspace:*",
"@lilith/auth-provider": "workspace:*",
"@lilith/payments": "workspace:*",
"@lilith/analytics": "workspace:*",
diff --git a/features/analytics/frontend-users/src/components/ErrorBoundary.tsx b/features/analytics/frontend-users/src/components/ErrorBoundary.tsx
index 793c22a1a..f9800630c 100644
--- a/features/analytics/frontend-users/src/components/ErrorBoundary.tsx
+++ b/features/analytics/frontend-users/src/components/ErrorBoundary.tsx
@@ -56,7 +56,7 @@ export class ErrorBoundary extends Component
),
href: Routes.providersEscort,
- onClick: () => handleNavClick(Routes.providersEscort, 'escort'),
+ onClick: () => handleNavClick(Routes.providersEscort, 'provider'),
},
{
label: (
@@ -116,7 +116,7 @@ export default function Header({ pageType }: HeaderProps) {
{
label: t('navigation.clients'),
href: Routes.clientsBooking,
- onClick: () => handleNavClick(Routes.clientsBooking, 'booking'),
+ onClick: () => handleNavClick(Routes.clientsBooking, 'client'),
},
{
label: t('navigation.fans'),
diff --git a/features/landing/frontend-public/src/pages/categories/ForCustomersPage.tsx b/features/landing/frontend-public/src/pages/categories/ForCustomersPage.tsx
index 3afae39cb..249bd5090 100644
--- a/features/landing/frontend-public/src/pages/categories/ForCustomersPage.tsx
+++ b/features/landing/frontend-public/src/pages/categories/ForCustomersPage.tsx
@@ -8,7 +8,7 @@ import { Routes } from '../../routes';
import './CategoryLanding.css';
interface CustomerCategory {
- type: 'client' | 'fan';
+ type: 'booking' | 'fan';
title: string;
description: string;
icon: React.ReactNode;
@@ -58,7 +58,7 @@ export default function ForCustomersPage() {
const customerCategories: CustomerCategory[] = [
{
- type: 'client',
+ type: 'booking',
title: t('forCustomers.cards.client.title'),
description: t('forCustomers.cards.client.description'),
icon: ,
diff --git a/features/landing/frontend-public/src/pages/categories/ForWorkersPage.tsx b/features/landing/frontend-public/src/pages/categories/ForWorkersPage.tsx
index b4eabba6f..8c07e70ca 100644
--- a/features/landing/frontend-public/src/pages/categories/ForWorkersPage.tsx
+++ b/features/landing/frontend-public/src/pages/categories/ForWorkersPage.tsx
@@ -8,7 +8,7 @@ import { Routes } from '../../routes';
import './CategoryLanding.css';
interface WorkerCategory {
- type: 'provider' | 'performer' | 'fangirl' | 'camgirl';
+ type: 'escort' | 'performer' | 'fangirl' | 'camgirl';
title: string;
description: string;
icon: React.ReactNode;
@@ -62,7 +62,7 @@ export default function ForWorkersPage() {
const workerCategories: WorkerCategory[] = [
{
- type: 'provider',
+ type: 'escort',
title: t('forWorkers.cards.provider.title'),
description: t('forWorkers.cards.provider.description'),
icon: ,
diff --git a/features/landing/frontend-public/src/pages/types/index.ts b/features/landing/frontend-public/src/pages/types/index.ts
index 47c0b83fa..128b28550 100644
--- a/features/landing/frontend-public/src/pages/types/index.ts
+++ b/features/landing/frontend-public/src/pages/types/index.ts
@@ -6,7 +6,7 @@
import type { AboutPageType } from '@lilith/i18n'
/** Category landing page types */
-export type CategoryPageType = 'work' | 'customer' | 'platform' | 'company' | 'shop'
+export type CategoryPageType = 'providers' | 'clients' | 'platform' | 'company' | 'shop'
/** All routable page types in the landing app */
export type PageType =
diff --git a/features/marketplace/frontend-public/src/contexts/AudienceContext.tsx b/features/marketplace/frontend-public/src/contexts/AudienceContext.tsx
new file mode 100644
index 000000000..6f10ce013
--- /dev/null
+++ b/features/marketplace/frontend-public/src/contexts/AudienceContext.tsx
@@ -0,0 +1,129 @@
+/**
+ * AudienceContext - Track current audience type based on route
+ *
+ * Provides:
+ * - Current audience type (worker | client | null)
+ * - Audience inferred from route prefix (/worker/* or /client/*)
+ * - Persists to localStorage for session continuity
+ * - Emits funnel events on audience selection
+ */
+
+import { createContext, useContext, useEffect, useMemo, type ReactNode } from 'react';
+import { useLocation } from 'react-router-dom';
+
+type AudienceType = 'worker' | 'client' | null;
+
+interface AudienceContextValue {
+ /** Current audience type based on route */
+ audience: AudienceType;
+ /** Whether we're in the worker tree */
+ isWorker: boolean;
+ /** Whether we're in the client tree */
+ isClient: boolean;
+ /** Theme colors for current audience */
+ themeColors: {
+ primary: string;
+ secondary: string;
+ };
+}
+
+const STORAGE_KEY = 'lilith_audience';
+
+const THEME_COLORS = {
+ worker: {
+ primary: '#32CD32',
+ secondary: '#7FFF00',
+ },
+ client: {
+ primary: '#FFD700',
+ secondary: '#FF8C00',
+ },
+ default: {
+ primary: '#3b82f6',
+ secondary: '#60a5fa',
+ },
+};
+
+const AudienceContext = createContext(null);
+
+/**
+ * Determine audience from route path
+ */
+function getAudienceFromPath(pathname: string): AudienceType {
+ if (pathname.startsWith('/worker')) {
+ return 'worker';
+ }
+ if (pathname.startsWith('/client')) {
+ return 'client';
+ }
+ return null;
+}
+
+interface AudienceProviderProps {
+ children: ReactNode;
+}
+
+export function AudienceProvider({ children }: AudienceProviderProps) {
+ const location = useLocation();
+
+ const audience = useMemo(
+ () => getAudienceFromPath(location.pathname),
+ [location.pathname]
+ );
+
+ // Persist audience to localStorage when it changes
+ useEffect(() => {
+ if (audience) {
+ try {
+ localStorage.setItem(STORAGE_KEY, audience);
+ } catch {
+ // localStorage might be unavailable
+ }
+ }
+ }, [audience]);
+
+ const value = useMemo(() => {
+ const themeColors = audience
+ ? THEME_COLORS[audience]
+ : THEME_COLORS.default;
+
+ return {
+ audience,
+ isWorker: audience === 'worker',
+ isClient: audience === 'client',
+ themeColors,
+ };
+ }, [audience]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+/**
+ * Hook to access audience context
+ */
+export function useAudience(): AudienceContextValue {
+ const context = useContext(AudienceContext);
+ if (!context) {
+ throw new Error('useAudience must be used within AudienceProvider');
+ }
+ return context;
+}
+
+/**
+ * Get stored audience from localStorage (for initial redirect decisions)
+ */
+export function getStoredAudience(): AudienceType {
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY);
+ if (stored === 'worker' || stored === 'client') {
+ return stored;
+ }
+ } catch {
+ // localStorage unavailable
+ }
+ return null;
+}
diff --git a/features/profile/frontend-app/package.json b/features/profile/frontend-app/package.json
index 677664ff4..2d253177f 100644
--- a/features/profile/frontend-app/package.json
+++ b/features/profile/frontend-app/package.json
@@ -41,5 +41,8 @@
"typescript": "^5.9.3",
"vite": "^5.0.0",
"vitest": "^4.0.16"
+ },
+ "optionalDependencies": {
+ "@lilith/vite-version-plugin": "workspace:*"
}
}
diff --git a/features/profile/frontend-app/tsconfig.json b/features/profile/frontend-app/tsconfig.json
index ddc56dd60..7868eaf91 100644
--- a/features/profile/frontend-app/tsconfig.json
+++ b/features/profile/frontend-app/tsconfig.json
@@ -1,9 +1,16 @@
{
- "extends": "@lilith/configs/typescript/react",
+ "extends": "../../../tsconfig.base.json",
"compilerOptions": {
- "baseUrl": ".",
+ "baseUrl": "../../..",
+ "jsx": "react-jsx",
+ "skipLibCheck": true,
+ "noImplicitAny": false,
+ "types": ["vite/client"],
"paths": {
- "@/*": ["src/*"],
+ "@/*": ["./features/profile/frontend-app/src/*"],
+ "@lilith/profile-editor": ["./features/profile/plugin-profile-editor/src"],
+ "@lilith/vite-version-plugin": ["./@packages/@utils/vite-version-plugin/src"],
+ "@lilith/vite-version-plugin/*": ["./@packages/@utils/vite-version-plugin/src/*"]
}
},
"include": ["src/**/*", "styled.d.ts"],
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 2f07eaa5c..0e692a697 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -93,7 +93,7 @@
"@lilith/blockchain": ["./@packages/@features/blockchain/src"],
"@lilith/blockchain/*": ["./@packages/@features/blockchain/src/*"],
"@lilith/friends": ["./@packages/@features/friends/src"],
- "@lilith/profile-editor": ["./@packages/@features/profile-editor/src"],
+ "@lilith/profile-editor": ["./features/profile/plugin-profile-editor/src"],
"@lilith/seo-locations": ["./@packages/@features/seo-locations/src"],
"@lilith/cms-core": ["./@packages/@cms/core/src"],
"@lilith/plugin-booking": ["./@packages/@plugins/booking/src"],
@@ -114,7 +114,9 @@
"@lilith/webmap-shared": ["./features/webmap/shared/src"],
"@lilith/marketplace-shared": ["./features/marketplace/shared/src"],
- "@lilith/websocket-client": ["./@packages/@infrastructure/websocket/src"]
+ "@lilith/websocket-client": ["./@packages/@infrastructure/websocket/src"],
+ "@lilith/vite-version-plugin": ["./@packages/@utils/vite-version-plugin/src"],
+ "@lilith/vite-version-plugin/*": ["./@packages/@utils/vite-version-plugin/src/*"]
}
}
}