import React, { useState, useEffect } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { apiClient } from "../lib/api-client"; import { toDateValue } from "../lib/dateFormat"; import { __ } from "../lib/i18n"; import { useToast } from "../components/ui/toast"; import { Key, Check, XCircle, ExternalLink, Eye, EyeOff, RefreshCw, Bug, ChevronDown, Crown, } from "lucide-react"; interface LicenseInfo { key: string; status: string; last_checked: number; server_response: { expires?: string; customer_name?: string; customer_email?: string; license_limit?: number; site_count?: number; activations_left?: number; price_id?: string | number; }; plan?: { name: string; tier: string; is_agency: boolean; is_growth?: boolean; is_personal?: boolean; is_lifetime?: boolean; price_id: number | string | null; matched_by?: "price_id" | "label" | "fallback" | "inactive" | ""; detected_label?: string; }; } interface LicenseData { is_pro: boolean; license_info?: LicenseInfo; upgrade_url: string; } const License: React.FC = () => { const [licenseKey, setLicenseKey] = useState(""); const [showKey, setShowKey] = useState(false); const [debugMode, setDebugMode] = useState(false); const [debugData, setDebugData] = useState(null); const [showDeactivateConfirm, setShowDeactivateConfirm] = useState(false); const queryClient = useQueryClient(); const { showToast } = useToast(); // Check if Pro is active from global config const yatraAdmin = (window as any)?.yatraAdmin; const isPro = yatraAdmin?.isPro || false; // Fetch license data const { data: licenseData, isLoading } = useQuery({ queryKey: ["license"], queryFn: async () => { const response = await apiClient.get("/license"); return response; }, }); // Set license key from data useEffect(() => { if (licenseData?.license_info?.key) { setLicenseKey(licenseData.license_info.key); } else { } }, [licenseData]); // Activate license mutation const activateMutation = useMutation({ mutationFn: async (key: string) => { try { const response = await apiClient.post("/license/activate", { license_key: key, }); return response; } catch (error) { console.error("License activation error:", error); throw error; } }, onSuccess: (data) => { // Always store debug data (will be shown if debug mode is enabled) const debugInfo = { type: "activate", request: { license_key: licenseKey }, response: data, timestamp: new Date().toISOString(), }; setDebugData(debugInfo); if (data?.status === "valid") { showToast(data.notice || "License activated successfully!", "success"); // Refetch license data to get updated status queryClient.invalidateQueries({ queryKey: ["license"] }); // Dispatch event for real-time badge update window.dispatchEvent( new CustomEvent("yatra-license-status-updated", { detail: { status: "active" }, }), ); } else { showToast(data?.notice || "License activation failed.", "error"); } }, onError: (error: any) => { const errorMsg = error.response?.data?.message || "Failed to activate license. Please try again."; showToast(errorMsg, "error"); // Always store debug data (will be shown if debug mode is enabled) setDebugData({ type: "activate", request: { license_key: licenseKey }, response: error.response?.data, error: error.response?.data || error.message, timestamp: new Date().toISOString(), }); }, }); // Deactivate license mutation const deactivateMutation = useMutation({ mutationFn: async () => { const response = await apiClient.post("/license/deactivate"); return response.data; }, onSuccess: (data) => { // Always store debug data (will be shown if debug mode is enabled) setDebugData({ type: "deactivate", request: {}, response: data, timestamp: new Date().toISOString(), }); setLicenseKey(""); showToast(data?.notice || "License deactivated successfully!", "success"); // Refetch license data to get updated status queryClient.invalidateQueries({ queryKey: ["license"] }); // Dispatch event for real-time badge update window.dispatchEvent( new CustomEvent("yatra-license-status-updated", { detail: { status: "inactive" }, }), ); }, onError: (error: any) => { const errorMsg = error.response?.data?.message || "Failed to deactivate license. Please try again."; showToast(errorMsg, "error"); // Always store debug data (will be shown if debug mode is enabled) setDebugData({ type: "deactivate", request: {}, response: error.response?.data, error: error.response?.data || error.message, timestamp: new Date().toISOString(), }); }, }); // Save license mutation const saveMutation = useMutation({ mutationFn: async (key: string) => { const response = await apiClient.post("/license/save", { license_key: key, }); return response; }, onSuccess: (data) => { showToast(data?.notice || "License key saved successfully!", "success"); // Refetch license data to show the saved key queryClient.invalidateQueries({ queryKey: ["license"] }); }, onError: (error: any) => { const errorMsg = error.response?.data?.message || "Failed to save license key."; showToast(errorMsg, "error"); }, }); // Check license mutation const checkMutation = useMutation({ mutationFn: async () => { const response = await apiClient.post("/license/check"); return response; }, onSuccess: (data) => { // Always store debug data (will be shown if debug mode is enabled) setDebugData({ type: "check", request: {}, response: data, timestamp: new Date().toISOString(), }); showToast(data?.notice || "License status updated.", "success"); // Refetch license data to get updated status queryClient.invalidateQueries({ queryKey: ["license"] }); // Dispatch event for real-time badge update with actual status from response if (data?.license_info?.status) { window.dispatchEvent( new CustomEvent("yatra-license-status-updated", { detail: { status: data.license_info.status }, }), ); } }, onError: (error: any) => { const errorMsg = error.response?.data?.message || "Failed to check license status."; showToast(errorMsg, "error"); // Always store debug data (will be shown if debug mode is enabled) setDebugData({ type: "check", request: {}, response: error.response?.data, error: error.response?.data || error.message, timestamp: new Date().toISOString(), }); }, }); const handleActivate = (e: React.FormEvent) => { e.preventDefault(); if (!licenseKey.trim()) { showToast("Please enter a license key.", "error"); return; } activateMutation.mutate(licenseKey); }; const handleDeactivate = () => { setShowDeactivateConfirm(true); }; const confirmDeactivate = () => { setShowDeactivateConfirm(false); deactivateMutation.mutate(); }; const handleCheckStatus = () => { checkMutation.mutate(); }; const maskLicenseKey = (key: string) => { if (!key || key.length < 8) return key; return key.substring(0, 4) + "••••••••••••" + key.substring(key.length - 4); }; const getStatusBadge = (status: string) => { const statusConfig: Record< string, { bg: string; text: string; label: string } > = { active: { bg: "bg-green-100 dark:bg-green-900/20", text: "text-green-800 dark:text-green-400", label: "Active", }, valid: { bg: "bg-green-100 dark:bg-green-900/20", text: "text-green-800 dark:text-green-400", label: "Active", }, inactive: { bg: "bg-gray-100 dark:bg-gray-800", text: "text-gray-800 dark:text-gray-300", label: "Inactive", }, expired: { bg: "bg-red-100 dark:bg-red-900/20", text: "text-red-800 dark:text-red-400", label: "Expired", }, disabled: { bg: "bg-red-100 dark:bg-red-900/20", text: "text-red-800 dark:text-red-400", label: "Disabled", }, invalid: { bg: "bg-yellow-100 dark:bg-yellow-900/20", text: "text-yellow-800 dark:text-yellow-400", label: "Invalid", }, }; const config = statusConfig[status] || statusConfig.inactive; return ( {config.label} ); }; // Skeleton loading component const SkeletonLoader = () => (
{/* Header Skeleton */}
{/* Content Skeleton */}
); if (isLoading) { return ; } // Yatra Free - Show upgrade message if (!isPro) { return (
{/* Header */}

