| @@ -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> |