/** * Discount Form Page * Add/Edit Discount Coupon form */ import React, { useState, useEffect, useMemo } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { ArrowLeft, Save, Loader2, Info, Plus, Trash2, Users, } from "lucide-react"; import { __ } from "../lib/i18n"; import { usePermissions } from "../hooks/usePermissions"; import { useToast } from "../components/ui/toast"; import { apiClient } from "../lib/api-client"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Select } from "../components/ui/select"; import { PageHeader } from "../components/common/PageHeader"; import { Card, CardContent, CardHeader, CardTitle, } from "../components/ui/card"; import { ConditionalRender } from "../components/ui/conditional-render"; import { HelpText } from "../components/ui/help-text"; import { DatePicker } from "../components/ui/date-picker"; import { ApplicableTripSelector } from "../components/shared/ApplicableTripSelector"; import { PremiumUpgradeDialog } from "../components/modules/PremiumUpgradeDialog"; interface DiscountFormData { code: string; description: string; type: "percentage" | "fixed"; amount: string; max_discount_amount: string; // Maximum discount amount for percentage discounts usage_limit: string; // Total usage limit usage_limit_per_customer: string; // Usage limit per customer valid_from: string; // Start date expiry_date: string; // End date status: "draft" | "publish" | "trash"; applicable_to: "all" | "specific_trips"; trip_ids: number[]; min_amount: string; // Minimum booking amount first_time_customer_only: boolean; // Only for first-time customers is_group_discount: boolean; discount_mode: "promo" | "group" | "both"; // Type of discount: promo code only, group only, or both group_discount_mode: "total" | "category_based"; group_discount_ranges: Array<{ id: string; min_group_size: string; max_group_size: string; // empty = unlimited discount_type: "percentage" | "fixed"; discount_amount: string; categories: Array<{ id: string; traveler_category_id: string; discount_type: "percentage" | "fixed"; discount_amount: string; }>; }>; category_discounts: Array<{ traveler_category_id: string; traveler_category_label: string; isSelecting?: boolean; ranges: Array<{ id: string; min_group_size: string; max_group_size: string; // empty = unlimited discount_type: "percentage" | "fixed"; discount_amount: string; }>; }>; } const DiscountForm: React.FC = () => { const queryClient = useQueryClient(); const { can, permissions, isPro } = usePermissions(); const { showToast } = useToast(); const [showPremiumModal, setShowPremiumModal] = useState(false); // Get initial discount_mode from URL at initialization time const getInitialState = (): DiscountFormData => { const params = new URLSearchParams(window.location.search); const urlDiscountMode = (params.get("discount_mode") || "both") as | "promo" | "group" | "both"; const isGroupOnly = urlDiscountMode === "group"; return { code: "", description: "", type: "percentage", amount: isGroupOnly ? "0" : "", max_discount_amount: "", usage_limit: "0", usage_limit_per_customer: "0", valid_from: "", expiry_date: "", status: "draft", applicable_to: "all", trip_ids: [], min_amount: "", first_time_customer_only: false, is_group_discount: isGroupOnly, discount_mode: urlDiscountMode, group_discount_mode: "total", group_discount_ranges: [ { id: String(Date.now()), min_group_size: "", max_group_size: "", discount_type: "percentage", discount_amount: "", categories: [], }, ], category_discounts: [], }; }; const [formData, setFormData] = useState(getInitialState); const [errors, setErrors] = useState>({}); const [isSubmitting, setIsSubmitting] = useState(false); const [showCategoryDropdown, setShowCategoryDropdown] = useState(false); // Get action and id from URL const action = useMemo(() => { const params = new URLSearchParams(window.location.search); return params.get("action") || "create"; }, []); const discountId = useMemo(() => { const params = new URLSearchParams(window.location.search); return params.get("id") ? parseInt(params.get("id") || "0") : null; }, []); const duplicateId = useMemo(() => { const params = new URLSearchParams(window.location.search); return params.get("duplicate") ? parseInt(params.get("duplicate") || "0") : null; }, []); // Get discount_mode from URL (promo, group, or both) - compute once at module level const discountMode = useMemo(() => { const params = new URLSearchParams(window.location.search); return (params.get("discount_mode") || "both") as | "promo" | "group" | "both"; }, []); const isEditMode = action === "edit" && discountId !== null; const isDuplicateMode = action === "create" && duplicateId !== null; // For new discounts, use URL param; for edit mode, we'll use formData.discount_mode after it's loaded const isGroupOnlyMode = discountMode === "group"; const isPromoOnlyMode = discountMode === "promo"; // Computed mode that works for both create and edit - use formData when available const effectiveDiscountMode = isEditMode ? formData.discount_mode : discountMode; const isEffectiveGroupOnly = effectiveDiscountMode === "group"; const isEffectivePromoOnly = effectiveDiscountMode === "promo"; // Fetch discount data if editing or duplicating const { data: discountData, isLoading: isLoadingDiscount } = useQuery({ queryKey: ["discount", discountId || duplicateId], queryFn: async () => { const id = discountId || duplicateId; if (!id) return null; try { const response = await apiClient.get(`/discounts/${id}`); return response; } catch (error: any) { showToast( error?.message || __("Failed to load discount", "yatra"), "error", ); throw error; } }, enabled: (isEditMode || isDuplicateMode) && can("yatra_manage_discounts"), }); // Load discount data into form when editing or duplicating useEffect(() => { if (discountData && (isEditMode || isDuplicateMode)) { setFormData({ code: isDuplicateMode ? `${discountData.code}_COPY` : discountData.code || "", description: discountData.description || "", type: (discountData.type || "percentage") as "percentage" | "fixed", amount: discountData.amount?.toString() || "", max_discount_amount: discountData.max_discount_amount?.toString() || "", usage_limit: discountData.usage_limit?.toString() || "0", usage_limit_per_customer: discountData.usage_limit_per_customer?.toString() || "0", valid_from: discountData.valid_from || "", expiry_date: discountData.expiry_date || "", status: (isDuplicateMode ? "draft" : discountData.status || "draft") as | "draft" | "publish" | "trash", applicable_to: (discountData.applicable_to || "all") as | "all" | "specific_trips", trip_ids: discountData.trip_ids || [], min_amount: discountData.min_amount?.toString() || "", first_time_customer_only: discountData.first_time_customer_only || false, is_group_discount: discountData.is_group_discount || false, discount_mode: (discountData.discount_mode || "both") as | "promo" | "group" | "both", group_discount_mode: (discountData.group_discount_mode || "total") as | "total" | "category_based", group_discount_ranges: Array.isArray(discountData.group_discount_ranges) && discountData.group_discount_ranges.length ? discountData.group_discount_ranges : [ { id: String(Date.now()), min_group_size: discountData.min_group_size?.toString() || "", max_group_size: discountData.max_group_size?.toString() || "", discount_type: (discountData.group_discount_type || "percentage") as "percentage" | "fixed", discount_amount: discountData.group_discount_amount?.toString() || "", categories: [], }, ], category_discounts: Array.isArray(discountData.category_discounts) && discountData.category_discounts?.length ? discountData.category_discounts : [], }); } }, [discountData, isEditMode, isDuplicateMode]); // Set initial form state based on discount mode (only for new discounts) useEffect(() => { if (!isEditMode && !isDuplicateMode && !discountData) { if (isGroupOnlyMode) { // Group discount only - auto-enable group discount, set amount to 0 setFormData((prev) => ({ ...prev, is_group_discount: true, discount_mode: "group", amount: "0", type: "percentage", })); } else if (isPromoOnlyMode) { // Promo code only - disable group discount setFormData((prev) => ({ ...prev, is_group_discount: false, discount_mode: "promo", })); } else { // Both - default setFormData((prev) => ({ ...prev, discount_mode: "both", })); } } }, [ isGroupOnlyMode, isPromoOnlyMode, isEditMode, isDuplicateMode, discountData, ]); const handleFieldChange = (field: keyof DiscountFormData, value: any) => { setFormData((prev) => ({ ...prev, [field]: value })); if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: "" })); } // Clear ranges when switching discount modes if (field === "group_discount_mode") { if (value === "category_based") { // Switch to category-based - clear regular ranges and start with empty categories setFormData((prev) => ({ ...prev, group_discount_mode: "category_based", group_discount_ranges: [], category_discounts: [], // Ensure empty categories array })); } else { // Switch to total - clear category discounts and start with one empty range setFormData((prev) => ({ ...prev, group_discount_mode: "total", group_discount_ranges: [ { id: String(Date.now()), min_group_size: "", max_group_size: "", discount_type: "percentage", discount_amount: "", categories: [], }, ], category_discounts: [], // Ensure empty categories array })); } } }; const handleGroupDiscountToggle = (checked: boolean) => { handleFieldChange("is_group_discount", checked); }; const travelerCategoriesQuery = useQuery({ queryKey: ["traveler-categories"], queryFn: async () => { try { const response = await apiClient.get("/traveler-categories", { params: { per_page: 100, status: "publish", }, }); const categories = response?.data?.data || response?.data || response || []; return Array.isArray(categories) ? categories : []; } catch (error: any) { console.error("Failed to load traveler categories:", error); return []; } }, staleTime: 5 * 60 * 1000, }); const travelerCategoryOptions = useMemo(() => { const items = travelerCategoriesQuery.data || []; return items .filter( (c: any) => !!c && (typeof c.id === "number" || typeof c.id === "string") && (c.status === "publish" || c.status === "active"), ) .map((c: any) => ({ value: String(c.id), label: c.label || c.slug || String(c.id), description: c.description || "", age_min: c.age_min, age_max: c.age_max, pricing_mode: c.pricing_mode || "per_person", })); }, [travelerCategoriesQuery.data]); const addRange = () => { setFormData((prev) => ({ ...prev, group_discount_ranges: [ ...prev.group_discount_ranges, { id: String(Date.now() + Math.random()), min_group_size: "", max_group_size: "", discount_type: "percentage", discount_amount: "", categories: [], }, ], })); }; const removeRange = (rangeId: string) => { setFormData((prev) => ({ ...prev, group_discount_ranges: prev.group_discount_ranges.filter( (r) => r.id !== rangeId, ), })); }; const updateRange = ( rangeId: string, patch: Partial, ) => { setFormData((prev) => ({ ...prev, group_discount_ranges: prev.group_discount_ranges.map((r) => r.id === rangeId ? { ...r, ...patch } : r, ), })); }; // Category-based discount functions const addTravelerCategory = () => { // Toggle category selection dropdown setShowCategoryDropdown((prev) => !prev); }; const selectCategory = (categoryId: string, categoryLabel: string) => { // Add selected category and close dropdown setFormData((prev) => ({ ...prev, category_discounts: [ ...prev.category_discounts, { traveler_category_id: categoryId, traveler_category_label: categoryLabel, ranges: [], isSelecting: false, }, ], })); setShowCategoryDropdown(false); }; const removeTravelerCategory = (categoryIndex: number) => { setFormData((prev) => ({ ...prev, category_discounts: prev.category_discounts.filter( (_, idx) => idx !== categoryIndex, ), })); }; const updateTravelerCategory = ( categoryIndex: number, patch: Partial, ) => { setFormData((prev) => ({ ...prev, category_discounts: prev.category_discounts.map((cat, idx) => idx === categoryIndex ? { ...cat, ...patch } : cat, ), })); }; const addRangeToCategory = (categoryIndex: number) => { setFormData((prev) => ({ ...prev, category_discounts: prev.category_discounts.map((cat, idx) => { if (idx !== categoryIndex) return cat; return { ...cat, ranges: [ ...cat.ranges, { id: String(Date.now() + Math.random()), min_group_size: "", max_group_size: "", discount_type: "percentage", discount_amount: "", }, ], }; }), })); }; const removeRangeFromCategory = (categoryIndex: number, rangeId: string) => { setFormData((prev) => ({ ...prev, category_discounts: prev.category_discounts.map((cat, idx) => { if (idx !== categoryIndex) return cat; return { ...cat, ranges: cat.ranges.filter((r) => r.id !== rangeId) }; }), })); }; const updateRangeInCategory = ( categoryIndex: number, rangeId: string, patch: Partial< DiscountFormData["category_discounts"][number]["ranges"][number] >, ) => { setFormData((prev) => ({ ...prev, category_discounts: prev.category_discounts.map((cat, idx) => { if (idx !== categoryIndex) return cat; const ranges = cat.ranges.map((r) => r.id === rangeId ? { ...r, ...patch } : r, ); return { ...cat, ranges }; }), })); }; const validateForm = (): boolean => { const newErrors: Record = {}; // Internal name/code is always required if (!formData.code.trim()) { newErrors.code = isEffectiveGroupOnly ? __("Internal name is required", "yatra") : __("Coupon code is required", "yatra"); } else if (!/^[A-Z0-9_-]+$/.test(formData.code)) { newErrors.code = __( "Only uppercase letters, numbers, hyphens, and underscores are allowed", "yatra", ); } // Discount amount validation - skip for group-only mode if (!isEffectiveGroupOnly) { if (!formData.amount.trim()) { newErrors.amount = __("Discount amount is required", "yatra"); } else { const amount = parseFloat(formData.amount); if (isNaN(amount) || amount < 0) { newErrors.amount = __( "Discount amount must be a positive number", "yatra", ); } else if (formData.type === "percentage" && amount > 100) { newErrors.amount = __( "Percentage discount cannot exceed 100%", "yatra", ); } } // Maximum discount amount validation (for percentage discounts) if ( formData.type === "percentage" && formData.max_discount_amount && formData.max_discount_amount.trim() ) { const maxAmount = parseFloat(formData.max_discount_amount); if (isNaN(maxAmount) || maxAmount <= 0) { newErrors.max_discount_amount = __( "Maximum discount amount must be a positive number", "yatra", ); } } } if (formData.usage_limit && formData.usage_limit !== "0") { const limit = parseInt(formData.usage_limit); if (isNaN(limit) || limit < 0) { newErrors.usage_limit = __( "Usage limit must be a positive number or 0 for unlimited", "yatra", ); } } if ( formData.usage_limit_per_customer && formData.usage_limit_per_customer !== "0" ) { const limit = parseInt(formData.usage_limit_per_customer); if (isNaN(limit) || limit < 0) { newErrors.usage_limit_per_customer = __( "Per-customer usage limit must be a positive number or 0 for unlimited", "yatra", ); } } // Date validation const today = new Date(); today.setHours(0, 0, 0, 0); // Valid from date can be in the past (backdating allowed) // No validation needed for valid_from date if (formData.expiry_date) { const expiryDate = new Date(formData.expiry_date); if (expiryDate < today) { newErrors.expiry_date = __( "Expiry date cannot be in the past", "yatra", ); } } // Check if expiry date is after valid from date if (formData.valid_from && formData.expiry_date) { const validFrom = new Date(formData.valid_from); const expiryDate = new Date(formData.expiry_date); if (expiryDate < validFrom) { newErrors.expiry_date = __( "Expiry date must be after the valid from date", "yatra", ); } } if (formData.min_amount && formData.min_amount.trim()) { const minAmount = parseFloat(formData.min_amount); if (isNaN(minAmount) || minAmount < 0) { newErrors.min_amount = __( "Minimum amount must be a positive number", "yatra", ); } } // Group discount validation (multiple ranges) - only for 'total' mode if ( formData.is_group_discount && formData.group_discount_mode === "total" ) { if ( !formData.group_discount_ranges || formData.group_discount_ranges.length === 0 ) { newErrors.group_discount_ranges = __( "At least one group discount range is required", "yatra", ); } else { formData.group_discount_ranges.forEach((range, idx) => { const prefix = `group_discount_ranges_${idx}`; const min = parseInt(range.min_group_size); const max = range.max_group_size ? parseInt(range.max_group_size) : null; if (Number.isNaN(min) || min < 1) { newErrors[`${prefix}_min`] = __( "Minimum group size must be at least 1", "yatra", ); } if (max !== null) { if (Number.isNaN(max) || max < 1) { newErrors[`${prefix}_max`] = __( "Maximum group size must be a positive number", "yatra", ); } else if (!Number.isNaN(min) && max <= min) { newErrors[`${prefix}_max`] = __( "Maximum group size must be greater than minimum group size", "yatra", ); } } const amount = range.discount_amount ? parseFloat(range.discount_amount) : NaN; if (!range.discount_type) { newErrors[`${prefix}_type`] = __( "Discount type is required", "yatra", ); } if (Number.isNaN(amount) || amount <= 0) { newErrors[`${prefix}_amount`] = __( "Discount amount must be a positive number", "yatra", ); } else if (range.discount_type === "percentage" && amount > 100) { newErrors[`${prefix}_amount`] = __( "Percentage discount cannot exceed 100%", "yatra", ); } }); } } // Category-based discounts validation if ( formData.is_group_discount && formData.group_discount_mode === "category_based" ) { if ( !formData.category_discounts || formData.category_discounts.length === 0 ) { newErrors.category_discounts = __( "Add at least one traveler category", "yatra", ); } else { formData.category_discounts.forEach((cat, catIdx) => { const catPrefix = `category_discounts_${catIdx}`; if (!cat.traveler_category_id) { newErrors[`${catPrefix}_id`] = __( "Traveler category is required", "yatra", ); } if (!cat.ranges || cat.ranges.length === 0) { newErrors[`${catPrefix}_ranges`] = __( "Add at least one discount range", "yatra", ); } else { cat.ranges.forEach((range, rIdx) => { const rangePrefix = `${catPrefix}_ranges_${rIdx}`; const min = parseInt(range.min_group_size); const max = range.max_group_size ? parseInt(range.max_group_size) : null; if (Number.isNaN(min) || min < 1) { newErrors[`${rangePrefix}_min`] = __( "Minimum group size must be at least 1", "yatra", ); } if (max !== null) { if (Number.isNaN(max) || max < 1) { newErrors[`${rangePrefix}_max`] = __( "Maximum group size must be a positive number", "yatra", ); } else if (!Number.isNaN(min) && max <= min) { newErrors[`${rangePrefix}_max`] = __( "Maximum group size must be greater than minimum group size", "yatra", ); } } const amount = range.discount_amount ? parseFloat(range.discount_amount) : NaN; if (!range.discount_type) { newErrors[`${rangePrefix}_type`] = __( "Discount type is required", "yatra", ); } if (Number.isNaN(amount) || amount <= 0) { newErrors[`${rangePrefix}_amount`] = __( "Discount amount must be a positive number", "yatra", ); } else if (range.discount_type === "percentage" && amount > 100) { newErrors[`${rangePrefix}_amount`] = __( "Percentage discount cannot exceed 100%", "yatra", ); } }); } }); } } setErrors(newErrors); // Scroll to first error field if (Object.keys(newErrors).length > 0) { const firstErrorKey = Object.keys(newErrors)[0]; // Try to find the element by name or id const errorElement = document.querySelector( `[name="${firstErrorKey}"], #${firstErrorKey}, [data-field="${firstErrorKey}"]`, ); if (errorElement) { errorElement.scrollIntoView({ behavior: "smooth", block: "center" }); // Focus the element if it's an input if ( errorElement instanceof HTMLInputElement || errorElement instanceof HTMLSelectElement || errorElement instanceof HTMLTextAreaElement ) { setTimeout(() => errorElement.focus(), 300); } } else { // Fallback: scroll to the first error message const errorMessage = document.querySelector( ".text-red-500, .text-red-600", ); if (errorMessage) { errorMessage.scrollIntoView({ behavior: "smooth", block: "center" }); } } } return Object.keys(newErrors).length === 0; }; // Create/Update mutation const saveMutation = useMutation({ mutationFn: async (data: DiscountFormData) => { const payload: any = { code: data.code.trim().toUpperCase(), description: data.description.trim(), type: data.type, amount: parseFloat(data.amount) || 0, max_discount_amount: data.max_discount_amount ? parseFloat(data.max_discount_amount) : null, usage_limit: parseInt(data.usage_limit) || 0, usage_limit_per_customer: parseInt(data.usage_limit_per_customer) || 0, valid_from: data.valid_from || null, expiry_date: data.expiry_date || null, status: data.status, applicable_to: data.applicable_to, trip_ids: data.applicable_to === "specific_trips" ? data.trip_ids : [], min_amount: data.min_amount ? parseFloat(data.min_amount) : null, first_time_customer_only: data.first_time_customer_only, is_group_discount: data.is_group_discount, discount_mode: data.discount_mode, group_discount_mode: data.is_group_discount ? data.group_discount_mode : null, group_discount_ranges: data.is_group_discount ? data.group_discount_ranges : [], category_discounts: data.is_group_discount && data.group_discount_mode === "category_based" ? data.category_discounts.map((cat) => ({ traveler_category_id: cat.traveler_category_id, traveler_category_label: cat.traveler_category_label, ranges: cat.ranges.map((range) => ({ id: range.id, min_group_size: range.min_group_size, max_group_size: range.max_group_size, discount_type: range.discount_type, discount_amount: range.discount_amount, })), })) : [], }; if (isEditMode && discountId) { return await apiClient.put(`/discounts/${discountId}`, payload); } else { return await apiClient.post("/discounts", payload); } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["discounts"] }); queryClient.invalidateQueries({ queryKey: ["discount", discountId || duplicateId], }); showToast( isEditMode ? __("Discount updated successfully", "yatra") : __("Discount created successfully", "yatra"), "success", ); // Only redirect to listing page on create, not on update if (!isEditMode) { setTimeout(() => { window.location.href = `${window.yatraAdmin?.siteUrl || ""}/wp-admin/admin.php?page=yatra&subpage=discounts`; }, 1000); } setIsSubmitting(false); }, onError: (error: any) => { const errorMessage = error?.message || __("An error occurred while saving the discount", "yatra"); showToast(errorMessage, "error"); setIsSubmitting(false); }, }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validateForm()) { return; } setIsSubmitting(true); try { await saveMutation.mutateAsync(formData); } catch (error) { // Error handled in mutation } finally { setIsSubmitting(false); } }; const handleBack = () => { window.location.href = `${window.yatraAdmin?.siteUrl || ""}/wp-admin/admin.php?page=yatra&subpage=discounts`; }; if (isLoadingDiscount) { return (
{/* Skeleton Header */}
{/* Skeleton Info Card */}
{/* Main Form Skeleton */}
{/* Basic Information Card Skeleton */}
{/* Coupon Code */}
{/* Description */}
{/* Type and Amount */}
{/* Usage Limits Card Skeleton */}
{/* Dates */}
{/* Group Discount Card Skeleton */}
{/* Sidebar Skeleton */}
{/* Status Card */}
{/* Submit Button */}
); } return (
{__("Back", "yatra")} } /> {/* Discount Information Card */}

