| @@ -734,9 +734,10 @@ | ||
| 734 | 734 | | "currency" |
| 735 | 735 | | "integration" |
| 736 | 736 | | "permalink" |
| 737 | 737 | | "seo" |
| 738 | - | "advanced"; | |
| 738 | + | "advanced" | |
| 739 | + | "uninstall"; | |
| 739 | 740 | |
| 740 | 741 | // Form Builder Types |
| 741 | 742 | // Booking form builder types live in components/settings/booking-form-types.ts |
| 742 | 743 | // (shared with the per-trip Conditions popup). |
| @@ -972,8 +973,9 @@ | ||
| 972 | 973 | review_moderation: boolean; |
| 973 | 974 | min_rating: number; |
| 974 | 975 | allow_anonymous_reviews: boolean; |
| 975 | 976 | review_reminder_days: number; |
| 977 | + review_reminder_max_age_days: number; | |
| 976 | 978 | |
| 977 | 979 | // Tax Settings |
| 978 | 980 | enable_tax: boolean; |
| 979 | 981 | tax_name: string; |
| @@ -1073,8 +1075,9 @@ | ||
| 1073 | 1075 | |
| 1074 | 1076 | // Advanced Settings |
| 1075 | 1077 | debug_mode: boolean; |
| 1076 | 1078 | enable_logging: boolean; |
| 1079 | + delete_data_on_uninstall: boolean; | |
| 1077 | 1080 | cache_enabled: boolean; |
| 1078 | 1081 | api_key: string; |
| 1079 | 1082 | api_rate_limit: number; |
| 1080 | 1083 | session_timeout: number; |
| @@ -1087,8 +1090,10 @@ | ||
| 1087 | 1090 | seo_trip_meta_description: string; |
| 1088 | 1091 | seo_trip_meta_keywords: string; |
| 1089 | 1092 | seo_trip_meta_image: number; |
| 1090 | 1093 | enable_sitemap: boolean; |
| 1094 | + sitemap_types: string[]; | |
| 1095 | + sitemap_noindex_excluded: boolean; | |
| 1091 | 1096 | } |
| 1092 | 1097 | |
| 1093 | 1098 | // Form Builder Component |
| 1094 | 1099 | type BookingFormSubTab = |
| @@ -1572,8 +1577,9 @@ | ||
| 1572 | 1577 | "integration", |
| 1573 | 1578 | "permalink", |
| 1574 | 1579 | "seo", |
| 1575 | 1580 | "advanced", |
| 1581 | + "uninstall", | |
| 1576 | 1582 | ]; |
| 1577 | 1583 | if (typeof window !== "undefined") { |
| 1578 | 1584 | // Allow deep-linking a section via ?section=… (e.g. the Google Calendar |
| 1579 | 1585 | // dashboard's "Back to Settings → Integration" link). |
| @@ -1603,8 +1609,9 @@ | ||
| 1603 | 1609 | "integration", |
| 1604 | 1610 | "permalink", |
| 1605 | 1611 | "seo", |
| 1606 | 1612 | "advanced", |
| 1613 | + "uninstall", | |
| 1607 | 1614 | ].includes(saved) |
| 1608 | 1615 | ) { |
| 1609 | 1616 | return saved as SettingsSection; |
| 1610 | 1617 | } |
| @@ -1875,9 +1882,9 @@ | ||
| 1875 | 1882 | email_template_admin_scheduled_payment_failed: true, |
| 1876 | 1883 | email_template_enquiry_received: true, |
| 1877 | 1884 | email_template_enquiry_admin: true, |
| 1878 | 1885 | email_template_enquiry_response: true, |
| 1879 | - email_template_review_request: true, | |
| 1886 | + email_template_review_request: false, | |
| 1880 | 1887 | email_template_abandoned_booking_recovery_first: true, |
| 1881 | 1888 | email_template_abandoned_booking_recovery_second: true, |
| 1882 | 1889 | email_template_abandoned_booking_recovery_final: true, |
| 1883 | 1890 | smtp_enabled: false, |
| @@ -1945,9 +1952,10 @@ | ||
| 1945 | 1952 | auto_approve_reviews: false, |
| 1946 | 1953 | review_moderation: true, |
| 1947 | 1954 | min_rating: 1, |
| 1948 | 1955 | allow_anonymous_reviews: false, |
| 1949 | - review_reminder_days: 7, | |
| 1956 | + review_reminder_days: 3, | |
| 1957 | + review_reminder_max_age_days: 14, | |
| 1950 | 1958 | enable_tax: true, |
| 1951 | 1959 | tax_name: __("Tax", "yatra"), |
| 1952 | 1960 | tax_rate: 10, |
| 1953 | 1961 | tax_inclusive: false, |
| @@ -1988,8 +1996,9 @@ | ||
| 1988 | 1996 | terms_page_id: 0, |
| 1989 | 1997 | privacy_policy_page_id: 0, |
| 1990 | 1998 | debug_mode: false, |
| 1991 | 1999 | enable_logging: false, |
| 2000 | + delete_data_on_uninstall: false, | |
| 1992 | 2001 | cache_enabled: true, |
| 1993 | 2002 | api_key: "", |
| 1994 | 2003 | api_rate_limit: 100, |
| 1995 | 2004 | session_timeout: 3600, |
| @@ -2224,8 +2233,10 @@ | ||
| 2224 | 2233 | seo_trip_meta_description: "", |
| 2225 | 2234 | seo_trip_meta_keywords: "", |
| 2226 | 2235 | seo_trip_meta_image: 0, |
| 2227 | 2236 | enable_sitemap: true, |
| 2237 | + sitemap_types: ["archive", "trip", "destination", "activity", "category"], | |
| 2238 | + sitemap_noindex_excluded: false, | |
| 2228 | 2239 | }), |
| 2229 | 2240 | [], |
| 2230 | 2241 | ); |
| 2231 | 2242 | |
| @@ -3143,8 +3154,13 @@ | ||
| 3143 | 3154 | id: "advanced" as SettingsSection, |
| 3144 | 3155 | label: __("Advanced", "yatra"), |
| 3145 | 3156 | icon: Shield, |
| 3146 | 3157 | }, |
| 3158 | + { | |
| 3159 | + id: "uninstall" as SettingsSection, | |
| 3160 | + label: __("Uninstall", "yatra"), | |
| 3161 | + icon: Trash2, | |
| 3162 | + }, | |
| 3147 | 3163 | ]; |
| 3148 | 3164 | |
| 3149 | 3165 | // Helper component for section divider |
| 3150 | 3166 | const SectionDivider = ({ title }: { title: string }) => ( |
| @@ -5923,9 +5939,9 @@ | ||
| 5923 | 5939 | <FormField |
| 5924 | 5940 | id="review_reminder_days" |
| 5925 | 5941 | label={__("Review Reminder Days", "yatra")} |
| 5926 | 5942 | description={__( |
| 5927 | - "Send review reminder email this many days after trip completion", | |
| 5943 | + "Send the review request this many days after the trip ends. Set to 0 to never send one.", | |
| 5928 | 5944 | "yatra", |
| 5929 | 5945 | )} |
| 5930 | 5946 | > |
| 5931 | 5947 | <Input |
| @@ -5936,8 +5952,26 @@ | ||
| 5936 | 5952 | onChange={handleFieldChange} |
| 5937 | 5953 | min="0" |
| 5938 | 5954 | /> |
| 5939 | 5955 | </FormField> |
| 5956 | + | |
| 5957 | + <FormField | |
| 5958 | + id="review_reminder_max_age_days" | |
| 5959 | + label={__("Don't Ask About Trips Older Than", "yatra")} | |
| 5960 | + description={__( | |
| 5961 | + "Days. A trip that ended longer ago than this is never asked about — so marking a backlog of old bookings complete does not email those customers.", | |
| 5962 | + "yatra", | |
| 5963 | + )} | |
| 5964 | + > | |
| 5965 | + <Input | |
| 5966 | + id="review_reminder_max_age_days" | |
| 5967 | + type="number" | |
| 5968 | + value={formData.review_reminder_max_age_days} | |
| 5969 | + name="review_reminder_max_age_days" | |
| 5970 | + onChange={handleFieldChange} | |
| 5971 | + min="0" | |
| 5972 | + /> | |
| 5973 | + </FormField> | |
| 5940 | 5974 | </> |
| 5941 | 5975 | )} |
| 5942 | 5976 | </div> |
| 5943 | 5977 | </div> |
| @@ -8604,12 +8638,159 @@ | ||
| 8604 | 8638 | {__( |
| 8605 | 8639 | "Optional: submit this URL in Google Search Console or Bing Webmaster Tools. It is already listed in robots.txt, so search engines find it automatically.", |
| 8606 | 8640 | "yatra", |
| 8607 | 8641 | )} |
| 8642 | + | |
| 8643 | + {formData.enable_sitemap && ( | |
| 8644 | + <div className="p-3 border border-gray-200 dark:border-gray-700 rounded-md space-y-3"> | |
| 8645 | + <div> | |
| 8646 | + <Label className="font-medium"> | |
| 8647 | + {__("Content in the sitemap", "yatra")} | |
| 8648 | + </Label> | |
| 8649 | + <p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5"> | |
| 8650 | + {__( | |
| 8651 | + "Uncheck a type to keep it out of /yatra-sitemap.xml.", | |
| 8652 | + "yatra", | |
| 8653 | + )} | |
| 8654 | + </p> | |
| 8655 | + </div> | |
| 8656 | + <div className="grid grid-cols-2 sm:grid-cols-3 gap-2"> | |
| 8657 | + {[ | |
| 8658 | + { key: "trip", label: __("Trips", "yatra") }, | |
| 8659 | + { key: "destination", label: __("Destinations", "yatra") }, | |
| 8660 | + { key: "activity", label: __("Activities", "yatra") }, | |
| 8661 | + { key: "category", label: __("Trip categories", "yatra") }, | |
| 8662 | + { key: "archive", label: __("Listing pages", "yatra") }, | |
| 8663 | + ].map((t) => { | |
| 8664 | + const selected = formData.sitemap_types ?? []; | |
| 8665 | + const checked = selected.includes(t.key); | |
| 8666 | + return ( | |
| 8667 | + <label | |
| 8668 | + key={t.key} | |
| 8669 | + className="flex items-center gap-2 text-sm cursor-pointer" | |
| 8670 | + > | |
| 8671 | + <input | |
| 8672 | + type="checkbox" | |
| 8673 | + checked={checked} | |
| 8674 | + onChange={() => | |
| 8675 | + setFormData((prev) => { | |
| 8676 | + if (!prev) return prev; | |
| 8677 | + return { | |
| 8678 | + ...prev, | |
| 8679 | + sitemap_types: checked | |
| 8680 | + ? (prev.sitemap_types ?? []).filter( | |
| 8681 | + (x: string) => x !== t.key, | |
| 8682 | + ) | |
| 8683 | + : [...(prev.sitemap_types ?? []), t.key], | |
| 8684 | + }; | |
| 8685 | + }) | |
| 8686 | + } | |
| 8687 | + className="w-4 h-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" | |
| 8688 | + /> | |
| 8689 | + <span>{t.label}</span> | |
| 8690 | + </label> | |
| 8691 | + ); | |
| 8692 | + })} | |
| 8693 | + </div> | |
| 8694 | + | |
| 8695 | + <div className="flex items-start gap-2 pt-2 border-t border-gray-200 dark:border-gray-700"> | |
| 8696 | + <input | |
| 8697 | + type="checkbox" | |
| 8698 | + id="sitemap_noindex_excluded" | |
| 8699 | + name="sitemap_noindex_excluded" | |
| 8700 | + checked={formData.sitemap_noindex_excluded} | |
| 8701 | + onChange={(e) => | |
| 8702 | + setFormData((prev) => { | |
| 8703 | + if (!prev) return prev; | |
| 8704 | + return { | |
| 8705 | + ...prev, | |
| 8706 | + sitemap_noindex_excluded: e.target.checked, | |
| 8707 | + }; | |
| 8708 | + }) | |
| 8709 | + } | |
| 8710 | + className="mt-0.5 w-4 h-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" | |
| 8711 | + /> | |
| 8712 | + <div className="flex-1"> | |
| 8713 | + <Label | |
| 8714 | + htmlFor="sitemap_noindex_excluded" | |
| 8715 | + className="font-medium cursor-pointer" | |
| 8716 | + > | |
| 8717 | + {__("Also add noindex to excluded types", "yatra")} | |
| 8718 | + </Label> | |
| 8719 | + <p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5"> | |
| 8720 | + {__( | |
| 8721 | + "Leaving a type out of the sitemap does not stop Google indexing it. Turn this on to send noindex on those pages as well — they will drop out of search results, so only do this for content you do not want found.", | |
| 8722 | + "yatra", | |
| 8723 | + )} | |
| 8724 | + </p> | |
| 8725 | + </div> | |
| 8726 | + </div> | |
| 8727 | + </div> | |
| 8728 | + )} | |
| 8608 | 8729 | </p> |
| 8609 | 8730 | </div> |
| 8610 | 8731 | )} |
| 8611 | 8732 | </div> |
| 8733 | + </div> | |
| 8734 | + </div> | |
| 8735 | + ); | |
| 8736 | + | |
| 8737 | + case "uninstall": | |
| 8738 | + return ( | |
| 8739 | + <div className="space-y-6"> | |
| 8740 | + <div className="space-y-4"> | |
| 8741 | + <div className="rounded-md border border-red-200 bg-red-50 p-3 text-xs text-red-700 dark:border-red-900/50 dark:bg-red-950/30 dark:text-red-300"> | |
| 8742 | + {__( | |
| 8743 | + "This only applies when Yatra is deleted from the Plugins screen. Deactivating the plugin never removes anything.", | |
| 8744 | + "yatra", | |
| 8745 | + )} | |
| 8746 | + </div> | |
| 8747 | + | |
| 8748 | + <div className="flex items-start gap-2 p-3 bg-gray-50 dark:bg-gray-800 rounded-md"> | |
| 8749 | + <input | |
| 8750 | + type="checkbox" | |
| 8751 | + id="delete_data_on_uninstall" | |
| 8752 | + checked={formData.delete_data_on_uninstall} | |
| 8753 | + name="delete_data_on_uninstall" | |
| 8754 | + onChange={handleFieldChange} | |
| 8755 | + className="mt-1 w-4 h-4 rounded border-gray-300 text-red-600 focus:ring-red-500" | |
| 8756 | + /> | |
| 8757 | + <div className="flex-1"> | |
| 8758 | + <Label | |
| 8759 | + htmlFor="delete_data_on_uninstall" | |
| 8760 | + className="font-medium cursor-pointer" | |
| 8761 | + > | |
| 8762 | + {__("Delete all data on uninstall", "yatra")} | |
| 8763 | + </Label> | |
| 8764 | + <p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5"> | |
| 8765 | + {__( | |
| 8766 | + "Permanently remove every Yatra database table, setting and scheduled task when the plugin is deleted. Yatra Pro follows the same switch and removes its own data too.", | |
| 8767 | + "yatra", | |
| 8768 | + )} | |
| 8769 | + </p> | |
| 8770 | + </div> | |
| 8771 | + </div> | |
| 8772 | + | |
| 8773 | + {formData.delete_data_on_uninstall ? ( | |
| 8774 | + <div className="rounded-md border border-red-300 bg-red-50 p-3 dark:border-red-900 dark:bg-red-950/40"> | |
| 8775 | + <p className="text-sm font-semibold text-red-800 dark:text-red-200"> | |
| 8776 | + {__("This cannot be undone", "yatra")} | |
| 8777 | + </p> | |
| 8778 | + <p className="text-xs text-red-700 dark:text-red-300 mt-1"> | |
| 8779 | + {__( | |
| 8780 | + "Trips, bookings, customers, payments, reviews and enquiries are destroyed along with the plugin. Take a backup, or use Tools to export first, if you may want this data again.", | |
| 8781 | + "yatra", | |
| 8782 | + )} | |
| 8783 | + </p> | |
| 8784 | + </div> | |
| 8785 | + ) : ( | |
| 8786 | + <div className="rounded-md border border-gray-200 bg-gray-50 p-3 text-xs text-gray-600 dark:border-gray-700 dark:bg-gray-800/50 dark:text-gray-400"> | |
| 8787 | + {__( | |
| 8788 | + "Your data is kept when Yatra is deleted, so reinstalling restores everything as it was.", | |
| 8789 | + "yatra", | |
| 8790 | + )} | |
| 8791 | + </div> | |
| 8792 | + )} | |
| 8612 | 8793 | </div> |
| 8613 | 8794 | </div> |
| 8614 | 8795 | ); |
| 8615 | 8796 | |