{__("License", "yatra")}

{/* Content */}

Yatra Free Version

You're using the free version of Yatra. No license is required for the free version.

Upgrade to Yatra Pro

Unlock premium features with Yatra Pro:

  • Dynamic Pricing & Revenue Management
  • {__("Advanced Booking Management", "yatra")}
  • {__("Premium Payment Gateways", "yatra")}
  • Priority Support & Updates
Upgrade to Pro
); } // Yatra Pro - Show license management const licenseInfo = licenseData?.license_info; const isActive = licenseInfo?.status === "active" || licenseInfo?.status === "valid"; return (
{/* Header */}

{__("License Management", "yatra")}

{licenseInfo?.status && getStatusBadge(licenseInfo.status)}
{/* Content */}
{/* License Activation Form */} {!isActive && (

Activate Your License

setLicenseKey(e.target.value)} placeholder="Enter your license key" className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent" disabled={activateMutation.isPending} />

Don't have a license key?{" "} Get it from your account

)} {/* Active License Information */} {isActive && licenseInfo && (
{/* License Key Display */}

{__("License Key", "yatra")}

{showKey ? licenseInfo.key : maskLicenseKey(licenseInfo.key)}
{/* License Details */}
{licenseInfo.plan?.name && (
{__("Plan", "yatra")}
{licenseInfo.plan.name} {licenseInfo.plan.is_agency && ( {__("Scale", "yatra")} )} {licenseInfo.plan.is_growth && ( {__("Growth", "yatra")} )} {licenseInfo.plan.is_lifetime && ( {__("Lifetime", "yatra")} )}
{licenseInfo.plan.matched_by === "label" && (

{__( "Detected from plan label (the original price ID was re-numbered on the store).", "yatra", )}

)} {licenseInfo.plan.matched_by === "fallback" && (

{__( "Could not match price ID or label — defaulted to Starter. Contact support if your subscription is Growth or Scale.", "yatra", )}

)}
)} {licenseInfo.server_response?.customer_name && (
{__("Licensed To", "yatra")}
{licenseInfo.server_response.customer_name}
)} {licenseInfo.server_response?.customer_email && (
{__("Email", "yatra")}
{licenseInfo.server_response.customer_email}
)} {licenseInfo.server_response?.expires && licenseInfo.server_response.expires !== "lifetime" && (
{__("Expires", "yatra")}
{(() => { try { // toDateValue: a date-only "expires" must not roll back a // day in behind-UTC zones (datetime values pass through). const date = toDateValue( licenseInfo.server_response.expires, ); return isNaN(date.getTime()) ? licenseInfo.server_response.expires : date.toLocaleDateString(undefined, { year: "numeric", month: "long", day: "numeric", }); } catch { return licenseInfo.server_response.expires; } })()}
)} {licenseInfo.server_response?.expires === "lifetime" && (
{__("Expires", "yatra")}
{__("Lifetime License", "yatra")}
)} {licenseInfo.server_response?.site_count !== undefined && (
{__("Active Sites", "yatra")}
{licenseInfo.server_response.site_count} /{" "} {licenseInfo.server_response.license_limit || "∞"}
)}
{/* Actions */}
)} {/* Help Section */}

Need Help?

If you're having trouble with your license, please contact our support team.

{/* Debug Panel */} {debugMode && (

Debug Information

{debugData && ( )}
{debugData ? (
{/* Operation Type */}
Operation: {debugData.type} {debugData.timestamp}
{/* Full Response Body */}
Full Response Body:
                      
                        {JSON.stringify(debugData.response, null, 2)}
                      
                    
{/* EDD API Request */} {debugData.response?.edd_api_request && (
EDD API Request (to store.mantrabrain.com):
                        
                          {JSON.stringify(
                            debugData.response.edd_api_request,
                            null,
                            2,
                          )}
                        
                      
)} {/* EDD API Response */} {debugData.response?.edd_api_response && (
EDD API Response (from store.mantrabrain.com):
                        
                          {JSON.stringify(
                            debugData.response.edd_api_response,
                            null,
                            2,
                          )}
                        
                      
)} {/* Error Data */} {debugData.error && (
Error Data:
                        
                          {JSON.stringify(debugData.error, null, 2)}
                        
                      
)}
) : (

Perform a license operation (activate, deactivate, or check status) to see debug information here.

)}
)}
{/* Deactivate Confirmation Dialog */} {showDeactivateConfirm && (

Deactivate License

Are you sure you want to deactivate this license? You will lose support and updates for Yatra Pro.

)}
); }; export default License;