PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.4
Yatra – Travel Booking & Tour Operator Software v3.0.4
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.4, at resources/js/blocks/tour/edit.tsx

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