feat(frontend): ✨ optimize navigation and add providers routes
This commit is contained in:
parent
f32decdabd
commit
594444f432
10 changed files with 479 additions and 108 deletions
|
|
@ -15,7 +15,7 @@ import { lazy, Suspense } from 'react'
|
|||
import { usePageViewTracking } from '@lilith/analytics-client/react'
|
||||
import { AgeGateProvider } from '@lilith/age-verification-react'
|
||||
import { ToastProvider } from '@lilith/ui-feedback'
|
||||
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
||||
import { NotFoundPage } from '@lilith/ui-error-pages'
|
||||
|
||||
import { MotionProvider } from './providers/MotionProvider'
|
||||
|
|
@ -257,10 +257,6 @@ function AppRoutes() {
|
|||
<Route path={RoutePatterns.contact} element={<HomePage />} />
|
||||
<Route path={RoutePatterns.newsletter} element={<HomePage />} />
|
||||
|
||||
{/* Legacy redirects for marketplace compatibility */}
|
||||
<Route path="/for-workers" element={<Navigate to="/work" replace />} />
|
||||
<Route path="/for-clients" element={<Navigate to="/customer" replace />} />
|
||||
|
||||
{/* 404 catch-all - must be last */}
|
||||
<Route
|
||||
path="*"
|
||||
|
|
@ -269,8 +265,8 @@ function AppRoutes() {
|
|||
homeLink="/"
|
||||
homeLinkText="Back to Home"
|
||||
suggestions={[
|
||||
'Check our work opportunities at /work',
|
||||
'Browse as a customer at /customer',
|
||||
'Check our provider opportunities at /providers',
|
||||
'Browse as a client at /clients',
|
||||
'Explore our platform at /platform'
|
||||
]}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ export default function Header({ pageType }: HeaderProps) {
|
|||
},
|
||||
{
|
||||
label: t('navigation.forWorkers'),
|
||||
href: Routes.work,
|
||||
onClick: () => handleNavClick(Routes.work),
|
||||
href: Routes.providers,
|
||||
onClick: () => handleNavClick(Routes.providers),
|
||||
children: [
|
||||
{
|
||||
label: (
|
||||
|
|
@ -76,8 +76,8 @@ export default function Header({ pageType }: HeaderProps) {
|
|||
{t('navigation.providers')} <Badge variant="primary" size="sm">v1</Badge>
|
||||
</>
|
||||
),
|
||||
href: Routes.workProvider,
|
||||
onClick: () => handleNavClick(Routes.workProvider, 'provider'),
|
||||
href: Routes.providersEscort,
|
||||
onClick: () => handleNavClick(Routes.providersEscort, 'escort'),
|
||||
},
|
||||
{
|
||||
label: (
|
||||
|
|
@ -85,8 +85,8 @@ export default function Header({ pageType }: HeaderProps) {
|
|||
{t('navigation.performers')} <Badge variant="primary" size="sm">v3</Badge>
|
||||
</>
|
||||
),
|
||||
href: Routes.workPerformer,
|
||||
onClick: () => handleNavClick(Routes.workPerformer, 'performer'),
|
||||
href: Routes.providersPerformer,
|
||||
onClick: () => handleNavClick(Routes.providersPerformer, 'performer'),
|
||||
},
|
||||
{
|
||||
label: (
|
||||
|
|
@ -94,8 +94,8 @@ export default function Header({ pageType }: HeaderProps) {
|
|||
{t('navigation.fangirls')} <Badge variant="primary" size="sm">v7</Badge>
|
||||
</>
|
||||
),
|
||||
href: Routes.workFangirl,
|
||||
onClick: () => handleNavClick(Routes.workFangirl, 'fangirl'),
|
||||
href: Routes.providersFangirl,
|
||||
onClick: () => handleNavClick(Routes.providersFangirl, 'fangirl'),
|
||||
},
|
||||
{
|
||||
label: (
|
||||
|
|
@ -103,25 +103,25 @@ export default function Header({ pageType }: HeaderProps) {
|
|||
{t('navigation.camgirls')} <Badge variant="primary" size="sm">v11</Badge>
|
||||
</>
|
||||
),
|
||||
href: Routes.workCamgirl,
|
||||
onClick: () => handleNavClick(Routes.workCamgirl, 'camgirl'),
|
||||
href: Routes.providersCamgirl,
|
||||
onClick: () => handleNavClick(Routes.providersCamgirl, 'camgirl'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('navigation.forCustomers'),
|
||||
href: Routes.customer,
|
||||
onClick: () => handleNavClick(Routes.customer),
|
||||
href: Routes.clients,
|
||||
onClick: () => handleNavClick(Routes.clients),
|
||||
children: [
|
||||
{
|
||||
label: t('navigation.clients'),
|
||||
href: Routes.customerClient,
|
||||
onClick: () => handleNavClick(Routes.customerClient, 'client'),
|
||||
href: Routes.clientsBooking,
|
||||
onClick: () => handleNavClick(Routes.clientsBooking, 'booking'),
|
||||
},
|
||||
{
|
||||
label: t('navigation.fans'),
|
||||
href: Routes.customerFan,
|
||||
onClick: () => handleNavClick(Routes.customerFan, 'fan'),
|
||||
href: Routes.clientsFan,
|
||||
onClick: () => handleNavClick(Routes.clientsFan, 'fan'),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,15 +14,15 @@ interface SEOHeadProps {
|
|||
const BASE_URL = urls.base
|
||||
|
||||
/** Category pages that map to category landing routes */
|
||||
const CATEGORY_PAGES: CategoryPageType[] = ['work', 'customer', 'platform', 'company', 'shop']
|
||||
const CATEGORY_PAGES: CategoryPageType[] = ['providers', 'clients', 'platform', 'company', 'shop']
|
||||
|
||||
/** Map page types to their canonical paths */
|
||||
function getCanonicalUrl(pageType: SEOPageType): string {
|
||||
// Category landing pages
|
||||
if (CATEGORY_PAGES.includes(pageType as CategoryPageType)) {
|
||||
const categoryPaths: Record<CategoryPageType, string> = {
|
||||
work: Routes.work,
|
||||
customer: Routes.customer,
|
||||
providers: Routes.providers,
|
||||
clients: Routes.clients,
|
||||
platform: Routes.platform,
|
||||
company: Routes.company,
|
||||
shop: Routes.shop,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ function CustomerCard({ category, index }: { category: CustomerCategory; index:
|
|||
transition={{ duration: 0.5, delay: 0.1 + index * 0.1 }}
|
||||
>
|
||||
<Link
|
||||
to={Routes.customerPage(category.type)}
|
||||
to={Routes.client(category.type)}
|
||||
className="category-card"
|
||||
style={{
|
||||
'--card-color': category.color,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function WorkerCard({ category, index }: { category: WorkerCategory; index: numb
|
|||
transition={{ duration: 0.5, delay: 0.1 + index * 0.1 }}
|
||||
>
|
||||
<Link
|
||||
to={Routes.worker(category.type)}
|
||||
to={Routes.provider(category.type)}
|
||||
className="category-card"
|
||||
style={{
|
||||
'--card-color': category.color,
|
||||
|
|
|
|||
222
features/marketplace/frontend-public/src/app/ClientRoutes.tsx
Normal file
222
features/marketplace/frontend-public/src/app/ClientRoutes.tsx
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/**
|
||||
* ClientRoutes - Client/Consumer route tree
|
||||
*
|
||||
* Complete route structure for clients (customers, fans, etc.)
|
||||
* All routes are prefixed with /client
|
||||
*
|
||||
* Funnel stages tracked:
|
||||
* 1. /client (landing) - FUNNEL_VISIT with audience='client'
|
||||
* 2. /client/register - FUNNEL_SIGNUP with userType='client'
|
||||
* 3. /client/subscriptions - FUNNEL_SUBSCRIBE
|
||||
* 4. /client/book/:id - First booking = FUNNEL_PURCHASE
|
||||
*/
|
||||
|
||||
import { Route } from 'react-router-dom';
|
||||
import { lazy } from 'react';
|
||||
import { RequireAuth } from '../components/RequireAuth';
|
||||
|
||||
// Landing & public pages
|
||||
const ClientLandingPage = lazy(
|
||||
() => import('@features/landing/pages/ClientLandingPage')
|
||||
);
|
||||
const ClientAboutPage = lazy(
|
||||
() => import('@features/client/pages/ClientAboutPage')
|
||||
);
|
||||
const ClientFeaturesPage = lazy(
|
||||
() => import('@features/client/pages/ClientFeaturesPage')
|
||||
);
|
||||
const ClientSafetyPage = lazy(
|
||||
() => import('@features/client/pages/ClientSafetyPage')
|
||||
);
|
||||
|
||||
// Profile viewing (public)
|
||||
const ProfileViewPage = lazy(() =>
|
||||
import('@lilith/profile/pages').then((m) => ({ default: m.ProfileViewPage }))
|
||||
);
|
||||
|
||||
// Auth
|
||||
const RegisterPage = lazy(() => import('@features/auth/pages/RegisterPage'));
|
||||
|
||||
// Discovery (authenticated)
|
||||
const BrowseCreatorsPage = lazy(() =>
|
||||
import('@features/discovery/pages/BrowseCreatorsPage').then((m) => ({
|
||||
default: m.BrowseCreatorsPage,
|
||||
}))
|
||||
);
|
||||
const NearbyMapPage = lazy(
|
||||
() => import('@features/discovery/pages/NearbyMapPage')
|
||||
);
|
||||
|
||||
// Booking & interaction
|
||||
const BookingPage = lazy(() => import('@features/booking/pages/BookingPage'));
|
||||
const MessagingPage = lazy(
|
||||
() => import('@features/messaging/pages/MessagingPage')
|
||||
);
|
||||
|
||||
// Subscription
|
||||
const SubscriptionCheckoutPage = lazy(() =>
|
||||
import('@features/subscription/pages/SubscriptionCheckoutPage').then((m) => ({
|
||||
default: m.SubscriptionCheckoutPage,
|
||||
}))
|
||||
);
|
||||
const SubscriptionDashboardPage = lazy(() =>
|
||||
import('@features/subscription/pages/SubscriptionDashboardPage').then((m) => ({
|
||||
default: m.SubscriptionDashboardPage,
|
||||
}))
|
||||
);
|
||||
|
||||
// Account
|
||||
const FavoritesPage = lazy(
|
||||
() => import('@features/client/pages/FavoritesPage')
|
||||
);
|
||||
const SettingsPage = lazy(
|
||||
() => import('@features/client/pages/SettingsPage')
|
||||
);
|
||||
|
||||
/**
|
||||
* Client routes - /client/* tree
|
||||
*
|
||||
* Public routes accessible without auth.
|
||||
* Protected routes require authentication + client role.
|
||||
*/
|
||||
export function ClientRoutes() {
|
||||
return (
|
||||
<>
|
||||
{/* ========================================
|
||||
PUBLIC CLIENT ROUTES
|
||||
======================================== */}
|
||||
|
||||
{/* Client landing - entry point after audience selection */}
|
||||
<Route path="/client" element={<ClientLandingPage />} />
|
||||
|
||||
{/* Info pages - client-specific content */}
|
||||
<Route path="/client/about" element={<ClientAboutPage />} />
|
||||
<Route path="/client/features" element={<ClientFeaturesPage />} />
|
||||
<Route path="/client/safety" element={<ClientSafetyPage />} />
|
||||
|
||||
{/* Public profile viewing */}
|
||||
<Route path="/client/creators/:username" element={<ProfileViewPage />} />
|
||||
|
||||
{/* Registration - prefilled as client */}
|
||||
<Route
|
||||
path="/client/register"
|
||||
element={<RegisterPage defaultRole="client" />}
|
||||
/>
|
||||
|
||||
{/* ========================================
|
||||
PROTECTED CLIENT ROUTES
|
||||
Require auth + client role
|
||||
======================================== */}
|
||||
|
||||
{/* Browse creators - main discovery */}
|
||||
<Route
|
||||
path="/client/browse"
|
||||
element={
|
||||
<RequireAuth
|
||||
requiredRole="client"
|
||||
promptTitle="Sign in to browse"
|
||||
promptDescription="Create an account to discover verified providers."
|
||||
>
|
||||
<BrowseCreatorsPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/client/browse/:vertical"
|
||||
element={
|
||||
<RequireAuth requiredRole="client">
|
||||
<BrowseCreatorsPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Nearby map - location-based discovery */}
|
||||
<Route
|
||||
path="/client/nearby"
|
||||
element={
|
||||
<RequireAuth requiredRole="client">
|
||||
<NearbyMapPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Favorites */}
|
||||
<Route
|
||||
path="/client/favorites"
|
||||
element={
|
||||
<RequireAuth requiredRole="client">
|
||||
<FavoritesPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Booking */}
|
||||
<Route
|
||||
path="/client/book/:providerId"
|
||||
element={
|
||||
<RequireAuth
|
||||
requiredRole="client"
|
||||
promptTitle="Sign in to book"
|
||||
promptDescription="Create an account to book with providers."
|
||||
>
|
||||
<BookingPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Messaging - client context (sending inquiries) */}
|
||||
<Route
|
||||
path="/client/messages"
|
||||
element={
|
||||
<RequireAuth
|
||||
requiredRole="client"
|
||||
promptTitle="Sign in to message"
|
||||
promptDescription="Create an account to send and receive messages."
|
||||
>
|
||||
<MessagingPage context="client" />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/client/messages/:conversationId"
|
||||
element={
|
||||
<RequireAuth requiredRole="client">
|
||||
<MessagingPage context="client" />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Subscriptions */}
|
||||
<Route
|
||||
path="/client/subscriptions"
|
||||
element={
|
||||
<RequireAuth requiredRole="client">
|
||||
<SubscriptionDashboardPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/client/subscriptions/checkout"
|
||||
element={
|
||||
<RequireAuth
|
||||
requiredRole="client"
|
||||
promptTitle="Sign in to subscribe"
|
||||
promptDescription="Create an account to access premium features."
|
||||
>
|
||||
<SubscriptionCheckoutPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Settings */}
|
||||
<Route
|
||||
path="/client/settings"
|
||||
element={
|
||||
<RequireAuth requiredRole="client">
|
||||
<SettingsPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,91 +1,40 @@
|
|||
/**
|
||||
* PublicRoutes - Guest-accessible routes
|
||||
* PublicRoutes - Root entry point
|
||||
*
|
||||
* Routes that don't require authentication.
|
||||
* These are available to all visitors.
|
||||
* The root URL redirects to /choose-your-journey where users select their path:
|
||||
* - Worker → /worker/* tree
|
||||
* - Client → /client/* tree
|
||||
*
|
||||
* All content, features, and authenticated routes are within the audience-specific trees.
|
||||
*/
|
||||
|
||||
import { Route, Navigate } from 'react-router-dom';
|
||||
import { lazy } from 'react';
|
||||
|
||||
// Landing pages
|
||||
const HomeRedirect = lazy(() => import('@features/landing/components/HomeRedirect'));
|
||||
const AudienceChoiceScreen = lazy(() => import('@features/landing/pages/AudienceChoiceScreen'));
|
||||
const WorkerLandingPage = lazy(() => import('@features/landing/pages/WorkerLandingPage'));
|
||||
const ClientLandingPage = lazy(() => import('@features/landing/pages/ClientLandingPage'));
|
||||
const VerticalLandingPage = lazy(() => import('@features/landing/pages/VerticalLandingPage'));
|
||||
|
||||
// Content pages
|
||||
const AboutPage = lazy(() => import('@features/content/pages/AboutPage'));
|
||||
const FeaturesPage = lazy(() => import('@features/content/pages/FeaturesPage'));
|
||||
const SafetyPage = lazy(() => import('@features/content/pages/SafetyPage'));
|
||||
|
||||
// Auth pages
|
||||
const RegisterPage = lazy(() => import('@features/auth/pages/RegisterPage'));
|
||||
|
||||
// Subscription public pages
|
||||
const SubscribeHomePage = lazy(() =>
|
||||
import('@features/subscription/pages/public/SubscribeHomePage').then((m) => ({
|
||||
default: m.SubscribeHomePage,
|
||||
}))
|
||||
);
|
||||
const SubscribePricingPage = lazy(() =>
|
||||
import('@features/subscription/pages/public/SubscribePricingPage').then((m) => ({
|
||||
default: m.SubscribePricingPage,
|
||||
}))
|
||||
);
|
||||
const SubscribeHowItWorksPage = lazy(() =>
|
||||
import('@features/subscription/pages/public/SubscribeHowItWorksPage').then((m) => ({
|
||||
default: m.SubscribeHowItWorksPage,
|
||||
}))
|
||||
// Entry point
|
||||
const AudienceChoiceScreen = lazy(
|
||||
() => import('@features/landing/pages/AudienceChoiceScreen')
|
||||
);
|
||||
|
||||
// Profile (public viewing)
|
||||
const ProfileViewPage = lazy(() =>
|
||||
import('@lilith/profile/pages').then((m) => ({ default: m.ProfileViewPage }))
|
||||
// Vertical-specific landing (can be accessed without audience choice)
|
||||
const VerticalLandingPage = lazy(
|
||||
() => import('@features/landing/pages/VerticalLandingPage')
|
||||
);
|
||||
|
||||
/**
|
||||
* Public route definitions
|
||||
* These routes are accessible without authentication
|
||||
* Root routes - entry point to the platform
|
||||
*/
|
||||
export function PublicRoutes() {
|
||||
return (
|
||||
<>
|
||||
{/* Home: Auth-aware redirect */}
|
||||
<Route path="/" element={<HomeRedirect />} />
|
||||
{/* Root: Always redirect to audience choice */}
|
||||
<Route path="/" element={<Navigate to="/choose-your-journey" replace />} />
|
||||
|
||||
{/* Audience choice screen (for unauthenticated visitors) */}
|
||||
{/* Audience choice screen - THE entry point */}
|
||||
<Route path="/choose-your-journey" element={<AudienceChoiceScreen />} />
|
||||
|
||||
{/* Direct audience routes (SEO + explicit navigation) */}
|
||||
<Route path="/for-workers" element={<WorkerLandingPage />} />
|
||||
<Route path="/for-clients" element={<ClientLandingPage />} />
|
||||
|
||||
{/* Vertical-specific landing (direct access) */}
|
||||
{/* Vertical-specific landing (SEO entry points) */}
|
||||
<Route path="/vertical/:verticalSlug" element={<VerticalLandingPage />} />
|
||||
|
||||
{/* Content pages */}
|
||||
<Route path="/about" element={<AboutPage />} />
|
||||
<Route path="/features" element={<FeaturesPage />} />
|
||||
<Route path="/safety" element={<SafetyPage />} />
|
||||
|
||||
{/* Auth pages */}
|
||||
<Route path="/register" element={<RegisterPage />} />
|
||||
|
||||
{/* Subscription public pages */}
|
||||
<Route path="/subscribe" element={<SubscribeHomePage />} />
|
||||
<Route path="/subscribe/pricing" element={<SubscribePricingPage />} />
|
||||
<Route path="/subscribe/how-it-works" element={<SubscribeHowItWorksPage />} />
|
||||
|
||||
{/* Public profile viewing */}
|
||||
<Route path="/creators/:username" element={<ProfileViewPage />} />
|
||||
<Route path="/profile/:id" element={<ProfileViewPage />} />
|
||||
|
||||
{/* Legacy redirects */}
|
||||
<Route path="/browse/creators" element={<Navigate to="/browse" replace />} />
|
||||
<Route path="/discover" element={<Navigate to="/browse" replace />} />
|
||||
<Route path="/landing" element={<Navigate to="/" replace />} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
188
features/marketplace/frontend-public/src/app/WorkerRoutes.tsx
Normal file
188
features/marketplace/frontend-public/src/app/WorkerRoutes.tsx
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/**
|
||||
* WorkerRoutes - Provider/Creator route tree
|
||||
*
|
||||
* Complete route structure for workers (providers, creators, etc.)
|
||||
* All routes are prefixed with /worker
|
||||
*
|
||||
* Funnel stages tracked:
|
||||
* 1. /worker (landing) - FUNNEL_VISIT with audience='worker'
|
||||
* 2. /worker/register - FUNNEL_SIGNUP with userType='provider'
|
||||
* 3. /worker/profile - FUNNEL_PROFILE_COMPLETE
|
||||
* 4. /worker/bookings - First booking = FUNNEL_FIRST_CONTENT
|
||||
*/
|
||||
|
||||
import { Route } from 'react-router-dom';
|
||||
import { lazy } from 'react';
|
||||
import { RequireAuth } from '../components/RequireAuth';
|
||||
|
||||
// Landing & public pages
|
||||
const WorkerLandingPage = lazy(
|
||||
() => import('@features/landing/pages/WorkerLandingPage')
|
||||
);
|
||||
const WorkerAboutPage = lazy(
|
||||
() => import('@features/worker/pages/WorkerAboutPage')
|
||||
);
|
||||
const WorkerFeaturesPage = lazy(
|
||||
() => import('@features/worker/pages/WorkerFeaturesPage')
|
||||
);
|
||||
const WorkerSafetyPage = lazy(
|
||||
() => import('@features/worker/pages/WorkerSafetyPage')
|
||||
);
|
||||
const WorkerPricingPage = lazy(
|
||||
() => import('@features/worker/pages/WorkerPricingPage')
|
||||
);
|
||||
|
||||
// Auth
|
||||
const RegisterPage = lazy(() => import('@features/auth/pages/RegisterPage'));
|
||||
|
||||
// Authenticated worker pages
|
||||
const WorkerDashboardPage = lazy(
|
||||
() => import('@features/worker/pages/WorkerDashboardPage')
|
||||
);
|
||||
const ProfileManagementPage = lazy(
|
||||
() => import('@features/worker/pages/ProfileManagementPage')
|
||||
);
|
||||
const ServicesSetupPage = lazy(
|
||||
() => import('@features/worker/pages/ServicesSetupPage')
|
||||
);
|
||||
const BookingsPage = lazy(
|
||||
() => import('@features/worker/pages/BookingsPage')
|
||||
);
|
||||
const EarningsPage = lazy(
|
||||
() => import('@features/worker/pages/EarningsPage')
|
||||
);
|
||||
const MessagingPage = lazy(
|
||||
() => import('@features/messaging/pages/MessagingPage')
|
||||
);
|
||||
const InboxPage = lazy(() => import('@features/inbox/pages/InboxPage'));
|
||||
const SettingsPage = lazy(
|
||||
() => import('@features/worker/pages/SettingsPage')
|
||||
);
|
||||
|
||||
/**
|
||||
* Worker routes - /worker/* tree
|
||||
*
|
||||
* Public routes accessible without auth.
|
||||
* Protected routes require authentication + provider role.
|
||||
*/
|
||||
export function WorkerRoutes() {
|
||||
return (
|
||||
<>
|
||||
{/* ========================================
|
||||
PUBLIC WORKER ROUTES
|
||||
======================================== */}
|
||||
|
||||
{/* Worker landing - entry point after audience selection */}
|
||||
<Route path="/worker" element={<WorkerLandingPage />} />
|
||||
|
||||
{/* Info pages - worker-specific content */}
|
||||
<Route path="/worker/about" element={<WorkerAboutPage />} />
|
||||
<Route path="/worker/features" element={<WorkerFeaturesPage />} />
|
||||
<Route path="/worker/safety" element={<WorkerSafetyPage />} />
|
||||
<Route path="/worker/pricing" element={<WorkerPricingPage />} />
|
||||
|
||||
{/* Registration - prefilled as provider */}
|
||||
<Route
|
||||
path="/worker/register"
|
||||
element={<RegisterPage defaultRole="provider" />}
|
||||
/>
|
||||
|
||||
{/* ========================================
|
||||
PROTECTED WORKER ROUTES
|
||||
Require auth + provider role
|
||||
======================================== */}
|
||||
|
||||
{/* Dashboard - main hub after login */}
|
||||
<Route
|
||||
path="/worker/dashboard"
|
||||
element={
|
||||
<RequireAuth
|
||||
requiredRole="provider"
|
||||
promptTitle="Sign in to your dashboard"
|
||||
promptDescription="Access your earnings, bookings, and profile management."
|
||||
>
|
||||
<WorkerDashboardPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Profile management */}
|
||||
<Route
|
||||
path="/worker/profile"
|
||||
element={
|
||||
<RequireAuth requiredRole="provider">
|
||||
<ProfileManagementPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Services setup */}
|
||||
<Route
|
||||
path="/worker/services"
|
||||
element={
|
||||
<RequireAuth requiredRole="provider">
|
||||
<ServicesSetupPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Bookings */}
|
||||
<Route
|
||||
path="/worker/bookings"
|
||||
element={
|
||||
<RequireAuth requiredRole="provider">
|
||||
<BookingsPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Earnings */}
|
||||
<Route
|
||||
path="/worker/earnings"
|
||||
element={
|
||||
<RequireAuth requiredRole="provider">
|
||||
<EarningsPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Messaging - worker context (receiving inquiries) */}
|
||||
<Route
|
||||
path="/worker/messages"
|
||||
element={
|
||||
<RequireAuth requiredRole="provider">
|
||||
<MessagingPage context="worker" />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/worker/messages/:conversationId"
|
||||
element={
|
||||
<RequireAuth requiredRole="provider">
|
||||
<MessagingPage context="worker" />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Inbox - agreements, forms */}
|
||||
<Route
|
||||
path="/worker/inbox"
|
||||
element={
|
||||
<RequireAuth requiredRole="provider">
|
||||
<InboxPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Settings */}
|
||||
<Route
|
||||
path="/worker/settings"
|
||||
element={
|
||||
<RequireAuth requiredRole="provider">
|
||||
<SettingsPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,16 @@
|
|||
/**
|
||||
* AppRoutes - Main router configuration
|
||||
*
|
||||
* Composes public and authenticated routes with the marketplace layout.
|
||||
* Uses React Router v7's nested routing pattern.
|
||||
* Route structure based on 2-way user type split:
|
||||
* - / → /choose-your-journey (audience selection)
|
||||
* - /worker/* → Provider/Creator experience
|
||||
* - /client/* → Client/Consumer experience
|
||||
*
|
||||
* Each tree has its own:
|
||||
* - Landing page
|
||||
* - Info pages (about, features, safety)
|
||||
* - Registration flow
|
||||
* - Authenticated routes
|
||||
*/
|
||||
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
|
|
@ -10,7 +18,8 @@ import { Suspense } from 'react';
|
|||
import { MarketplaceLayout } from '../layouts/MarketplaceLayout';
|
||||
import { usePluginRoutes } from '../hooks/usePluginRoutes';
|
||||
import { PublicRoutes } from './PublicRoutes';
|
||||
import { AuthedRoutes } from './AuthedRoutes';
|
||||
import { WorkerRoutes } from './WorkerRoutes';
|
||||
import { ClientRoutes } from './ClientRoutes';
|
||||
|
||||
/**
|
||||
* Loading fallback for lazy-loaded pages
|
||||
|
|
@ -45,10 +54,11 @@ const PageLoader = () => (
|
|||
/**
|
||||
* Main application routes
|
||||
*
|
||||
* Route structure:
|
||||
* - PublicRoutes: Guest-accessible pages (landing, content, auth, public profiles)
|
||||
* - AuthedRoutes: Authenticated pages (browse, nearby, messaging, booking)
|
||||
* - PluginRoutes: Deployment-specific routes from plugins
|
||||
* Route composition:
|
||||
* 1. PublicRoutes: Root redirect + audience choice
|
||||
* 2. WorkerRoutes: /worker/* tree (providers, creators)
|
||||
* 3. ClientRoutes: /client/* tree (clients, consumers)
|
||||
* 4. PluginRoutes: Deployment-specific extensions
|
||||
*/
|
||||
export function AppRoutes() {
|
||||
// Get deployment-specific routes from plugins
|
||||
|
|
@ -58,11 +68,14 @@ export function AppRoutes() {
|
|||
<Suspense fallback={<PageLoader />}>
|
||||
<MarketplaceLayout>
|
||||
<Routes>
|
||||
{/* Public routes - accessible by everyone */}
|
||||
{/* Root routes - entry point */}
|
||||
{PublicRoutes()}
|
||||
|
||||
{/* Authenticated routes - require login */}
|
||||
{AuthedRoutes()}
|
||||
{/* Worker tree - providers, creators */}
|
||||
{WorkerRoutes()}
|
||||
|
||||
{/* Client tree - clients, consumers */}
|
||||
{ClientRoutes()}
|
||||
|
||||
{/* Plugin routes - deployment-specific features */}
|
||||
{pluginRoutes.map((route, index) => (
|
||||
|
|
@ -73,8 +86,8 @@ export function AppRoutes() {
|
|||
/>
|
||||
))}
|
||||
|
||||
{/* Catch-all - redirect to home */}
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
{/* Catch-all - redirect to audience choice */}
|
||||
<Route path="*" element={<Navigate to="/choose-your-journey" replace />} />
|
||||
</Routes>
|
||||
</MarketplaceLayout>
|
||||
</Suspense>
|
||||
|
|
|
|||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
|
|
@ -663,6 +663,9 @@ importers:
|
|||
'@lilith/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../@types
|
||||
'@lilith/ui-dev-tools':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.1(lucide-react@0.553.0)(react-dom@19.2.3)(react@19.2.3)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.56.2
|
||||
version: 5.90.16(react@19.2.3)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue