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/DestinationForm.tsx +34 -3 3.0.3 → 3.0.16 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 interface DestinationFormData {
31 36 name: string;
32 37 slug: string;
@@ -38,8 +43,9 @@
38 43 status: string;
39 44 seo_title: string;
40 45 seo_description: string;
41 46 seo_keywords: string;
47 + landing_page_id: number | null;
42 48 }
43 49
44 50 const DestinationForm: React.FC = () => {
45 51 const queryClient = useQueryClient();
@@ -53,8 +59,9 @@
53 59 status: "publish",
54 60 seo_title: "",
55 61 seo_description: "",
56 62 seo_keywords: "",
63 + landing_page_id: null,
57 64 });
58 65 const [errors, setErrors] = useState<Record<string, string>>({});
59 66 const [isSubmitting, setIsSubmitting] = useState(false);
60 67 const [isSlugEditable, setIsSlugEditable] = useState(false);
@@ -115,8 +122,11 @@
115 122 status: destinationData.status || "draft",
116 123 seo_title: destinationData.metadata?.seo_title || "",
117 124 seo_description: destinationData.metadata?.seo_description || "",
118 125 seo_keywords: destinationData.metadata?.seo_keywords || "",
126 + landing_page_id: parseLandingPageIdFromMetadata(
127 + destinationData.metadata as { landing_page_id?: unknown },
128 + ),
119 129 });
120 130 }
121 131 }, [destinationData, isEditMode]);
122 132
@@ -178,11 +188,11 @@
178 188 }
179 189
180 190 if (!formData.slug.trim()) {
181 191 newErrors.slug = __("Slug is required", "yatra");
182 - } else if (!/^[a-z0-9-]+$/.test(formData.slug)) {
192 + } else if (!/^[\p{L}\p{N}-]+$/u.test(formData.slug)) {
183 193 newErrors.slug = __(
184 - "Slug can only contain lowercase letters, numbers, and hyphens",
194 + "Slug can only contain letters, numbers, and hyphens",
185 195 "yatra",
186 196 );
187 197 }
188 198
@@ -201,8 +211,9 @@
201 211 status: data.status,
202 212 seo_title: data.seo_title.trim(),
203 213 seo_description: data.seo_description.trim(),
204 214 seo_keywords: data.seo_keywords.trim(),
215 + landing_page_id: data.landing_page_id ?? null,
205 216 };
206 217
207 218 // If slug was manually edited, add flag to preserve it
208 219 if (isEditMode && isSlugEditable) {
@@ -343,9 +354,21 @@
343 354 </Button>
344 355 {formData.slug && (
345 356 <Button
346 357 variant="outline"
347 - onClick={() => {
358 + onClick={async () => {
359 + if (
360 + formData.landing_page_id &&
361 + window.yatraAdmin?.customLandingPagesModuleEnabled
362 + ) {
363 + const link = await fetchPublishedPagePermalink(
364 + formData.landing_page_id,
365 + );
366 + if (link) {
367 + window.open(link, "_blank", "noopener,noreferrer");
368 + return;
369 + }
370 + }
348 371 const targetUrl = buildYatraSinglePreviewUrl({
349 372 entity: "destination",
350 373 slug: formData.slug || "",
351 374 bases: settings as Record<string, unknown> | null,
@@ -471,8 +494,16 @@
471 494 maxHeight={720}
472 495 />
473 496 </CardContent>
474 497 </Card>
498 +
499 + <ClassificationLandingPageField
500 + selectId="yatra-destination-landing-page"
501 + value={formData.landing_page_id}
502 + onChange={(id) =>
503 + setFormData((prev) => ({ ...prev, landing_page_id: id }))
504 + }
505 + />
475 506 </div>
476 507
477 508 {/* Sidebar */}
478 509 <div className="space-y-3">