Skip to main content

Directory Listing Components

The directory components handle the submission form for adding new listings to the directory. This includes a multi-step form with basic information, payment plan selection, review and submission, along with location support and link management.

Architecture Overview

template/components/directory/
details-form.tsx # Main multi-step form orchestrator
details-form/
components/
form-navigation.tsx # Step navigation buttons (back/next/submit)
link-input.tsx # Product link manager sub-component
step-indicator.tsx # Visual step progress indicator
steps/
basic-info-step.tsx # Step 1: Name, links, category, tags, description
payment-step.tsx # Step 2: Plan selection (Free/Standard/Premium)
review-step.tsx # Step 3: Review all data before submission
validation/
form-validators.ts # Step definitions and validation logic
input-link.tsx # Standalone link input component
location-fields.tsx # Location picker integration
review-section.tsx # Full review UI with plan summary
type.ts # Shared TypeScript types

Core Types

FormData

The primary form data shape used across all steps:

interface FormData {
name: string;
link: string;
links: ProductLink[];
category: string | null;
tags: string[];
description: string;
introduction: string;
selectedPlan?: string;
[key: string]: any;
}

Represents a single link entry in the form:

PropertyTypeDescription
idstringUnique identifier
urlstringFull URL value
labelstringDisplay label for the link
type"main" | "secondary"Primary vs additional link
iconstring (optional)Icon identifier

DetailsForm

The main form component that orchestrates the three-step submission flow. It uses the useDetailForm hook for state management and useEditorFieldSync for rich-text editor integration.

import { DetailsForm } from '@/components/directory/details-form';

<DetailsForm
initialData={{ name: 'My Product' }}
onSubmit={handleSubmit}
onBack={handleBack}
listingProps={{ categories, tags, items }}
isSubmitting={false}
/>

DetailsFormProps

PropTypeDefaultDescription
initialDataPartial<FormData>{}Pre-fill form fields
onSubmit(data: FormData) => voidrequiredForm submission handler
onBack() => voidrequiredBack navigation handler
listingPropsListingProps-Categories, tags, and items for selectors
isSubmittingbooleanfalseDisable controls during submission

ListingProps

PropertyTypeDescription
categoriesCategory[]Available categories for selection
tagsTagType[]Available tags for multi-select
itemsItemData[]Existing items for reference

Form Steps

Step 1: BasicInfoStep

Collects the primary listing information:

  • Product Name - Text input with validation
  • Product Links - Dynamic list with primary and secondary links
  • Category - Single-select from available categories
  • Tags - Multi-select tag picker
  • Description - Short text description
  • Introduction - Rich text editor (synced via useEditorFieldSync)
  • Location - Optional location picker (when enabled in settings)

Step 2: PaymentStep

Presents the available payment plans:

PlanPriceFeatures
Free$0Basic listing, manual review (1-2 days)
Standard$10 one-timeStandard features, faster review
Premium$20/weekPremium features, instant publishing

Step 3: ReviewStep

Displays a comprehensive summary of all entered data including product name, category, links, tags, description, selected plan with pricing, and publication status.

Sub-Components

StepIndicator

Visual progress bar showing current step, completed steps, and remaining steps. Supports click navigation to completed steps.

<StepIndicator
currentStep={2}
onStepClick={setCurrentStep}
completedFields={completedFields}
/>

FormNavigation

Navigation controls at the bottom of each step with conditional rendering:

  • Step 1: "Back" + "Next Step" (disabled until validation passes)
  • Step 2: "Previous" + "Next Step"
  • Step 3: "Back to Edit" + "Submit Listing"

Shows a progress indicator: "X of Y required fields completed."

<FormNavigation
currentStep={currentStep}
canProceed={canProceed}
completedRequiredFields={5}
requiredFieldsCount={7}
onPrevious={prevStep}
onNext={nextStep}
onBack={onBack}
isSubmitting={false}
/>

The link management component allows adding multiple product links. The first link is always the "main" link (marked with a Primary badge and required). Additional secondary links can be added and removed dynamically.

InputLinkProps

PropTypeDescription
formDataFormDataCurrent form state
setFormDataDispatch<SetStateAction<FormData>>Form state setter
animatingLinkIdstring | nullLink currently being animated
handleLinkChange(id, field, value) => voidUpdate a link field
addLink() => voidAdd new secondary link
removeLink(id: string) => voidRemove a secondary link

Features:

  • URL validation with visual feedback (green check for valid, amber warning for invalid)
  • Hover effects with gradient overlays
  • Animated entry/removal transitions

LocationFields

Integrates the LocationPicker component into the directory form, handling conversion between the form's ItemLocationData (snake_case) format and the picker's LocationPickerValue (camelCase) format.

<LocationFields
location={formData.location}
onLocationChange={(loc) => setFormData(prev => ({ ...prev, location: loc }))}
/>

Only renders when location is enabled in site settings (useLocationSettings). Shows an "(Optional)" label when requireLocationOnSubmit is false.

ReviewSection

A standalone review component used outside the multi-step form context. Displays plan summary, product information, publication status, and action buttons.

ReviewSectionProps

PropTypeDescription
formDataFormDataComplete form data to review
selectedPlanPaymentPlan | nullChosen payment plan
onSubmit() => voidSubmit action callback
onBack() => voidBack navigation callback

Status determination logic:

  • Free Plan: Status is "Pending Review" with 1-2 business day processing
  • Paid Plans: Status is "Ready to Publish" with instant publishing

Styling Approach

The directory components use a consistent glass-morphism design with:

  • Backdrop blur effects (backdrop-blur-xl)
  • Semi-transparent backgrounds (bg-white/95)
  • Gradient accent borders and overlays
  • Animated floating gradient orbs for visual depth
  • Full dark mode support via dark: Tailwind variants
  • Staggered entrance animations using animationDelay

Integration with Editor

The DetailsForm component integrates with the project's rich text editor through:

const editor = useEditor();
useEditorFieldSync(editor, formData, 'introduction', setFormData, {
fieldName: 'introduction',
enableLogging: true,
});

This keeps the introduction field in sync between the form state and the editor content, supporting real-time preview and bidirectional updates.