{__("How Discounts Work", "yatra")}

  • {__("Regular Discount", "yatra")}:{" "} {__( "Applied first to the booking total (trip price × travelers)", "yatra", )}
  • {__("Group Discount", "yatra")}:{" "} {__( "Additional discount applied AFTER regular discount when group size requirement is met", "yatra", )}
  • {__("Maximum Cap", "yatra")}:{" "} {__( "For percentage discounts, you can set a maximum dollar amount to cap the discount", "yatra", )}
  • {__("Example", "yatra")}:{" "} {__( "$1000 booking, 15% regular discount ($150 off), then 10% group discount on $850 = $85 more off. Final: $765", "yatra", )}
{/* Main Form Fields */}
{/* Discount Mode Indicator - Show on both Create and Edit */} {(() => { // Determine mode: use URL param for new discounts, formData for existing const currentMode = isEditMode ? formData.discount_mode : discountMode; const isGroupOnly = currentMode === "group"; const isPromoOnly = currentMode === "promo"; return (
{isGroupOnly ? ( ) : isPromoOnly ? ( ) : ( )} {isGroupOnly ? isEditMode ? __( "Group Discount (Auto-applies, no code needed)", "yatra", ) : __( "Creating Group Discount (Auto-applies, no code needed)", "yatra", ) : isPromoOnly ? isEditMode ? __("Promo Code Discount", "yatra") : __("Creating Promo Code Discount", "yatra") : isEditMode ? __("Promo Code + Group Discount", "yatra") : __( "Creating Promo Code + Group Discount", "yatra", )}
); })()} {/* Basic Information */} {isEffectiveGroupOnly ? __("Group Discount Information", "yatra") : __("Basic Information", "yatra")} {/* Coupon Code - Only show for promo modes */} {!isEffectiveGroupOnly && (
handleFieldChange( "code", e.target.value.toUpperCase(), ) } placeholder={__("e.g., SUMMER2024", "yatra")} className={errors.code ? "border-red-500" : ""} required /> {errors.code && (

{errors.code}

)}
)} {/* Internal Name for Group Discount Only */} {isEffectiveGroupOnly && (
handleFieldChange( "code", e.target.value.toUpperCase().replace(/\s+/g, "_"), ) } placeholder={__("e.g., GROUP_DISCOUNT_2024", "yatra")} className={errors.code ? "border-red-500" : ""} required /> {errors.code && (

{errors.code}

)}
)} {/* Description */}