PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.8
Yatra – Travel Booking & Tour Operator Software v3.0.8
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.8, at assets/dist/blocks/tour.js

580 lines 18.0 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(components.PanelBody, { title: i18n.__("Filters", "yatra"), initialOpen: false }, /* @__PURE__ */ element.createElement(
359 ClassificationMultiSelect,
360 {
361 taxonomy: "destination",
362 label: i18n.__("Destinations", "yatra"),
363 help: i18n.__(
364 'Choose "All published" or restrict the grid to destinations you tick. Other filters narrow results further.',
365 "yatra"
366 ),
367 value: destinationIds,
368 onChange: (ids) => setAttributes({ destinationIds: ids })
369 }
370 ), /* @__PURE__ */ element.createElement(
371 ClassificationMultiSelect,
372 {
373 taxonomy: "activity",
374 label: i18n.__("Activities", "yatra"),
375 help: i18n.__(
376 "Optional. When restricting, trips must match every classification filter that has selections.",
377 "yatra"
378 ),
379 value: activityIds,
380 onChange: (ids) => setAttributes({ activityIds: ids })
381 }
382 ), /* @__PURE__ */ element.createElement(
383 ClassificationMultiSelect,
384 {
385 taxonomy: "trip_category",
386 label: i18n.__("Trip categories", "yatra"),
387 help: i18n.__(
388 "Yatra trip types (not WooCommerce categories). Combine with destinations/activities if needed.",
389 "yatra"
390 ),
391 value: categoryIds,
392 onChange: (ids) => setAttributes({ categoryIds: ids })
393 }
394 ), /* @__PURE__ */ element.createElement(
395 ClassificationMultiSelect,
396 {
397 taxonomy: "difficulty",
398 label: i18n.__("Difficulty levels", "yatra"),
399 help: i18n.__(
400 "Optional. When restricting, trips must use one of the selected difficulty classifications.",
401 "yatra"
402 ),
403 value: difficultyIds,
404 onChange: (ids) => setAttributes({ difficultyIds: ids })
405 }
406 ), /* @__PURE__ */ element.createElement(
407 components.TextControl,
408 {
409 label: i18n.__("Search", "yatra"),
410 value: attributes2.search,
411 onChange: (v) => setAttributes({ search: v })
412 }
413 ), /* @__PURE__ */ element.createElement(
414 components.TextControl,
415 {
416 label: i18n.__("Price min", "yatra"),
417 type: "number",
418 value: attributes2.price_min,
419 onChange: (v) => setAttributes({ price_min: v })
420 }
421 ), /* @__PURE__ */ element.createElement(
422 components.TextControl,
423 {
424 label: i18n.__("Price max", "yatra"),
425 type: "number",
426 value: attributes2.price_max,
427 onChange: (v) => setAttributes({ price_max: v })
428 }
429 ), /* @__PURE__ */ element.createElement(
430 components.TextControl,
431 {
432 label: i18n.__("Duration min (days)", "yatra"),
433 type: "number",
434 value: attributes2.duration_min,
435 onChange: (v) => setAttributes({ duration_min: v })
436 }
437 ), /* @__PURE__ */ element.createElement(
438 components.TextControl,
439 {
440 label: i18n.__("Duration max (days)", "yatra"),
441 type: "number",
442 value: attributes2.duration_max,
443 onChange: (v) => setAttributes({ duration_max: v })
444 }
445 ))), /* @__PURE__ */ element.createElement("div", { className: "yatra-block-editor-preview" }, /* @__PURE__ */ element.createElement(
446 ServerSideRender,
447 {
448 block: "yatra/tour",
449 attributes: attributes2,
450 key: JSON.stringify(attributes2)
451 }
452 )));
453 }
454 const $schema = "https://schemas.wp.org/trunk/block.json";
455 const apiVersion = 3;
456 const name = "yatra/tour";
457 const title = "Trip";
458 const category = "yatra";
459 const icon = "palmtree";
460 const description = "Display trip listings with customizable options";
461 const keywords = [
462 "tour",
463 "trip",
464 "trips",
465 "tours",
466 "travel",
467 "yatra"
468 ];
469 const textdomain = "yatra";
470 const attributes = {
471 order: {
472 type: "string",
473 "default": "desc"
474 },
475 featured: {
476 type: "boolean",
477 "default": false
478 },
479 featured_priority: {
480 type: "string",
481 "default": "",
482 "enum": [
483 "",
484 "featured",
485 "new",
486 "limited"
487 ]
488 },
489 per_page: {
490 type: "number",
491 "default": 10
492 },
493 columns: {
494 type: "number",
495 "default": 3
496 },
497 title: {
498 type: "string",
499 "default": "Our Trips"
500 },
501 show_pagination: {
502 type: "boolean",
503 "default": true
504 },
505 destinationIds: {
506 type: "array",
507 "default": []
508 },
509 activityIds: {
510 type: "array",
511 "default": []
512 },
513 categoryIds: {
514 type: "array",
515 "default": []
516 },
517 difficultyIds: {
518 type: "array",
519 "default": []
520 },
521 price_min: {
522 type: "string",
523 "default": ""
524 },
525 price_max: {
526 type: "string",
527 "default": ""
528 },
529 duration_min: {
530 type: "string",
531 "default": ""
532 },
533 duration_max: {
534 type: "string",
535 "default": ""
536 },
537 search: {
538 type: "string",
539 "default": ""
540 }
541 };
542 const supports = {
543 html: false,
544 inserter: true,
545 align: [
546 "wide",
547 "full"
548 ]
549 };
550 const editorScript = "yatra-tour-block-editor";
551 const blockMetadata = {
552 $schema,
553 apiVersion,
554 name,
555 title,
556 category,
557 icon,
558 description,
559 keywords,
560 textdomain,
561 attributes,
562 supports,
563 editorScript
564 };
565 const {
566 $schema: _$schema,
567 editorScript: _$editorScript,
568 ...metadata
569 } = blockMetadata;
570 if (blocks.getBlockType(metadata.name)) {
571 blocks.unregisterBlockType(metadata.name);
572 }
573 blocks.registerBlockType(metadata.name, {
574 ...metadata,
575 edit: Edit,
576 save: () => null
577 });
578 })(wp.element, wp.blocks, wp.blockEditor, wp.components, wp.i18n, wp.serverSideRender, wp.apiFetch);
579 //# sourceMappingURL=tour.js.map
580