diff --git a/features/landing/frontend-public/package.json b/features/landing/frontend-public/package.json
index e44aa229f..4fcf74c6d 100644
--- a/features/landing/frontend-public/package.json
+++ b/features/landing/frontend-public/package.json
@@ -37,6 +37,7 @@
"@lilith/analytics-client": "workspace:*",
"@lilith/api-client": "workspace:*",
"@lilith/auth-provider": "workspace:*",
+ "@lilith/ui-auth": "^1.1.0",
"@lilith/design-tokens": "workspace:*",
"@lilith/i18n": "workspace:*",
"@lilith/image-security": "workspace:*",
diff --git a/features/landing/frontend-public/src/components/CTAModal/CTAModal.tsx b/features/landing/frontend-public/src/components/CTAModal/CTAModal.tsx
index c31e186be..344f6e44c 100644
--- a/features/landing/frontend-public/src/components/CTAModal/CTAModal.tsx
+++ b/features/landing/frontend-public/src/components/CTAModal/CTAModal.tsx
@@ -9,11 +9,18 @@
* - Sub-components in ./components/ (FormField, SuccessState, etc.)
* - Business logic in ./hooks/ (useCTAModal, useModalRouting, useModalBehavior)
* - Configuration in ./contexts/ (form configs, themes)
+ *
+ * Auth Integration:
+ * - Login/Register forms use @lilith/ui-auth components
+ * - Connected to @lilith/auth-provider for actual authentication
+ * - Investor/Contact/Newsletter use custom FormField implementation
*/
import { m, AnimatePresence } from 'framer-motion'
import { X } from 'lucide-react'
import { useState, useMemo } from 'react'
+import { useAuth } from '@lilith/auth-provider'
+import { LoginForm, RegisterForm, type AuthHandler, type User, type UserRole } from '@lilith/ui-auth'
import { useCTAModal } from './hooks/useCTAModal'
import { useModalRouting } from './hooks/useModalRouting'
@@ -33,9 +40,31 @@ import {
getNewsletterConfig,
} from './contexts'
import type { CTAContext, FormConfig } from './types'
+import type { UserType } from '@lilith/i18n'
import './CTAModal.css'
+/**
+ * Map landing UserType to auth-provider role
+ * Landing has: creator, fan, client, provider, investor
+ * Auth has: user, provider, client
+ */
+function mapUserTypeToRole(userType?: UserType): UserRole | undefined {
+ if (!userType) return undefined
+ switch (userType) {
+ case 'creator':
+ case 'fan':
+ case 'investor':
+ return 'user'
+ case 'client':
+ return 'client'
+ case 'provider':
+ return 'provider'
+ default:
+ return 'user'
+ }
+}
+
interface CTAModalProps {
context: CTAContext
onClose: () => void
@@ -47,13 +76,34 @@ interface CTAModalProps {
export default function CTAModal({ context, onClose }: CTAModalProps) {
const { openLogin, openRegister } = useModalRouting()
- // Get form configuration based on context
- const config = useMemo((): FormConfig => {
+ // Auth provider integration for login/register forms
+ const {
+ loginWithCredentials,
+ registerWithCredentials,
+ isLoading: authLoading,
+ user: authUser,
+ } = useAuth()
+
+ // Create authHandler for @lilith/ui-auth components
+ const authHandler: AuthHandler = useMemo(() => ({
+ loginWithCredentials,
+ registerWithCredentials,
+ isLoading: authLoading,
+ user: authUser,
+ }), [loginWithCredentials, registerWithCredentials, authLoading, authUser])
+
+ // Check if this is an auth context (login or register)
+ const isAuthContext = context.type === 'login' || context.type === 'register'
+
+ // Get form configuration based on context (only needed for non-auth forms)
+ const config = useMemo((): FormConfig | null => {
switch (context.type) {
case 'register':
- return getRegistrationConfig(context.userType)
case 'login':
- return getLoginConfig(context.userType)
+ // Auth forms use @lilith/ui-auth - still need config for theming
+ return context.type === 'register'
+ ? getRegistrationConfig(context.userType)
+ : getLoginConfig(context.userType)
case 'investor':
return getInvestorConfig()
case 'contact':
@@ -66,7 +116,7 @@ export default function CTAModal({ context, onClose }: CTAModalProps) {
}
}, [context])
- // Form state management
+ // Form state management - only used for non-auth contexts
const {
formData,
errors,
@@ -74,20 +124,28 @@ export default function CTAModal({ context, onClose }: CTAModalProps) {
setFieldValue,
validateField,
handleSubmit,
- } = useCTAModal({ config })
+ } = useCTAModal({ config: isAuthContext ? undefined : (config ?? undefined) })
+
+ // Auth-specific success state
+ const [authSuccess, setAuthSuccess] = useState(false)
// Feature waitlist step state
const [showFeatureStep, setShowFeatureStep] = useState(false)
const [featureStepComplete, setFeatureStepComplete] = useState(false)
+ // Combined submission state for auth and non-auth contexts
+ const isSubmitting = isAuthContext ? authLoading : submissionState === 'submitting'
+ const isSuccess = isAuthContext ? authSuccess : submissionState === 'success'
+
// Modal behavior (focus, keyboard, animations)
const { modalRef, playSound } = useModalBehavior({
onClose,
- isSubmitting: submissionState === 'submitting',
+ isSubmitting,
})
// Get theme CSS variables
const themeVars = useMemo(() => {
+ if (!config) return {} as React.CSSProperties
const { theme } = config
return {
'--modal-primary': theme.primary,
@@ -102,14 +160,29 @@ export default function CTAModal({ context, onClose }: CTAModalProps) {
}
const handleOverlayClick = (e: React.MouseEvent) => {
- if (e.target === e.currentTarget && submissionState !== 'submitting') {
+ if (e.target === e.currentTarget && !isSubmitting) {
handleClose()
}
}
+ // Handle auth success from @lilith/ui-auth forms
+ const handleAuthSuccess = (_user: User, _sessionId: string) => {
+ setAuthSuccess(true)
+ // Check if we should show feature waitlist
+ if (config?.showFeatureWaitlist) {
+ setShowFeatureStep(true)
+ }
+ }
+
+ // Handle auth error
+ const handleAuthError = (error: Error) => {
+ console.error('Auth error:', error)
+ // Errors are handled within the form components themselves
+ }
+
// Determine what to show
- const showForm = submissionState !== 'success' && !showFeatureStep
- const showSuccess = submissionState === 'success' && !showFeatureStep && !featureStepComplete
+ const showForm = !isSuccess && !showFeatureStep
+ const showSuccess = isSuccess && !showFeatureStep && !featureStepComplete
const showFeatures = showFeatureStep && !featureStepComplete
const showFinalSuccess = featureStepComplete
@@ -142,7 +215,7 @@ export default function CTAModal({ context, onClose }: CTAModalProps) {
onClick={handleClose}
onMouseEnter={() => playSound('button-hover')}
aria-label="Close modal"
- disabled={submissionState === 'submitting'}
+ disabled={isSubmitting}
>
{config.subtitle}
- )} - {/* User type selector for registration without pre-selected type */} - {context.type === 'register' && !context.userType && ( -{config.subtitle}
)} - - )} - +