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
← All changes | assets/dist/blocks/activity.js +303 -4 3.0.2.8 → 3.0.16 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.activityIds);
222 + if (ids.length === 0) {
223 + ids = migrateNumericCsvPairToIds(
224 + attributes2.activity_ids,
225 + attributes2.activity
226 + );
227 + }
228 + if (ids.length === 0) {
229 + return;
230 + }
231 + setAttributes({
232 + activityIds: ids,
233 + activity: "",
234 + activity_ids: ""
235 + });
236 + }, [setAttributes]);
237 + const activityIds = normalizeIds(attributes2.activityIds);
4 238 return /* @__PURE__ */ element.createElement(element.Fragment, null, /* @__PURE__ */ element.createElement(blockEditor.InspectorControls, null, /* @__PURE__ */ element.createElement(components.PanelBody, { title: i18n.__("Activity Settings", "yatra"), initialOpen: true }, /* @__PURE__ */ element.createElement(
5 239 components.TextControl,
6 240 {
7 241 label: i18n.__("Title", "yatra"),
@@ -47,8 +281,52 @@
47 281 label: i18n.__("Show pagination", "yatra"),
48 282 checked: attributes2.show_pagination,
49 283 onChange: (value) => setAttributes({ show_pagination: value })
50 284 }
285 + ), /* @__PURE__ */ element.createElement(
286 + ClassificationMultiSelect,
287 + {
288 + taxonomy: "activity",
289 + label: i18n.__("Activities to show", "yatra"),
290 + help: i18n.__(
291 + 'Use "All published" or narrow with search and checkboxes.',
292 + "yatra"
293 + ),
294 + value: activityIds,
295 + onChange: (ids) => setAttributes({ activityIds: ids })
296 + }
297 + )), /* @__PURE__ */ element.createElement(components.PanelBody, { title: i18n.__("Display options", "yatra"), initialOpen: false }, /* @__PURE__ */ element.createElement(
298 + components.ToggleControl,
299 + {
300 + label: i18n.__("Show trip count", "yatra"),
301 + checked: attributes2.show_trip_count,
302 + onChange: (value) => setAttributes({ show_trip_count: value })
303 + }
304 + ), /* @__PURE__ */ element.createElement(
305 + components.ToggleControl,
306 + {
307 + label: i18n.__("Show description", "yatra"),
308 + checked: attributes2.show_description,
309 + onChange: (value) => setAttributes({ show_description: value })
310 + }
311 + ), /* @__PURE__ */ element.createElement(
312 + components.ToggleControl,
313 + {
314 + label: i18n.__("Show image", "yatra"),
315 + checked: attributes2.show_image,
316 + onChange: (value) => setAttributes({ show_image: value })
317 + }
318 + ), /* @__PURE__ */ element.createElement(
319 + components.ToggleControl,
320 + {
321 + label: i18n.__("Hide empty activities", "yatra"),
322 + help: i18n.__(
323 + "Skip activities that have no published trips assigned. Off by default — turn on for a tighter live catalog, leave off for editorial / onboarding views.",
324 + "yatra"
325 + ),
326 + checked: attributes2.hide_empty,
327 + onChange: (value) => setAttributes({ hide_empty: value })
328 + }
51 329 ))), /* @__PURE__ */ element.createElement("div", { className: "yatra-block-editor-preview" }, /* @__PURE__ */ element.createElement(
52 330 ServerSideRender,
53 331 {
54 332 block: "yatra/activity",
@@ -61,13 +339,13 @@
61 339 const apiVersion = 3;
62 340 const name = "yatra/activity";
63 341 const title = "Activity";
64 342 const category = "yatra";
65 - const icon = "buddicons-pm";
343 + const icon = "universal-access";
66 344 const description = "Display activity listings with customizable options";
67 345 const keywords = [
68 346 "activity",
69 - "things to do",
347 + "adventure",
70 348 "yatra"
71 349 ];
72 350 const textdomain = "yatra";
73 351 const attributes = {
@@ -89,8 +367,29 @@
89 367 },
90 368 show_pagination: {
91 369 type: "boolean",
92 370 "default": true
371 + },
372 + activityIds: {
373 + type: "array",
374 + "default": []
375 + },
376 + hide_empty: {
377 + type: "boolean",
378 + "default": false,
379 + description: "When on, activities with zero published trips are skipped. Off by default to preserve the historical 'show everything' behavior."
380 + },
381 + show_trip_count: {
382 + type: "boolean",
383 + "default": true
384 + },
385 + show_description: {
386 + type: "boolean",
387 + "default": true
388 + },
389 + show_image: {
390 + type: "boolean",
391 + "default": true
93 392 }
94 393 };
95 394 const supports = {
96 395 html: false,
@@ -127,6 +426,6 @@
127 426 ...metadata,
128 427 edit: Edit,
129 428 save: () => null
130 429 });
131 -})(wp.element, wp.blocks, wp.blockEditor, wp.components, wp.i18n, wp.serverSideRender);
430 +})(wp.element, wp.blocks, wp.blockEditor, wp.components, wp.i18n, wp.serverSideRender, wp.apiFetch);
132 431 //# sourceMappingURL=activity.js.map