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 / assets / dist / js / DynamicPricing-CBtPFtKK.js

DynamicPricing-CBtPFtKK.js in Yatra – Travel Booking & Tour Operator Software 3.0.16, at assets/dist/js/DynamicPricing-CBtPFtKK.js

2,123 lines 110.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { a6 as React, a4 as isFunction, ap as Animate, ao as isEqual, r as reactExports, c8 as Target, q as Calendar, U as Users, bq as BarChart, T as TrendingUp, at as DollarSign, as as Clock, Z as Zap, j as jsxRuntimeExports, S as Sparkles, au as ArrowRight, A as Activity, aw as CheckCircle, v as useQueryClient, u as useQuery, w as useMutation, l as Settings, ax as Plus, aA as AlertCircle, h as Package, aK as PenSquare, aB as Check, aO as Trash2, aW as Save } from "./react-vendor-CithbthY.js";
2 import { ak as clsx, al as Dot, am as findAllByType, an as ErrorBar, ao as Layer, ap as filterProps, aq as Curve, ar as interpolateNumber, as as isNil, at as hasClipDot, au as LabelList, av as getValueByDataKey, aw as uniqueId, ax as Global, ay as getCateCoordinateOfLine, az as generateCategoricalChart, aA as XAxis, aB as YAxis, aC as formatAxisMap, B as Button, C as Card, d as CardContent, P as PageHeader, af as SearchFilterToolbar, a6 as BulkActionToolbar, f as CardHeader, g as CardTitle, h as CardDescription, Y as Table, e as Badge, aD as ResponsiveContainer, aE as CartesianGrid, aF as Tooltip, k as ConfirmationDialog } from "../../admin/dist/js/app.js";
3 import { R as RuleTypeSelectionModal } from "./RuleTypeSelectionModal-_WAr6JyA.js";
4 import { T as Toggle } from "./toggle-CKa3ExrH.js";
5 import { _ as __, u as useToast, t as toDateValue, a as apiClient, f as formatYatraMoney } from "./index-Dec_weLB.js";
6 var _excluded = ["type", "layout", "connectNulls", "ref"], _excluded2 = ["key"];
7 function _typeof(o) {
8 "@babel/helpers - typeof";
9 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
10 return typeof o2;
11 } : function(o2) {
12 return o2 && "function" == typeof Symbol && o2.constructor === Symbol && o2 !== Symbol.prototype ? "symbol" : typeof o2;
13 }, _typeof(o);
14 }
15 function _objectWithoutProperties(source, excluded) {
16 if (source == null) return {};
17 var target = _objectWithoutPropertiesLoose(source, excluded);
18 var key, i;
19 if (Object.getOwnPropertySymbols) {
20 var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
21 for (i = 0; i < sourceSymbolKeys.length; i++) {
22 key = sourceSymbolKeys[i];
23 if (excluded.indexOf(key) >= 0) continue;
24 if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
25 target[key] = source[key];
26 }
27 }
28 return target;
29 }
30 function _objectWithoutPropertiesLoose(source, excluded) {
31 if (source == null) return {};
32 var target = {};
33 for (var key in source) {
34 if (Object.prototype.hasOwnProperty.call(source, key)) {
35 if (excluded.indexOf(key) >= 0) continue;
36 target[key] = source[key];
37 }
38 }
39 return target;
40 }
41 function _extends() {
42 _extends = Object.assign ? Object.assign.bind() : function(target) {
43 for (var i = 1; i < arguments.length; i++) {
44 var source = arguments[i];
45 for (var key in source) {
46 if (Object.prototype.hasOwnProperty.call(source, key)) {
47 target[key] = source[key];
48 }
49 }
50 }
51 return target;
52 };
53 return _extends.apply(this, arguments);
54 }
55 function ownKeys(e, r) {
56 var t = Object.keys(e);
57 if (Object.getOwnPropertySymbols) {
58 var o = Object.getOwnPropertySymbols(e);
59 r && (o = o.filter(function(r2) {
60 return Object.getOwnPropertyDescriptor(e, r2).enumerable;
61 })), t.push.apply(t, o);
62 }
63 return t;
64 }
65 function _objectSpread(e) {
66 for (var r = 1; r < arguments.length; r++) {
67 var t = null != arguments[r] ? arguments[r] : {};
68 r % 2 ? ownKeys(Object(t), true).forEach(function(r2) {
69 _defineProperty(e, r2, t[r2]);
70 }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r2) {
71 Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2));
72 });
73 }
74 return e;
75 }
76 function _toConsumableArray(arr) {
77 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
78 }
79 function _nonIterableSpread() {
80 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
81 }
82 function _unsupportedIterableToArray(o, minLen) {
83 if (!o) return;
84 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
85 var n = Object.prototype.toString.call(o).slice(8, -1);
86 if (n === "Object" && o.constructor) n = o.constructor.name;
87 if (n === "Map" || n === "Set") return Array.from(o);
88 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
89 }
90 function _iterableToArray(iter) {
91 if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
92 }
93 function _arrayWithoutHoles(arr) {
94 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
95 }
96 function _arrayLikeToArray(arr, len) {
97 if (len == null || len > arr.length) len = arr.length;
98 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
99 return arr2;
100 }
101 function _classCallCheck(instance, Constructor) {
102 if (!(instance instanceof Constructor)) {
103 throw new TypeError("Cannot call a class as a function");
104 }
105 }
106 function _defineProperties(target, props) {
107 for (var i = 0; i < props.length; i++) {
108 var descriptor = props[i];
109 descriptor.enumerable = descriptor.enumerable || false;
110 descriptor.configurable = true;
111 if ("value" in descriptor) descriptor.writable = true;
112 Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
113 }
114 }
115 function _createClass(Constructor, protoProps, staticProps) {
116 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
117 if (staticProps) _defineProperties(Constructor, staticProps);
118 Object.defineProperty(Constructor, "prototype", { writable: false });
119 return Constructor;
120 }
121 function _callSuper(t, o, e) {
122 return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
123 }
124 function _possibleConstructorReturn(self, call) {
125 if (call && (_typeof(call) === "object" || typeof call === "function")) {
126 return call;
127 } else if (call !== void 0) {
128 throw new TypeError("Derived constructors may only return object or undefined");
129 }
130 return _assertThisInitialized(self);
131 }
132 function _assertThisInitialized(self) {
133 if (self === void 0) {
134 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
135 }
136 return self;
137 }
138 function _isNativeReflectConstruct() {
139 try {
140 var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
141 }));
142 } catch (t2) {
143 }
144 return (_isNativeReflectConstruct = function _isNativeReflectConstruct2() {
145 return !!t;
146 })();
147 }
148 function _getPrototypeOf(o) {
149 _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
150 return o2.__proto__ || Object.getPrototypeOf(o2);
151 };
152 return _getPrototypeOf(o);
153 }
154 function _inherits(subClass, superClass) {
155 if (typeof superClass !== "function" && superClass !== null) {
156 throw new TypeError("Super expression must either be null or a function");
157 }
158 subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } });
159 Object.defineProperty(subClass, "prototype", { writable: false });
160 if (superClass) _setPrototypeOf(subClass, superClass);
161 }
162 function _setPrototypeOf(o, p) {
163 _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
164 o2.__proto__ = p2;
165 return o2;
166 };
167 return _setPrototypeOf(o, p);
168 }
169 function _defineProperty(obj, key, value) {
170 key = _toPropertyKey(key);
171 if (key in obj) {
172 Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
173 } else {
174 obj[key] = value;
175 }
176 return obj;
177 }
178 function _toPropertyKey(t) {
179 var i = _toPrimitive(t, "string");
180 return "symbol" == _typeof(i) ? i : i + "";
181 }
182 function _toPrimitive(t, r) {
183 if ("object" != _typeof(t) || !t) return t;
184 var e = t[Symbol.toPrimitive];
185 if (void 0 !== e) {
186 var i = e.call(t, r);
187 if ("object" != _typeof(i)) return i;
188 throw new TypeError("@@toPrimitive must return a primitive value.");
189 }
190 return String(t);
191 }
192 var Line = /* @__PURE__ */ function(_PureComponent) {
193 function Line2() {
194 var _this;
195 _classCallCheck(this, Line2);
196 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
197 args[_key] = arguments[_key];
198 }
199 _this = _callSuper(this, Line2, [].concat(args));
200 _defineProperty(_this, "state", {
201 isAnimationFinished: true,
202 totalLength: 0
203 });
204 _defineProperty(_this, "generateSimpleStrokeDasharray", function(totalLength, length) {
205 return "".concat(length, "px ").concat(totalLength - length, "px");
206 });
207 _defineProperty(_this, "getStrokeDasharray", function(length, totalLength, lines) {
208 var lineLength = lines.reduce(function(pre, next) {
209 return pre + next;
210 });
211 if (!lineLength) {
212 return _this.generateSimpleStrokeDasharray(totalLength, length);
213 }
214 var count = Math.floor(length / lineLength);
215 var remainLength = length % lineLength;
216 var restLength = totalLength - length;
217 var remainLines = [];
218 for (var i = 0, sum = 0; i < lines.length; sum += lines[i], ++i) {
219 if (sum + lines[i] > remainLength) {
220 remainLines = [].concat(_toConsumableArray(lines.slice(0, i)), [remainLength - sum]);
221 break;
222 }
223 }
224 var emptyLines = remainLines.length % 2 === 0 ? [0, restLength] : [restLength];
225 return [].concat(_toConsumableArray(Line2.repeat(lines, count)), _toConsumableArray(remainLines), emptyLines).map(function(line) {
226 return "".concat(line, "px");
227 }).join(", ");
228 });
229 _defineProperty(_this, "id", uniqueId("recharts-line-"));
230 _defineProperty(_this, "pathRef", function(node) {
231 _this.mainCurve = node;
232 });
233 _defineProperty(_this, "handleAnimationEnd", function() {
234 _this.setState({
235 isAnimationFinished: true
236 });
237 if (_this.props.onAnimationEnd) {
238 _this.props.onAnimationEnd();
239 }
240 });
241 _defineProperty(_this, "handleAnimationStart", function() {
242 _this.setState({
243 isAnimationFinished: false
244 });
245 if (_this.props.onAnimationStart) {
246 _this.props.onAnimationStart();
247 }
248 });
249 return _this;
250 }
251 _inherits(Line2, _PureComponent);
252 return _createClass(Line2, [{
253 key: "componentDidMount",
254 value: function componentDidMount() {
255 if (!this.props.isAnimationActive) {
256 return;
257 }
258 var totalLength = this.getTotalLength();
259 this.setState({
260 totalLength
261 });
262 }
263 }, {
264 key: "componentDidUpdate",
265 value: function componentDidUpdate() {
266 if (!this.props.isAnimationActive) {
267 return;
268 }
269 var totalLength = this.getTotalLength();
270 if (totalLength !== this.state.totalLength) {
271 this.setState({
272 totalLength
273 });
274 }
275 }
276 }, {
277 key: "getTotalLength",
278 value: function getTotalLength() {
279 var curveDom = this.mainCurve;
280 try {
281 return curveDom && curveDom.getTotalLength && curveDom.getTotalLength() || 0;
282 } catch (err) {
283 return 0;
284 }
285 }
286 }, {
287 key: "renderErrorBar",
288 value: function renderErrorBar(needClip, clipPathId) {
289 if (this.props.isAnimationActive && !this.state.isAnimationFinished) {
290 return null;
291 }
292 var _this$props = this.props, points = _this$props.points, xAxis = _this$props.xAxis, yAxis = _this$props.yAxis, layout = _this$props.layout, children = _this$props.children;
293 var errorBarItems = findAllByType(children, ErrorBar);
294 if (!errorBarItems) {
295 return null;
296 }
297 var dataPointFormatter = function dataPointFormatter2(dataPoint, dataKey) {
298 return {
299 x: dataPoint.x,
300 y: dataPoint.y,
301 value: dataPoint.value,
302 errorVal: getValueByDataKey(dataPoint.payload, dataKey)
303 };
304 };
305 var errorBarProps = {
306 clipPath: needClip ? "url(#clipPath-".concat(clipPathId, ")") : null
307 };
308 return /* @__PURE__ */ React.createElement(Layer, errorBarProps, errorBarItems.map(function(item) {
309 return /* @__PURE__ */ React.cloneElement(item, {
310 key: "bar-".concat(item.props.dataKey),
311 data: points,
312 xAxis,
313 yAxis,
314 layout,
315 dataPointFormatter
316 });
317 }));
318 }
319 }, {
320 key: "renderDots",
321 value: function renderDots(needClip, clipDot, clipPathId) {
322 var isAnimationActive = this.props.isAnimationActive;
323 if (isAnimationActive && !this.state.isAnimationFinished) {
324 return null;
325 }
326 var _this$props2 = this.props, dot = _this$props2.dot, points = _this$props2.points, dataKey = _this$props2.dataKey;
327 var lineProps = filterProps(this.props, false);
328 var customDotProps = filterProps(dot, true);
329 var dots = points.map(function(entry, i) {
330 var dotProps = _objectSpread(_objectSpread(_objectSpread({
331 key: "dot-".concat(i),
332 r: 3
333 }, lineProps), customDotProps), {}, {
334 index: i,
335 cx: entry.x,
336 cy: entry.y,
337 value: entry.value,
338 dataKey,
339 payload: entry.payload,
340 points
341 });
342 return Line2.renderDotItem(dot, dotProps);
343 });
344 var dotsProps = {
345 clipPath: needClip ? "url(#clipPath-".concat(clipDot ? "" : "dots-").concat(clipPathId, ")") : null
346 };
347 return /* @__PURE__ */ React.createElement(Layer, _extends({
348 className: "recharts-line-dots",
349 key: "dots"
350 }, dotsProps), dots);
351 }
352 }, {
353 key: "renderCurveStatically",
354 value: function renderCurveStatically(points, needClip, clipPathId, props) {
355 var _this$props3 = this.props, type = _this$props3.type, layout = _this$props3.layout, connectNulls = _this$props3.connectNulls;
356 _this$props3.ref;
357 var others = _objectWithoutProperties(_this$props3, _excluded);
358 var curveProps = _objectSpread(_objectSpread(_objectSpread({}, filterProps(others, true)), {}, {
359 fill: "none",
360 className: "recharts-line-curve",
361 clipPath: needClip ? "url(#clipPath-".concat(clipPathId, ")") : null,
362 points
363 }, props), {}, {
364 type,
365 layout,
366 connectNulls
367 });
368 return /* @__PURE__ */ React.createElement(Curve, _extends({}, curveProps, {
369 pathRef: this.pathRef
370 }));
371 }
372 }, {
373 key: "renderCurveWithAnimation",
374 value: function renderCurveWithAnimation(needClip, clipPathId) {
375 var _this2 = this;
376 var _this$props4 = this.props, points = _this$props4.points, strokeDasharray = _this$props4.strokeDasharray, isAnimationActive = _this$props4.isAnimationActive, animationBegin = _this$props4.animationBegin, animationDuration = _this$props4.animationDuration, animationEasing = _this$props4.animationEasing, animationId = _this$props4.animationId, animateNewValues = _this$props4.animateNewValues, width = _this$props4.width, height = _this$props4.height;
377 var _this$state = this.state, prevPoints = _this$state.prevPoints, totalLength = _this$state.totalLength;
378 return /* @__PURE__ */ React.createElement(Animate, {
379 begin: animationBegin,
380 duration: animationDuration,
381 isActive: isAnimationActive,
382 easing: animationEasing,
383 from: {
384 t: 0
385 },
386 to: {
387 t: 1
388 },
389 key: "line-".concat(animationId),
390 onAnimationEnd: this.handleAnimationEnd,
391 onAnimationStart: this.handleAnimationStart
392 }, function(_ref) {
393 var t = _ref.t;
394 if (prevPoints) {
395 var prevPointsDiffFactor = prevPoints.length / points.length;
396 var stepData = points.map(function(entry, index) {
397 var prevPointIndex = Math.floor(index * prevPointsDiffFactor);
398 if (prevPoints[prevPointIndex]) {
399 var prev = prevPoints[prevPointIndex];
400 var interpolatorX = interpolateNumber(prev.x, entry.x);
401 var interpolatorY = interpolateNumber(prev.y, entry.y);
402 return _objectSpread(_objectSpread({}, entry), {}, {
403 x: interpolatorX(t),
404 y: interpolatorY(t)
405 });
406 }
407 if (animateNewValues) {
408 var _interpolatorX = interpolateNumber(width * 2, entry.x);
409 var _interpolatorY = interpolateNumber(height / 2, entry.y);
410 return _objectSpread(_objectSpread({}, entry), {}, {
411 x: _interpolatorX(t),
412 y: _interpolatorY(t)
413 });
414 }
415 return _objectSpread(_objectSpread({}, entry), {}, {
416 x: entry.x,
417 y: entry.y
418 });
419 });
420 return _this2.renderCurveStatically(stepData, needClip, clipPathId);
421 }
422 var interpolator = interpolateNumber(0, totalLength);
423 var curLength = interpolator(t);
424 var currentStrokeDasharray;
425 if (strokeDasharray) {
426 var lines = "".concat(strokeDasharray).split(/[,\s]+/gim).map(function(num) {
427 return parseFloat(num);
428 });
429 currentStrokeDasharray = _this2.getStrokeDasharray(curLength, totalLength, lines);
430 } else {
431 currentStrokeDasharray = _this2.generateSimpleStrokeDasharray(totalLength, curLength);
432 }
433 return _this2.renderCurveStatically(points, needClip, clipPathId, {
434 strokeDasharray: currentStrokeDasharray
435 });
436 });
437 }
438 }, {
439 key: "renderCurve",
440 value: function renderCurve(needClip, clipPathId) {
441 var _this$props5 = this.props, points = _this$props5.points, isAnimationActive = _this$props5.isAnimationActive;
442 var _this$state2 = this.state, prevPoints = _this$state2.prevPoints, totalLength = _this$state2.totalLength;
443 if (isAnimationActive && points && points.length && (!prevPoints && totalLength > 0 || !isEqual(prevPoints, points))) {
444 return this.renderCurveWithAnimation(needClip, clipPathId);
445 }
446 return this.renderCurveStatically(points, needClip, clipPathId);
447 }
448 }, {
449 key: "render",
450 value: function render() {
451 var _filterProps;
452 var _this$props6 = this.props, hide = _this$props6.hide, dot = _this$props6.dot, points = _this$props6.points, className = _this$props6.className, xAxis = _this$props6.xAxis, yAxis = _this$props6.yAxis, top = _this$props6.top, left = _this$props6.left, width = _this$props6.width, height = _this$props6.height, isAnimationActive = _this$props6.isAnimationActive, id = _this$props6.id;
453 if (hide || !points || !points.length) {
454 return null;
455 }
456 var isAnimationFinished = this.state.isAnimationFinished;
457 var hasSinglePoint = points.length === 1;
458 var layerClass = clsx("recharts-line", className);
459 var needClipX = xAxis && xAxis.allowDataOverflow;
460 var needClipY = yAxis && yAxis.allowDataOverflow;
461 var needClip = needClipX || needClipY;
462 var clipPathId = isNil(id) ? this.id : id;
463 var _ref2 = (_filterProps = filterProps(dot, false)) !== null && _filterProps !== void 0 ? _filterProps : {
464 r: 3,
465 strokeWidth: 2
466 }, _ref2$r = _ref2.r, r = _ref2$r === void 0 ? 3 : _ref2$r, _ref2$strokeWidth = _ref2.strokeWidth, strokeWidth = _ref2$strokeWidth === void 0 ? 2 : _ref2$strokeWidth;
467 var _ref3 = hasClipDot(dot) ? dot : {}, _ref3$clipDot = _ref3.clipDot, clipDot = _ref3$clipDot === void 0 ? true : _ref3$clipDot;
468 var dotSize = r * 2 + strokeWidth;
469 return /* @__PURE__ */ React.createElement(Layer, {
470 className: layerClass
471 }, needClipX || needClipY ? /* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement("clipPath", {
472 id: "clipPath-".concat(clipPathId)
473 }, /* @__PURE__ */ React.createElement("rect", {
474 x: needClipX ? left : left - width / 2,
475 y: needClipY ? top : top - height / 2,
476 width: needClipX ? width : width * 2,
477 height: needClipY ? height : height * 2
478 })), !clipDot && /* @__PURE__ */ React.createElement("clipPath", {
479 id: "clipPath-dots-".concat(clipPathId)
480 }, /* @__PURE__ */ React.createElement("rect", {
481 x: left - dotSize / 2,
482 y: top - dotSize / 2,
483 width: width + dotSize,
484 height: height + dotSize
485 }))) : null, !hasSinglePoint && this.renderCurve(needClip, clipPathId), this.renderErrorBar(needClip, clipPathId), (hasSinglePoint || dot) && this.renderDots(needClip, clipDot, clipPathId), (!isAnimationActive || isAnimationFinished) && LabelList.renderCallByParent(this.props, points));
486 }
487 }], [{
488 key: "getDerivedStateFromProps",
489 value: function getDerivedStateFromProps(nextProps, prevState) {
490 if (nextProps.animationId !== prevState.prevAnimationId) {
491 return {
492 prevAnimationId: nextProps.animationId,
493 curPoints: nextProps.points,
494 prevPoints: prevState.curPoints
495 };
496 }
497 if (nextProps.points !== prevState.curPoints) {
498 return {
499 curPoints: nextProps.points
500 };
501 }
502 return null;
503 }
504 }, {
505 key: "repeat",
506 value: function repeat(lines, count) {
507 var linesUnit = lines.length % 2 !== 0 ? [].concat(_toConsumableArray(lines), [0]) : lines;
508 var result = [];
509 for (var i = 0; i < count; ++i) {
510 result = [].concat(_toConsumableArray(result), _toConsumableArray(linesUnit));
511 }
512 return result;
513 }
514 }, {
515 key: "renderDotItem",
516 value: function renderDotItem(option, props) {
517 var dotItem;
518 if (/* @__PURE__ */ React.isValidElement(option)) {
519 dotItem = /* @__PURE__ */ React.cloneElement(option, props);
520 } else if (isFunction(option)) {
521 dotItem = option(props);
522 } else {
523 var key = props.key, dotProps = _objectWithoutProperties(props, _excluded2);
524 var className = clsx("recharts-line-dot", typeof option !== "boolean" ? option.className : "");
525 dotItem = /* @__PURE__ */ React.createElement(Dot, _extends({
526 key
527 }, dotProps, {
528 className
529 }));
530 }
531 return dotItem;
532 }
533 }]);
534 }(reactExports.PureComponent);
535 _defineProperty(Line, "displayName", "Line");
536 _defineProperty(Line, "defaultProps", {
537 xAxisId: 0,
538 yAxisId: 0,
539 connectNulls: false,
540 activeDot: true,
541 dot: true,
542 legendType: "line",
543 stroke: "#3182bd",
544 strokeWidth: 1,
545 fill: "#fff",
546 points: [],
547 isAnimationActive: !Global.isSsr,
548 animateNewValues: true,
549 animationBegin: 0,
550 animationDuration: 1500,
551 animationEasing: "ease",
552 hide: false,
553 label: false
554 });
555 _defineProperty(Line, "getComposedData", function(_ref4) {
556 var props = _ref4.props, xAxis = _ref4.xAxis, yAxis = _ref4.yAxis, xAxisTicks = _ref4.xAxisTicks, yAxisTicks = _ref4.yAxisTicks, dataKey = _ref4.dataKey, bandSize = _ref4.bandSize, displayedData = _ref4.displayedData, offset = _ref4.offset;
557 var layout = props.layout;
558 var points = displayedData.map(function(entry, index) {
559 var value = getValueByDataKey(entry, dataKey);
560 if (layout === "horizontal") {
561 return {
562 x: getCateCoordinateOfLine({
563 axis: xAxis,
564 ticks: xAxisTicks,
565 bandSize,
566 entry,
567 index
568 }),
569 y: isNil(value) ? null : yAxis.scale(value),
570 value,
571 payload: entry
572 };
573 }
574 return {
575 x: isNil(value) ? null : xAxis.scale(value),
576 y: getCateCoordinateOfLine({
577 axis: yAxis,
578 ticks: yAxisTicks,
579 bandSize,
580 entry,
581 index
582 }),
583 value,
584 payload: entry
585 };
586 });
587 return _objectSpread({
588 points,
589 layout
590 }, offset);
591 });
592 var LineChart = generateCategoricalChart({
593 chartName: "LineChart",
594 GraphicalChild: Line,
595 axisComponents: [{
596 axisType: "xAxis",
597 AxisComp: XAxis
598 }, {
599 axisType: "yAxis",
600 AxisComp: YAxis
601 }],
602 formatAxisMap
603 });
604 const DynamicPricingPremium = () => {
605 const features = [
606 {
607 icon: Target,
608 title: __("Smart Pricing Rules"),
609 description: __(
610 "Create intelligent pricing rules based on demand, season, and booking patterns."
611 )
612 },
613 {
614 icon: Calendar,
615 title: __("Time-based Pricing"),
616 description: __(
617 "Set different prices for early bookings, last-minute deals, and peak seasons."
618 )
619 },
620 {
621 icon: Users,
622 title: __("Group Pricing"),
623 description: __(
624 "Offer tiered pricing based on group size and booking volume."
625 )
626 },
627 {
628 icon: BarChart,
629 title: __("Revenue Analytics"),
630 description: __(
631 "Track pricing performance and optimize your revenue strategy."
632 )
633 }
634 ];
635 const stats = [
636 {
637 icon: TrendingUp,
638 value: "42%",
639 label: __("Revenue Increase")
640 },
641 {
642 icon: Users,
643 value: "3.2x",
644 label: __("Booking Conversion")
645 },
646 {
647 icon: Target,
648 value: "Unlimited",
649 label: __("Pricing Rules")
650 },
651 {
652 icon: DollarSign,
653 value: "Auto",
654 label: __("Price Optimization")
655 }
656 ];
657 const popularRules = [
658 {
659 icon: Clock,
660 title: __("Early Bird Discounts"),
661 description: __("Reward early bookings with attractive discounts."),
662 discount: __("Save up to 20%")
663 },
664 {
665 icon: Zap,
666 title: __("Last-minute Deals"),
667 description: __("Fill remaining spots with dynamic last-minute pricing."),
668 discount: __("Save up to 30%")
669 },
670 {
671 icon: Calendar,
672 title: __("Seasonal Pricing"),
673 description: __(
674 "Adjust prices automatically based on peak and off-peak seasons."
675 ),
676 discount: __("Varies by season")
677 },
678 {
679 icon: Users,
680 title: __("Group Discounts"),
681 description: __("Offer better rates for larger group bookings."),
682 discount: __("Save 10-25%")
683 }
684 ];
685 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-8", children: [
686 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-gradient-to-r from-amber-50 to-orange-50 dark:from-amber-900/20 dark:to-orange-900/20 border border-amber-200 dark:border-amber-800 rounded-xl p-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
687 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
688 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "w-6 h-6 text-amber-600 dark:text-amber-400" }),
689 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
690 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-amber-900 dark:text-amber-100", children: __("Premium Feature") }),
691 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-amber-700 dark:text-amber-300 text-sm", children: __(
692 "Dynamic Pricing is a premium module. Upgrade to Yatra Pro to unlock intelligent pricing automation."
693 ) })
694 ] })
695 ] }),
696 /* @__PURE__ */ jsxRuntimeExports.jsxs(
697 Button,
698 {
699 className: "bg-amber-600 text-white hover:bg-amber-700 px-6 py-2 text-sm font-medium",
700 onClick: () => window.open(
701 "https://wpyatra.com/pricing?module=dynamic-pricing",
702 "_blank"
703 ),
704 children: [
705 __("Upgrade to Pro"),
706 /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { className: "w-4 h-4 ml-2" })
707 ]
708 }
709 )
710 ] }) }),
711 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center mb-12", children: [
712 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "inline-flex items-center justify-center w-16 h-16 rounded-full bg-blue-100 dark:bg-blue-900 mb-6", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Target, { className: "w-8 h-8 text-blue-600 dark:text-blue-400" }) }),
713 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center space-x-2 mb-4", children: [
714 /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-4xl font-bold text-gray-900 dark:text-white", children: __("Dynamic Pricing") }),
715 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex items-center px-2 py-1 rounded-full bg-gradient-to-r from-amber-500 to-orange-500 text-white text-xs font-semibold shadow-md", children: [
716 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "w-3 h-3 mr-1" }),
717 __("PRO")
718 ] })
719 ] }),
720 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xl text-gray-600 dark:text-gray-400 max-w-2xl mx-auto", children: __(
721 "Maximize your revenue with intelligent pricing that automatically adjusts based on demand, season, and booking patterns."
722 ) })
723 ] }),
724 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-6 mb-12", children: stats.map((stat, index) => {
725 const Icon = stat.icon;
726 return /* @__PURE__ */ jsxRuntimeExports.jsx(
727 Card,
728 {
729 className: "border border-gray-200 dark:border-gray-700",
730 children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-2", children: [
731 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-10 h-10 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { className: "w-5 h-5 text-blue-600 dark:text-blue-400" }) }),
732 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
733 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-bold text-gray-900 dark:text-white", children: stat.value }),
734 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-gray-600 dark:text-gray-400", children: stat.label })
735 ] })
736 ] }) })
737 },
738 index
739 );
740 }) }),
741 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-12", children: [
742 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white mb-6 text-center", children: __("Powerful Features") }),
743 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: features.map((feature, index) => {
744 const Icon = feature.icon;
745 return /* @__PURE__ */ jsxRuntimeExports.jsx(
746 Card,
747 {
748 className: "border border-gray-200 dark:border-gray-700",
749 children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-4", children: [
750 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { className: "w-6 h-6 text-blue-600 dark:text-blue-400" }) }),
751 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
752 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-2", children: feature.title }),
753 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: feature.description })
754 ] })
755 ] }) })
756 },
757 index
758 );
759 }) })
760 ] }),
761 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-12", children: [
762 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white mb-6 text-center", children: __("Popular Pricing Rules") }),
763 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6", children: popularRules.map((rule, index) => {
764 const Icon = rule.icon;
765 return /* @__PURE__ */ jsxRuntimeExports.jsx(
766 Card,
767 {
768 className: "border border-gray-200 dark:border-gray-700",
769 children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
770 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { className: "w-6 h-6 text-blue-600 dark:text-blue-400" }) }),
771 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold mb-2", children: rule.title }),
772 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-3", children: rule.description }),
773 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-blue-600 dark:text-blue-400 font-semibold text-sm", children: rule.discount })
774 ] }) })
775 },
776 index
777 );
778 }) })
779 ] }),
780 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { className: "mb-12 border border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { className: "pt-6", children: [
781 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6", children: [
782 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white mb-2", children: __("How It Works") }),
783 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-gray-600 dark:text-gray-400", children: __("Intelligent pricing in 4 simple steps") })
784 ] }),
785 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-6", children: [
786 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
787 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Target, { className: "w-8 h-8 text-blue-600 dark:text-blue-400" }) }),
788 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold mb-2", children: __("1. Set Rules") }),
789 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __(
790 "Define pricing rules based on demand, season, and booking patterns"
791 ) })
792 ] }),
793 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
794 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { className: "w-8 h-8 text-green-600 dark:text-green-400" }) }),
795 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold mb-2", children: __("2. Monitor Demand") }),
796 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("System tracks booking patterns and demand in real-time") })
797 ] }),
798 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
799 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 rounded-full bg-purple-100 dark:bg-purple-900 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "w-8 h-8 text-purple-600 dark:text-purple-400" }) }),
800 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold mb-2", children: __("3. Auto-Adjust") }),
801 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __(
802 "Prices automatically adjust based on your predefined rules"
803 ) })
804 ] }),
805 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
806 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 rounded-full bg-orange-100 dark:bg-orange-900 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TrendingUp, { className: "w-8 h-8 text-orange-600 dark:text-orange-400" }) }),
807 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold mb-2", children: __("4. Maximize Revenue") }),
808 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Optimize pricing for maximum revenue and occupancy") })
809 ] })
810 ] })
811 ] }) }),
812 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 border border-blue-200 dark:border-blue-800 rounded-xl p-8", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row items-center justify-between", children: [
813 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6 md:mb-0 md:pr-8", children: [
814 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
815 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "w-6 h-6 text-blue-600 dark:text-blue-400" }) }),
816 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-2xl font-bold text-gray-900 dark:text-white", children: __("Unlock Dynamic Pricing") })
817 ] }),
818 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-gray-600 dark:text-gray-400 mb-4", children: __(
819 "Get intelligent price adjustments and maximize your revenue with Yatra Pro."
820 ) }),
821 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
822 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
823 /* @__PURE__ */ jsxRuntimeExports.jsx(CheckCircle, { className: "w-4 h-4 text-green-500" }),
824 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Early bird discounts") })
825 ] }),
826 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
827 /* @__PURE__ */ jsxRuntimeExports.jsx(CheckCircle, { className: "w-4 h-4 text-green-500" }),
828 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Last-minute deals") })
829 ] }),
830 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
831 /* @__PURE__ */ jsxRuntimeExports.jsx(CheckCircle, { className: "w-4 h-4 text-green-500" }),
832 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Demand-based pricing") })
833 ] })
834 ] })
835 ] }),
836 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center", children: [
837 /* @__PURE__ */ jsxRuntimeExports.jsxs(
838 Button,
839 {
840 className: "bg-blue-600 text-white hover:bg-blue-700 px-8 py-3 text-sm font-medium mb-3",
841 onClick: () => window.open(
842 "https://wpyatra.com/pricing?module=dynamic-pricing",
843 "_blank"
844 ),
845 children: [
846 __("Upgrade to Pro"),
847 /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { className: "w-4 h-4 ml-2" })
848 ]
849 }
850 ),
851 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: __("14-day money-back guarantee") })
852 ] })
853 ] }) })
854 ] });
855 };
856 const isModuleAvailable = () => {
857 const yatraAdmin = window == null ? void 0 : window.yatraAdmin;
858 return Boolean(yatraAdmin == null ? void 0 : yatraAdmin.isPro);
859 };
860 const DynamicPricingPage = () => {
861 var _a, _b, _c;
862 const moduleAvailable = isModuleAvailable();
863 const [activeTab, setActiveTab] = reactExports.useState("rules");
864 const [showRuleTypeModal, setShowRuleTypeModal] = reactExports.useState(false);
865 const [settings, setSettings] = reactExports.useState({
866 rule_priority_mode: "highest",
867 maximum_markup_percent: 50,
868 maximum_discount_percent: 30,
869 calculation_period: 7,
870 update_frequency: "hourly",
871 show_original_price: true,
872 show_savings_badge: true,
873 show_urgency_messages: false,
874 /** discounted = rules use sale/effective price; regular = rules use list price when known */
875 calculation_base: "discounted"
876 });
877 const { showToast } = useToast();
878 const queryClient = useQueryClient();
879 const [searchTerm, setSearchTerm] = reactExports.useState("");
880 const [statusFilter, setStatusFilter] = reactExports.useState("all");
881 const [sortBy, setSortBy] = reactExports.useState("name");
882 const [sortOrder, setSortOrder] = reactExports.useState("asc");
883 const [selectedIds, setSelectedIds] = reactExports.useState([]);
884 const [bulkAction, setBulkAction] = reactExports.useState("");
885 const [showColumnsDropdown, setShowColumnsDropdown] = reactExports.useState(false);
886 const [confirmDialog, setConfirmDialog] = reactExports.useState({
887 isOpen: false,
888 rule: null
889 });
890 const [isSaving, setIsSaving] = reactExports.useState(false);
891 const closeConfirmDialog = () => setConfirmDialog({
892 isOpen: false,
893 rule: null,
894 title: "",
895 message: "",
896 confirmText: "",
897 variant: "danger",
898 isLoading: false,
899 onConfirm: () => {
900 }
901 });
902 const invalidateRules = () => {
903 queryClient.invalidateQueries({ queryKey: ["dynamic-pricing-rules"] });
904 queryClient.invalidateQueries({
905 queryKey: ["dynamic-pricing-statistics"]
906 });
907 };
908 const handleSettingChange = (key, value) => {
909 setSettings((prev) => ({ ...prev, [key]: value }));
910 };
911 const handleSaveSettings = async () => {
912 setIsSaving(true);
913 try {
914 await apiClient.post("/dynamic-pricing/settings", settings);
915 showToast(__("Settings saved successfully"), "success");
916 } catch (error) {
917 console.error("Failed to save settings:", error);
918 showToast(__("Failed to save settings. Please try again."), "error");
919 } finally {
920 setIsSaving(false);
921 }
922 };
923 const handleSelectRuleType = (ruleType) => {
924 const baseUrl = window.location.href.split("&action=")[0];
925 window.location.href = `${baseUrl}&action=create-pricing-rule&rule_type=${ruleType}`;
926 };
927 const { data: settingsData } = useQuery({
928 queryKey: ["dynamic-pricing-settings"],
929 queryFn: async () => {
930 const response = await apiClient.get("/dynamic-pricing/settings");
931 const body = (response == null ? void 0 : response.data) ?? response;
932 const payload = (body == null ? void 0 : body.data) ?? body;
933 return payload && typeof payload === "object" ? payload : {};
934 },
935 enabled: moduleAvailable
936 });
937 React.useEffect(() => {
938 if (settingsData && typeof settingsData === "object") {
939 setSettings((prev) => ({
940 ...prev,
941 ...settingsData,
942 calculation_base: settingsData.calculation_base === "regular" ? "regular" : "discounted"
943 }));
944 }
945 }, [settingsData]);
946 const { data: rulesData, isLoading } = useQuery({
947 queryKey: ["dynamic-pricing-rules"],
948 queryFn: async () => {
949 const response = await apiClient.get("/dynamic-pricing/rules");
950 return response;
951 },
952 enabled: moduleAvailable
953 });
954 const {
955 data: statsData,
956 isLoading: isStatsLoading,
957 error: statsError
958 } = useQuery({
959 queryKey: ["dynamic-pricing-statistics"],
960 queryFn: async () => {
961 const response = await apiClient.get("/dynamic-pricing/statistics");
962 return response;
963 },
964 enabled: moduleAvailable
965 });
966 const rulesPayload = ((_a = rulesData == null ? void 0 : rulesData.data) == null ? void 0 : _a.data) ?? (rulesData == null ? void 0 : rulesData.data) ?? [];
967 const statsPayload = ((_b = statsData == null ? void 0 : statsData.data) == null ? void 0 : _b.data) ?? (statsData == null ? void 0 : statsData.data) ?? {};
968 const rules = rulesPayload || [];
969 const stats = statsPayload || {};
970 const globalCurrency = ((_c = window == null ? void 0 : window.yatraAdmin) == null ? void 0 : _c.currency) || "USD";
971 const formatCurrencyAmount = (amount) => formatYatraMoney(Number(amount) || 0, globalCurrency, {
972 zeroAsUnknown: false
973 });
974 const trendData = Array.isArray(
975 stats == null ? void 0 : stats.pricing_history_trend_last_30_days
976 ) ? stats.pricing_history_trend_last_30_days.map((row) => {
977 const day = String(row.day || "");
978 const dateLabel = day ? toDateValue(day).toLocaleDateString() : "";
979 return {
980 day,
981 dateLabel,
982 events: Number(row.events) || 0,
983 totalAdjustmentAmount: Number(row.total_adjustment_amount) || 0,
984 avgAdjustmentPercentage: Number(row.avg_adjustment_percentage) || 0
985 };
986 }) : [];
987 const ruleImpact = Array.isArray(
988 stats == null ? void 0 : stats.pricing_history_rule_impact_last_30_days
989 ) ? stats.pricing_history_rule_impact_last_30_days : [];
990 const analyticsLoading = isLoading || isStatsLoading;
991 const statsErrorMessage = statsError ? (statsError == null ? void 0 : statsError.message) || String(statsError) : "";
992 const filteredRules = reactExports.useMemo(() => {
993 let filtered = [...rules];
994 if (searchTerm) {
995 filtered = filtered.filter(
996 (rule) => {
997 var _a2, _b2;
998 return ((_a2 = rule.name) == null ? void 0 : _a2.toLowerCase().includes(searchTerm.toLowerCase())) || ((_b2 = rule.rule_type) == null ? void 0 : _b2.toLowerCase().includes(searchTerm.toLowerCase()));
999 }
1000 );
1001 }
1002 if (statusFilter === "all") {
1003 filtered = filtered.filter((rule) => rule.status !== "trash");
1004 } else {
1005 filtered = filtered.filter((rule) => rule.status === statusFilter);
1006 }
1007 filtered.sort((a, b) => {
1008 let aVal = a[sortBy];
1009 let bVal = b[sortBy];
1010 if (sortBy === "created_at" || sortBy === "updated_at") {
1011 aVal = new Date(aVal).getTime();
1012 bVal = new Date(bVal).getTime();
1013 }
1014 if (sortOrder === "asc") {
1015 return aVal > bVal ? 1 : -1;
1016 } else {
1017 return aVal < bVal ? 1 : -1;
1018 }
1019 });
1020 return filtered;
1021 }, [rules, searchTerm, statusFilter, sortBy, sortOrder]);
1022 const bulkMutation = useMutation({
1023 mutationFn: async ({ action, ids }) => {
1024 const requests = ids.map((id) => {
1025 if (action === "delete") {
1026 return apiClient.delete(`/dynamic-pricing/rules/${id}`);
1027 } else if (action === "restore") {
1028 return apiClient.put(`/dynamic-pricing/rules/${id}`, {
1029 status: "active"
1030 });
1031 } else if (action === "trash") {
1032 return apiClient.put(`/dynamic-pricing/rules/${id}`, {
1033 status: "trash"
1034 });
1035 } else if (action === "active" || action === "inactive") {
1036 return apiClient.put(`/dynamic-pricing/rules/${id}`, {
1037 status: action
1038 });
1039 }
1040 return Promise.resolve();
1041 });
1042 const results = await Promise.allSettled(requests);
1043 const succeeded = results.filter((r) => r.status === "fulfilled").length;
1044 const failed = results.length - succeeded;
1045 return { succeeded, failed, total: results.length };
1046 },
1047 onSuccess: (result) => {
1048 invalidateRules();
1049 setSelectedIds([]);
1050 setBulkAction("");
1051 if (result.failed === 0) {
1052 showToast(__("Bulk action completed successfully"), "success");
1053 } else if (result.succeeded === 0) {
1054 showToast(__("Failed to complete bulk action"), "error");
1055 } else {
1056 showToast(
1057 `${result.succeeded}/${result.total} ${__("rules updated; some failed")}`,
1058 "warning"
1059 );
1060 }
1061 },
1062 onError: () => {
1063 showToast(__("Failed to complete bulk action"), "error");
1064 }
1065 });
1066 const handleBulkApply = () => {
1067 if (!bulkAction) {
1068 showToast(__("Select a bulk action first"), "warning");
1069 return;
1070 }
1071 if (selectedIds.length === 0) {
1072 showToast(__("Select at least one rule"), "warning");
1073 return;
1074 }
1075 bulkMutation.mutate({ action: bulkAction, ids: selectedIds });
1076 };
1077 if (!moduleAvailable) return /* @__PURE__ */ jsxRuntimeExports.jsx(DynamicPricingPremium, {});
1078 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
1079 /* @__PURE__ */ jsxRuntimeExports.jsx(
1080 PageHeader,
1081 {
1082 title: __("Dynamic Pricing"),
1083 description: __(
1084 "Intelligent price adjustments based on demand, seasonality, and booking patterns"
1085 )
1086 }
1087 ),
1088 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1089 /* @__PURE__ */ jsxRuntimeExports.jsxs("nav", { className: "-mb-px flex space-x-8", children: [
1090 /* @__PURE__ */ jsxRuntimeExports.jsxs(
1091 "button",
1092 {
1093 onClick: () => setActiveTab("rules"),
1094 className: `py-4 px-1 border-b-2 font-medium text-sm ${activeTab === "rules" ? "border-blue-500 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300"}`,
1095 children: [
1096 /* @__PURE__ */ jsxRuntimeExports.jsx(Target, { className: "w-5 h-5 inline-block mr-2" }),
1097 __("Pricing Rules")
1098 ]
1099 }
1100 ),
1101 /* @__PURE__ */ jsxRuntimeExports.jsxs(
1102 "button",
1103 {
1104 onClick: () => setActiveTab("analytics"),
1105 className: `py-4 px-1 border-b-2 font-medium text-sm ${activeTab === "analytics" ? "border-blue-500 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300"}`,
1106 children: [
1107 /* @__PURE__ */ jsxRuntimeExports.jsx(BarChart, { className: "w-5 h-5 inline-block mr-2" }),
1108 __("Analytics")
1109 ]
1110 }
1111 ),
1112 /* @__PURE__ */ jsxRuntimeExports.jsxs(
1113 "button",
1114 {
1115 onClick: () => setActiveTab("settings"),
1116 className: `py-4 px-1 border-b-2 font-medium text-sm ${activeTab === "settings" ? "border-blue-500 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300"}`,
1117 children: [
1118 /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { className: "w-5 h-5 inline-block mr-2" }),
1119 __("Settings")
1120 ]
1121 }
1122 )
1123 ] }),
1124 activeTab === "rules" && /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { onClick: () => setShowRuleTypeModal(true), className: "mb-4", children: [
1125 /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "w-4 h-4 mr-2" }),
1126 __("Create Rule")
1127 ] })
1128 ] }) }),
1129 activeTab === "rules" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
1130 isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-6", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1131 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
1132 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-4 bg-gray-200 dark:bg-gray-700 rounded w-24 mb-2 animate-pulse" }),
1133 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 bg-gray-200 dark:bg-gray-700 rounded w-16 animate-pulse" })
1134 ] }),
1135 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 bg-gray-200 dark:bg-gray-700 rounded-full animate-pulse" })
1136 ] }) }) }, i)) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-6", children: [
1137 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1138 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1139 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Total Rules") }),
1140 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold", children: stats.total_rules || 0 })
1141 ] }),
1142 /* @__PURE__ */ jsxRuntimeExports.jsx(Target, { className: "w-8 h-8 text-blue-600 dark:text-blue-400" })
1143 ] }) }) }),
1144 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1145 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1146 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Active Rules") }),
1147 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-green-600 dark:text-green-400", children: stats.active_rules || 0 })
1148 ] }),
1149 /* @__PURE__ */ jsxRuntimeExports.jsx(CheckCircle, { className: "w-8 h-8 text-green-600 dark:text-green-400" })
1150 ] }) }) }),
1151 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1152 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1153 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Inactive Rules") }),
1154 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-gray-600 dark:text-gray-400", children: stats.inactive_rules || 0 })
1155 ] }),
1156 /* @__PURE__ */ jsxRuntimeExports.jsx(AlertCircle, { className: "w-8 h-8 text-gray-600 dark:text-gray-400" })
1157 ] }) }) }),
1158 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1159 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1160 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Rule Types") }),
1161 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-purple-600 dark:text-purple-400", children: stats.rule_types_used || 0 })
1162 ] }),
1163 /* @__PURE__ */ jsxRuntimeExports.jsx(Package, { className: "w-8 h-8 text-purple-600 dark:text-purple-400" })
1164 ] }) }) })
1165 ] }),
1166 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1167 SearchFilterToolbar,
1168 {
1169 searchTerm,
1170 onSearchChange: setSearchTerm,
1171 statusFilter,
1172 onStatusChange: setStatusFilter,
1173 statusOptions: [
1174 { value: "all", label: __("All Status") },
1175 { value: "active", label: __("Active") },
1176 { value: "inactive", label: __("Inactive") },
1177 { value: "trash", label: __("Trash") }
1178 ],
1179 sortBy,
1180 onSortByChange: setSortBy,
1181 sortOrder,
1182 onSortOrderChange: setSortOrder,
1183 sortOptions: [
1184 { value: "name", label: __("Name") },
1185 { value: "priority", label: __("Priority") },
1186 { value: "created_at", label: __("Created Date") },
1187 { value: "updated_at", label: __("Updated Date") }
1188 ],
1189 onResetFilters: () => {
1190 setSearchTerm("");
1191 setStatusFilter("all");
1192 setSortBy("name");
1193 setSortOrder("asc");
1194 },
1195 hasFilters: !!searchTerm || statusFilter !== "all" || sortBy !== "name" || sortOrder !== "asc",
1196 placeholder: __("Search rules...")
1197 }
1198 ) }) }),
1199 /* @__PURE__ */ jsxRuntimeExports.jsx(
1200 BulkActionToolbar,
1201 {
1202 selectedIds,
1203 bulkAction,
1204 setBulkAction,
1205 onApply: handleBulkApply,
1206 onClearSelection: () => setSelectedIds([]),
1207 statusFilter,
1208 setStatusFilter,
1209 statusOptions: [
1210 {
1211 key: "all",
1212 label: __("All"),
1213 count: stats.total_rules ?? rules.filter((r) => r.status !== "trash").length
1214 },
1215 {
1216 key: "active",
1217 label: __("Active"),
1218 count: stats.active_rules || 0
1219 },
1220 {
1221 key: "inactive",
1222 label: __("Inactive"),
1223 count: stats.inactive_rules || 0
1224 },
1225 {
1226 key: "trash",
1227 label: __("Trash"),
1228 count: stats.trash_rules || 0
1229 }
1230 ],
1231 showColumnsDropdown,
1232 setShowColumnsDropdown,
1233 columnOptions: [
1234 { key: "name", label: __("Rule Name"), visible: true },
1235 { key: "adjustment", label: __("Adjustment"), visible: true },
1236 {
1237 key: "applicable_trips",
1238 label: __("Applicable To"),
1239 visible: true
1240 },
1241 { key: "priority", label: __("Priority"), visible: true },
1242 { key: "status", label: __("Status"), visible: true },
1243 { key: "created_at", label: __("Created"), visible: true }
1244 ],
1245 onToggleColumn: () => {
1246 },
1247 bulkMutationPending: bulkMutation.isPending,
1248 totalItems: filteredRules.length,
1249 bulkActionOptions: statusFilter === "trash" ? [
1250 { value: "restore", label: __("Restore") },
1251 { value: "delete", label: __("Delete Permanently") }
1252 ] : [
1253 { value: "active", label: __("Mark as Active") },
1254 { value: "inactive", label: __("Mark as Inactive") },
1255 { value: "trash", label: __("Move to Trash") }
1256 ]
1257 }
1258 ),
1259 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
1260 /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
1261 /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { children: __("Pricing Rules") }),
1262 /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: __("Manage your dynamic pricing rules") })
1263 ] }),
1264 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "p-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1265 Table,
1266 {
1267 data: filteredRules,
1268 columns: [
1269 {
1270 key: "name",
1271 label: __("Rule Name"),
1272 sortable: true,
1273 visible: true,
1274 render: (rule) => {
1275 var _a2;
1276 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1277 /* @__PURE__ */ jsxRuntimeExports.jsx(
1278 "a",
1279 {
1280 href: `${window.location.href.split("&action=")[0]}&action=edit-pricing-rule&id=${rule.id}`,
1281 className: `font-medium hover:underline transition-colors cursor-pointer ${rule.status === "trash" || statusFilter === "trash" ? "text-gray-400 dark:text-gray-600" : "text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300"}`,
1282 children: rule.name
1283 }
1284 ),
1285 /* @__PURE__ */ jsxRuntimeExports.jsx(
1286 "div",
1287 {
1288 className: `text-sm ${rule.status === "trash" || statusFilter === "trash" ? "text-gray-400 dark:text-gray-600" : "text-gray-500 dark:text-gray-400"}`,
1289 children: (_a2 = rule.rule_type) == null ? void 0 : _a2.replace("_", " ")
1290 }
1291 )
1292 ] });
1293 }
1294 },
1295 {
1296 key: "adjustment",
1297 label: __("Adjustment"),
1298 visible: true,
1299 render: (rule) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-gray-900 dark:text-white", children: rule.adjustment_type === "percentage" ? `${rule.adjustment_value > 0 ? "+" : ""}${rule.adjustment_value}%` : formatYatraMoney(
1300 Number(rule.adjustment_value) || 0,
1301 globalCurrency,
1302 { zeroAsUnknown: false }
1303 ) })
1304 },
1305 {
1306 key: "applicable_trips",
1307 label: __("Applicable To"),
1308 visible: true,
1309 render: (rule) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: rule.applicable_trips === "all" ? __("All Trips") : __("Specific Trips") })
1310 },
1311 {
1312 key: "priority",
1313 label: __("Priority"),
1314 sortable: true,
1315 visible: true,
1316 render: (rule) => /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", children: rule.priority })
1317 },
1318 {
1319 key: "status",
1320 label: __("Status"),
1321 sortable: true,
1322 visible: true,
1323 render: (rule) => /* @__PURE__ */ jsxRuntimeExports.jsx(
1324 Badge,
1325 {
1326 variant: rule.status === "active" ? "success" : "outline",
1327 children: rule.status
1328 }
1329 )
1330 },
1331 {
1332 key: "created_at",
1333 label: __("Created"),
1334 sortable: true,
1335 visible: true,
1336 render: (rule) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: new Date(rule.created_at).toLocaleDateString() })
1337 }
1338 ],
1339 actions: [
1340 {
1341 key: "edit",
1342 label: __("Edit"),
1343 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(PenSquare, { className: "w-4 h-4" }),
1344 onClick: (rule) => {
1345 const baseUrl = window.location.href.split("&action=")[0];
1346 window.location.href = `${baseUrl}&action=edit-pricing-rule&id=${rule.id}`;
1347 }
1348 },
1349 {
1350 key: "active",
1351 label: __("Mark as Active"),
1352 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "w-4 h-4" }),
1353 onClick: (rule) => {
1354 setConfirmDialog({
1355 isOpen: true,
1356 rule,
1357 title: __("Mark as Active"),
1358 message: __(
1359 "Are you sure you want to mark this pricing rule as active? It will start applying to trip pricing."
1360 ),
1361 onConfirm: async () => {
1362 try {
1363 await apiClient.put(
1364 `/dynamic-pricing/rules/${rule.id}`,
1365 {
1366 status: "active"
1367 }
1368 );
1369 showToast(
1370 __("Pricing rule marked as active successfully"),
1371 "success"
1372 );
1373 queryClient.invalidateQueries({
1374 queryKey: ["dynamic-pricing-rules"]
1375 });
1376 queryClient.invalidateQueries({
1377 queryKey: ["dynamic-pricing-statistics"]
1378 });
1379 setConfirmDialog({
1380 isOpen: false,
1381 rule: null,
1382 title: "",
1383 message: "",
1384 onConfirm: () => {
1385 }
1386 });
1387 } catch (error) {
1388 showToast(
1389 __("Failed to update pricing rule status"),
1390 "error"
1391 );
1392 }
1393 }
1394 });
1395 },
1396 condition: (rule) => rule.status !== "active" && rule.status !== "trash"
1397 },
1398 {
1399 key: "inactive",
1400 label: __("Mark as Inactive"),
1401 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(AlertCircle, { className: "w-4 h-4" }),
1402 onClick: (rule) => {
1403 setConfirmDialog({
1404 isOpen: true,
1405 rule,
1406 title: __("Mark as Inactive"),
1407 message: __(
1408 "Are you sure you want to mark this pricing rule as inactive? It will no longer apply to trip pricing."
1409 ),
1410 onConfirm: async () => {
1411 try {
1412 await apiClient.put(
1413 `/dynamic-pricing/rules/${rule.id}`,
1414 {
1415 status: "inactive"
1416 }
1417 );
1418 showToast(
1419 __(
1420 "Pricing rule marked as inactive successfully"
1421 ),
1422 "success"
1423 );
1424 queryClient.invalidateQueries({
1425 queryKey: ["dynamic-pricing-rules"]
1426 });
1427 queryClient.invalidateQueries({
1428 queryKey: ["dynamic-pricing-statistics"]
1429 });
1430 setConfirmDialog({
1431 isOpen: false,
1432 rule: null,
1433 title: "",
1434 message: "",
1435 onConfirm: () => {
1436 }
1437 });
1438 } catch (error) {
1439 showToast(
1440 __("Failed to update pricing rule status"),
1441 "error"
1442 );
1443 }
1444 }
1445 });
1446 },
1447 condition: (rule) => rule.status === "active"
1448 },
1449 {
1450 key: "restore",
1451 label: __("Restore"),
1452 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "w-4 h-4" }),
1453 onClick: (rule) => {
1454 setConfirmDialog({
1455 isOpen: true,
1456 rule,
1457 title: __("Restore Rule"),
1458 message: __(
1459 "Are you sure you want to restore this pricing rule? It will be moved back to active status."
1460 ),
1461 onConfirm: async () => {
1462 try {
1463 await apiClient.put(
1464 `/dynamic-pricing/rules/${rule.id}`,
1465 {
1466 status: "active"
1467 }
1468 );
1469 showToast(
1470 __("Pricing rule restored successfully"),
1471 "success"
1472 );
1473 queryClient.invalidateQueries({
1474 queryKey: ["dynamic-pricing-rules"]
1475 });
1476 queryClient.invalidateQueries({
1477 queryKey: ["dynamic-pricing-statistics"]
1478 });
1479 setConfirmDialog({
1480 isOpen: false,
1481 rule: null,
1482 title: "",
1483 message: "",
1484 onConfirm: () => {
1485 }
1486 });
1487 } catch (error) {
1488 showToast(
1489 __("Failed to restore pricing rule"),
1490 "error"
1491 );
1492 }
1493 }
1494 });
1495 },
1496 condition: (rule) => rule.status === "trash" || statusFilter === "trash"
1497 },
1498 {
1499 key: "trash",
1500 label: __("Move to Trash"),
1501 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "w-4 h-4" }),
1502 onClick: (rule) => {
1503 setConfirmDialog({
1504 isOpen: true,
1505 rule,
1506 title: __("Move Rule to Trash"),
1507 message: __(
1508 "Are you sure you want to move this pricing rule to Trash? It will stop applying to trip pricing immediately. You can restore it from the Trash filter."
1509 ),
1510 confirmText: __("Move to Trash"),
1511 variant: "warning",
1512 onConfirm: async () => {
1513 setConfirmDialog((prev) => ({
1514 ...prev,
1515 isLoading: true
1516 }));
1517 try {
1518 await apiClient.put(
1519 `/dynamic-pricing/rules/${rule.id}`,
1520 { status: "trash" }
1521 );
1522 showToast(
1523 __("Pricing rule moved to Trash"),
1524 "success"
1525 );
1526 invalidateRules();
1527 closeConfirmDialog();
1528 } catch (error) {
1529 setConfirmDialog((prev) => ({
1530 ...prev,
1531 isLoading: false
1532 }));
1533 showToast(
1534 __("Failed to move rule to Trash"),
1535 "error"
1536 );
1537 }
1538 }
1539 });
1540 },
1541 condition: (rule) => rule.status !== "trash" && statusFilter !== "trash"
1542 },
1543 {
1544 key: "delete",
1545 label: __("Delete Permanently"),
1546 icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "w-4 h-4" }),
1547 onClick: (rule) => {
1548 setConfirmDialog({
1549 isOpen: true,
1550 rule,
1551 title: __("Delete Pricing Rule Permanently"),
1552 message: __(
1553 "This will permanently delete the pricing rule. This action cannot be undone. Continue?"
1554 ),
1555 confirmText: __("Delete Permanently"),
1556 variant: "danger",
1557 onConfirm: async () => {
1558 setConfirmDialog((prev) => ({
1559 ...prev,
1560 isLoading: true
1561 }));
1562 try {
1563 await apiClient.delete(
1564 `/dynamic-pricing/rules/${rule.id}`
1565 );
1566 showToast(
1567 __("Pricing rule deleted permanently"),
1568 "success"
1569 );
1570 invalidateRules();
1571 closeConfirmDialog();
1572 } catch (error) {
1573 setConfirmDialog((prev) => ({
1574 ...prev,
1575 isLoading: false
1576 }));
1577 showToast(__("Failed to delete rule"), "error");
1578 }
1579 }
1580 });
1581 },
1582 condition: (rule) => rule.status === "trash" || statusFilter === "trash",
1583 variant: "destructive"
1584 }
1585 ],
1586 selectedItemIds: selectedIds,
1587 onSelectItem: (id, checked) => {
1588 if (checked) {
1589 setSelectedIds([...selectedIds, id]);
1590 } else {
1591 setSelectedIds(
1592 selectedIds.filter((selectedId) => selectedId !== id)
1593 );
1594 }
1595 },
1596 onSelectAll: (checked) => {
1597 if (checked) {
1598 setSelectedIds(filteredRules.map((rule) => rule.id));
1599 } else {
1600 setSelectedIds([]);
1601 }
1602 },
1603 isAllSelected: selectedIds.length === filteredRules.length && filteredRules.length > 0,
1604 getItemId: (rule) => rule.id,
1605 isLoading,
1606 emptyText: __("No pricing rules found"),
1607 emptyDescription: __(
1608 'Click "Create Rule" button above to get started'
1609 )
1610 }
1611 ) })
1612 ] })
1613 ] }),
1614 activeTab === "analytics" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
1615 analyticsLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-6", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { className: "pt-6", children: [
1616 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-4 bg-gray-200 dark:bg-gray-700 rounded w-24 mb-2 animate-pulse" }),
1617 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 bg-gray-200 dark:bg-gray-700 rounded w-16 mb-2 animate-pulse" }),
1618 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-3 bg-gray-200 dark:bg-gray-700 rounded w-20 animate-pulse" })
1619 ] }) }, i)) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-6", children: [
1620 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1621 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1622 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Total Rules") }),
1623 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-blue-600 dark:text-blue-400", children: stats.total_rules || 0 }),
1624 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __("All pricing rules") })
1625 ] }),
1626 /* @__PURE__ */ jsxRuntimeExports.jsx(Package, { className: "w-8 h-8 text-blue-600 dark:text-blue-400" })
1627 ] }) }) }),
1628 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1629 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1630 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Active Rules") }),
1631 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-green-600 dark:text-green-400", children: stats.active_rules || 0 }),
1632 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-green-600 dark:text-green-400 mt-1", children: __("Currently active") })
1633 ] }),
1634 /* @__PURE__ */ jsxRuntimeExports.jsx(CheckCircle, { className: "w-8 h-8 text-green-600 dark:text-green-400" })
1635 ] }) }) }),
1636 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1637 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1638 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Inactive Rules") }),
1639 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-orange-600 dark:text-orange-400", children: stats.inactive_rules || 0 }),
1640 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-orange-600 dark:text-orange-400 mt-1", children: __("Paused rules") })
1641 ] }),
1642 /* @__PURE__ */ jsxRuntimeExports.jsx(AlertCircle, { className: "w-8 h-8 text-orange-600 dark:text-orange-400" })
1643 ] }) }) }),
1644 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1645 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1646 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Rule Types Used") }),
1647 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-purple-600 dark:text-purple-400", children: stats.rule_types_used || 0 }),
1648 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-purple-600 dark:text-purple-400 mt-1", children: __("Different types") })
1649 ] }),
1650 /* @__PURE__ */ jsxRuntimeExports.jsx(Target, { className: "w-8 h-8 text-purple-600 dark:text-purple-400" })
1651 ] }) }) })
1652 ] }),
1653 analyticsLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-6", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { className: "pt-6", children: [
1654 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-4 bg-gray-200 dark:bg-gray-700 rounded w-28 mb-2 animate-pulse" }),
1655 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 bg-gray-200 dark:bg-gray-700 rounded w-20 mb-2 animate-pulse" }),
1656 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-3 bg-gray-200 dark:bg-gray-700 rounded w-32 animate-pulse" })
1657 ] }) }, i)) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-6", children: [
1658 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1659 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1660 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Pricing Events") }),
1661 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-blue-600 dark:text-blue-400", children: stats.pricing_history_total || 0 }),
1662 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __("Recorded price adjustments") })
1663 ] }),
1664 /* @__PURE__ */ jsxRuntimeExports.jsx(BarChart, { className: "w-8 h-8 text-blue-600 dark:text-blue-400" })
1665 ] }) }) }),
1666 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1667 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1668 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Trips Affected") }),
1669 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-orange-600 dark:text-orange-400", children: stats.pricing_history_trips_affected || 0 }),
1670 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __("Unique trips with adjustments") })
1671 ] }),
1672 /* @__PURE__ */ jsxRuntimeExports.jsx(Package, { className: "w-8 h-8 text-orange-600 dark:text-orange-400" })
1673 ] }) }) }),
1674 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1675 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1676 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Avg Adjustment") }),
1677 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-purple-600 dark:text-purple-400", children: typeof stats.pricing_history_avg_adjustment_percentage === "number" ? `${stats.pricing_history_avg_adjustment_percentage.toFixed(2)}%` : "0%" }),
1678 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __("Average % change") })
1679 ] }),
1680 /* @__PURE__ */ jsxRuntimeExports.jsx(DollarSign, { className: "w-8 h-8 text-purple-600 dark:text-purple-400" })
1681 ] }) }) }),
1682 /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1683 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1684 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: __("Last 30 Days") }),
1685 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-3xl font-bold text-green-600 dark:text-green-400", children: stats.pricing_history_last_30_days || 0 }),
1686 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __("Recent pricing events") })
1687 ] }),
1688 /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "w-8 h-8 text-green-600 dark:text-green-400" })
1689 ] }) }) })
1690 ] }),
1691 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
1692 /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
1693 /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { children: __("Rule Performance") }),
1694 /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: __("How each pricing rule is performing") })
1695 ] }),
1696 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: analyticsLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-4", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1697 "div",
1698 {
1699 className: "flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg",
1700 children: [
1701 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 space-y-2", children: [
1702 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-5 bg-gray-200 dark:bg-gray-700 rounded w-48 animate-pulse" }),
1703 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-4 bg-gray-200 dark:bg-gray-700 rounded w-32 animate-pulse" })
1704 ] }),
1705 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 bg-gray-200 dark:bg-gray-700 rounded w-24 animate-pulse" })
1706 ]
1707 },
1708 i
1709 )) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-4", children: ruleImpact.length > 0 ? ruleImpact.map((ri) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1710 "div",
1711 {
1712 className: "flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors",
1713 children: [
1714 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
1715 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-2", children: [
1716 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold text-gray-900 dark:text-white", children: ri.name || `${__("Rule")} #${ri.rule_id}` }),
1717 /* @__PURE__ */ jsxRuntimeExports.jsx(
1718 Badge,
1719 {
1720 variant: "default",
1721 className: "bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300",
1722 children: (ri.rule_type || "").replace("_", " ")
1723 }
1724 ),
1725 /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", children: ri.adjustment_type || "" })
1726 ] }),
1727 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400", children: [
1728 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
1729 __("Events"),
1730 ": ",
1731 ri.events || 0
1732 ] }),
1733 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" }),
1734 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
1735 __("Avg Adjustment"),
1736 ":",
1737 " ",
1738 typeof ri.avg_adjustment_value === "number" ? ri.avg_adjustment_value.toFixed(2) : Number(ri.avg_adjustment_value || 0).toFixed(
1739 2
1740 )
1741 ] })
1742 ] })
1743 ] }),
1744 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-right", children: [
1745 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: __("Impact (30d)") }),
1746 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: formatCurrencyAmount(
1747 Number(ri.total_adjustment_amount) || 0
1748 ) })
1749 ] })
1750 ]
1751 },
1752 ri.rule_id
1753 )) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center py-8 text-gray-500 dark:text-gray-400", children: rules.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1754 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: __(
1755 "No rule-level impact recorded in the last 30 days"
1756 ) }),
1757 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm mt-2 max-w-lg mx-auto", children: __(
1758 "Charts use dynamic pricing history when a booking applies an adjusted price. Complete a checkout (or recalculate pricing) after rules are active to populate metrics."
1759 ) })
1760 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1761 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: __("No pricing rules created yet") }),
1762 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm mt-2", children: __(
1763 "Create your first rule to see performance metrics"
1764 ) })
1765 ] }) }) }) })
1766 ] }),
1767 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
1768 /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
1769 /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { children: __("Revenue Impact Trend") }),
1770 /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: __(
1771 "30-day revenue comparison with and without dynamic pricing"
1772 ) })
1773 ] }),
1774 /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { children: [
1775 !analyticsLoading && statsErrorMessage ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 text-sm text-red-600 dark:text-red-400", children: [
1776 __("Failed to load statistics:"),
1777 " ",
1778 statsErrorMessage
1779 ] }) : null,
1780 analyticsLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-80 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" }) : trendData.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
1781 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full", style: { height: 280 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1782 LineChart,
1783 {
1784 data: trendData,
1785 margin: { top: 10, right: 20, left: 0, bottom: 0 },
1786 children: [
1787 /* @__PURE__ */ jsxRuntimeExports.jsx(CartesianGrid, { strokeDasharray: "3 3" }),
1788 /* @__PURE__ */ jsxRuntimeExports.jsx(
1789 XAxis,
1790 {
1791 dataKey: "day",
1792 tick: { fontSize: 12 },
1793 tickFormatter: (v) => v ? toDateValue(String(v)).toLocaleDateString() : ""
1794 }
1795 ),
1796 /* @__PURE__ */ jsxRuntimeExports.jsx(
1797 YAxis,
1798 {
1799 tick: { fontSize: 12 },
1800 tickFormatter: (v) => formatCurrencyAmount(Number(v) || 0)
1801 }
1802 ),
1803 /* @__PURE__ */ jsxRuntimeExports.jsx(
1804 Tooltip,
1805 {
1806 formatter: (value, name) => {
1807 if (name === "totalAdjustmentAmount") {
1808 return [
1809 formatCurrencyAmount(Number(value) || 0),
1810 __("Revenue Impact")
1811 ];
1812 }
1813 if (name === "events") {
1814 return [Number(value) || 0, __("Events")];
1815 }
1816 if (name === "avgAdjustmentPercentage") {
1817 return [
1818 `${Number(value || 0).toFixed(2)}%`,
1819 __("Avg Adjustment")
1820 ];
1821 }
1822 return [value, name];
1823 }
1824 }
1825 ),
1826 /* @__PURE__ */ jsxRuntimeExports.jsx(
1827 Line,
1828 {
1829 type: "monotone",
1830 dataKey: "totalAdjustmentAmount",
1831 stroke: "#22c55e",
1832 strokeWidth: 2,
1833 dot: { r: 2 },
1834 activeDot: { r: 4 }
1835 }
1836 )
1837 ]
1838 }
1839 ) }) }),
1840 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full overflow-x-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-h-[420px] overflow-y-auto rounded-lg border border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full table-fixed border-collapse text-sm", children: [
1841 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "sticky top-0 bg-white dark:bg-gray-900", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "text-left text-gray-600 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700", children: [
1842 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-3 px-4 w-1/4", children: __("Date") }),
1843 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-3 px-4 w-1/6 text-right", children: __("Events") }),
1844 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-3 px-4 w-1/3 text-right", children: __("Revenue Impact") }),
1845 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "py-3 px-4 w-1/4 text-right", children: __("Avg Adjustment") })
1846 ] }) }),
1847 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: trendData.map((row) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1848 "tr",
1849 {
1850 className: "border-b border-gray-100 dark:border-gray-800",
1851 children: [
1852 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-3 px-4 text-gray-900 dark:text-white", children: row.day ? toDateValue(
1853 String(row.day)
1854 ).toLocaleDateString() : "" }),
1855 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-3 px-4 text-right text-gray-900 dark:text-white", children: row.events ?? 0 }),
1856 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-3 px-4 text-right text-gray-900 dark:text-white tabular-nums", children: formatCurrencyAmount(
1857 Number(row.totalAdjustmentAmount) || 0
1858 ) }),
1859 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-3 px-4 text-right text-gray-900 dark:text-white tabular-nums", children: `${Number(row.avgAdjustmentPercentage || 0).toFixed(2)}%` })
1860 ]
1861 },
1862 row.day
1863 )) })
1864 ] }) }) })
1865 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-40 flex items-center justify-center border border-dashed border-gray-300 dark:border-gray-600 rounded-lg", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center px-4", children: [
1866 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-gray-500 dark:text-gray-400", children: Number(stats.pricing_history_total) > 0 && Number(stats.pricing_history_last_30_days) === 0 ? __(
1867 "No pricing events in the last 30 days (older history exists)."
1868 ) : __("No pricing history in the last 30 days") }),
1869 rules.length > 0 && Number(stats.pricing_history_total) === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400 mt-2 max-w-md mx-auto", children: __(
1870 "Revenue impact is recorded when a booking uses a trip price adjusted by your rules. Place a test booking to see this chart fill in."
1871 ) }) : null
1872 ] }) })
1873 ] })
1874 ] })
1875 ] }),
1876 activeTab === "settings" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
1877 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
1878 /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
1879 /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { children: __("General Settings") }),
1880 /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: __("Configure global dynamic pricing behavior") })
1881 ] }),
1882 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-6", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1883 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-4 bg-gray-200 dark:bg-gray-700 rounded w-32 animate-pulse" }),
1884 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-10 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" })
1885 ] }, i)) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
1886 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1887 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: __("Rule calculation base") }),
1888 /* @__PURE__ */ jsxRuntimeExports.jsxs(
1889 "select",
1890 {
1891 value: settings.calculation_base || "discounted",
1892 onChange: (e) => handleSettingChange(
1893 "calculation_base",
1894 e.target.value === "regular" ? "regular" : "discounted"
1895 ),
1896 className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500",
1897 children: [
1898 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "discounted", children: __(
1899 "Promotional / sale price (stack dynamic pricing on the price customers already see after trip discounts)"
1900 ) }),
1901 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "regular", children: __(
1902 "Regular list price (compute adjustments from catalog price; requires original price in context)"
1903 ) })
1904 ]
1905 }
1906 ),
1907 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __(
1908 "Choose whether percentage and fixed rules use the discounted trip price or the regular list price as their starting point. Bookings and availability pass both values when possible."
1909 ) })
1910 ] }),
1911 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1912 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: __("Rule Priority Mode") }),
1913 /* @__PURE__ */ jsxRuntimeExports.jsxs(
1914 "select",
1915 {
1916 value: settings.rule_priority_mode,
1917 onChange: (e) => handleSettingChange(
1918 "rule_priority_mode",
1919 e.target.value
1920 ),
1921 className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500",
1922 children: [
1923 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "highest", children: __(
1924 "Apply Largest Adjustment Only (one rule wins by magnitude)"
1925 ) }),
1926 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "cumulative", children: __("Apply All Matching Rules (Cumulative / Stack)") }),
1927 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "best", children: __("Apply Best Price for Customer (Lowest Final)") })
1928 ]
1929 }
1930 ),
1931 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __(
1932 "How to combine matching rules. Caps below still apply afterwards. Per-rule numeric Priority is used as a tie-breaker when multiple rules tie for largest / best."
1933 ) })
1934 ] }),
1935 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1936 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: __("Maximum Price Increase (%)") }),
1937 /* @__PURE__ */ jsxRuntimeExports.jsx(
1938 "input",
1939 {
1940 type: "number",
1941 value: settings.maximum_markup_percent,
1942 onChange: (e) => handleSettingChange(
1943 "maximum_markup_percent",
1944 parseInt(e.target.value)
1945 ),
1946 min: "0",
1947 max: "100",
1948 className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
1949 }
1950 ),
1951 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __(
1952 "Hard cap on upward adjustments (markup) across all matched rules to avoid surprising customers with sudden surges."
1953 ) })
1954 ] }),
1955 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1956 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: __("Maximum Price Decrease (%)") }),
1957 /* @__PURE__ */ jsxRuntimeExports.jsx(
1958 "input",
1959 {
1960 type: "number",
1961 value: settings.maximum_discount_percent,
1962 onChange: (e) => handleSettingChange(
1963 "maximum_discount_percent",
1964 parseInt(e.target.value)
1965 ),
1966 min: "0",
1967 max: "100",
1968 className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
1969 }
1970 ),
1971 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __(
1972 "Hard cap on downward adjustments (discount) so dynamic pricing can never push a trip below this fraction of its base price."
1973 ) })
1974 ] })
1975 ] }) })
1976 ] }),
1977 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
1978 /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
1979 /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { children: __("Demand Calculation") }),
1980 /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: __("Configure how booking demand is calculated") })
1981 ] }),
1982 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-6", children: [1, 2].map((i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1983 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-4 bg-gray-200 dark:bg-gray-700 rounded w-32 animate-pulse" }),
1984 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-10 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" })
1985 ] }, i)) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
1986 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1987 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: __("Calculation Period (Days)") }),
1988 /* @__PURE__ */ jsxRuntimeExports.jsx(
1989 "input",
1990 {
1991 type: "number",
1992 value: settings.calculation_period,
1993 onChange: (e) => handleSettingChange(
1994 "calculation_period",
1995 parseInt(e.target.value)
1996 ),
1997 min: "1",
1998 max: "30",
1999 className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
2000 }
2001 ),
2002 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __("Number of days to analyze for demand trends") })
2003 ] }),
2004 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2005 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: __("Update Frequency") }),
2006 /* @__PURE__ */ jsxRuntimeExports.jsxs(
2007 "select",
2008 {
2009 value: settings.update_frequency,
2010 onChange: (e) => handleSettingChange("update_frequency", e.target.value),
2011 className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500",
2012 children: [
2013 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "hourly", children: __("Every Hour") }),
2014 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "daily", children: __("Once Daily") }),
2015 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "realtime", children: __("Real-time (On Each Booking)") })
2016 ]
2017 }
2018 ),
2019 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: __("How often to recalculate demand scores") })
2020 ] })
2021 ] }) })
2022 ] }),
2023 /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
2024 /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
2025 /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { children: __("Display Settings") }),
2026 /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: __("How dynamic pricing is shown to customers") })
2027 ] }),
2028 /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-6", children: [1, 2].map((i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
2029 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-4 bg-gray-200 dark:bg-gray-700 rounded w-32 animate-pulse" }),
2030 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-10 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" })
2031 ] }, i)) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
2032 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg", children: [
2033 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2034 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold text-gray-900 dark:text-white", children: __("Show Original Price") }),
2035 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mt-1", children: __(
2036 "Display crossed-out original price when discount applied"
2037 ) })
2038 ] }),
2039 /* @__PURE__ */ jsxRuntimeExports.jsx(
2040 Toggle,
2041 {
2042 checked: settings.show_original_price,
2043 onChange: (checked) => handleSettingChange("show_original_price", checked)
2044 }
2045 )
2046 ] }),
2047 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg", children: [
2048 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2049 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold text-gray-900 dark:text-white", children: __("Show Savings Badge") }),
2050 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mt-1", children: __('Display "Save X%" badge on discounted trips') })
2051 ] }),
2052 /* @__PURE__ */ jsxRuntimeExports.jsx(
2053 Toggle,
2054 {
2055 checked: settings.show_savings_badge,
2056 onChange: (checked) => handleSettingChange("show_savings_badge", checked)
2057 }
2058 )
2059 ] }),
2060 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg", children: [
2061 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2062 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-semibold text-gray-900 dark:text-white", children: __("Show Urgency Messages") }),
2063 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mt-1", children: __(
2064 'Display messages like "Price increases soon" for demand-based rules'
2065 ) })
2066 ] }),
2067 /* @__PURE__ */ jsxRuntimeExports.jsx(
2068 Toggle,
2069 {
2070 checked: settings.show_urgency_messages,
2071 onChange: (checked) => handleSettingChange("show_urgency_messages", checked)
2072 }
2073 )
2074 ] })
2075 ] }) })
2076 ] }),
2077 !isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2078 Button,
2079 {
2080 onClick: handleSaveSettings,
2081 disabled: isSaving,
2082 className: "px-6",
2083 children: isSaving ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2084 /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { className: "w-4 h-4 mr-2 animate-spin" }),
2085 __("Saving...")
2086 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2087 /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { className: "w-4 h-4 mr-2" }),
2088 __("Save Settings")
2089 ] })
2090 }
2091 ) })
2092 ] }),
2093 /* @__PURE__ */ jsxRuntimeExports.jsx(
2094 RuleTypeSelectionModal,
2095 {
2096 isOpen: showRuleTypeModal,
2097 onClose: () => setShowRuleTypeModal(false),
2098 onSelectType: handleSelectRuleType
2099 }
2100 ),
2101 /* @__PURE__ */ jsxRuntimeExports.jsx(
2102 ConfirmationDialog,
2103 {
2104 isOpen: confirmDialog.isOpen,
2105 onClose: closeConfirmDialog,
2106 onConfirm: () => {
2107 var _a2;
2108 return (_a2 = confirmDialog.onConfirm) == null ? void 0 : _a2.call(confirmDialog);
2109 },
2110 title: confirmDialog.title || "",
2111 message: confirmDialog.message || "",
2112 confirmText: confirmDialog.confirmText,
2113 variant: confirmDialog.variant ?? "danger",
2114 isLoading: confirmDialog.isLoading ?? false
2115 }
2116 )
2117 ] });
2118 };
2119 export {
2120 DynamicPricingPage as default
2121 };
2122 //# sourceMappingURL=DynamicPricing-CBtPFtKK.js.map
2123