PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
← All changes | resources/js/pages/DynamicPricingRuleForm.tsx +355 -42 3.0.3 → 3.0.16 View file →
@@ -57,11 +57,13 @@
57 57 priority: number;
58 58 // Demand-based conditions
59 59 demand_threshold_high?: number;
60 60 demand_threshold_low?: number;
61 - // Time-based conditions
62 - apply_on_weekends?: boolean;
63 - apply_on_weekdays?: boolean;
61 + // Time-based conditions. `weekend_days` is the source of truth — the engine
62 + // evaluates the rule only on those weekdays. The `apply_on_weekends` /
63 + // `apply_on_weekdays` columns still exist in the DB schema for legacy reasons
64 + // but the engine never reads them, so we don't surface them in the form to
65 + // avoid the impression they do anything.
64 66 weekend_days?: string[];
65 67 }
66 68
67 69 const RULE_TYPES = [
@@ -67,52 +69,76 @@
67 69 const RULE_TYPES = [
68 70 {
69 71 id: "early_bird",
70 72 name: __("Early Bird Discount"),
71 - description: __("Reward customers who book well in advance"),
73 + description: __(
74 + "Reward customers who book well in advance. Always behaves as a discount.",
75 + ),
72 76 icon: Calendar,
73 77 color: "blue",
74 - example: __("10% off for bookings 30+ days early"),
78 + example: __(
79 + "10% off when departure is at least 30 days away (set Min Days Before = 30).",
80 + ),
75 81 },
76 82 {
77 83 id: "last_minute",
78 84 name: __("Last Minute Deals"),
79 - description: __("Fill remaining spots close to departure"),
85 + description: __(
86 + "Discount close to departure to fill remaining seats. Always behaves as a discount.",
87 + ),
80 88 icon: Clock,
81 89 color: "orange",
82 - example: __("15% off for bookings within 7 days"),
90 + example: __(
91 + "15% off when departure is within 7 days (set Max Days Before = 7).",
92 + ),
83 93 },
84 94 {
85 95 id: "demand",
86 96 name: __("Demand-Based Pricing"),
87 - description: __("Adjust prices based on booking velocity"),
97 + description: __(
98 + "Adjusts price by booking velocity score (recomputed by cron / on each booking).",
99 + ),
88 100 icon: TrendingUp,
89 101 color: "green",
90 - example: __("Increase price for hot trips, decrease for slow"),
102 + example: __(
103 + "Score above 70 → markup; below 30 → discount. Magnitude scales with how far the score is outside the band.",
104 + ),
91 105 },
92 106 {
93 107 id: "inventory",
94 108 name: __("Inventory-Based"),
95 - description: __("Price changes based on remaining capacity"),
109 + description: __(
110 + "Reacts to seats remaining on the chosen departure (works with time slots too).",
111 + ),
96 112 icon: Package,
97 113 color: "purple",
98 - example: __("Higher price when few spots left"),
114 + example: __(
115 + "Seats ≤ 5 → +10% scarcity markup; seats ≥ 20 → 50% of magnitude as discount.",
116 + ),
99 117 },
100 118 {
101 119 id: "seasonal",
102 120 name: __("Seasonal Pricing"),
103 - description: __("Adjust for peak and off-peak seasons"),
121 + description: __(
122 + "Applies when the DEPARTURE date falls within the season window.",
123 + ),
104 124 icon: Sun,
105 125 color: "yellow",
106 - example: __("25% premium during summer months"),
126 + example: __(
127 + "+25% on departures between Jul 1 – Aug 31. Use a negative value for off-peak discounts.",
128 + ),
107 129 },
108 130 {
109 131 id: "time_based",
110 - name: __("Time-Based"),
111 - description: __("Different prices for specific days and date ranges"),
132 + name: __("Time-Based (Weekday/Weekend)"),
133 + description: __(
134 + "Applies on selected weekdays of the DEPARTURE date, optionally limited to a date window.",
135 + ),
112 136 icon: Target,
113 137 color: "indigo",
114 - example: __("15% premium on weekends within specific dates"),
138 + example: __(
139 + "+15% premium on Saturday & Sunday departures. Add a date range to make it effective only for that period.",
140 + ),
115 141 },
116 142 ];
117 143
118 144 const DynamicPricingRuleForm: React.FC = () => {
@@ -139,10 +165,8 @@
139 165 status: "active",
140 166 priority: 1,
141 167 demand_threshold_high: 80,
142 168 demand_threshold_low: 30,
143 - apply_on_weekends: false,
144 - apply_on_weekdays: false,
145 169 weekend_days: [],
146 170 });
147 171
148 172 const [showTripDropdown, setShowTripDropdown] = useState(false);
@@ -149,13 +173,80 @@
149 173 const [tripSearchQuery, setTripSearchQuery] = useState("");
150 174 const [debouncedTripSearch, setDebouncedTripSearch] = useState("");
151 175 const [showRuleTypeModal, setShowRuleTypeModal] = useState(false);
152 176
177 + // Rule simulation state — drives the "When will this apply?" panel.
178 + // We keep a small base price input so admins can see absolute dollar amounts
179 + // for the discount/markup rather than just percentages.
180 + const [previewBasePrice, setPreviewBasePrice] = useState<number>(100);
181 + const [previewLoading, setPreviewLoading] = useState(false);
182 + const [previewError, setPreviewError] = useState<string | null>(null);
183 + const [previewResult, setPreviewResult] = useState<{
184 + rule_type: string;
185 + base_price: number;
186 + current_date: string;
187 + scenarios: Array<{
188 + label: string;
189 + departure_date: string | null;
190 + spots_remaining: number | null;
191 + applies: boolean;
192 + base_price: number;
193 + final_price: number;
194 + adjustment: number;
195 + adjustment_percent: number;
196 + }>;
197 + } | null>(null);
198 +
153 199 const handleSelectRuleType = (ruleType: string) => {
154 200 handleChange("rule_type", ruleType);
155 201 setShowRuleTypeModal(false);
202 + // A new rule type changes which scenarios are meaningful — drop the old
203 + // preview so stale results don't mislead the admin.
204 + setPreviewResult(null);
205 + setPreviewError(null);
156 206 };
157 207
208 + const handleRunSimulation = async () => {
209 + if (!formData.rule_type) {
210 + setPreviewError(__("Pick a rule type first."));
211 + return;
212 + }
213 + setPreviewLoading(true);
214 + setPreviewError(null);
215 + try {
216 + const response = await apiClient.post("/dynamic-pricing/simulate-rule", {
217 + rule_type: formData.rule_type,
218 + adjustment_type: formData.adjustment_type,
219 + adjustment_value: formData.adjustment_value,
220 + min_days_before: formData.min_days_before,
221 + max_days_before: formData.max_days_before,
222 + min_inventory: formData.min_inventory,
223 + max_inventory: formData.max_inventory,
224 + start_date: formData.start_date || null,
225 + end_date: formData.end_date || null,
226 + weekend_days: formData.weekend_days || [],
227 + demand_threshold_low: formData.demand_threshold_low,
228 + demand_threshold_high: formData.demand_threshold_high,
229 + base_price: previewBasePrice > 0 ? previewBasePrice : 100,
230 + });
231 + // Controller returns { success: true, data: {...scenarios...} } — checking
232 + // .success on .data would always be undefined and throw a false negative.
233 + const envelope = response as any;
234 + if (envelope?.success === false) {
235 + throw new Error(envelope?.message || __("Simulation failed."));
236 + }
237 + const result = envelope?.data ?? envelope;
238 + if (!result || !Array.isArray(result.scenarios)) {
239 + throw new Error(__("Simulation returned an unexpected response."));
240 + }
241 + setPreviewResult(result);
242 + } catch (e: any) {
243 + setPreviewError(e?.message || __("Simulation failed."));
244 + } finally {
245 + setPreviewLoading(false);
246 + }
247 + };
248 +
158 249 // Debounce trip search
159 250 useEffect(() => {
160 251 const timer = setTimeout(() => {
161 252 setDebouncedTripSearch(tripSearchQuery);
@@ -231,10 +322,8 @@
231 322 : 80,
232 323 demand_threshold_low: ruleDetails.demand_threshold_low
233 324 ? Number(ruleDetails.demand_threshold_low)
234 325 : 30,
235 - apply_on_weekends: Boolean(ruleDetails.apply_on_weekends),
236 - apply_on_weekdays: Boolean(ruleDetails.apply_on_weekdays),
237 326 weekend_days: Array.isArray(ruleDetails.weekend_days)
238 327 ? ruleDetails.weekend_days
239 328 : ruleDetails.weekend_days
240 329 ? JSON.parse(ruleDetails.weekend_days)
@@ -567,9 +656,11 @@
567 656 <Card>
568 657 <CardHeader>
569 658 <CardTitle>{__("Rule Details")}</CardTitle>
570 659 <CardDescription>
571 - {__("Name and description for this pricing rule")}
660 + {__(
661 + "Internal name for this pricing rule (visible to admins only).",
662 + )}
572 663 </CardDescription>
573 664 </CardHeader>
574 665 <CardContent className="space-y-4">
575 666 <div>
@@ -580,8 +671,13 @@
580 671 onChange={(e) => handleChange("name", e.target.value)}
581 672 placeholder={__("e.g., Summer Early Bird Discount")}
582 673 required
583 674 />
675 + <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
676 + {__(
677 + "Used in admin lists, analytics, and price-history exports. Customers never see this name.",
678 + )}
679 + </p>
584 680 </div>
585 681 </CardContent>
586 682 </Card>
587 683
@@ -632,15 +728,42 @@
632 728 }
633 729 required
634 730 />
635 731 <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
636 - {formData.adjustment_type === "percentage"
637 - ? __(
638 - "Enter percentage (e.g., 10 for 10% discount, -10 for 10% increase)",
639 - )
640 - : __(
641 - "Enter fixed amount (e.g., 100 for $100 discount, -100 for $100 increase)",
642 - )}
732 + {(() => {
733 + const rt = formData.rule_type;
734 + // Early bird & last minute always behave as discounts:
735 + // enter a positive magnitude.
736 + if (rt === "early_bird" || rt === "last_minute") {
737 + return formData.adjustment_type === "percentage"
738 + ? __(
739 + "Enter the discount magnitude as a positive number (e.g. 10 = 10% off). The rule always applies as a discount.",
740 + )
741 + : __(
742 + "Enter the discount magnitude as a positive number (e.g. 100 = 100 off). The rule always applies as a discount.",
743 + );
744 + }
745 + // Inventory & demand auto-derive direction from
746 + // capacity / demand score; admins enter only the
747 + // magnitude.
748 + if (rt === "inventory" || rt === "demand") {
749 + return formData.adjustment_type === "percentage"
750 + ? __(
751 + "Enter the magnitude (positive). Direction is derived automatically: scarce inventory / high demand → markup, plenty / low demand → discount.",
752 + )
753 + : __(
754 + "Enter the magnitude (positive). Direction is derived automatically: scarce inventory / high demand → markup, plenty / low demand → discount.",
755 + );
756 + }
757 + // Seasonal & time-based use the sign as entered.
758 + return formData.adjustment_type === "percentage"
759 + ? __(
760 + "Use a positive value to increase price (e.g. 10 = +10%) and a negative value to discount (e.g. -10 = 10% off).",
761 + )
762 + : __(
763 + "Use a positive value to increase price (e.g. 100 = +100) and a negative value to discount (e.g. -100 = 100 off).",
764 + );
765 + })()}
643 766 </p>
644 767 </div>
645 768 </CardContent>
646 769 </Card>
@@ -672,8 +795,13 @@
672 795 )
673 796 }
674 797 min="0"
675 798 />
799 + <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
800 + {__(
801 + "Apply only when the selected departure date is at least this many days away from today. Example: 30 means 30+ days before departure.",
802 + )}
803 + </p>
676 804 </div>
677 805
678 806 <div>
679 807 <Label htmlFor="max_days_before">
@@ -690,10 +818,23 @@
690 818 )
691 819 }
692 820 min="0"
693 821 />
822 + <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
823 + {__(
824 + "Apply only when the selected departure date is within this many days from today. Example: 7 means 0–7 days before departure.",
825 + )}
826 + </p>
694 827 </div>
695 828
829 + <div className="p-3 bg-gray-50 dark:bg-gray-900/40 border border-gray-200 dark:border-gray-800 rounded-lg">
830 + <p className="text-xs text-gray-700 dark:text-gray-300">
831 + {__(
832 + "If you set both Minimum and Maximum, the rule applies only inside that window (Minimum ≤ days before departure ≤ Maximum).",
833 + )}
834 + </p>
835 + </div>
836 +
696 837 <div>
697 838 <Label htmlFor="start_date">
698 839 {__("Start Date (Optional)")}
699 840 </Label>
@@ -704,9 +845,11 @@
704 845 }
705 846 placeholder={__("Select start date")}
706 847 />
707 848 <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
708 - {__("Leave empty for no date restriction")}
849 + {__(
850 + "Optional lifetime limit. If set, this rule will run only on or after this date. Leave empty for no restriction.",
851 + )}
709 852 </p>
710 853 </div>
711 854
712 855 <div>
@@ -720,9 +863,11 @@
720 863 }
721 864 placeholder={__("Select end date")}
722 865 />
723 866 <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
724 - {__("Leave empty for no date restriction")}
867 + {__(
868 + "Optional lifetime limit. If set, this rule will run only on or before this date. Leave empty for no restriction.",
869 + )}
725 870 </p>
726 871 </div>
727 872 </>
728 873 )}
@@ -730,9 +875,9 @@
730 875 {formData.rule_type === "inventory" && (
731 876 <>
732 877 <div>
733 878 <Label htmlFor="min_inventory">
734 - {__("Minimum Available Seats")}
879 + {__("Low-inventory threshold (seats)")}
735 880 </Label>
736 881 <Input
737 882 id="min_inventory"
738 883 type="number"
@@ -744,13 +889,18 @@
744 889 )
745 890 }
746 891 min="0"
747 892 />
893 + <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
894 + {__(
895 + "When seats remaining ≤ this number, the rule applies a markup (scarcity).",
896 + )}
897 + </p>
748 898 </div>
749 899
750 900 <div>
751 901 <Label htmlFor="max_inventory">
752 - {__("Maximum Available Seats")}
902 + {__("High-inventory threshold (seats)")}
753 903 </Label>
754 904 <Input
755 905 id="max_inventory"
756 906 type="number"
@@ -762,17 +912,24 @@
762 912 )
763 913 }
764 914 min="0"
765 915 />
916 + <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
917 + {__(
918 + "When seats remaining ≥ this number, the rule applies a partial discount (50% of the magnitude).",
919 + )}
920 + </p>
766 921 </div>
767 922 </>
768 923 )}
769 924
770 - {/* Seasonal - Date range */}
925 + {/* Seasonal - Departure window */}
771 926 {formData.rule_type === "seasonal" && (
772 927 <>
773 928 <div>
774 - <Label htmlFor="start_date">{__("Start Date")}</Label>
929 + <Label htmlFor="start_date">
930 + {__("Season Start (Departure Date)")}
931 + </Label>
775 932 <DatePicker
776 933 value={formData.start_date}
777 934 onChange={(value) =>
778 935 handleChange("start_date", value)
@@ -779,14 +936,18 @@
779 936 }
780 937 placeholder={__("Select start date")}
781 938 />
782 939 <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
783 - {__("When this seasonal pricing starts")}
940 + {__(
941 + "Earliest departure date this seasonal rule applies to.",
942 + )}
784 943 </p>
785 944 </div>
786 945
787 946 <div>
788 - <Label htmlFor="end_date">{__("End Date")}</Label>
947 + <Label htmlFor="end_date">
948 + {__("Season End (Departure Date)")}
949 + </Label>
789 950 <DatePicker
790 951 value={formData.end_date}
791 952 onChange={(value) =>
792 953 handleChange("end_date", value)
@@ -793,11 +954,21 @@
793 954 }
794 955 placeholder={__("Select end date")}
795 956 />
796 957 <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
797 - {__("When this seasonal pricing ends")}
958 + {__(
959 + "Latest departure date this seasonal rule applies to. The rule matches whenever the booking's departure date falls between these two dates (inclusive).",
960 + )}
798 961 </p>
799 962 </div>
963 +
964 + <div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg">
965 + <p className="text-xs text-yellow-700 dark:text-yellow-300">
966 + {__(
967 + "💡 Tip: Use a positive Adjustment Value for peak-season markup (e.g. 25% during summer) or a negative value for an off-peak discount.",
968 + )}
969 + </p>
970 + </div>
800 971 </>
801 972 )}
802 973
803 974 {/* Demand-Based - Booking velocity thresholds */}
@@ -910,9 +1081,9 @@
910 1081 ))}
911 1082 </div>
912 1083 <p className="text-xs text-gray-500 dark:text-gray-400 mt-2">
913 1084 {__(
914 - "Select the days of the week when this pricing rule applies",
1085 + "Selected days are matched against the booking's DEPARTURE date — e.g. tick Saturday + Sunday to apply a weekend premium to Saturday/Sunday departures, regardless of when the customer books.",
915 1086 )}
916 1087 </p>
917 1088 </div>
918 1089
@@ -927,9 +1098,11 @@
927 1098 }
928 1099 placeholder={__("Select start date")}
929 1100 />
930 1101 <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
931 - {__("Leave empty for no date restriction")}
1102 + {__(
1103 + "Limits the rule to departures on or after this date. Leave empty for no restriction.",
1104 + )}
932 1105 </p>
933 1106 </div>
934 1107
935 1108 <div>
@@ -943,9 +1116,11 @@
943 1116 }
944 1117 placeholder={__("Select end date")}
945 1118 />
946 1119 <p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
947 - {__("Leave empty for no date restriction")}
1120 + {__(
1121 + "Limits the rule to departures on or before this date. Leave empty for no restriction.",
1122 + )}
948 1123 </p>
949 1124 </div>
950 1125
951 1126 <div className="p-3 bg-indigo-50 dark:bg-indigo-900/20 border border-indigo-200 dark:border-indigo-800 rounded-lg">
@@ -950,9 +1125,9 @@
950 1125
951 1126 <div className="p-3 bg-indigo-50 dark:bg-indigo-900/20 border border-indigo-200 dark:border-indigo-800 rounded-lg">
952 1127 <p className="text-xs text-indigo-700 dark:text-indigo-300">
953 1128 {__(
954 - "💡 Use positive values for premium pricing (weekends) or negative for discounts (weekdays).",
1129 + "💡 Sign matters: a positive Adjustment Value adds a premium on the selected days; a negative value gives a discount.",
955 1130 )}
956 1131 </p>
957 1132 </div>
958 1133 </>
@@ -1000,9 +1175,11 @@
1000 1175 min="1"
1001 1176 max="100"
1002 1177 />
1003 1178 <p className="text-xs text-gray-500 dark:text-gray-400">
1004 - {__("Higher priority rules are applied first (1-100)")}
1179 + {__(
1180 + "A higher number = higher priority (1–100). When more than one rule matches, the global Rule Priority Mode in Settings decides whether the highest-priority rule wins, every rule stacks, or the customer gets the best price.",
1181 + )}
1005 1182 </p>
1006 1183 </CardContent>
1007 1184 </Card>
1008 1185
@@ -1138,8 +1315,144 @@
1138 1315 })}
1139 1316 </div>
1140 1317 </div>
1141 1318 )}
1319 + </div>
1320 + )}
1321 + </CardContent>
1322 + </Card>
1323 +
1324 + {/* "When will this apply?" preview panel.
1325 + Runs the current (unsaved) form data through the engine
1326 + against rule-type-appropriate sample scenarios so admins can
1327 + see exactly when their rule fires, before saving. */}
1328 + <Card>
1329 + <CardHeader>
1330 + <CardTitle className="text-base">
1331 + {__("When will this rule apply?")}
1332 + </CardTitle>
1333 + <CardDescription>
1334 + {__(
1335 + "Run a simulation with the current settings to see which scenarios trigger the discount/markup. Nothing is saved.",
1336 + )}
1337 + </CardDescription>
1338 + </CardHeader>
1339 + <CardContent>
1340 + <div className="flex items-end gap-3 mb-3">
1341 + <div className="flex-1">
1342 + <Label htmlFor="preview-base-price">
1343 + {__("Sample base price")}
1344 + </Label>
1345 + <Input
1346 + id="preview-base-price"
1347 + type="number"
1348 + min={1}
1349 + step="0.01"
1350 + value={previewBasePrice}
1351 + onChange={(e) =>
1352 + setPreviewBasePrice(Number(e.target.value) || 0)
1353 + }
1354 + />
1355 + </div>
1356 + <Button
1357 + type="button"
1358 + onClick={handleRunSimulation}
1359 + disabled={previewLoading || !formData.rule_type}
1360 + >
1361 + {previewLoading ? (
1362 + <>
1363 + <Loader2 className="w-4 h-4 mr-2 animate-spin" />
1364 + {__("Running...")}
1365 + </>
1366 + ) : (
1367 + <>
1368 + <Target className="w-4 h-4 mr-2" />
1369 + {__("Run Preview")}
1370 + </>
1371 + )}
1372 + </Button>
1373 + </div>
1374 +
1375 + {previewError && (
1376 + <p className="text-sm text-red-600 dark:text-red-400 mb-2">
1377 + {previewError}
1378 + </p>
1379 + )}
1380 +
1381 + {previewResult && (
1382 + <div className="overflow-x-auto rounded-md border border-gray-200 dark:border-gray-700">
1383 + <table className="w-full text-sm">
1384 + <thead className="bg-gray-50 dark:bg-gray-900">
1385 + <tr className="text-left">
1386 + <th className="px-3 py-2 font-medium">
1387 + {__("Scenario")}
1388 + </th>
1389 + <th className="px-3 py-2 font-medium">
1390 + {__("Final Price")}
1391 + </th>
1392 + <th className="px-3 py-2 font-medium">
1393 + {__("Change")}
1394 + </th>
1395 + <th className="px-3 py-2 font-medium">
1396 + {__("Rule")}
1397 + </th>
1398 + </tr>
1399 + </thead>
1400 + <tbody>
1401 + {previewResult.scenarios.map((s, i) => (
1402 + <tr
1403 + key={i}
1404 + className="border-t border-gray-200 dark:border-gray-700"
1405 + >
1406 + <td className="px-3 py-2">
1407 + <div>{s.label}</div>
1408 + {s.departure_date && (
1409 + <div className="text-xs text-gray-500 dark:text-gray-400">
1410 + {s.departure_date}
1411 + </div>
1412 + )}
1413 + </td>
1414 + <td className="px-3 py-2 font-medium">
1415 + {previewResult.base_price > 0
1416 + ? `$${s.final_price.toFixed(2)}`
1417 + : "—"}
1418 + </td>
1419 + <td className="px-3 py-2">
1420 + {s.applies ? (
1421 + <span
1422 + className={
1423 + s.adjustment < 0
1424 + ? "text-green-700 dark:text-green-400"
1425 + : "text-orange-700 dark:text-orange-400"
1426 + }
1427 + >
1428 + {s.adjustment > 0 ? "+" : ""}$
1429 + {s.adjustment.toFixed(2)} (
1430 + {s.adjustment_percent > 0 ? "+" : ""}
1431 + {s.adjustment_percent.toFixed(2)}%)
1432 + </span>
1433 + ) : (
1434 + <span className="text-gray-500 dark:text-gray-400">
1435 + —
1436 + </span>
1437 + )}
1438 + </td>
1439 + <td className="px-3 py-2">
1440 + {s.applies ? (
1441 + <span className="inline-flex items-center gap-1 text-green-700 dark:text-green-400">
1442 + <Check className="w-3.5 h-3.5" />
1443 + {__("Fires")}
1444 + </span>
1445 + ) : (
1446 + <span className="text-gray-500 dark:text-gray-400">
1447 + {__("Skipped")}
1448 + </span>
1449 + )}
1450 + </td>
1451 + </tr>
1452 + ))}
1453 + </tbody>
1454 + </table>
1142 1455 </div>
1143 1456 )}
1144 1457 </CardContent>
1145 1458 </Card>