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/destination.js +319 -4 3.0.3 → 3.0.17 View file →
@@ -1,7 +1,241 @@
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 + let ids = normalizeIds(attributes2.destinationIds);
222 + if (ids.length === 0) {
223 + ids = migrateNumericCsvPairToIds(
224 + attributes2.destination_ids,
225 + attributes2.destination
226 + );
227 + }
228 + if (ids.length === 0) {
229 + return;
230 + }
231 + setAttributes({
232 + destinationIds: ids,
233 + destination: "",
234 + destination_ids: ""
235 + });
236 + }, [setAttributes]);
237 + const destinationIds = normalizeIds(attributes2.destinationIds);
4 238 return /* @__PURE__ */ element.createElement(element.Fragment, null, /* @__PURE__ */ element.createElement(blockEditor.InspectorControls, null, /* @__PURE__ */ element.createElement(
5 239 components.PanelBody,
6 240 {
7 241 title: i18n.__("Destination Settings", "yatra"),
@@ -57,10 +291,66 @@
57 291 label: i18n.__("Show pagination", "yatra"),
58 292 checked: attributes2.show_pagination,
59 293 onChange: (value) => setAttributes({ show_pagination: value })
60 294 }
295 + ),
296 + /* @__PURE__ */ element.createElement(
297 + ClassificationMultiSelect,
298 + {
299 + taxonomy: "destination",
300 + label: i18n.__("Destinations to show", "yatra"),
301 + help: i18n.__(
302 + 'Pick "All published" for the full catalog; otherwise search and tick specific destinations.',
303 + "yatra"
304 + ),
305 + value: destinationIds,
306 + onChange: (ids) => setAttributes({ destinationIds: ids })
307 + }
61 308 )
62 - )), /* @__PURE__ */ element.createElement("div", { className: "yatra-block-editor-preview" }, /* @__PURE__ */ element.createElement(
309 + ), /* @__PURE__ */ element.createElement(components.PanelBody, { title: i18n.__("Display options", "yatra"), initialOpen: false }, /* @__PURE__ */ element.createElement(
310 + components.ToggleControl,
311 + {
312 + label: i18n.__("Show trip count", "yatra"),
313 + checked: attributes2.show_trip_count,
314 + onChange: (value) => setAttributes({ show_trip_count: value })
315 + }
316 + ), /* @__PURE__ */ element.createElement(
317 + components.ToggleControl,
318 + {
319 + label: i18n.__("Show description", "yatra"),
320 + checked: attributes2.show_description,
321 + onChange: (value) => setAttributes({ show_description: value })
322 + }
323 + ), /* @__PURE__ */ element.createElement(
324 + components.ToggleControl,
325 + {
326 + label: i18n.__("Show image", "yatra"),
327 + checked: attributes2.show_image,
328 + onChange: (value) => setAttributes({ show_image: value })
329 + }
330 + ), /* @__PURE__ */ element.createElement(
331 + components.ToggleControl,
332 + {
333 + label: i18n.__("Hide empty destinations", "yatra"),
334 + help: i18n.__(
335 + "Skip destinations that have no published trips assigned. Off by default — turn on for a tighter live catalog, leave off for editorial / onboarding views.",
336 + "yatra"
337 + ),
338 + checked: attributes2.hide_empty,
339 + onChange: (value) => setAttributes({ hide_empty: value })
340 + }
341 + ), /* @__PURE__ */ element.createElement(
342 + components.ToggleControl,
343 + {
344 + label: i18n.__("Featured only (filter list)", "yatra"),
345 + help: i18n.__(
346 + "When on, only destinations marked as featured in Yatra → Destinations are listed.",
347 + "yatra"
348 + ),
349 + checked: attributes2.featured_only,
350 + onChange: (value) => setAttributes({ featured_only: value })
351 + }
352 + ))), /* @__PURE__ */ element.createElement("div", { className: "yatra-block-editor-preview" }, /* @__PURE__ */ element.createElement(
63 353 ServerSideRender,
64 354 {
65 355 block: "yatra/destination",
66 356 attributes: attributes2,
@@ -72,9 +362,9 @@
72 362 const apiVersion = 3;
73 363 const name = "yatra/destination";
74 364 const title = "Destination";
75 365 const category = "yatra";
76 - const icon = "admin-site";
366 + const icon = "location-alt";
77 367 const description = "Display destination listings with customizable options";
78 368 const keywords = [
79 369 "destination",
80 370 "place",
@@ -101,8 +391,33 @@
101 391 },
102 392 show_pagination: {
103 393 type: "boolean",
104 394 "default": true
395 + },
396 + destinationIds: {
397 + type: "array",
398 + "default": []
399 + },
400 + hide_empty: {
401 + type: "boolean",
402 + "default": false,
403 + description: "When on, destinations with zero published trips are skipped. Off by default to preserve the historical 'show everything' behavior."
404 + },
405 + featured_only: {
406 + type: "boolean",
407 + "default": false
408 + },
409 + show_trip_count: {
410 + type: "boolean",
411 + "default": true
412 + },
413 + show_description: {
414 + type: "boolean",
415 + "default": true
416 + },
417 + show_image: {
418 + type: "boolean",
419 + "default": true
105 420 }
106 421 };
107 422 const supports = {
108 423 html: false,
@@ -139,6 +454,6 @@
139 454 ...metadata,
140 455 edit: Edit,
141 456 save: () => null
142 457 });
143 -})(wp.element, wp.blocks, wp.blockEditor, wp.components, wp.i18n, wp.serverSideRender);
458 +})(wp.element, wp.blocks, wp.blockEditor, wp.components, wp.i18n, wp.serverSideRender, wp.apiFetch);
144 459 //# sourceMappingURL=destination.js.map