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/ActivityForm.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 ActivityFormData {
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 ActivityForm: 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: activityData.status || "draft",
116 123 seo_title: activityData.metadata?.seo_title || "",
117 124 seo_description: activityData.metadata?.seo_description || "",
118 125 seo_keywords: activityData.metadata?.seo_keywords || "",
126 + landing_page_id: parseLandingPageIdFromMetadata(
127 + activityData.metadata as { landing_page_id?: unknown },
128 + ),
119 129 });
120 130 }
121 131 }, [activityData, 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) {
@@ -338,9 +349,21 @@
338 349 </Button>
339 350 {formData.slug && (
340 351 <Button
341 352 variant="outline"
342 - onClick={() => {
353 + onClick={async () => {
354 + if (
355 + formData.landing_page_id &&
356 + window.yatraAdmin?.customLandingPagesModuleEnabled
357 + ) {
358 + const link = await fetchPublishedPagePermalink(
359 + formData.landing_page_id,
360 + );
361 + if (link) {
362 + window.open(link, "_blank", "noopener,noreferrer");
363 + return;
364 + }
365 + }
343 366 const targetUrl = buildYatraSinglePreviewUrl({
344 367 entity: "activity",
345 368 slug: formData.slug || "",
346 369 bases: settings as Record<string, unknown> | null,
@@ -466,8 +489,16 @@
466 489 maxHeight={720}
467 490 />
468 491 </CardContent>
469 492 </Card>
493 +
494 + <ClassificationLandingPageField
495 + selectId="yatra-activity-landing-page"
496 + value={formData.landing_page_id}
497 + onChange={(id) =>
498 + setFormData((prev) => ({ ...prev, landing_page_id: id }))
499 + }
500 + />
470 501 </div>
471 502
472 503 {/* Sidebar */}
473 504 <div className="space-y-3">