'use client' import { zodResolver } from '@hookform/resolvers/zod' import { useAuth } from '@thedaviddias/auth' import { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' import { toast } from 'sonner' import { submitLlmsTxt } from '@/actions/submit-llms-xxt' import { useAnalyticsEvents } from '@/components/analytics-tracker' import { SubmitFormGuidelines } from './submit-form-guidelines' import { type Step1Data, type Step2Data, step1Schema, step2Schema } from './submit-form-schemas' import { SubmitFormStep1 } from './submit-form-step1' import { SubmitFormStep2 } from './submit-form-step2' import { SubmitFormSuccess } from './submit-form-success' import { generateLlmsUrl } from './submit-form-utils' /** * Main form component for submitting websites */ export function SubmitForm() { const [step, setStep] = useState(1) const [isLoading, setIsLoading] = useState(false) const [fetchFailed, setFetchFailed] = useState(false) const [prUrl, setPrUrl] = useState('') const [llmsUrlStatus, setLlmsUrlStatus] = useState<{ checking: boolean accessible: boolean | null error?: string }>({ checking: false, accessible: null }) const [llmsFullUrlStatus, setLlmsFullUrlStatus] = useState<{ checking: boolean accessible: boolean | null error?: string }>({ checking: false, accessible: null }) const [websiteUrlStatus] = useState<{ checking: boolean accessible: boolean | null error?: string }>({ checking: false, accessible: null }) const { user } = useAuth() const { trackFormStepStart, trackFormStepComplete, trackFetchMetadataSuccess, trackFetchMetadataError, trackSubmitSuccess, trackSubmitError } = useAnalyticsEvents() // Determine submission type const hasGitHubAuth = user && (user.user_metadata?.github_username || user.user_metadata?.user_name) const userDisplayName = user?.user_metadata?.user_name || user?.email?.split('@')[0] || 'Anonymous' const step1Form = useForm({ resolver: zodResolver(step1Schema), defaultValues: { website: '' } }) const step2Form = useForm({ resolver: zodResolver(step2Schema), defaultValues: { name: '', description: '', mdxContent: '', website: '', llmsUrl: '', llmsFullUrl: null, category: '' } }) // Track form start on component mount useEffect(() => { trackFormStepStart(1, 'submit-form', 'submit-page') }, [trackFormStepStart]) /** * Transitions to step 2 with pre-populated form data */ function transitionToStep2(formData: Step2Data, failed: boolean) { step2Form.reset(formData) setFetchFailed(failed) setStep(2) trackFormStepStart(2, 'submit-form', 'submit-page') } /** * Fetches metadata for the website */ async function onFetchMetadata(data: Step1Data) { setIsLoading(true) trackFormStepComplete(1, 'submit-form', 'submit-page') try { const csrfMetaTag = document.querySelector('meta[name="csrf-token"]') const headers: Record = { 'Content-Type': 'application/json' } if (csrfMetaTag?.content) { headers['x-csrf-token'] = csrfMetaTag.content } const response = await fetch('/api/fetch-metadata', { method: 'POST', headers, body: JSON.stringify({ website: data.website }) }) if (!response.ok) { throw new Error('Failed to fetch metadata') } const result = await response.json() if (result.isDuplicate) { trackFetchMetadataError(data.website, 'duplicate_website', 'submit-page') toast.warning( `This website is already in our directory under the name "${result.existingWebsite.name}".` ) setIsLoading(false) return } trackFetchMetadataSuccess(data.website, 'submit-page') const autoLlmsUrl = result.metadata.llmsUrl || generateLlmsUrl(data.website) transitionToStep2( { name: result.metadata.name || '', description: result.metadata.description || '', mdxContent: '', website: data.website, llmsUrl: autoLlmsUrl, llmsFullUrl: result.metadata.llmsFullUrl || '', category: result.metadata.category || '' }, false ) toast.success('Website info fetched. Please review and complete the submission.') } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error' trackFetchMetadataError(data.website, errorMessage, 'submit-page') toast.error('Failed to fetch website information. You can fill in the details manually.') transitionToStep2( { name: '', description: '', mdxContent: '', website: data.website, llmsUrl: generateLlmsUrl(data.website), llmsFullUrl: '', category: '' }, true ) } finally { setIsLoading(false) } } /** * Submits the final form data */ async function onSubmitStep2(values: Step2Data) { setIsLoading(true) trackFormStepComplete(2, 'submit-form', 'submit-page') try { const formData = new FormData() const currentDate = new Date().toISOString().split('T')[0] const csrfMetaTag = document.querySelector('meta[name="csrf-token"]') if (csrfMetaTag?.content) { formData.append('_csrf', csrfMetaTag.content) } const processedValues = { ...values, name: values.name.trim() } Object.entries(processedValues).forEach(([key, value]) => { if (key !== 'publishedAt' && value) { formData.append(key, value) } }) formData.append('publishedAt', currentDate) const result = await submitLlmsTxt(formData) if (result.success && result.prUrl) { trackSubmitSuccess(values.website, values.category, 'submit-page') toast.success('Your PR has been created successfully!') setPrUrl(String(result.prUrl)) setStep(3) trackFormStepStart(3, 'submit-form', 'submit-page') } else { throw new Error(result.error || 'Unknown error occurred') } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error' trackSubmitError(values.website, errorMessage, 'submit-page') toast.error( error instanceof Error ? error.message : 'There was an error submitting your llms.txt. Please try again.' ) } finally { setIsLoading(false) } } /** * Resets the form to initial state */ function handleReset() { step2Form.reset() step1Form.reset() setFetchFailed(false) setStep(1) } return ( <> {(step === 1 || step === 2) && (

Submit your llms.txt

Enter your project's domain to automatically fetch your llms.txt information. You'll have a chance to review and edit the details before submitting.

{/* User submission info */}
{hasGitHubAuth ? (
) : (
)}

{hasGitHubAuth ? ( <> GitHub connected:{' '} Your submission will create a pull request under the account below: ) : ( <> Email account: Your submission will be reviewed and added to the directory )}

{hasGitHubAuth && `Submitting as: ${userDisplayName}`}

)} {step === 1 ? ( ) : step === 2 ? ( ) : ( )} {step !== 3 && } ) }