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
yatra / assets / dist / blocks / tour.js

tour.js in Yatra – Travel Booking & Tour Operator Software 3.0.16, at assets/dist/blocks/tour.js

615 lines 19.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function(element, blocks, blockEditor, components, i18n, ServerSideRender, apiFetch) {
2 "use strict";
3 const listWrapStyle = {
4 maxHeight: 220,
5 overflowY: "auto",
6 marginTop: 8,
7 padding: "4px 0",
8 border: "1px solid #94949451",
9 borderRadius: 2
10 };
11 function ClassificationMultiSelect({
12 taxonomy,
13 label,
14 help,
15 value,
16 onChange
17 }) {
18 const [items, setItems] = element.useState([]);
19 const [loading, setLoading] = element.useState(true);
20 const [loadError, setLoadError] = element.useState(false);
21 const [search, setSearch] = element.useState("");
22 const [scope, setScope] = element.useState(
23 () => value.length > 0 ? "narrow" : "all"
24 );
25 element.useEffect(() => {
26 if (value.length > 0) {
27 setScope("narrow");
28 }
29 }, [value.length]);
30 element.useEffect(() => {
31 let cancelled = false;
32 setLoading(true);
33 setLoadError(false);
34 apiFetch({
35 path: `/yatra/v1/block-editor/taxonomy-choices?taxonomy=${encodeURIComponent(
36 taxonomy
37 )}`
38 }).then((response) => {
39 if (!cancelled) {
40 setItems(response.items ?? []);
41 }
42 }).catch(() => {
43 if (!cancelled) {
44 setItems([]);
45 setLoadError(true);
46 }
47 }).finally(() => {
48 if (!cancelled) {
49 setLoading(false);
50 }
51 });
52 return () => {
53 cancelled = true;
54 };
55 }, [taxonomy]);
56 const filteredItems = element.useMemo(() => {
57 const q = search.trim().toLowerCase();
58 if (q === "") {
59 return items;
60 }
61 return items.filter((i) => i.name.toLowerCase().includes(q));
62 }, [items, search]);
63 const selectedSet = element.useMemo(() => new Set(value), [value]);
64 const toggleId = (id, checked) => {
65 const next = new Set(selectedSet);
66 if (checked) {
67 next.add(id);
68 } else {
69 next.delete(id);
70 }
71 onChange([...next].sort((a, b) => a - b));
72 };
73 const onScopeChange = (next) => {
74 if (next === "all") {
75 setScope("all");
76 onChange([]);
77 setSearch("");
78 } else {
79 setScope("narrow");
80 }
81 };
82 if (loading) {
83 return /* @__PURE__ */ element.createElement(
84 "fieldset",
85 {
86 className: "yatra-block-taxonomy-field",
87 style: { margin: "0 0 16px", border: "none", padding: 0 }
88 },
89 /* @__PURE__ */ element.createElement(
90 "legend",
91 {
92 className: "components-base-control__label",
93 style: { padding: 0 }
94 },
95 label
96 ),
97 /* @__PURE__ */ element.createElement(components.Spinner, null)
98 );
99 }
100 return /* @__PURE__ */ element.createElement(
101 "fieldset",
102 {
103 className: "yatra-block-taxonomy-field",
104 style: { margin: "0 0 16px", border: "none", padding: 0 }
105 },
106 /* @__PURE__ */ element.createElement("legend", { className: "components-base-control__label", style: { padding: 0 } }, label),
107 /* @__PURE__ */ element.createElement(
108 "p",
109 {
110 className: "components-base-control__help",
111 style: { marginTop: 4, marginBottom: 10 }
112 },
113 help
114 ),
115 loadError && /* @__PURE__ */ element.createElement("p", { style: { color: "#b32d2e", fontSize: 12, marginBottom: 8 } }, i18n.__(
116 "Could not load options. Confirm you can edit posts and Yatra REST is available.",
117 "yatra"
118 )),
119 /* @__PURE__ */ element.createElement(
120 components.RadioControl,
121 {
122 label: i18n.__("Listing scope", "yatra"),
123 selected: scope,
124 options: [
125 {
126 label: i18n.__("All published (no restriction)", "yatra"),
127 value: "all"
128 },
129 {
130 label: i18n.__("Only selected (search below)", "yatra"),
131 value: "narrow"
132 }
133 ],
134 onChange: onScopeChange
135 }
136 ),
137 scope === "all" && /* @__PURE__ */ element.createElement("p", { className: "components-base-control__help", style: { marginTop: 4 } }, i18n.__(
138 "The frontend will include every matching published item.",
139 "yatra"
140 )),
141 scope === "narrow" && /* @__PURE__ */ element.createElement(element.Fragment, null, /* @__PURE__ */ element.createElement(
142 components.SearchControl,
143 {
144 label: i18n.sprintf(
145 /* translators: %d = number of loaded taxonomy items */
146 i18n.__("Filter items (%d loaded)", "yatra"),
147 items.length
148 ),
149 hideLabelFromVision: true,
150 placeholder: i18n.__("Type to filter the list…", "yatra"),
151 value: search,
152 onChange: (s) => setSearch(s),
153 __nextHasNoMarginBottom: true
154 }
155 ), value.length > 0 && /* @__PURE__ */ element.createElement(
156 "p",
157 {
158 className: "components-base-control__help",
159 style: { marginTop: 4 }
160 },
161 i18n.sprintf(
162 /* translators: %d = number of selected taxonomy items */
163 i18n.__("%d selected", "yatra"),
164 value.length
165 )
166 ), items.length === 0 && !loadError ? /* @__PURE__ */ element.createElement("p", { className: "components-base-control__help" }, i18n.__("No published items of this type yet.", "yatra")) : /* @__PURE__ */ element.createElement("div", { role: "group", "aria-label": label, style: listWrapStyle }, filteredItems.length === 0 ? /* @__PURE__ */ element.createElement("p", { style: { padding: "8px 12px", margin: 0, fontSize: 12 } }, i18n.__("No matching items.", "yatra")) : filteredItems.map((item) => {
167 const cid = Number(item.id);
168 return /* @__PURE__ */ element.createElement("div", { key: cid, style: { padding: "2px 8px" } }, /* @__PURE__ */ element.createElement(
169 components.CheckboxControl,
170 {
171 label: `${item.name} (${cid})`,
172 checked: selectedSet.has(cid),
173 onChange: (checked) => toggleId(cid, checked === true),
174 __nextHasNoMarginBottom: true
175 }
176 ));
177 })), value.length > 0 && /* @__PURE__ */ element.createElement(
178 components.Button,
179 {
180 variant: "link",
181 style: { paddingLeft: 0, marginTop: 6 },
182 onClick: () => onChange([])
183 },
184 i18n.__("Clear selected", "yatra")
185 ), /* @__PURE__ */ element.createElement("p", { className: "components-base-control__help", style: { marginTop: 6 } }, i18n.__(
186 "If none are checked, the block behaves like “All” (no taxonomy filter).",
187 "yatra"
188 )))
189 );
190 }
191 function migrateNumericCsvPairToIds(legacyA, legacyB) {
192 const raw = [legacyA ?? "", legacyB ?? ""].find(
193 (s) => String(s).trim() !== ""
194 );
195 if (raw === void 0) {
196 return [];
197 }
198 const parts = String(raw).split(",").map((s) => s.trim()).filter(Boolean);
199 const ids = [];
200 for (const p of parts) {
201 if (!/^\d+$/.test(p)) {
202 return [];
203 }
204 const n = parseInt(p, 10);
205 if (n > 0) {
206 ids.push(n);
207 }
208 }
209 return [...new Set(ids)];
210 }
211 function normalizeIds(arr) {
212 return Array.isArray(arr) ? [...new Set(arr.map((n) => parseInt(String(n), 10)).filter((n) => n > 0))] : [];
213 }
214 function Edit({ attributes: attributes2, setAttributes }) {
215 const migratedOnce = element.useRef(false);
216 element.useEffect(() => {
217 if (migratedOnce.current) {
218 return;
219 }
220 migratedOnce.current = true;
221 const clears = {};
222 let destIds = normalizeIds(attributes2.destinationIds);
223 if (destIds.length === 0) {
224 destIds = migrateNumericCsvPairToIds(
225 attributes2.destination_ids,
226 attributes2.destination
227 );
228 if (destIds.length) {
229 clears.destination = "";
230 clears.destination_ids = "";
231 }
232 }
233 let actIds = normalizeIds(attributes2.activityIds);
234 if (actIds.length === 0) {
235 actIds = migrateNumericCsvPairToIds(
236 attributes2.activity_ids,
237 attributes2.activity
238 );
239 if (actIds.length) {
240 clears.activity = "";
241 clears.activity_ids = "";
242 }
243 }
244 let catIds = normalizeIds(attributes2.categoryIds);
245 if (catIds.length === 0) {
246 catIds = migrateNumericCsvPairToIds(
247 attributes2.category_ids,
248 attributes2.category
249 );
250 if (catIds.length) {
251 clears.category = "";
252 clears.category_ids = "";
253 }
254 }
255 let diffIds = normalizeIds(attributes2.difficultyIds);
256 if (diffIds.length === 0) {
257 const fromDiff = migrateNumericCsvPairToIds(attributes2.difficulty, "");
258 diffIds = fromDiff;
259 if (fromDiff.length) {
260 clears.difficulty = "";
261 }
262 }
263 if (attributes2.featured && !attributes2.featured_priority) {
264 clears.featured_priority = "featured";
265 clears.featured = false;
266 }
267 const next = { ...clears };
268 if (destIds.length) {
269 next.destinationIds = destIds;
270 }
271 if (actIds.length) {
272 next.activityIds = actIds;
273 }
274 if (catIds.length) {
275 next.categoryIds = catIds;
276 }
277 if (diffIds.length) {
278 next.difficultyIds = diffIds;
279 }
280 const touched = Object.keys(next).filter(
281 (key) => next[key] !== void 0
282 ).length > 0;
283 if (touched) {
284 setAttributes(next);
285 }
286 }, [setAttributes]);
287 const destinationIds = normalizeIds(attributes2.destinationIds);
288 const activityIds = normalizeIds(attributes2.activityIds);
289 const categoryIds = normalizeIds(attributes2.categoryIds);
290 const difficultyIds = normalizeIds(attributes2.difficultyIds);
291 return /* @__PURE__ */ element.createElement(element.Fragment, null, /* @__PURE__ */ element.createElement(blockEditor.InspectorControls, null, /* @__PURE__ */ element.createElement(components.PanelBody, { title: i18n.__("Trip Settings", "yatra"), initialOpen: true }, /* @__PURE__ */ element.createElement(
292 components.TextControl,
293 {
294 label: i18n.__("Title", "yatra"),
295 value: attributes2.title,
296 onChange: (value) => setAttributes({ title: value })
297 }
298 ), /* @__PURE__ */ element.createElement(
299 components.SelectControl,
300 {
301 label: i18n.__("Order", "yatra"),
302 value: attributes2.order,
303 options: [
304 { label: i18n.__("Descending", "yatra"), value: "desc" },
305 { label: i18n.__("Ascending", "yatra"), value: "asc" }
306 ],
307 onChange: (value) => setAttributes({ order: value })
308 }
309 ), /* @__PURE__ */ element.createElement(
310 components.RangeControl,
311 {
312 label: i18n.__("Number of Trips", "yatra"),
313 value: attributes2.per_page,
314 onChange: (value) => setAttributes({ per_page: value || 10 }),
315 min: 1,
316 max: 50
317 }
318 ), /* @__PURE__ */ element.createElement(
319 components.RangeControl,
320 {
321 label: i18n.__("Columns", "yatra"),
322 value: attributes2.columns,
323 onChange: (value) => setAttributes({
324 columns: value !== void 0 && value !== null ? value : 3
325 }),
326 min: 1,
327 max: 6
328 }
329 ), /* @__PURE__ */ element.createElement(
330 components.SelectControl,
331 {
332 label: i18n.__("Featured Priority", "yatra"),
333 help: i18n.__(
334 "Restrict to trips marked with a specific priority on the trip form (Categorization → Featured Priority). Choose Any to show all trips.",
335 "yatra"
336 ),
337 value: attributes2.featured_priority || "",
338 options: [
339 { label: i18n.__("Any", "yatra"), value: "" },
340 { label: i18n.__("Featured", "yatra"), value: "featured" },
341 { label: i18n.__("New", "yatra"), value: "new" },
342 { label: i18n.__("Limited Time", "yatra"), value: "limited" }
343 ],
344 onChange: (value) => setAttributes({
345 featured_priority: value,
346 // Keep legacy `featured` boolean in sync for back-compat
347 // (existing block instances saved with featured=true).
348 featured: value === "featured"
349 })
350 }
351 ), /* @__PURE__ */ element.createElement(
352 components.ToggleControl,
353 {
354 label: i18n.__("Show pagination", "yatra"),
355 checked: attributes2.show_pagination,
356 onChange: (value) => setAttributes({ show_pagination: value })
357 }
358 ), /* @__PURE__ */ element.createElement(
359 components.SelectControl,
360 {
361 label: i18n.__("Card layout", "yatra"),
362 help: i18n.__(
363 "Override the site-wide Listing Card Layout (Settings → Design) for this block only. Compact shows a denser image + title + price + View Details card.",
364 "yatra"
365 ),
366 value: attributes2.cardLayout || "inherit",
367 options: [
368 { label: i18n.__("Use site default", "yatra"), value: "inherit" },
369 { label: i18n.__("Standard", "yatra"), value: "standard" },
370 {
371 label: i18n.__("Compact (mobile only)", "yatra"),
372 value: "compact_mobile"
373 },
374 {
375 label: i18n.__("Compact (everywhere)", "yatra"),
376 value: "compact_all"
377 }
378 ],
379 onChange: (value) => setAttributes({
380 cardLayout: value
381 })
382 }
383 )), /* @__PURE__ */ element.createElement(components.PanelBody, { title: i18n.__("Filters", "yatra"), initialOpen: false }, /* @__PURE__ */ element.createElement(
384 ClassificationMultiSelect,
385 {
386 taxonomy: "destination",
387 label: i18n.__("Destinations", "yatra"),
388 help: i18n.__(
389 'Choose "All published" or restrict the grid to destinations you tick. Other filters narrow results further.',
390 "yatra"
391 ),
392 value: destinationIds,
393 onChange: (ids) => setAttributes({ destinationIds: ids })
394 }
395 ), /* @__PURE__ */ element.createElement(
396 ClassificationMultiSelect,
397 {
398 taxonomy: "activity",
399 label: i18n.__("Activities", "yatra"),
400 help: i18n.__(
401 "Optional. When restricting, trips must match every classification filter that has selections.",
402 "yatra"
403 ),
404 value: activityIds,
405 onChange: (ids) => setAttributes({ activityIds: ids })
406 }
407 ), /* @__PURE__ */ element.createElement(
408 ClassificationMultiSelect,
409 {
410 taxonomy: "trip_category",
411 label: i18n.__("Trip categories", "yatra"),
412 help: i18n.__(
413 "Yatra trip types (not WooCommerce categories). Combine with destinations/activities if needed.",
414 "yatra"
415 ),
416 value: categoryIds,
417 onChange: (ids) => setAttributes({ categoryIds: ids })
418 }
419 ), /* @__PURE__ */ element.createElement(
420 ClassificationMultiSelect,
421 {
422 taxonomy: "difficulty",
423 label: i18n.__("Difficulty levels", "yatra"),
424 help: i18n.__(
425 "Optional. When restricting, trips must use one of the selected difficulty classifications.",
426 "yatra"
427 ),
428 value: difficultyIds,
429 onChange: (ids) => setAttributes({ difficultyIds: ids })
430 }
431 ), /* @__PURE__ */ element.createElement(
432 components.TextControl,
433 {
434 label: i18n.__("Search", "yatra"),
435 value: attributes2.search,
436 onChange: (v) => setAttributes({ search: v })
437 }
438 ), /* @__PURE__ */ element.createElement(
439 components.TextControl,
440 {
441 label: i18n.__("Price min", "yatra"),
442 type: "number",
443 value: attributes2.price_min,
444 onChange: (v) => setAttributes({ price_min: v })
445 }
446 ), /* @__PURE__ */ element.createElement(
447 components.TextControl,
448 {
449 label: i18n.__("Price max", "yatra"),
450 type: "number",
451 value: attributes2.price_max,
452 onChange: (v) => setAttributes({ price_max: v })
453 }
454 ), /* @__PURE__ */ element.createElement(
455 components.TextControl,
456 {
457 label: i18n.__("Duration min (days)", "yatra"),
458 type: "number",
459 value: attributes2.duration_min,
460 onChange: (v) => setAttributes({ duration_min: v })
461 }
462 ), /* @__PURE__ */ element.createElement(
463 components.TextControl,
464 {
465 label: i18n.__("Duration max (days)", "yatra"),
466 type: "number",
467 value: attributes2.duration_max,
468 onChange: (v) => setAttributes({ duration_max: v })
469 }
470 ))), /* @__PURE__ */ element.createElement("div", { className: "yatra-block-editor-preview" }, /* @__PURE__ */ element.createElement(
471 ServerSideRender,
472 {
473 block: "yatra/tour",
474 attributes: attributes2,
475 key: JSON.stringify(attributes2)
476 }
477 )));
478 }
479 const $schema = "https://schemas.wp.org/trunk/block.json";
480 const apiVersion = 3;
481 const name = "yatra/tour";
482 const title = "Trip";
483 const category = "yatra";
484 const icon = "palmtree";
485 const description = "Display trip listings with customizable options";
486 const keywords = [
487 "tour",
488 "trip",
489 "trips",
490 "tours",
491 "travel",
492 "yatra"
493 ];
494 const textdomain = "yatra";
495 const attributes = {
496 order: {
497 type: "string",
498 "default": "desc"
499 },
500 featured: {
501 type: "boolean",
502 "default": false
503 },
504 featured_priority: {
505 type: "string",
506 "default": "",
507 "enum": [
508 "",
509 "featured",
510 "new",
511 "limited"
512 ]
513 },
514 per_page: {
515 type: "number",
516 "default": 10
517 },
518 columns: {
519 type: "number",
520 "default": 3
521 },
522 title: {
523 type: "string",
524 "default": "Our Trips"
525 },
526 show_pagination: {
527 type: "boolean",
528 "default": true
529 },
530 cardLayout: {
531 type: "string",
532 "default": "inherit",
533 "enum": [
534 "inherit",
535 "standard",
536 "compact_mobile",
537 "compact_all"
538 ]
539 },
540 destinationIds: {
541 type: "array",
542 "default": []
543 },
544 activityIds: {
545 type: "array",
546 "default": []
547 },
548 categoryIds: {
549 type: "array",
550 "default": []
551 },
552 difficultyIds: {
553 type: "array",
554 "default": []
555 },
556 price_min: {
557 type: "string",
558 "default": ""
559 },
560 price_max: {
561 type: "string",
562 "default": ""
563 },
564 duration_min: {
565 type: "string",
566 "default": ""
567 },
568 duration_max: {
569 type: "string",
570 "default": ""
571 },
572 search: {
573 type: "string",
574 "default": ""
575 }
576 };
577 const supports = {
578 html: false,
579 inserter: true,
580 align: [
581 "wide",
582 "full"
583 ]
584 };
585 const editorScript = "yatra-tour-block-editor";
586 const blockMetadata = {
587 $schema,
588 apiVersion,
589 name,
590 title,
591 category,
592 icon,
593 description,
594 keywords,
595 textdomain,
596 attributes,
597 supports,
598 editorScript
599 };
600 const {
601 $schema: _$schema,
602 editorScript: _$editorScript,
603 ...metadata
604 } = blockMetadata;
605 if (blocks.getBlockType(metadata.name)) {
606 blocks.unregisterBlockType(metadata.name);
607 }
608 blocks.registerBlockType(metadata.name, {
609 ...metadata,
610 edit: Edit,
611 save: () => null
612 });
613 })(wp.element, wp.blocks, wp.blockEditor, wp.components, wp.i18n, wp.serverSideRender, wp.apiFetch);
614 //# sourceMappingURL=tour.js.map
615