| @@ -27,8 +27,9 @@ | ||
| 27 | 27 | import { ConditionalRender } from "../components/ui/conditional-render"; |
| 28 | 28 | import { useToast } from "../components/ui/toast"; |
| 29 | 29 | import { apiClient } from "../lib/api-client"; |
| 30 | 30 | import { __ } from "../lib/i18n"; |
| 31 | +import { formatYatraMoney } from "../lib/currency-display"; | |
| 31 | 32 | import { Button } from "../components/ui/button"; |
| 32 | 33 | import { PageHeader } from "../components/common/PageHeader"; |
| 33 | 34 | import { |
| 34 | 35 | Edit, |
| @@ -82,15 +83,13 @@ | ||
| 82 | 83 | }; |
| 83 | 84 | |
| 84 | 85 | // Main Component |
| 85 | 86 | const AdditionalServices: React.FC = () => { |
| 87 | + // Module-availability gate. Evaluated up front; the actual early | |
| 88 | + // return runs AFTER every hook below so React's hook-order | |
| 89 | + // invariant holds (rules-of-hooks). | |
| 86 | 90 | const moduleAvailable = isModuleAvailable(); |
| 87 | 91 | |
| 88 | - // Show premium upgrade content if module is not available | |
| 89 | - if (!moduleAvailable) { | |
| 90 | - return <PremiumUpgradeCard />; | |
| 91 | - } | |
| 92 | - | |
| 93 | 92 | const [searchTerm, setSearchTerm] = useState(""); |
| 94 | 93 | const [statusFilter, setStatusFilter] = useState("all"); |
| 95 | 94 | const [sortBy, setSortBy] = useState("name"); |
| 96 | 95 | const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc"); |
| @@ -178,8 +177,9 @@ | ||
| 178 | 177 | }, |
| 179 | 178 | enabled: isModuleAvailable(), |
| 180 | 179 | }); |
| 181 | 180 | |
| 181 | + // eslint-disable-next-line react-hooks/exhaustive-deps | |
| 182 | 182 | const services = data?.data || []; |
| 183 | 183 | const total = data?.total || 0; |
| 184 | 184 | const totalPages = Math.ceil(total / 10); |
| 185 | 185 | |
| @@ -236,9 +236,8 @@ | ||
| 236 | 236 | priceType: string, |
| 237 | 237 | pricePer: string, |
| 238 | 238 | ) => { |
| 239 | 239 | const currency = (window as any)?.yatraAdmin?.currency || "USD"; |
| 240 | - const symbol = currency === "USD" ? "$" : currency; | |
| 241 | 240 | const numPrice = Number(price) || 0; |
| 242 | 241 | if (priceType === "percentage") return `${numPrice}%`; |
| 243 | 242 | const perLabel = |
| 244 | 243 | { person: __("/person"), booking: __("/booking"), day: __("/day") }[ |
| @@ -243,9 +242,9 @@ | ||
| 243 | 242 | const perLabel = |
| 244 | 243 | { person: __("/person"), booking: __("/booking"), day: __("/day") }[ |
| 245 | 244 | pricePer |
| 246 | 245 | ] || ""; |
| 247 | - return `${symbol}${numPrice.toFixed(2)}${perLabel}`; | |
| 246 | + return `${formatYatraMoney(numPrice, currency, { zeroAsUnknown: false })}${perLabel}`; | |
| 248 | 247 | }; |
| 249 | 248 | |
| 250 | 249 | const handleEdit = (service: AdditionalService) => { |
| 251 | 250 | navigateToForm("edit", service.id); |
| @@ -326,8 +325,15 @@ | ||
| 326 | 325 | { key: "draft", label: __("Draft"), count: statusCounts.draft ?? 0 }, |
| 327 | 326 | { key: "trash", label: __("Trash"), count: statusCounts.trash ?? 0 }, |
| 328 | 327 | ]; |
| 329 | 328 | |
| 329 | + // Module-availability gate runs here so every hook above runs on | |
| 330 | + // every render — see rules-of-hooks. The PremiumUpgradeCard render | |
| 331 | + // matches what used to be above the hooks. | |
| 332 | + if (!moduleAvailable) { | |
| 333 | + return <PremiumUpgradeCard />; | |
| 334 | + } | |
| 335 | + | |
| 330 | 336 | return ( |
| 331 | 337 | <div className="space-y-3"> |
| 332 | 338 | <PageHeader |
| 333 | 339 | title={__("Additional Services", "yatra")} |
| @@ -503,8 +509,9 @@ | ||
| 503 | 509 | /> |
| 504 | 510 | ) : ( |
| 505 | 511 | <IconSelector |
| 506 | 512 | iconName={service.icon.value} |
| 513 | + provider={service.icon.provider ?? "yatra"} | |
| 507 | 514 | className="w-5 h-5 text-purple-600 dark:text-purple-400" |
| 508 | 515 | /> |
| 509 | 516 | ) |
| 510 | 517 | ) : ( |