/** * Shared Itinerary Entry Fields Component * Reusable form fields for itinerary entries - used by both ItineraryForm and ItinerarySection */ import React, { useState, useEffect } from "react"; import { Clock, X, Plus, Info } from "lucide-react"; import { __ } from "../../../lib/i18n"; import { ItineraryEntry, MediaItem } from "../types"; import { Input } from "../../ui/input"; import { Select } from "../../ui/select"; import { Button } from "../../ui/button"; import { Badge } from "../../ui/badge"; import { HelpText } from "../../ui/help-text"; import { Card, CardContent } from "../../ui/card"; import { TimePicker } from "../../ui/time-picker"; import { MediaUpload } from "../../ui/media-upload"; import { Modal } from "../../ui/modal"; import { getCurrencySymbol } from "../../../data/currencies"; import { LocationPicker } from "../LocationPicker"; interface ItineraryEntryFieldsProps { entry: Partial; errors?: Record; itemTypes: Array<{ id: number; name: string; icon?: string }>; items: Array<{ id: number; name: string; type_id: number }>; newIncludedItem?: string; newExcludedItem?: string; onFieldChange: (field: keyof ItineraryEntry, value: any) => void; onIncludedItemChange?: (value: string) => void; onExcludedItemChange?: (value: string) => void; onAddIncludedItem?: () => void; onAddExcludedItem?: () => void; onRemoveIncludedItem?: (index: number) => void; onRemoveExcludedItem?: (index: number) => void; calculateDuration?: ( startTime: string, endTime: string, timeType: string, ) => string; size?: "default" | "compact"; showCardWrapper?: boolean; onRefreshData?: () => void; // Callback to refresh item types and items after creation } export const ItineraryEntryFields: React.FC = ({ entry, errors = {}, itemTypes, items, newIncludedItem = "", newExcludedItem = "", onFieldChange, onIncludedItemChange, onExcludedItemChange, onAddIncludedItem, onAddExcludedItem, onRemoveIncludedItem, onRemoveExcludedItem, calculateDuration, size = "default", showCardWrapper = false, onRefreshData, }) => { // Local state for gallery (workaround for parent state issue) const [localGallery, setLocalGallery] = React.useState([]); // Local state for video_url (workaround for parent state issue) const [localVideoUrl, setLocalVideoUrl] = React.useState(""); // Sync local state with entry.gallery changes (including initial load) React.useEffect(() => { if (entry.gallery && Array.isArray(entry.gallery)) { setLocalGallery(entry.gallery); } else { setLocalGallery([]); } }, [entry.gallery]); // Sync local state with entry.video_url changes (including initial load) React.useEffect(() => { if (entry.video_url !== undefined && entry.video_url !== null) { setLocalVideoUrl(entry.video_url); } else { setLocalVideoUrl(""); } }, [entry.video_url]); // Modal states const [isItemTypeModalOpen, setIsItemTypeModalOpen] = useState(false); const [isItemModalOpen, setIsItemModalOpen] = useState(false); const isCompact = size === "compact"; const textSize = isCompact ? "text-xs" : "text-sm"; const labelSize = isCompact ? "text-xs" : "text-sm"; const autoDuration = calculateDuration ? calculateDuration( entry.start_time || "", entry.end_time || "", entry.time_type || "exact", ) : ""; // Global currency symbol (same logic as other admin pages) const globalCurrency = (window as any).yatraAdmin?.currency || "USD"; const currencySymbol = getCurrencySymbol(globalCurrency) || ""; const renderField = (content: React.ReactNode) => { if (showCardWrapper) { return ( {content} ); } return
{content}
; }; const content = ( <> {/* Basic Information */}
{!isCompact && (
{__("Basic Information", "yatra")}
)}
{errors.item_type_id && (

{errors.item_type_id}

)}
{errors.item_id && (

{errors.item_id}

)}
onFieldChange("title", e.target.value)} placeholder={__("e.g., Arrival in Kathmandu", "yatra")} className={`${errors.title ? "border-red-500" : ""} ${textSize}`} required /> {errors.title && (

{errors.title}

)}