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

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