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 / resources / js / blocks / tour / edit.tsx

edit.tsx in Yatra – Travel Booking & Tour Operator Software 3.0.16, at resources/js/blocks/tour/edit.tsx

320 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { InspectorControls } from "@wordpress/block-editor";
2 import {
3 PanelBody,
4 TextControl,
5 RangeControl,
6 SelectControl,
7 ToggleControl,
8 } from "@wordpress/components";
9 import { __ } from "@wordpress/i18n";
10 import { useEffect, useRef } from "@wordpress/element";
11 import ServerSideRender from "@wordpress/server-side-render";
12 import { ClassificationMultiSelect } from "../components/ClassificationMultiSelect";
13 import { migrateNumericCsvPairToIds } from "../utils/migrateTaxonomyIds";
14
15 interface TourBlockAttributes {
16 order: "desc" | "asc";
17 featured: boolean;
18 featured_priority: "" | "featured" | "new" | "limited";
19 per_page: number;
20 columns: number;
21 title: string;
22 show_pagination: boolean;
23 cardLayout: "inherit" | "standard" | "compact_mobile" | "compact_all";
24 destinationIds: number[];
25 activityIds: number[];
26 categoryIds: number[];
27 difficultyIds: number[];
28 destination_ids?: string;
29 destination?: string;
30 activity_ids?: string;
31 activity?: string;
32 category_ids?: string;
33 category?: string;
34 difficulty?: string;
35 price_min: string;
36 price_max: string;
37 duration_min: string;
38 duration_max: string;
39 search: string;
40 }
41
42 interface EditProps {
43 attributes: TourBlockAttributes;
44 setAttributes: (attrs: Partial<TourBlockAttributes>) => void;
45 }
46
47 function normalizeIds(arr: number[] | undefined): number[] {
48 return Array.isArray(arr)
49 ? [...new Set(arr.map((n) => parseInt(String(n), 10)).filter((n) => n > 0))]
50 : [];
51 }
52
53 export default function Edit({ attributes, setAttributes }: EditProps) {
54 const migratedOnce = useRef(false);
55
56 useEffect(() => {
57 if (migratedOnce.current) {
58 return;
59 }
60 migratedOnce.current = true;
61
62 const clears: Partial<TourBlockAttributes> = {};
63
64 let destIds = normalizeIds(attributes.destinationIds);
65 if (destIds.length === 0) {
66 destIds = migrateNumericCsvPairToIds(
67 attributes.destination_ids,
68 attributes.destination,
69 );
70 if (destIds.length) {
71 clears.destination = "";
72 clears.destination_ids = "";
73 }
74 }
75
76 let actIds = normalizeIds(attributes.activityIds);
77 if (actIds.length === 0) {
78 actIds = migrateNumericCsvPairToIds(
79 attributes.activity_ids,
80 attributes.activity,
81 );
82 if (actIds.length) {
83 clears.activity = "";
84 clears.activity_ids = "";
85 }
86 }
87
88 let catIds = normalizeIds(attributes.categoryIds);
89 if (catIds.length === 0) {
90 catIds = migrateNumericCsvPairToIds(
91 attributes.category_ids,
92 attributes.category,
93 );
94 if (catIds.length) {
95 clears.category = "";
96 clears.category_ids = "";
97 }
98 }
99
100 let diffIds = normalizeIds(attributes.difficultyIds);
101 if (diffIds.length === 0) {
102 const fromDiff = migrateNumericCsvPairToIds(attributes.difficulty, "");
103 diffIds = fromDiff;
104 if (fromDiff.length) {
105 clears.difficulty = "";
106 }
107 }
108
109 // Back-compat: migrate legacy `featured: true` to featured_priority="featured"
110 // so existing block instances reflect the value in the new dropdown.
111 if (attributes.featured && !attributes.featured_priority) {
112 clears.featured_priority = "featured";
113 clears.featured = false;
114 }
115
116 const next: Partial<TourBlockAttributes> = { ...clears };
117 if (destIds.length) {
118 next.destinationIds = destIds;
119 }
120 if (actIds.length) {
121 next.activityIds = actIds;
122 }
123 if (catIds.length) {
124 next.categoryIds = catIds;
125 }
126 if (diffIds.length) {
127 next.difficultyIds = diffIds;
128 }
129
130 const touched =
131 Object.keys(next).filter(
132 (key) => next[key as keyof typeof next] !== undefined,
133 ).length > 0;
134 if (touched) {
135 setAttributes(next);
136 }
137 // eslint-disable-next-line react-hooks/exhaustive-deps -- one-time migrate from legacy serialized attributes
138 }, [setAttributes]);
139
140 const destinationIds = normalizeIds(attributes.destinationIds);
141 const activityIds = normalizeIds(attributes.activityIds);
142 const categoryIds = normalizeIds(attributes.categoryIds);
143 const difficultyIds = normalizeIds(attributes.difficultyIds);
144
145 return (
146 <>
147 <InspectorControls>
148 <PanelBody title={__("Trip Settings", "yatra")} initialOpen={true}>
149 <TextControl
150 label={__("Title", "yatra")}
151 value={attributes.title}
152 onChange={(value) => setAttributes({ title: value })}
153 />
154 <SelectControl
155 label={__("Order", "yatra")}
156 value={attributes.order}
157 options={[
158 { label: __("Descending", "yatra"), value: "desc" },
159 { label: __("Ascending", "yatra"), value: "asc" },
160 ]}
161 onChange={(value: string) =>
162 setAttributes({ order: value as "asc" | "desc" })
163 }
164 />
165 <RangeControl
166 label={__("Number of Trips", "yatra")}
167 value={attributes.per_page}
168 onChange={(value) => setAttributes({ per_page: value || 10 })}
169 min={1}
170 max={50}
171 />
172 <RangeControl
173 label={__("Columns", "yatra")}
174 value={attributes.columns}
175 onChange={(value) =>
176 setAttributes({
177 columns: value !== undefined && value !== null ? value : 3,
178 })
179 }
180 min={1}
181 max={6}
182 />
183 <SelectControl
184 label={__("Featured Priority", "yatra")}
185 help={__(
186 "Restrict to trips marked with a specific priority on the trip form (Categorization → Featured Priority). Choose Any to show all trips.",
187 "yatra",
188 )}
189 value={attributes.featured_priority || ""}
190 options={[
191 { label: __("Any", "yatra"), value: "" },
192 { label: __("Featured", "yatra"), value: "featured" },
193 { label: __("New", "yatra"), value: "new" },
194 { label: __("Limited Time", "yatra"), value: "limited" },
195 ]}
196 onChange={(value: string) =>
197 setAttributes({
198 featured_priority:
199 value as TourBlockAttributes["featured_priority"],
200 // Keep legacy `featured` boolean in sync for back-compat
201 // (existing block instances saved with featured=true).
202 featured: value === "featured",
203 })
204 }
205 />
206 <ToggleControl
207 label={__("Show pagination", "yatra")}
208 checked={attributes.show_pagination}
209 onChange={(value) => setAttributes({ show_pagination: value })}
210 />
211 <SelectControl
212 label={__("Card layout", "yatra")}
213 help={__(
214 "Override the site-wide Listing Card Layout (Settings → Design) for this block only. Compact shows a denser image + title + price + View Details card.",
215 "yatra",
216 )}
217 value={attributes.cardLayout || "inherit"}
218 options={[
219 { label: __("Use site default", "yatra"), value: "inherit" },
220 { label: __("Standard", "yatra"), value: "standard" },
221 {
222 label: __("Compact (mobile only)", "yatra"),
223 value: "compact_mobile",
224 },
225 {
226 label: __("Compact (everywhere)", "yatra"),
227 value: "compact_all",
228 },
229 ]}
230 onChange={(value: string) =>
231 setAttributes({
232 cardLayout: value as TourBlockAttributes["cardLayout"],
233 })
234 }
235 />
236 </PanelBody>
237 <PanelBody title={__("Filters", "yatra")} initialOpen={false}>
238 <ClassificationMultiSelect
239 taxonomy="destination"
240 label={__("Destinations", "yatra")}
241 help={__(
242 'Choose "All published" or restrict the grid to destinations you tick. Other filters narrow results further.',
243 "yatra",
244 )}
245 value={destinationIds}
246 onChange={(ids) => setAttributes({ destinationIds: ids })}
247 />
248 <ClassificationMultiSelect
249 taxonomy="activity"
250 label={__("Activities", "yatra")}
251 help={__(
252 "Optional. When restricting, trips must match every classification filter that has selections.",
253 "yatra",
254 )}
255 value={activityIds}
256 onChange={(ids) => setAttributes({ activityIds: ids })}
257 />
258 <ClassificationMultiSelect
259 taxonomy="trip_category"
260 label={__("Trip categories", "yatra")}
261 help={__(
262 "Yatra trip types (not WooCommerce categories). Combine with destinations/activities if needed.",
263 "yatra",
264 )}
265 value={categoryIds}
266 onChange={(ids) => setAttributes({ categoryIds: ids })}
267 />
268 <ClassificationMultiSelect
269 taxonomy="difficulty"
270 label={__("Difficulty levels", "yatra")}
271 help={__(
272 "Optional. When restricting, trips must use one of the selected difficulty classifications.",
273 "yatra",
274 )}
275 value={difficultyIds}
276 onChange={(ids) => setAttributes({ difficultyIds: ids })}
277 />
278 <TextControl
279 label={__("Search", "yatra")}
280 value={attributes.search}
281 onChange={(v) => setAttributes({ search: v })}
282 />
283 <TextControl
284 label={__("Price min", "yatra")}
285 type="number"
286 value={attributes.price_min}
287 onChange={(v) => setAttributes({ price_min: v })}
288 />
289 <TextControl
290 label={__("Price max", "yatra")}
291 type="number"
292 value={attributes.price_max}
293 onChange={(v) => setAttributes({ price_max: v })}
294 />
295 <TextControl
296 label={__("Duration min (days)", "yatra")}
297 type="number"
298 value={attributes.duration_min}
299 onChange={(v) => setAttributes({ duration_min: v })}
300 />
301 <TextControl
302 label={__("Duration max (days)", "yatra")}
303 type="number"
304 value={attributes.duration_max}
305 onChange={(v) => setAttributes({ duration_max: v })}
306 />
307 </PanelBody>
308 </InspectorControls>
309
310 <div className="yatra-block-editor-preview">
311 <ServerSideRender
312 block="yatra/tour"
313 attributes={attributes}
314 key={JSON.stringify(attributes)}
315 />
316 </div>
317 </>
318 );
319 }
320