@everymatrix/player-rglimits 1.44.0 → 1.45.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1260 +0,0 @@
1
- <svelte:options tag={null} />
2
- <script lang="ts">
3
- import moment from 'moment';
4
- import { _, addNewMessages, setLocale } from './i18n';
5
- import { TRANSLATIONS } from './translations.js';
6
- import { onMount } from 'svelte';
7
- import type { MonetaryLimitType, LimitDefinition, DisplayedLimit, ScheduleType, FormattedSchedule } from '../types/types';
8
-
9
- import archBgSvg from './images/arch-bg.svg';
10
- import caretSelect from './images/caret-select.svg';
11
- import caretRight from './images/fa-caret-right.svg';
12
- import curencyIcon from './images/curency.svg';
13
- import calendarIcon from './images/fa-calendar-times-o.svg';
14
- import pencilIcon from './images/fa-pencil.svg';
15
- import timesIcon from './images/fa-times.svg';
16
- import plusIcon from './images/fa-plus-solid.svg';
17
-
18
- import '@everymatrix/general-animation-loading'
19
-
20
- export let session: string = '';
21
- export let sessiontype: string = '';
22
- export let userid: string = '';
23
- export let endpoint: string = '';
24
- export let transdetailsurl:string = '';
25
- export let transactionspageurl:string = '';
26
- export let clientstyling:string = '';
27
- export let clientstylingurl:string = '';
28
- export let lang:string = 'en';
29
- export let translationurl:string = '';
30
- export let datetimeformat:string = '';
31
- export let editlimitaction:string = '';
32
- export let deletelimitaction:string = '';
33
- export let cancelimitscheduleaction:string = '';
34
- export let getutctime:string = '';
35
-
36
- let displayNone:boolean = false;
37
- let customStylingContainer: HTMLElement;
38
-
39
- let isMounted:boolean = false;
40
- let isLoggedIn:boolean = false;
41
- let sessionID:string = '';
42
- let playerID:string = '';
43
- let isLoading:boolean = true;
44
- let isGaugeLoading:boolean = true;
45
- let noLimitToDisplay:boolean;
46
- let showDropdown:boolean = false;
47
-
48
- let displayedLimit:DisplayedLimit = {
49
- id:'',
50
- totalAmount: null,
51
- spentAmount: '',
52
- limitCurrency: 'EUR',
53
- remainingAmount: '',
54
- limitPeriod: '',
55
- limitProducts: '',
56
- from: '',
57
- to: '',
58
- };
59
-
60
- let limitWalletTypeList: Array<string> = [];
61
- let limitPeriodList: Array<string> = [];
62
- let limitTypeList: Array<string> = [];
63
- let selectedWalletType: string = '';
64
- let selectedLimitPeriod: string = '';
65
- let selectedLimitType: string = '';
66
- let selectedProduct: string = '';
67
- let limitDefinitionList: Array<MonetaryLimitType> = [];
68
- let productList: Array<string> = [];
69
-
70
- let gaugeValue:number = 0;
71
-
72
- let errorMessage:string = '';
73
-
74
- const timeMap = {
75
- Daily: 0,
76
- Weekly: 1,
77
- Monthly: 2
78
- };
79
-
80
- const walletTypeMap = {
81
- All: 'All',
82
- RealCash: 'Real Money'
83
- };
84
-
85
- type TranslationKeys = keyof typeof TRANSLATIONS;
86
- Object.keys(TRANSLATIONS).forEach((item: TranslationKeys): void => {
87
- addNewMessages(item, TRANSLATIONS[item]);
88
- });
89
-
90
- /**
91
- * Sets the isMounted flag after a short delay when the component is first rendered.
92
- */
93
- onMount(() => {
94
- setTimeout(() => {
95
- isMounted = true;
96
- }, 50)
97
-
98
- // Define the function to handle clicks outside the dropdown
99
- const handleOutsideClick = (event: Event) => {
100
- // Check if the click is outside the .ProductsDropdown
101
- if (!(event.target as HTMLElement).closest('.ProductsDropdown')) {
102
- // Close the dropdown
103
- showDropdown = false;
104
- }
105
- };
106
-
107
- // Add event listener to window for clicks
108
- window.addEventListener('click', handleOutsideClick);
109
-
110
- // Cleanup function to remove event listener when component is unmounted
111
- return () => {
112
- window.removeEventListener('click', handleOutsideClick);
113
- };
114
- })
115
-
116
- /**
117
- * Sets the session information upon login.
118
- */
119
- const setSession = ():void => {
120
- isLoggedIn = true;
121
- sessionID = session;
122
- playerID = userid;
123
- }
124
-
125
- /**
126
- * Handles errors by updating the error message.
127
- *
128
- * @param error - The error message to be handled.
129
- */
130
- const handleError = (error:string):void => {
131
- errorMessage = error;
132
- }
133
-
134
- /**
135
- * Find the best matching limit based on selected options.
136
- */
137
- const findBestMatchingLimit = () => {
138
- let filteredLimits = limitDefinitionList;
139
-
140
- if (selectedLimitPeriod) {
141
- filteredLimits = filteredLimits.filter(item => item.period === selectedLimitPeriod);
142
- }
143
-
144
- if (selectedWalletType) {
145
- filteredLimits = filteredLimits.filter(item => item.walletTypes.includes(selectedWalletType));
146
- }
147
-
148
- if (selectedLimitType) {
149
- filteredLimits = filteredLimits.filter(item => item.type === selectedLimitType);
150
- }
151
-
152
- if (selectedProduct) {
153
- filteredLimits = filteredLimits.filter(item => item.products.includes(selectedProduct));
154
- }
155
-
156
- // Return the first matching limit or the first limit in the list if no matches are found
157
- return filteredLimits[0] || limitDefinitionList[0];
158
- }
159
-
160
- /**
161
- * Retrieves and sets the limit balance based on available limit definitions.
162
- * If no limit definitions are found, sets appropriate flags.
163
- */
164
- const getLimitBalance = async (): Promise<void> => {
165
- showDropdown = false;
166
- try {
167
- limitDefinitionList = await getLimitDefinitions();
168
-
169
- if (limitDefinitionList.length === 0) {
170
- isLoading = false;
171
- noLimitToDisplay = true;
172
- return;
173
- }
174
- limitPeriodList = [...new Set(limitDefinitionList.map((limit) => limit.period))].sort((a, b) => timeMap[a] - timeMap[b]);
175
-
176
- if (!selectedLimitPeriod || !limitPeriodList.includes(selectedLimitPeriod)) {
177
- selectedLimitPeriod = limitPeriodList[0];
178
- }
179
- limitWalletTypeList = getWalletTypeList(selectedLimitPeriod);
180
-
181
- if (!selectedWalletType || !limitWalletTypeList.includes(selectedWalletType)) {
182
- selectedWalletType = limitWalletTypeList[0];
183
- }
184
- productList = getProductList(selectedLimitPeriod, selectedWalletType);
185
-
186
- if (!productList.includes(selectedProduct)) {
187
- selectedProduct = productList[0];
188
- }
189
- getLimitBalanceById(findBestMatchingLimit());
190
-
191
- } catch ( err ) {
192
- isLoading = false;
193
- handleError($_('fetchLimitBalanceError'));
194
- return;
195
- }
196
- }
197
-
198
- /**
199
- * Retrieves the limit definitions for a player.
200
- *
201
- * @returns A promise that resolves to an array of limits.
202
- */
203
- const getLimitDefinitions = async (): Promise<MonetaryLimitType[]> => {
204
- try {
205
- const url = new URL(`${endpoint}/v1/player/${playerID}/limits/monetary/`);
206
- const options = {
207
- method: 'GET',
208
- headers: {
209
- 'X-SessionId': sessionID,
210
- 'X-Session-Type': sessiontype,
211
- },
212
- };
213
-
214
- const response = await fetch(url, options);
215
-
216
- if (!response.ok) {
217
- throw new Error($_('fetchLimitDefError'));
218
- }
219
-
220
- const res = await response.json();
221
-
222
- const allowedLimitTypes = ['Deposit', 'Loss', 'Wagering'];
223
- const limitTypes: string[] = res.limits.map((item: MonetaryLimitType) => item.type);
224
- limitTypeList = Array.from(new Set(limitTypes)).filter((type) => allowedLimitTypes.includes(type));
225
-
226
- if (!selectedLimitType) {
227
- selectedLimitType = limitTypeList[0];
228
- }
229
-
230
- const filteredLimits = res.limits.filter((item: MonetaryLimitType) => item.type === selectedLimitType);
231
- return filteredLimits.length > 0 ? filteredLimits : [];
232
- } catch (err) {
233
- const errorMessage = err instanceof TypeError ? $_('invalidUrl') : err.message;
234
- handleError(errorMessage);
235
- }
236
- };
237
-
238
- /**
239
- * Updates product list based on selected period and walletType.
240
- *
241
- * @param period - The selected period.
242
- * @param walletType - The selected walletType.
243
- */
244
- const getProductList = (period: string, walletType: string): Array<string> => {
245
-
246
- const limits = limitDefinitionList.filter((limit) => limit.period === period && limit.walletTypes[0] === walletType);
247
- const newProductList = [];
248
-
249
- limits.forEach((currentLimit) => {
250
- currentLimit.products.forEach( (product) => {
251
- if(newProductList.indexOf(product) === -1) {
252
- newProductList.push(product)
253
- }
254
- });
255
- });
256
-
257
- return newProductList.sort();
258
- }
259
-
260
-
261
- /**
262
- * Updates wallet types list based on selected period.
263
- *
264
- * @param period - The selected period.
265
- */
266
- const getWalletTypeList = (period: string): Array<string> => {
267
-
268
- const limits = limitDefinitionList.filter((limit) => limit.period === period);
269
- const newWalletTypeList = [];
270
-
271
- limits.forEach((currentLimit) => {
272
- currentLimit.walletTypes.forEach((walletType) => {
273
- if (newWalletTypeList.indexOf(walletType) === -1) {
274
- newWalletTypeList.push(walletType)
275
- }
276
- });
277
- });
278
-
279
- return newWalletTypeList.sort();
280
- }
281
-
282
- /**
283
- * Retrieves the limit balance for a specific limit.
284
- *
285
- * @param limit - The financial transaction representing the limit.
286
- */
287
- const getLimitBalanceById = async (limit: MonetaryLimitType): Promise<void> => {
288
- try {
289
- isLoading = true;
290
-
291
- const url = new URL(`${endpoint}/v1/player/${playerID}/limits/monetary/balance`);
292
- url.searchParams.append('limitDefinitionId', limit.id);
293
-
294
- const options = {
295
- method: 'GET',
296
- headers: {
297
- 'X-SessionId': sessionID,
298
- 'X-Session-Type': sessiontype,
299
- },
300
- };
301
-
302
- const response = await fetch(url, options);
303
- if (!response.ok) {
304
- throw new Error($_('fetchLimitBalanceError'));
305
- }
306
-
307
- const responseData = await response.json();
308
- setCurrentLimit(responseData.limitBalances[0], limit);
309
- } catch (err) {
310
- const errorMessage = err instanceof TypeError ? $_('invalidUrl') : err.message;
311
- handleError(errorMessage);
312
- } finally {
313
- isLoading = false;
314
- }
315
- }
316
-
317
- /**
318
- * Sets the current limit based on the provided limit balance and financial transaction.
319
- *
320
- * @param limitBalance - The limit balance information.
321
- * @param limit - The financial transaction representing the limit.
322
- */
323
- const setCurrentLimit = (limitBalance: LimitDefinition, limit: MonetaryLimitType): void => {
324
- selectedLimitPeriod = limitBalance.limitPeriod;
325
- selectedProduct = limitBalance.limitProducts[0];
326
- selectedWalletType = limitBalance.limitWalletTypes[0];
327
-
328
- const limitSchedule = formatScheduleData(limit.id, limit.schedules);
329
- displayedLimit = {
330
- id: limit.id,
331
- totalAmount: limitBalance.limitAmount,
332
- spentAmount: limitBalance.spentBalance.amount.toFixed(2),
333
- limitCurrency: limitBalance.limitCurrency,
334
- remainingAmount: (limitBalance.limitAmount - limitBalance.spentBalance.amount).toFixed(2),
335
- limitPeriod: limitBalance.limitPeriod,
336
- limitProducts: selectedProduct,
337
- from: limitBalance.from,
338
- to: limitBalance.to,
339
- };
340
-
341
- if ( limitSchedule ) {
342
- displayedLimit.formattedSchedule = limitSchedule;
343
- }
344
-
345
- setGauge(displayedLimit);
346
- }
347
-
348
- /**
349
- * Formats schedule data for a specific limit definition.
350
- *
351
- * @param limitDefinitionId - The ID of the limit definition.
352
- * @param scheduleData - The schedule data to be formatted.
353
- * @returns The formatted limit schedule.
354
- */
355
- const formatScheduleData = (
356
- limitDefinitionId: string,
357
- scheduleData: ScheduleType[]
358
- ):FormattedSchedule => {
359
- const limitSchedule = scheduleData.find(schedule => schedule.playerLimitId === limitDefinitionId);
360
- return limitSchedule
361
- ? {
362
- updateAmount: limitSchedule.updateAmount,
363
- expires: limitSchedule.applyAt,
364
- expiresString: dateToReadableString(limitSchedule.applyAt),
365
- id: limitSchedule.id,
366
- isRemoved: limitSchedule.updateAmount < 1,
367
- isUpdated: limitSchedule.updateAmount > 0
368
- }
369
- : {
370
- updateAmount: '',
371
- expires: '',
372
- expiresString: '',
373
- id: '',
374
- isRemoved: false,
375
- isUpdated: false
376
- }
377
- }
378
-
379
- /**
380
- * Converts the date to a human-readable string.
381
- *
382
- * @param date - Date to convert.
383
- */
384
- const dateToReadableString = (date:string):string => {
385
- const getUtc = getutctime === 'true';
386
- return moment(date).utc(getUtc).format(datetimeformat || `DD/MM/YYYY HH:mm:ss`);
387
- }
388
-
389
- /**
390
- * Sets the gauge value based on the provided limit information.
391
- *
392
- * @param limit - The limit information to calculate the gauge value.
393
- */
394
- const setGauge = (limit:DisplayedLimit):void => {
395
- // Calculate newGaugeValue within [0, 180]
396
- let newGaugeValue = (180 / limit.totalAmount) * parseFloat(limit.spentAmount);
397
- newGaugeValue = Math.min(180, Math.max(0, newGaugeValue));
398
-
399
- const step = 1.5; // Step size for increasing/decreasing gaugeValue
400
- const interval = setInterval(() => {
401
- gaugeValue += (gaugeValue < newGaugeValue) ? step : -step;
402
-
403
- if (Math.abs(gaugeValue - newGaugeValue) <= step) {
404
- gaugeValue = newGaugeValue;
405
- clearInterval(interval);
406
- }
407
- });
408
-
409
- isGaugeLoading = false;
410
- }
411
-
412
- /**
413
- * Get the widget translated title.
414
- *
415
- * @param type - The type of limit
416
- */
417
- const getWidgetTitle = (type: string) => {
418
- switch ( type ) {
419
- case 'Deposit':
420
- return $_('depositLimitHeader');
421
- case 'Loss':
422
- return $_('lossLimitHeader');
423
- case 'Wagering':
424
- return $_('wageringLimitHeader');
425
- default:
426
- return '';
427
- }
428
- }
429
-
430
- /**
431
- * Handles the change in the selected period.
432
- *
433
- * @param event - The event object containing the target value.
434
- */
435
- const handlePeriodChange = (event: Event):void => {
436
- const element = event.currentTarget as HTMLInputElement
437
-
438
- selectedLimitPeriod = element.value;
439
-
440
- limitWalletTypeList = getWalletTypeList(selectedLimitPeriod);
441
- if (!selectedWalletType || !limitWalletTypeList.includes(selectedWalletType)) {
442
- selectedWalletType = limitWalletTypeList[0];
443
- }
444
- productList = getProductList(selectedLimitPeriod, selectedWalletType);
445
-
446
- showDropdown = false;
447
-
448
- if (!productList.includes(selectedProduct)) {
449
- selectedProduct = productList[0];
450
- }
451
-
452
- const selectedLimit = limitDefinitionList.find((limit) => {
453
- return limit.period === selectedLimitPeriod && limit.walletTypes[0] === selectedWalletType && limit.products.includes(selectedProduct);
454
- });
455
-
456
- if (!selectedLimit) {
457
- return;
458
- }
459
-
460
- getLimitBalanceById(selectedLimit);
461
- }
462
-
463
- /**
464
- * Handles the change in the selected wallet type.
465
- *
466
- * @param event - The event object containing the target value.
467
- */
468
- const handleWalletTypeChange = (event: Event):void => {
469
- const element = event.currentTarget as HTMLInputElement
470
-
471
- selectedWalletType = element.value;
472
- productList = getProductList(selectedLimitPeriod, selectedWalletType);
473
- showDropdown = false;
474
-
475
- if (!productList.includes(selectedProduct)) {
476
- selectedProduct = productList[0];
477
- }
478
-
479
- const selectedLimit = limitDefinitionList.find((limit) => {
480
- return limit.period === selectedLimitPeriod && limit.walletTypes[0] === selectedWalletType && limit.products.includes(selectedProduct);
481
- });
482
-
483
- if (!selectedLimit) {
484
- return;
485
- }
486
-
487
- getLimitBalanceById(selectedLimit);
488
- }
489
-
490
- /**
491
- * Handles the click event on the product buttons.
492
- *
493
- * @param product - The selected product.
494
- */
495
- const handleProductChange = (product:string) => {
496
-
497
- const selectedLimit = limitDefinitionList.find((limit) => {
498
- return limit.period === selectedLimitPeriod && limit.products.includes(product);
499
- });
500
-
501
- displayedLimit.limitProducts = product;
502
- showDropdown = false;
503
-
504
- if (selectedLimit) {
505
- getLimitBalanceById(selectedLimit);
506
- }
507
- }
508
-
509
- /**
510
- * Sets client styling by appending a style element to the custom styling container.
511
- */
512
- const setClientStyling = ():void => {
513
- let sheet = document.createElement('style');
514
- sheet.innerHTML = clientstyling;
515
- customStylingContainer.appendChild(sheet);
516
- }
517
-
518
- /**
519
- * Sets client styling URL by fetching the CSS file and appending a style element to the custom styling container.
520
- */
521
- const setClientStylingURL = ():void => {
522
- try {
523
- displayNone = true;
524
-
525
- let url:URL = new URL(clientstylingurl);
526
- let cssFile:HTMLElement = document.createElement('style');
527
-
528
- fetch(url.href)
529
- .then((res:any) => res.text())
530
- .then((data:any) => {
531
- cssFile.innerHTML = data;
532
-
533
- setTimeout(() => { customStylingContainer.appendChild(cssFile) }, 1);
534
- setTimeout(() => { displayNone = false; }, 500);
535
- });
536
- } catch ( err ) {
537
- const errorMessage = err instanceof TypeError ? $_('invalidUrl') : err.message;
538
- handleError(errorMessage);
539
- }
540
- }
541
-
542
- /**
543
- * Sets the active language by updating the locale.
544
- */
545
- const setActiveLanguage = ():void => {
546
- setLocale(lang);
547
- }
548
-
549
- /**
550
- * Sets translation URL by fetching translation data and updating messages.
551
- */
552
- const setTranslationUrl = ():void => {
553
- let url:URL = new URL(translationurl);
554
-
555
- fetch(url.href).then((res:any) => res.json())
556
- .then((res) => {
557
- Object.keys(res).forEach((item:any):void => {
558
- addNewMessages(item, res[item]);
559
- });
560
- }).catch((err:any) => {
561
- console.log(err);
562
- });
563
- }
564
-
565
- /**
566
- * Toggle gropdown.
567
- */
568
- const toggleDropdown = () => {
569
- showDropdown = !showDropdown;
570
- }
571
-
572
- /**
573
- * Format a number with a separator.
574
- *
575
- * @param nb - Number to format
576
- */
577
- const formatWithSeparator = (nb:string|number):string => {
578
- const separator = ' ';
579
- return nb.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, separator);
580
- }
581
-
582
- /**
583
- * Get the transactions url.
584
- */
585
- const getTransactionsUrl = ():string => {
586
- const transactionUrl = transactionspageurl;
587
- const page = selectedLimitType === 'Deposit' ? 'payment-transactions' : 'gaming-transactions';
588
- const subtractMap = {
589
- 'Daily': 1,
590
- 'Weekly': 7,
591
- 'Monthly': 30
592
- };
593
- const filterStartDate = subtractDays(new Date(displayedLimit.to), subtractMap[selectedLimitPeriod]);
594
-
595
- try {
596
- const url = new URL(`${transactionUrl}/gammatrix/gmwi/${page}`);
597
- url.searchParams.append('transStatuses', '2');
598
- url.searchParams.append('startTime', filterStartDate.toISOString());
599
- url.searchParams.append('endTime', new Date().toISOString());
600
- url.searchParams.append('dateOption', 'Select custom dates');
601
- url.searchParams.append('userId', userid);
602
- switch (selectedLimitType) {
603
- case 'Deposit':
604
- url.searchParams.append('transTypes', '1');
605
- break;
606
- case 'Wagering':
607
- url.searchParams.append('transTypes', '12');
608
- break;
609
- case 'Loss':
610
- url.searchParams.append('transTypes', '8');
611
- break;
612
- default:
613
- break;
614
- }
615
-
616
- return url.toString();
617
- } catch (err) {
618
- const errorMessage = err instanceof TypeError ? $_('invalidUrl') : err.message;
619
- handleError(errorMessage);
620
- }
621
- }
622
-
623
- /**
624
- * Starting from a specific date, substract a number of days.
625
- *
626
- * @param from - Start date.
627
- * @param days - Number of days to substract.
628
- */
629
- const subtractDays = (from: Date, days:number) => {
630
- return new Date(from.getTime() - (days * 24 * 60 * 60 * 1000));
631
- }
632
-
633
- /**
634
- * Sets the session information upon login.
635
- */
636
- const initListeners = ():void => {
637
- window.addEventListener('message', (e) => {
638
- if (e.data.type === 'RGW:RefreshLimits') {
639
- getLimitBalance();
640
- }
641
- });
642
- }
643
-
644
-
645
- /**
646
- * Sends DOM event for opening edit limit modal.
647
- *
648
- */
649
- const editLimit = ():void => {
650
- window.postMessage({
651
- type: 'RGW:EditLimit',
652
- payload: {
653
- id: displayedLimit.id
654
- }
655
- }, window.location.href)
656
- }
657
-
658
- /**
659
- * Sends DOM event to cancel the limit
660
- *
661
- */
662
- const deleteLimit = ():void => {
663
- window.postMessage({
664
- type: 'RGW:DeleteLimit',
665
- payload: {
666
- id: displayedLimit.id
667
- }
668
- }, window.location.href)
669
- }
670
-
671
- /**
672
- * Sends DOM event to cancel the limit schedule
673
- *
674
- */
675
- const cancelSchedule = ():void => {
676
- if (displayedLimit && displayedLimit.formattedSchedule) {
677
- window.postMessage({
678
- type: 'RGW:CancelLimitSchedule',
679
- payload: {
680
- limitId: displayedLimit.id,
681
- scheduleId: displayedLimit.formattedSchedule.id
682
- }
683
- }, window.location.href)
684
- }
685
- }
686
-
687
- /**
688
- * Sends DOM event to open add limit modal
689
- *
690
- */
691
- const addLimit = ():void => {
692
- window.postMessage({
693
- type: 'RGW:AddNewLimit'
694
- }, window.location.href)
695
- }
696
-
697
- $: isMounted && session && userid && setSession();
698
- $: isMounted && isLoggedIn && initListeners();
699
- $: isMounted && isLoggedIn && getLimitBalance();
700
- $: clientstyling && customStylingContainer && setClientStyling();
701
- $: clientstylingurl && customStylingContainer && setClientStylingURL();
702
- $: lang && setActiveLanguage();
703
- $: translationurl && setTranslationUrl();
704
- </script>
705
-
706
- <div class={displayNone ? 'DisplayNone' : ''}>
707
- <div class="LimitsContainer" bind:this={customStylingContainer}>
708
- {#if noLimitToDisplay}
709
- <div class="ContainerCenter">
710
- <p>{$_('noLimitToDisplay')}</p>
711
- {#if editlimitaction === 'true'}
712
- <button class="AddLimitControl" title="{$_('addLimitTitle')}" on:click|stopPropagation={() => addLimit()}>
713
- <span class="ActionsSvg">{@html plusIcon}</span>
714
- <span>{$_('addLimitText')}</span>
715
- </button>
716
- {/if}
717
- </div>
718
- {:else if errorMessage}
719
- <div class="ContainerCenter">
720
- <strong class="ErrorMessage">{errorMessage}</strong>
721
- </div>
722
- {:else}
723
- <div class="ContentLeft">
724
- <h2 class="LimitTypeHeader">{@html curencyIcon}{getWidgetTitle(selectedLimitType)}</h2>
725
- {#if isLoading}
726
- <general-animation-loading clientstyling={clientstyling} clientstylingurl={clientstylingurl} />
727
- {:else}
728
- <div class="ProductSelector">
729
- <div class="DisplayContainer">
730
- <span class="SelectedProduct">{selectedProduct === 'All' ? `${selectedProduct} ${$_('walletsLabel')}` : `${selectedProduct} ${$_('walletLabel')}`}</span>
731
- <span class="TotalAmount" title={`${formatWithSeparator(displayedLimit.totalAmount)} ${displayedLimit.limitCurrency}`}>{formatWithSeparator(displayedLimit.totalAmount)} <span class="Currency">{displayedLimit.limitCurrency}</span></span>
732
- {#if productList.length > 1}
733
- <button class="DropdownToggle {showDropdown ? 'Active' : ''}" on:click|stopPropagation={() => toggleDropdown()}>
734
- {@html caretSelect}
735
- </button>
736
- {/if}
737
- </div>
738
- <div class="ProductsDropdown {showDropdown ? 'Show' : ''}">
739
- {#each productList as product}
740
- <button on:click={() => handleProductChange(product)}>
741
- {product === 'All' ? `${product} ${$_('walletsLabel')}`: `${product} ${$_('walletLabel')}`}
742
- </button>
743
- {/each}
744
- </div>
745
- </div>
746
- <div class="DetailsContainer Entries">
747
- <div class="Row">
748
- <div class="Col">{$_('spentAmount')}</div>
749
- <div class="Col">
750
- <span class="CaretRight">{@html caretRight}</span> {formatWithSeparator(displayedLimit.spentAmount)} {displayedLimit.limitCurrency}
751
- </div>
752
- </div>
753
- <div class="Row">
754
- <div class="Col">{$_('remainingAmount')}</div>
755
- <div class="Col">
756
- <span class="CaretRight">{@html caretRight}</span> {formatWithSeparator(displayedLimit.remainingAmount)} {displayedLimit.limitCurrency}
757
- </div>
758
- </div>
759
- {#if displayedLimit.formattedSchedule.isUpdated === true}
760
- <div class="Row">
761
- <div class="Col">{$_('futureAmount')}</div>
762
- <div class="Col">
763
- <span class="CaretRight">{@html caretRight}</span> {formatWithSeparator(displayedLimit.formattedSchedule.updateAmount)} {displayedLimit.limitCurrency}
764
- </div>
765
- </div>
766
- {/if}
767
- <div class="Row">
768
- <div class="Col">{$_('startLabel')}</div>
769
- <div class="Col">
770
- <span class="CaretRight">{@html caretRight}</span> {dateToReadableString(displayedLimit.from)}
771
- </div>
772
- </div>
773
- <div class="Row">
774
- <div class="Col">{$_('resetLabel')}</div>
775
- <div class="Col">
776
- <span class="CaretRight">{@html caretRight}</span> {dateToReadableString(displayedLimit.to)}
777
- </div>
778
- </div>
779
- </div>
780
- {#if displayedLimit.formattedSchedule.isRemoved === true || displayedLimit.formattedSchedule.isUpdated === true }
781
- <div class="ExtraInfoContainer">
782
- {#if displayedLimit.formattedSchedule.isRemoved === true}
783
- <span>{$_('limitRemoved')}: {displayedLimit.formattedSchedule.expiresString}</span>
784
- {/if}
785
- {#if displayedLimit.formattedSchedule.isUpdated === true}
786
- <span>{$_('limitUpdated')}: {displayedLimit.formattedSchedule.expiresString}</span>
787
- {/if}
788
- </div>
789
- {/if}
790
- {#if sessiontype === 'admin'}
791
- <div class="UsefulLinksSection">
792
- {#if transdetailsurl}
793
- <a href="{transdetailsurl}" target="_blank">{$_('additionalLink1')}</a>
794
- {:else if transactionspageurl}
795
- <a href="{getTransactionsUrl()}" target="_blank">{$_('additionalLink2')}</a>
796
- {/if}
797
- </div>
798
- {/if}
799
- {/if}
800
- </div>
801
- <div class="ContentRight">
802
- <div class="WidgetControls">
803
- {#if limitWalletTypeList.length > 0}
804
- <div class="ControlContainer">
805
- <label for="LimitPeriod">{$_('limitWalletTypeLabel')}: </label>
806
- <select bind:value={selectedWalletType} id="WalletType" on:change={handleWalletTypeChange}>
807
- {#each limitWalletTypeList as value}
808
- <option {value}>
809
- {walletTypeMap[value]}
810
- </option>
811
- {/each}
812
- </select>
813
- </div>
814
- {/if}
815
- {#if limitPeriodList.length > 0}
816
- <div class="ControlContainer">
817
- <label for="LimitPeriod">{$_('changeLimitLabel')}: </label>
818
- <select bind:value={selectedLimitPeriod} id="LimitPeriod" on:change={handlePeriodChange}>
819
- {#each limitPeriodList as value}
820
- <option {value}>
821
- {value}
822
- </option>
823
- {/each}
824
- </select>
825
- </div>
826
- {/if}
827
- {#if limitTypeList.length > 0}
828
- <div class="ControlContainer">
829
- <label for="LimitType">{$_('limitTypeLabel')}: </label>
830
- <select bind:value={selectedLimitType} id="LimitType" on:change={getLimitBalance}>
831
- {#each limitTypeList as value}
832
- <option {value}>
833
- {value}
834
- </option>
835
- {/each}
836
- </select>
837
- </div>
838
- {/if}
839
- {#if limitTypeList.length > 0 && sessiontype === 'admin' && displayedLimit}
840
- <div class="ControlContainer">
841
- {#if editlimitaction === 'true'}
842
- <button class="ActionButton" title="{$_('addLimitTitle')}" on:click|stopPropagation={() => addLimit()}>
843
- <span class="ActionsSvg">{@html plusIcon}</span>
844
- </button>
845
- {/if}
846
- {#if editlimitaction === 'true'}
847
- <button class="ActionButton" title="{$_('editLimitTitle')}" on:click|stopPropagation={() => editLimit()}>
848
- <span class="ActionsSvg">{@html pencilIcon}</span>
849
- </button>
850
- {/if}
851
- {#if deletelimitaction === 'true'}
852
- <button class="ActionButton" title="{$_('deleteLimitTitle')}" on:click|stopPropagation={() => deleteLimit()}>
853
- <span class="ActionsSvg">{@html timesIcon}</span>
854
- </button>
855
- {/if}
856
- {#if cancelimitscheduleaction === 'true' && (displayedLimit.formattedSchedule && (displayedLimit.formattedSchedule.isRemoved === true || displayedLimit.formattedSchedule.isUpdated === true))}
857
- <button class="ActionButton" title="{$_('cancelScheduleTitle')}" on:click|stopPropagation={() => cancelSchedule()}>
858
- <span class="ActionsSvg">{@html calendarIcon}</span>
859
- </button>
860
- {/if}
861
- </div>
862
- {/if}
863
- </div>
864
- {#if isGaugeLoading}
865
- <general-animation-loading clientstyling={clientstyling} clientstylingurl={clientstylingurl} />
866
- {:else}
867
- <div class="Gauge">
868
- <div class="GaugeBody">
869
- <div class="Archbg">{@html archBgSvg}</div>
870
- <div class="GaugeFill" style="--p:{gaugeValue}deg"></div>
871
- <div class="GaugeCover" title={`${formatWithSeparator(displayedLimit.spentAmount)} ${displayedLimit.limitCurrency}`}>{formatWithSeparator(displayedLimit.spentAmount)} {displayedLimit.limitCurrency}</div>
872
- <div class="GaugeNeedleCover"></div>
873
- <div class="GaugeNeedle" style="--transform-needle-value: rotate({gaugeValue - 90}deg)"></div>
874
- </div>
875
- <div class="MinMaxContainer">
876
- <div class="Min"> <strong class="MinContent">0</strong> <span class="MinContentCurrency">{displayedLimit.limitCurrency}</span></div>
877
- <div class="Max"> <strong class="MaxContent">{formatWithSeparator(displayedLimit.totalAmount)}</strong> <span class="MaxContentCurrency">{displayedLimit.limitCurrency}</span></div>
878
- </div>
879
- </div>
880
- {/if}
881
- </div>
882
- {/if}
883
- </div>
884
- </div>
885
-
886
-
887
- <style lang="scss">
888
- $primary-text-color: var(--emw--color-gray-150, #76768B);
889
- $primary-color: var(--emw--color-primary, #307fe2);
890
- $primary-gauge-fill-color: var(--emw--gauge-fill-bg, $primary-color);
891
- $primary-contrast-color: var(--emw--color-contrast, #07072A);
892
-
893
- *,
894
- *::before,
895
- *::after {
896
- margin: 0;
897
- padding: 0;
898
- list-style: none;
899
- text-decoration: none;
900
- outline: none;
901
- box-sizing: border-box;
902
- }
903
-
904
- .DisplayNone {
905
- display: none;
906
- }
907
-
908
- .ContainerCenter {
909
- width: 100%;
910
- display: flex;
911
- flex-direction: column;
912
- justify-content: center;
913
- align-items: center;
914
- min-height: 219px;
915
- p { padding: 6px; }
916
- }
917
-
918
- .ErrorMessage {
919
- margin: 0 15px;
920
- font-size: var(--emw--font-size-x-small, 12px);
921
- color: var(--emw--color-error, var(--emw--color-red, #ed0909));
922
- }
923
-
924
- .LimitsContainer {
925
- display: flex;
926
- width: 100%;
927
- max-width: 700px;
928
- min-height: 150px;
929
- border: 1px solid $primary-color;
930
- border-radius: var(--emw--border-radius-large, 20px);
931
- overflow: hidden;
932
- box-shadow: 14px 26px 19.7px 0px $primary-text-color;
933
- gap:25px;
934
- padding: 25px;
935
- }
936
-
937
- .ContentLeft {
938
- line-height: 20px;
939
- flex:1;
940
- }
941
-
942
- .LimitTypeHeader {
943
- display: flex;
944
- align-items: center;
945
- color: $primary-text-color;
946
- margin-bottom: 20px;
947
- gap: 5px;
948
- }
949
-
950
- .DetailsContainer {
951
- margin-bottom: 15px;
952
- display: inline;
953
- span {
954
- font-weight: var(--emw--font-weight-bold, 700);
955
- }
956
- }
957
-
958
- .CaretRight {
959
- color: $primary-color;
960
- }
961
-
962
- .ContentRight {
963
- min-width: 300px;
964
- display: flex;
965
- justify-content: center;
966
- flex-direction: column;
967
- }
968
-
969
- .ExtraInfoContainer {
970
- padding: 1rem 0;
971
- color: var(--emfe-w-color-red, #ed0909);
972
- }
973
-
974
- .UsefulLinksSection {
975
- padding-top: 5px;
976
- a {
977
- color: $primary-color;
978
- text-decoration: underline;
979
- }
980
- }
981
-
982
- .AddLimitControl {
983
- border: 0.1rem solid $primary-color;
984
- border-radius: var(--emw--border-radius-small, 0.3rem);
985
- min-height: 1rem;
986
- color: var(--emw--color-white, #FFFFFF);
987
- background: $primary-color;
988
- padding: 0.3rem;
989
- cursor: pointer;
990
- min-width: 10rem;
991
- display: flex;
992
- flex-direction: row;
993
- align-items: center;
994
- justify-content: center;
995
- }
996
-
997
- .WidgetControls {
998
- display: flex;
999
- width: 100%;
1000
- justify-content: end;
1001
- gap: 10px;
1002
- }
1003
-
1004
- .ControlContainer {
1005
- label {
1006
- display: block;
1007
- width: 100%;
1008
- font-size: var(--emw--font-size-x-small, 12px);
1009
- font-weight: var(--emw--font-weight-bold, 700);
1010
- margin-bottom: 5px;
1011
- }
1012
- button {
1013
- padding: 5px;
1014
- border: 1px solid $primary-contrast-color;
1015
- border-radius: var(--emw--border-radius-small, 5px);
1016
- width: 25px;
1017
- cursor: pointer;
1018
- display: flex;
1019
- flex-direction: column;
1020
- margin-bottom: 4px;
1021
- }
1022
-
1023
- select {
1024
- max-width: 100px;
1025
- }
1026
-
1027
- .ActionsSvg {
1028
- height: 1em;
1029
- width: 1em;
1030
- display: flex;
1031
- justify-content: center;
1032
- cursor: pointer;
1033
- }
1034
- }
1035
-
1036
- .ControlContainer button {
1037
- padding: 5px;
1038
- border: 1px solid $primary-contrast-color;
1039
- border-radius: var(--emw--border-radius-small, 5px);
1040
- width: 25px;
1041
- cursor: pointer;
1042
- display: flex;
1043
- flex-direction: column;
1044
- margin-bottom: 4px;
1045
- }
1046
-
1047
- .ControlContainer .ActionsSvg {
1048
- height: 1em;
1049
- width: 1em;
1050
- display: flex;
1051
- justify-content: center;
1052
- cursor: pointer;
1053
- }
1054
-
1055
- .AddLimitControl .ActionsSvg {
1056
- height: 2em;
1057
- width: 2em;
1058
- display: flex;
1059
- justify-content: center;
1060
- cursor: pointer;
1061
- padding: 5px;
1062
- margin-right: 5px;
1063
- border: 1px solid $primary-contrast-color;
1064
- border-radius: var(--emw--border-radius-small, 4px);
1065
- background: var(--emw--color-white, #FFFFFF);
1066
- }
1067
-
1068
- .Gauge {
1069
- width: 100%;
1070
- font-family: 'Roboto', sans-serif;
1071
- color: var(--emw--color-black, #000000);
1072
- margin: 1rem 0;
1073
- flex: 1;
1074
- align-content: center;
1075
- }
1076
-
1077
- .GaugeBody {
1078
- width: 100%;
1079
- position: relative;
1080
- text-align: center;
1081
- padding: 23px;
1082
- padding-bottom: 0;
1083
- }
1084
-
1085
- .Archbg {
1086
- position: absolute;
1087
- right: 0;
1088
- bottom: 0;
1089
- }
1090
-
1091
- .GaugeFill {
1092
- --p:0deg;
1093
- --b:26px;
1094
- border-radius:500px 500px 0 0;
1095
- background: $primary-gauge-fill-color;
1096
- mask: radial-gradient(farthest-side at bottom,transparent calc(100% - var(--b) - 1px),#fff calc(100% - var(--b))), linear-gradient(var(--p),#fff 50%,transparent 0) top/100% 200%;
1097
- -webkit-mask: radial-gradient(farthest-side at bottom,transparent calc(100% - var(--b) - 1px),#fff calc(100% - var(--b))), linear-gradient(var(--p),#fff 50%,transparent 0) top/100% 200%;
1098
- mask-composite:intersect;
1099
- -webkit-mask-composite:destination-in;
1100
- }
1101
-
1102
- .GaugeFill::before {
1103
- content:"";
1104
- display:block;
1105
- padding-top:50%;
1106
- }
1107
-
1108
- .GaugeCover {
1109
- position: absolute;
1110
- left: 50%;
1111
- transform: translateX(-50%);
1112
- top: 50%;
1113
- font-size: var(--emw--font-size-large, 18px);
1114
- white-space: nowrap;
1115
- width: 130px;
1116
- max-width: 200px;
1117
- overflow-x: auto;
1118
- overflow-y: hidden;
1119
- -ms-overflow-style: none;
1120
- scrollbar-width: none;
1121
- }
1122
-
1123
- .GaugeNeedle {
1124
- width: 0.2rem;
1125
- height: 3.5rem;
1126
- background: linear-gradient(0deg, #000000 0, #000000 55%, #c5c5c5 55%, #c5c5c5 90%, #000000 90%, #000000 100%);
1127
- display: inline-block;
1128
- left: 49.5%;
1129
- position: absolute;
1130
- bottom: 0.1rem;
1131
- transform: var(--transform-needle-value);
1132
- transform-origin: bottom;
1133
- }
1134
-
1135
- .GaugeNeedleCover {
1136
- width: 60px;
1137
- height: 30px;
1138
- border-radius: 150px 150px 0 0;
1139
- background: var(--emw--color-black, #000000);
1140
- background: radial-gradient(circle at 50% 100%, var(--emw--color-black, #000000) 0%, var(--emw--color-black, #000000) 25%, #fff 25%, #fff 40%, $primary-color 40%);
1141
- position: absolute;
1142
- bottom: 0;
1143
- left: 50%;
1144
- border: 4px solid var(--emw--color-white, #FFFFFF);
1145
- border-bottom: 0;
1146
- transform: translateX(-50%);
1147
- }
1148
-
1149
- .MinMaxContainer {
1150
- display: flex;
1151
- justify-content: space-between;
1152
- margin-top: 10px;
1153
- }
1154
-
1155
- .ProductSelector {
1156
- position: relative;
1157
- margin-bottom: 20px;
1158
- }
1159
-
1160
- .DisplayContainer {
1161
- border: 1px solid $primary-color;
1162
- background-color: var(--emw--color-gray-100, #f5f5f5);
1163
- border-radius: var(--emw--border-radius-medium, 12px);
1164
- box-shadow: 4px 4px 4px 0px rgba(0, 0, 0, 0.25);
1165
- padding: 9px;
1166
- display: flex;
1167
- align-items: center;
1168
- justify-content: space-between;
1169
- color: $primary-contrast-color;
1170
- gap: 5px;
1171
- margin-top: 20px;
1172
- }
1173
-
1174
- .SelectedProduct {
1175
- font-weight: var(--emw--font-weight-bold, 700);
1176
- text-align: center;
1177
- width: 70px;
1178
- color: $primary-text-color;
1179
- }
1180
-
1181
- .TotalAmount {
1182
- font-size: var(--emw--font-size-2x-large, 36px);
1183
- align-items: baseline;
1184
- gap: 5px;
1185
- overflow-x: auto;
1186
- overflow-y: hidden;
1187
- -ms-overflow-style: none;
1188
- scrollbar-width: none;
1189
- line-height: normal;
1190
- justify-content: center;
1191
- white-space: nowrap;
1192
- max-width: 188px;
1193
- flex: 1;
1194
- text-align: center;
1195
- .Currency {
1196
- font-size: var(--emw--font-size-x-large, 24px);
1197
- }
1198
- }
1199
-
1200
- .TotalAmount {
1201
- &::-webkit-scrollbar{
1202
- display: none;
1203
- }
1204
- }
1205
-
1206
-
1207
- .ProductsDropdown{
1208
- display: none;
1209
- position: absolute;
1210
- background-color: var(--emw--color-gray-100, #f5f5f5);
1211
- width: 100%;
1212
- box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
1213
- z-index: 1;
1214
- border: 1px solid $primary-color;
1215
- &.Show {
1216
- display: block;
1217
- border-radius: var(--emw--border-radius-medium, 12px);
1218
- }
1219
-
1220
- button {
1221
- background-color: transparent;
1222
- border: none;
1223
- outline: none;
1224
- cursor: pointer;
1225
- padding: 10px;
1226
- width: 100%;
1227
- text-align: left;
1228
- font-weight: var(--emw--font-weight-bold, 700);
1229
- &:hover{
1230
- border-radius: var(--emw--border-radius-medium, 10px);
1231
- background-color: $primary-color;
1232
- color: var(--emw--color-white, #FFFFFF);
1233
- }
1234
- }
1235
- }
1236
-
1237
- .DropdownToggle{
1238
- width: 35px;
1239
- height: 35px;
1240
- display: flex;
1241
- justify-content: center;
1242
- align-items: center;
1243
- background: none;
1244
- border: none;
1245
- cursor: pointer;
1246
- color: $primary-color;
1247
- line-height: 0;
1248
- &.Active {
1249
- transform: rotate(90deg);
1250
- }
1251
- }
1252
-
1253
- .Row {
1254
- display: flex;
1255
- }
1256
-
1257
- .Col {
1258
- flex: 1;
1259
- }
1260
- </style>