PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.6
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.6
1.4.21 1.4.20 1.4.19 1.4.18 1.4.17 1.4.16 1.4.15 1.4.14 1.4.13 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.54 1.3.53 1.3.52 All 184 releases
disco / backend / views / components / Main / features / discount / discountSlice.js

discountSlice.js in Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO 1.4.6, at backend/views/components/Main/features/discount/discountSlice.js

637 lines 15.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { createSlice } from '@reduxjs/toolkit';
2 import { v4 as uuidv4 } from 'uuid';
3 import { DEFAULT_COUNTDOWN_DESIGN } from '../../utilities/count-down';
4 import { DEFAULT_TEXT_HIGHLIGHT_DESIGN } from '../../utilities/text-highlight-design';
5 import { DEFAULT_PRODUCT_BADGE_DESIGN } from '../../utilities/product-badge-design';
6
7 const initialState = {
8 name: '',
9 discount_intent: '',
10
11 discount_method: 'automated',
12 discount_coupon: '',
13
14 priority: '1',
15 status: '1',
16
17 show_discount_on_cart_page: false,
18
19 bogo_type: 'all',
20 discount_based_on: 'item_quantity',
21 count_quantity_as: 'separate',
22 free_item_selection: 'cart_order',
23
24 discount_rules: [
25 {
26 id: uuidv4(),
27 min: '',
28 max: '',
29 get_quantity: '',
30 get_ids: [],
31 discount_type: 'percent',
32 discount_value: '',
33 discount_label: '',
34 recursive: 'no',
35 },
36 ],
37
38 discount_max_user: '0',
39 discount_valid_from: '',
40 discount_valid_to: '',
41
42 products: ['all'],
43 conditions: [],
44
45 ui: [0, 0],
46
47 design_blocks: {
48 badge: {
49 enable: false,
50 badge_type: 'editable',
51 selected_design: DEFAULT_PRODUCT_BADGE_DESIGN,
52 position: 'top_left',
53 hasSeparator: false,
54 singleContainer: false,
55
56 // Main container styles
57 container: {
58 position: 'absolute',
59 left: '0px',
60 top: '0px',
61 width: '100px',
62 height: '30px',
63 padding: '5px 10px',
64 background: 'rgb(7, 200, 131)',
65 'clip-path':
66 'polygon(0% 0%, 100% 0%, 85% 49%, 100% 100%, 0% 100%)',
67 },
68 // Title styles
69 title: {
70 text: '[discounted_percentage] OFF',
71 color: '#FFFFFF',
72 'font-family': 'Manrope, sans-serif',
73 'font-size': '14px',
74 'font-weight': 700,
75 },
76 },
77
78 // 🧾 Text highlight (single product page)
79 text_highlight: {
80 enable: false,
81 badge_type: 'editable',
82 selected_design: DEFAULT_TEXT_HIGHLIGHT_DESIGN,
83 position: 'after_add_to_cart',
84
85 // Main container styles
86 container: {
87 width: '150px',
88 height: '40px',
89 display: 'flex',
90 padding: '5px 20px',
91 alignItems: 'center',
92 justifyContent: 'center',
93 'background-color': 'rgb(146, 81, 227)',
94 'border-color': '#fff',
95 'border-width': '0px',
96 radius: {
97 'top-left': '0px',
98 'top-right': '0px',
99 'bottom-right': '0px',
100 'bottom-left': '0px',
101 },
102 'border-style': 'solid',
103 clipPath:
104 'polygon(100% 0%, 90% 50%, 100% 100%, 0% 100%, 10% 50%, 0% 0%)',
105 },
106 // Title styles
107 title: {
108 text: 'Big savings: [discounted_amount]',
109 color: '#FFFFFF',
110 'font-family': 'Manrope, sans-serif',
111 'font-size': '14px',
112 'font-weight': 600,
113 'text-align': 'center',
114 },
115 },
116
117 // ⏳ Countdown timer
118 countdown: {
119 enable: false,
120 selected_design: DEFAULT_COUNTDOWN_DESIGN,
121 position: 'after_add_to_cart',
122
123 // Primary Text (Title)
124 title: {
125 text: 'Exclusive Deals',
126 'font-family': '',
127 'font-size': '14px',
128 'font-weight': 600,
129 'font-style': 'normal',
130 'text-decoration': 'none',
131 color: '#FFFFFF',
132 },
133
134 // Secondary Text (Subtitle)
135 subtitle: {
136 text: 'Available for a limited time only!',
137 'font-family': '',
138 'font-size': '12px',
139 'font-weight': 400,
140 'font-style': 'normal',
141 'text-decoration': 'none',
142 color: '#FFFFFF',
143 },
144
145 // Container (Area) styles
146 container: {
147 background:
148 'linear-gradient(90deg, #F39177 0%, #F61F48 35.5%, #A758EE 69%, #86C5FE 100%)',
149 'border-radius': '6px',
150 padding: '12px 16px 12px 16px',
151 'max-width': '370px',
152 border: 0,
153 'border-color': 'rgba(0, 0, 0, 0)',
154 isChain: false,
155 radius: {
156 'top-left': '6px',
157 'top-right': '6px',
158 'bottom-right': '6px',
159 'bottom-left': '6px',
160 },
161 },
162
163 // Counter Box styles
164 box: {
165 background: '#FFFFFF',
166 'border-radius': '2px',
167 padding: '8px 12px 8px 12px',
168 'min-width': '50px',
169 border: 0,
170 'border-color': 'rgba(0, 0, 0, 0)',
171 isChain: false,
172 radius: {
173 'top-left': '2px',
174 'top-right': '2px',
175 'bottom-right': '2px',
176 'bottom-left': '2px',
177 },
178 },
179
180 // Number styles (countdown digits)
181 number: {
182 'font-family': '',
183 'font-size': '20px',
184 'font-weight': 700,
185 color: '#FF4133',
186 },
187
188 // Label styles (DAYS, HOURS, etc.)
189 label: {
190 'font-family': '',
191 'font-size': '9px',
192 'font-weight': 400,
193 color: '#1A1D1F',
194 },
195
196 // Separator styles
197 separator: {
198 color: '#FFFFFF',
199 'font-size': '20px',
200 'font-weight': 700,
201 },
202
203 // Design metadata
204 hasSeparator: false,
205 singleContainer: false,
206 },
207 cart: {
208 enable: false,
209 design_type: 'notice',
210 selected_design: 'banner1',
211
212 // Banner Customization (includes text, styling, and button)
213 banner: {
214 text: '[discounted_percentage] OFF - Limited Time!',
215 'font-family': '',
216 'font-size': '14px',
217 'font-weight': 600,
218 'font-style': 'normal',
219 'text-decoration': 'none',
220 color: '#ffffff',
221 background: '#07C889',
222 'border-color': '#07C889',
223 icon_color: '#ffffff',
224 border: 0,
225 height: '45px',
226 isChain: false,
227 radius: {
228 'top-left': '8px',
229 'top-right': '8px',
230 'bottom-right': '8px',
231 'bottom-left': '8px',
232 },
233 // Banner Button (Shop Now inside banner)
234 button: {
235 enable: true,
236 text: 'Shop Now',
237 url: '',
238 'font-family': '',
239 'font-size': '12px',
240 'font-weight': 600,
241 'font-style': 'normal',
242 'text-decoration': 'none',
243 color: '#07C889',
244 background: '#ffffff',
245 'border-color': '#ffffff',
246 border: 0,
247 height: '35px',
248 width: '100px',
249 isChain: false,
250 radius: {
251 'top-left': '4px',
252 'top-right': '4px',
253 'bottom-right': '4px',
254 'bottom-left': '4px',
255 },
256 },
257 },
258
259 // Checkout Message Customization
260 checkout_message: {
261 enable: true,
262 text: 'You have saved [Discount Amount] on this order',
263 'font-family': '',
264 'font-size': '14px',
265 'font-weight': 400,
266 'font-style': 'normal',
267 'text-decoration': 'none',
268 color: '#16a34a',
269 },
270
271 // Product Item Savings Badge
272 savings_badge: {
273 enable: true,
274 text: 'SAVE [Amount]',
275 bg_color: '#ef4444',
276 text_color: '#ffffff',
277 'font-size': '12px',
278 },
279 },
280 // 📊 Bulk, Bundle discount table
281 table: {
282 enable: false,
283 position: 'after_add_to_cart',
284 table_style: 'style1',
285
286 heading: {
287 title: 'Title',
288 discount: 'Discount',
289 range: 'Ranges',
290 buy_now: 'Buy Now',
291 },
292
293 // ========================
294 // Heading Text (Used for header font styles)
295 // ========================
296 heading_customization: {
297 'font-family': '',
298 'font-size': '14px',
299 'font-weight': 400,
300 color: '#FFFFFF',
301 background: '#283142',
302 'border-color': '#F3F4F6',
303 height: '45px',
304 'border-right': '1px solid',
305 'border-bottom': '1px solid',
306 },
307
308 // ========================
309 // Cell Customization (Used for body font styles)
310 // ========================
311 cell_customization: {
312 'font-family': '',
313 'font-size': '14px',
314 'font-weight': 400,
315 color: '#374151',
316 background: '#ffffff',
317 'border-color': '#e5e7eb',
318 height: '45px',
319 'border-right': '1px solid',
320 'border-bottom': '1px solid',
321 },
322
323 // ========================
324 // Button Customization (Using the vibrant purple from original component)
325 // ========================
326 button: {
327 enable: true,
328 text: 'Buy Now',
329 'font-family': '',
330 'font-size': '14px',
331 'font-weight': 400,
332 height: '35px',
333 color: '#ffffff',
334 background: '#07C889',
335 border: '1px solid',
336 'border-color': '#ffffff',
337 'padding-top': '4px',
338 'padding-right': '8px',
339 'padding-bottom': '4px',
340 'padding-left': '8px',
341 isChain: false,
342 radius: {
343 'top-left': '6px',
344 'top-right': '6px',
345 'bottom-right': '6px',
346 'bottom-left': '6px',
347 },
348 },
349 },
350 },
351 };
352
353 export const discountSlice = createSlice({
354 name: 'discount',
355 initialState,
356 reducers: {
357 reset: () => initialState,
358 editCampaign: (state, action) => {
359 return { ...initialState, ...action.payload };
360 },
361 updateOption: (state, action) => {
362 state[action.payload.option] = action.payload.value;
363 },
364
365 updateProducts: (state, action) => {
366 if (
367 state.products.find(
368 (_product) => _product.id === action.payload.id
369 )
370 ) {
371 state.products = state.products.filter(
372 (_product) => _product.id !== action.payload.id
373 );
374 } else {
375 state.products.push(action.payload);
376 }
377 },
378
379 removeProduct: (state, action) => {
380 state.products = state.products.filter(
381 (_product) => _product.id !== action.payload.id
382 );
383 },
384
385 // *! Condition's Reducers
386
387 addCondition: (state, action) => {
388 const condition_group_index = state.conditions.findIndex(
389 (filter) => filter.id === action.payload
390 );
391 if (condition_group_index !== -1) {
392 state.conditions[condition_group_index].base_filters.push({
393 id: uuidv4(),
394 compare_with: '',
395 operator: 'and',
396 });
397 }
398 },
399
400 addConditionGroup: (state) => {
401 state.conditions.push({
402 id: uuidv4(),
403 base_operator: 'and',
404 base_filters: [
405 {
406 id: uuidv4(),
407 compare_with: '',
408 operator: 'and',
409 },
410 ],
411 });
412 },
413
414 updateConditionGroup: (state, action) => {
415 const condition_group_index = state.conditions.findIndex(
416 (filter) => filter.id === action.payload.id
417 );
418 if (condition_group_index !== -1) {
419 state.conditions[condition_group_index].base_operator =
420 action.payload.operator;
421 }
422 },
423
424 deleteConditionGroup: (state, action) => {
425 state.conditions = state.conditions.filter(
426 (conditionGroup) => conditionGroup.id !== action.payload
427 );
428 },
429
430 updateConditionValues: (state, action) => {
431 const condition_group_index = state.conditions.findIndex(
432 (filter) => filter.id === action.payload.group_id
433 );
434
435 if (condition_group_index !== -1) {
436 const condition_index = state.conditions[
437 condition_group_index
438 ].base_filters.findIndex(
439 (filter) => filter.id === action.payload.values.id
440 );
441
442 if (condition_index !== -1) {
443 state.conditions[condition_group_index].base_filters[
444 condition_index
445 ] = action.payload.values;
446 }
447 }
448 },
449
450 deleteCondition: (state, action) => {
451 const condition_group_index = state.conditions.findIndex(
452 (filter) => filter.id === action.payload.group_id
453 );
454 if (condition_group_index !== -1) {
455 if (
456 state.conditions[condition_group_index].base_filters
457 .length === 1
458 ) {
459 state.conditions = state.conditions.filter(
460 (conditionGroup) =>
461 conditionGroup.id !== action.payload.group_id
462 );
463 } else {
464 state.conditions[condition_group_index].base_filters =
465 state.conditions[
466 condition_group_index
467 ].base_filters.filter(
468 (condition) =>
469 condition.id !== action.payload.condition_id
470 );
471 }
472 }
473 },
474
475 // *! UI Tab Reducers
476
477 setTab: (state, action) => {
478 state.ui[0] = action.payload;
479 if (state.ui[1] < state.ui[0]) {
480 state.ui[1] = state.ui[0];
481 }
482 },
483
484 // *! Discount Rules Reducers
485
486 changeBOGOType: (state, action) => {
487 if (state.bogo_type !== action.payload) {
488 state.bogo_type = action.payload;
489 state.discount_rules = state.discount_rules.map((rule) => ({
490 ...rule,
491 get_ids: [],
492 }));
493 }
494 },
495
496 changeDiscountIntention: (state, action) => {
497 state.discount_intent = action.payload;
498 state.discount_rules = [
499 {
500 id: uuidv4(),
501 min: '',
502 max: '',
503 get_quantity: '',
504 get_ids: [],
505 discount_type: 'percent',
506 discount_value: '',
507 discount_label: '',
508 recursive: 'no',
509 },
510 ];
511 state.design_blocks = initialState.design_blocks;
512 },
513
514 addNewDiscountRule: (state) => {
515 state.discount_rules.push({
516 id: uuidv4(),
517 min: '',
518 max: '',
519 get_quantity: '',
520 get_ids: [],
521 discount_type: 'percent',
522 discount_value: '',
523 discount_label: '',
524 recursive: 'no',
525 });
526 },
527
528 updateDiscountRule: (state, action) => {
529 const index = state.discount_rules.findIndex(
530 (item) => item.id === action.payload.id
531 );
532 if (index !== -1) {
533 state.discount_rules[index] = action.payload;
534 }
535 },
536 deleteDiscountRule: (state, action) => {
537 state.discount_rules = state.discount_rules.filter(
538 (item) => item.id !== action.payload
539 );
540 },
541
542 // Enable recursive on a single rule and drop every other rule. Recursive
543 // applies one repeating tier, so multiple tiers no longer make sense.
544 keepOnlyRecursiveRule: (state, action) => {
545 const rule = state.discount_rules.find(
546 (item) => item.id === action.payload
547 );
548 if (rule) {
549 state.discount_rules = [{ ...rule, recursive: 'yes' }];
550 }
551 },
552
553 // *! Design Block Reducers
554
555 updateBadge: (state, action) => {
556 state.design_blocks.badge[action.payload.name] =
557 action.payload.value;
558 },
559
560 updateTextHighlight: (state, action) => {
561 state.design_blocks.text_highlight[action.payload.name] =
562 action.payload.value;
563 },
564
565 updateCountdown: (state, action) => {
566 state.design_blocks.countdown[action.payload.name] =
567 action.payload.value;
568 },
569
570 applyCountdownDesign: (state, action) => {
571 const countdown = state.design_blocks.countdown;
572 const { designKey, design } = action.payload;
573 countdown.selected_design = designKey;
574 countdown.title = { ...countdown.title, text: design.title.text, color: design.title.color, 'font-size': design.title['font-size'], 'font-weight': design.title['font-weight'] };
575 countdown.subtitle = { ...countdown.subtitle, text: design.subtitle.text, color: design.subtitle.color, 'font-size': design.subtitle['font-size'], 'font-weight': design.subtitle['font-weight'] };
576 countdown.container = { ...countdown.container, background: design.container.background, 'border-radius': design.container['border-radius'], border: design.container.border, 'border-color': design.container['border-color'], radius: { 'top-left': design.container['border-radius'], 'top-right': design.container['border-radius'], 'bottom-right': design.container['border-radius'], 'bottom-left': design.container['border-radius'] } };
577 countdown.box = { ...countdown.box, background: design.box.background, 'border-radius': design.box['border-radius'], 'min-width': design.box['min-width'] || '50px', radius: { 'top-left': design.box['border-radius'], 'top-right': design.box['border-radius'], 'bottom-right': design.box['border-radius'], 'bottom-left': design.box['border-radius'] } };
578 countdown.number = { ...countdown.number, color: design.number.color, 'font-size': design.number['font-size'], 'font-weight': design.number['font-weight'] };
579 countdown.label = { ...countdown.label, color: design.label.color, 'font-size': design.label['font-size'], 'font-weight': design.label['font-weight'] };
580 countdown.separator = { color: design.separator.color, 'font-size': design.separator['font-size'], 'font-weight': design.separator['font-weight'] };
581 countdown.image = { url: design.image.url };
582 countdown.hasSeparator = design.hasSeparator;
583 countdown.singleContainer = design.singleContainer;
584 },
585
586 updateTable: (state, action) => {
587 state.design_blocks.table[action.payload.name] =
588 action.payload.value;
589 },
590
591 updateCartPage: (state, action) => {
592 state.design_blocks.cart[action.payload.name] =
593 action.payload.value;
594 },
595
596 // updateSinglePage: (state, action) => {
597 // state.design_blocks.singlePage[action.payload.name] =
598 // action.payload.value;
599 // },
600 // updateCartPage: (state, action) => {
601 // state.design_blocks.cartPage[action.payload.name] =
602 // action.payload.value;
603 // },
604 },
605 });
606
607 export const {
608 reset,
609 editCampaign,
610 updateOption,
611 updateProducts,
612 removeProduct,
613 addCondition,
614 addConditionGroup,
615 updateConditionGroup,
616 deleteConditionGroup,
617 updateConditionValues,
618 deleteCondition,
619 setTab,
620
621 changeBOGOType,
622 changeDiscountIntention,
623 addNewDiscountRule,
624 updateDiscountRule,
625 deleteDiscountRule,
626 keepOnlyRecursiveRule,
627
628 updateBadge,
629 updateTextHighlight,
630 updateCountdown,
631 applyCountdownDesign,
632 updateTable,
633 updateCartPage,
634 } = discountSlice.actions;
635
636 export default discountSlice.reducer;
637