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 | assets/dist/blocks/tour.js +477 -7 3.0.3 → 3.0.17 View file →
@@ -1,7 +1,294 @@
1 -(function(element, blocks, blockEditor, components, i18n, ServerSideRender) {
1 +(function(element, blocks, blockEditor, components, i18n, ServerSideRender, apiFetch) {
2 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 + }
3 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);
4 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(
5 292 components.TextControl,
6 293 {
7 294 label: i18n.__("Title", "yatra"),
@@ -21,9 +308,9 @@
21 308 }
22 309 ), /* @__PURE__ */ element.createElement(
23 310 components.RangeControl,
24 311 {
25 - label: i18n.__("Number of Tours", "yatra"),
312 + label: i18n.__("Number of Trips", "yatra"),
26 313 value: attributes2.per_page,
27 314 onChange: (value) => setAttributes({ per_page: value || 10 }),
28 315 min: 1,
29 316 max: 50
@@ -39,13 +326,28 @@
39 326 min: 1,
40 327 max: 6
41 328 }
42 329 ), /* @__PURE__ */ element.createElement(
43 - components.ToggleControl,
330 + components.SelectControl,
44 331 {
45 - label: i18n.__("Show only featured tours", "yatra"),
46 - checked: attributes2.featured,
47 - onChange: (value) => setAttributes({ featured: value })
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 + })
48 350 }
49 351 ), /* @__PURE__ */ element.createElement(
50 352 components.ToggleControl,
51 353 {
@@ -52,8 +354,120 @@
52 354 label: i18n.__("Show pagination", "yatra"),
53 355 checked: attributes2.show_pagination,
54 356 onChange: (value) => setAttributes({ show_pagination: value })
55 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 + }
56 470 ))), /* @__PURE__ */ element.createElement("div", { className: "yatra-block-editor-preview" }, /* @__PURE__ */ element.createElement(
57 471 ServerSideRender,
58 472 {
59 473 block: "yatra/tour",
@@ -86,8 +500,18 @@
86 500 featured: {
87 501 type: "boolean",
88 502 "default": false
89 503 },
504 + featured_priority: {
505 + type: "string",
506 + "default": "",
507 + "enum": [
508 + "",
509 + "featured",
510 + "new",
511 + "limited"
512 + ]
513 + },
90 514 per_page: {
91 515 type: "number",
92 516 "default": 10
93 517 },
@@ -101,8 +525,54 @@
101 525 },
102 526 show_pagination: {
103 527 type: "boolean",
104 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": ""
105 575 }
106 576 };
107 577 const supports = {
108 578 html: false,
@@ -139,6 +609,6 @@
139 609 ...metadata,
140 610 edit: Edit,
141 611 save: () => null
142 612 });
143 -})(wp.element, wp.blocks, wp.blockEditor, wp.components, wp.i18n, wp.serverSideRender);
613 +})(wp.element, wp.blocks, wp.blockEditor, wp.components, wp.i18n, wp.serverSideRender, wp.apiFetch);
144 614 //# sourceMappingURL=tour.js.map