| @@ -1,7 +1,8 @@ | ||
| 1 | 1 | import React, { useState, useEffect } from "react"; |
| 2 | 2 | import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; |
| 3 | 3 | import { apiClient } from "../lib/api-client"; |
| 4 | +import { toDateValue } from "../lib/dateFormat"; | |
| 4 | 5 | import { __ } from "../lib/i18n"; |
| 5 | 6 | import { useToast } from "../components/ui/toast"; |
| 6 | 7 | import { |
| 7 | 8 | Key, |
| @@ -12,8 +13,9 @@ | ||
| 12 | 13 | EyeOff, |
| 13 | 14 | RefreshCw, |
| 14 | 15 | Bug, |
| 15 | 16 | ChevronDown, |
| 17 | + Crown, | |
| 16 | 18 | } from "lucide-react"; |
| 17 | 19 | |
| 18 | 20 | interface LicenseInfo { |
| 19 | 21 | key: string; |
| @@ -25,9 +27,21 @@ | ||
| 25 | 27 | customer_email?: string; |
| 26 | 28 | license_limit?: number; |
| 27 | 29 | site_count?: number; |
| 28 | 30 | activations_left?: number; |
| 31 | + price_id?: string | number; | |
| 29 | 32 | }; |
| 33 | + plan?: { | |
| 34 | + name: string; | |
| 35 | + tier: string; | |
| 36 | + is_agency: boolean; | |
| 37 | + is_growth?: boolean; | |
| 38 | + is_personal?: boolean; | |
| 39 | + is_lifetime?: boolean; | |
| 40 | + price_id: number | string | null; | |
| 41 | + matched_by?: "price_id" | "label" | "fallback" | "inactive" | ""; | |
| 42 | + detected_label?: string; | |
| 43 | + }; | |
| 30 | 44 | } |
| 31 | 45 | |
| 32 | 46 | interface LicenseData { |
| 33 | 47 | is_pro: boolean; |
| @@ -568,8 +582,53 @@ | ||
| 568 | 582 | </div> |
| 569 | 583 | |
| 570 | 584 | {/* License Details */} |
| 571 | 585 | <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| 586 | + {licenseInfo.plan?.name && ( | |
| 587 | + <div className="bg-gray-50 dark:bg-gray-900/50 rounded-lg p-4"> | |
| 588 | + <div className="text-sm text-gray-600 dark:text-gray-400 mb-1"> | |
| 589 | + {__("Plan", "yatra")} | |
| 590 | + </div> | |
| 591 | + <div className="flex flex-wrap items-center gap-2"> | |
| 592 | + <span className="font-medium text-gray-900 dark:text-white"> | |
| 593 | + {licenseInfo.plan.name} | |
| 594 | + </span> | |
| 595 | + {licenseInfo.plan.is_agency && ( | |
| 596 | + <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-semibold bg-gradient-to-r from-purple-600 to-indigo-500 text-white"> | |
| 597 | + <Crown className="w-3 h-3" /> | |
| 598 | + {__("Scale", "yatra")} | |
| 599 | + </span> | |
| 600 | + )} | |
| 601 | + {licenseInfo.plan.is_growth && ( | |
| 602 | + <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-semibold bg-blue-600 text-white"> | |
| 603 | + {__("Growth", "yatra")} | |
| 604 | + </span> | |
| 605 | + )} | |
| 606 | + {licenseInfo.plan.is_lifetime && ( | |
| 607 | + <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-semibold bg-gradient-to-r from-amber-500 to-orange-500 text-white"> | |
| 608 | + {__("Lifetime", "yatra")} | |
| 609 | + </span> | |
| 610 | + )} | |
| 611 | + </div> | |
| 612 | + {licenseInfo.plan.matched_by === "label" && ( | |
| 613 | + <p className="mt-2 text-[11px] text-gray-500 dark:text-gray-400"> | |
| 614 | + {__( | |
| 615 | + "Detected from plan label (the original price ID was re-numbered on the store).", | |
| 616 | + "yatra", | |
| 617 | + )} | |
| 618 | + </p> | |
| 619 | + )} | |
| 620 | + {licenseInfo.plan.matched_by === "fallback" && ( | |
| 621 | + <p className="mt-2 text-[11px] text-amber-600 dark:text-amber-400"> | |
| 622 | + {__( | |
| 623 | + "Could not match price ID or label — defaulted to Starter. Contact support if your subscription is Growth or Scale.", | |
| 624 | + "yatra", | |
| 625 | + )} | |
| 626 | + </p> | |
| 627 | + )} | |
| 628 | + </div> | |
| 629 | + )} | |
| 630 | + | |
| 572 | 631 | {licenseInfo.server_response?.customer_name && ( |
| 573 | 632 | <div className="bg-gray-50 dark:bg-gray-900/50 rounded-lg p-4"> |
| 574 | 633 | <div className="text-sm text-gray-600 dark:text-gray-400 mb-1"> |
| 575 | 634 | {__("Licensed To", "yatra")} |
| @@ -599,9 +658,11 @@ | ||
| 599 | 658 | </div> |
| 600 | 659 | <div className="font-medium text-gray-900 dark:text-white"> |
| 601 | 660 | {(() => { |
| 602 | 661 | try { |
| 603 | - const date = new Date( | |
| 662 | + // toDateValue: a date-only "expires" must not roll back a | |
| 663 | + // day in behind-UTC zones (datetime values pass through). | |
| 664 | + const date = toDateValue( | |
| 604 | 665 | licenseInfo.server_response.expires, |
| 605 | 666 | ); |
| 606 | 667 | return isNaN(date.getTime()) |
| 607 | 668 | ? licenseInfo.server_response.expires |