PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.17
Yatra – Travel Booking & Tour Operator Software v3.0.17
3.0.17 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 All 85 releases
← All changes | resources/js/pages/CategoryForm.tsx +34 -3 3.0.3 → 3.0.17 View file →
@@ -25,8 +25,13 @@
25 25 } from "../components/ui/card";
26 26 import { ConditionalRender } from "../components/ui/conditional-render";
27 27 import { IconPicker, IconPickerValue } from "../components/ui/icon-picker";
28 28 import { RichTextEditor } from "../components/ui/rich-text-editor";
29 +import {
30 + ClassificationLandingPageField,
31 + fetchPublishedPagePermalink,
32 + parseLandingPageIdFromMetadata,
33 +} from "../components/classifications/ClassificationLandingPageField";
29 34
30 35 /** DB / REST may send 0|1 as number or string; never use Boolean() on string "0". */
31 36 function parseTripCategoryIsFeatured(raw: unknown): boolean {
32 37 if (raw === true || raw === 1) {
@@ -53,8 +58,9 @@
53 58 is_featured: boolean;
54 59 seo_title: string;
55 60 seo_description: string;
56 61 seo_keywords: string;
62 + landing_page_id: number | null;
57 63 }
58 64
59 65 interface CategoryOption {
60 66 id: number;
@@ -76,8 +82,9 @@
76 82 is_featured: false,
77 83 seo_title: "",
78 84 seo_description: "",
79 85 seo_keywords: "",
86 + landing_page_id: null,
80 87 });
81 88 const [errors, setErrors] = useState<Record<string, string>>({});
82 89 const [isSubmitting, setIsSubmitting] = useState(false);
83 90 const [isSlugEditable, setIsSlugEditable] = useState(false);
@@ -184,8 +191,11 @@
184 191 ),
185 192 seo_title: categoryData.metadata?.seo_title || "",
186 193 seo_description: categoryData.metadata?.seo_description || "",
187 194 seo_keywords: categoryData.metadata?.seo_keywords || "",
195 + landing_page_id: parseLandingPageIdFromMetadata(
196 + categoryData.metadata as { landing_page_id?: unknown },
197 + ),
188 198 });
189 199 }
190 200 }, [categoryData, isEditMode]);
191 201
@@ -238,11 +248,11 @@
238 248 }
239 249
240 250 if (!formData.slug.trim()) {
241 251 newErrors.slug = __("Slug is required", "yatra");
242 - } else if (!/^[a-z0-9-]+$/.test(formData.slug)) {
252 + } else if (!/^[\p{L}\p{N}-]+$/u.test(formData.slug)) {
243 253 newErrors.slug = __(
244 - "Slug can only contain lowercase letters, numbers, and hyphens",
254 + "Slug can only contain letters, numbers, and hyphens",
245 255 "yatra",
246 256 );
247 257 }
248 258
@@ -270,8 +280,9 @@
270 280 is_featured: data.is_featured ? 1 : 0,
271 281 seo_title: data.seo_title.trim(),
272 282 seo_description: data.seo_description.trim(),
273 283 seo_keywords: data.seo_keywords.trim(),
284 + landing_page_id: data.landing_page_id ?? null,
274 285 };
275 286
276 287 if (isEditMode && isSlugEditable) {
277 288 payload.preserve_slug = true;
@@ -414,9 +425,21 @@
414 425 </Button>
415 426 {formData.slug && (
416 427 <Button
417 428 variant="outline"
418 - onClick={() => {
429 + onClick={async () => {
430 + if (
431 + formData.landing_page_id &&
432 + window.yatraAdmin?.customLandingPagesModuleEnabled
433 + ) {
434 + const link = await fetchPublishedPagePermalink(
435 + formData.landing_page_id,
436 + );
437 + if (link) {
438 + window.open(link, "_blank", "noopener,noreferrer");
439 + return;
440 + }
441 + }
419 442 const targetUrl = buildYatraSinglePreviewUrl({
420 443 entity: "category",
421 444 slug: formData.slug || "",
422 445 bases: settings as Record<string, unknown> | null,
@@ -537,8 +560,16 @@
537 560 />
538 561 </div>
539 562 </CardContent>
540 563 </Card>
564 +
565 + <ClassificationLandingPageField
566 + selectId="yatra-category-landing-page"
567 + value={formData.landing_page_id}
568 + onChange={(id) =>
569 + setFormData((prev) => ({ ...prev, landing_page_id: id }))
570 + }
571 + />
541 572 </div>
542 573
543 574 <div className="space-y-3">
544 575 <Card>