@everymatrix/player-rglimits 1.28.3 → 1.28.5

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.
@@ -4,9 +4,12 @@
4
4
  import { _, addNewMessages, setLocale } from './i18n';
5
5
  import { TRANSLATIONS } from './translations.js';
6
6
  import { onMount } from 'svelte';
7
+ import type { MonetaryLimitType, LimitDefinition, DisplayedLimit, ScheduleType, FormattedSchedule } from '../types/types';
7
8
 
8
9
  import faCaretRightSvg from './images/fa-caret-right.svg';
9
10
  import faDollarCircleSvg from './images/usd-circle.svg';
11
+ import archBgSvg from './images/arch-bg.svg';
12
+
10
13
  import '@everymatrix/general-animation-loading'
11
14
 
12
15
  export let session: string = '';
@@ -32,21 +35,21 @@
32
35
  let isGaugeLoading: boolean = true;
33
36
  let noLimitToDisplay:boolean;
34
37
 
35
- let multipleDepositLimits:boolean = false;
36
- let displayedLimit:Object = {
38
+ let displayedLimit:DisplayedLimit = {
39
+ totalAmount: null,
37
40
  spentAmount: '',
38
41
  limitCurrency: 'EUR',
39
42
  remainingAmount: '',
40
- limitType: '',
41
- limitProducts: []
43
+ limitPeriod: '',
44
+ limitProducts: ''
42
45
  };
43
- let limitPeriodList:Array<any> = [];
46
+ let limitPeriodList:Array<string> = [];
47
+ let limitTypeList:Array<string> = [];
44
48
  let selectedLimitPeriod:string = '';
45
- let limitDefinitionList:Array<any> = [];
49
+ let selectedLimitType:string = '';
50
+ let limitDefinitionList:Array<MonetaryLimitType> = [];
46
51
 
47
52
  let gaugeValue:number = 0;
48
- let gaugeNeedleValue:number = 0.750;
49
- let totalSpentPerc:number = 0.00;
50
53
 
51
54
  let errorMessage:string = '';
52
55
 
@@ -54,175 +57,271 @@
54
57
  addNewMessages(item, TRANSLATIONS[item]);
55
58
  });
56
59
 
60
+ /**
61
+ * Sets the isMounted flag after a short delay when the component is first rendered.
62
+ */
63
+ onMount(() => {
64
+ setTimeout(() => {
65
+ isMounted = true;
66
+ }, 50)
67
+ })
68
+
69
+ /**
70
+ * Sets the session information upon login.
71
+ */
57
72
  const setSession = ():void => {
58
73
  isLoggedIn = true;
59
74
  sessionID = session;
60
75
  playerID = userid;
61
76
  }
62
77
 
63
- const handleError = (error):void => {
78
+ /**
79
+ * Handles errors by updating the error message.
80
+ * @param error - The error message to be handled.
81
+ */
82
+ const handleError = (error:string):void => {
64
83
  errorMessage = error;
65
84
  }
66
85
 
67
- const formatScheduleData = (limitDefinitionId:any, scheduleData:any):Object => {
68
- const limitSchedule = scheduleData.find(x => x.playerLimitId === limitDefinitionId);
69
- return limitSchedule
70
- ? {
71
- updateAmount: limitSchedule.updateAmount,
72
- expires: limitSchedule.applyAt,
73
- expiresString: dateToReadableString(limitSchedule.applyAt),
74
- id: limitSchedule.id,
75
- isRemoved: limitSchedule.updateAmount < 1,
76
- isUpdated: limitSchedule.updateAmount > 0
77
- }
78
- : {
79
- updateAmount: '',
80
- expires: '',
81
- expiresString: '',
82
- id: '',
83
- isRemoved: false,
84
- isUpdated: false
86
+ /**
87
+ * Retrieves the limit balance for a specific limit.
88
+ * @param limit - The financial transaction representing the limit.
89
+ */
90
+ const getLimitBalanceById = async (limit: MonetaryLimitType): Promise<void> => {
91
+ try {
92
+ isLoading = true;
93
+
94
+ const url = new URL(`${endpoint}/v1/player/${playerID}/limits/monetary/balance`);
95
+ url.searchParams.append('limitDefinitionId', limit.id);
96
+
97
+ const options = {
98
+ method: 'GET',
99
+ headers: {
100
+ 'X-SessionId': sessionID,
101
+ 'X-Session-Type': sessiontype,
102
+ },
103
+ };
104
+
105
+ const response = await fetch(url, options);
106
+ if (!response.ok) {
107
+ throw new Error($_('fetchLimitBalanceError'));
85
108
  }
109
+
110
+ const responseData = await response.json();
111
+ setCurrentLimit(responseData.limitBalances[0], limit);
112
+ } catch (err) {
113
+ handleError(err.message);
114
+ } finally {
115
+ isLoading = false;
116
+ }
86
117
  }
87
118
 
88
- const getLimitBalanceById = (limit:any):void => {
89
- isLoading = true;
90
- const queryParams:URLSearchParams = new URLSearchParams({limitDefinitionId: limit.id})
91
- const url:URL = new URL(`${endpoint}/v1/player/${playerID}/limits/monetary/balance?${queryParams}`);
92
- const options = {
93
- method: 'GET',
94
- headers: {
95
- 'X-SessionId': sessionID,
96
- 'X-Session-Type': sessiontype,
97
- }
119
+ /**
120
+ * Get the widget translated title
121
+ * @param type - The type of limit
122
+ */
123
+ const getWidgetTitle = (type: string) => {
124
+ switch ( type ) {
125
+ case 'Deposit':
126
+ return $_('depositLimitHeader');
127
+ case 'Loss':
128
+ return $_('lossLimitHeader');
129
+ case 'Wagering':
130
+ return $_('wageringLimitHeader');
131
+ default:
132
+ return '';
98
133
  }
99
- fetch(url, options)
100
- .then((res:any) => res.json())
101
- .then((res) => {
102
- if (res && res.error) {
103
- throw new Error(res.error);
104
- }
105
- setCurrentLimit(res.limitBalances[0], limit);
106
- })
107
- .catch((err) => {
108
- handleError(err)
109
- }).finally(() => {
110
- isLoading = false;
111
- })
112
134
  }
113
135
 
114
- const getLimitDefinitions = ():Promise<void> => {
115
- return new Promise((resolve, reject) => {
116
- const url:URL = new URL(`${endpoint}/v1/player/${playerID}/limits/monetary/`);
136
+ /**
137
+ * Retrieves the limit definitions for a player.
138
+ *
139
+ * @returns A promise that resolves to an array of limits.
140
+ */
141
+ const getLimitDefinitions = async (): Promise<MonetaryLimitType[]> => {
142
+ try {
143
+ const url = new URL(`${endpoint}/v1/player/${playerID}/limits/monetary/`);
117
144
  const options = {
118
145
  method: 'GET',
119
146
  headers: {
120
147
  'X-SessionId': sessionID,
121
148
  'X-Session-Type': sessiontype,
122
- }
149
+ },
150
+ };
151
+
152
+ const response = await fetch(url, options);
153
+
154
+ if (!response.ok) {
155
+ throw new Error($_('fetchLimitDefError'));
123
156
  }
124
- fetch(url, options)
125
- .then((res:any) => res.json())
126
- .then((res:any) => {
127
- if (res && res.error) {
128
- return reject(res.error);
129
- }
130
- return resolve(res.limits.length > 0 ? res.limits.filter((item:any) => item.type === 'Deposit') : []);
131
- })
132
- .catch((err) => {
133
- return reject(err);
134
- })
135
- })
136
- }
137
157
 
158
+ const res = await response.json();
159
+
160
+ const allowedLimitTypes = ['Deposit', 'Loss', 'Wagering'];
161
+ const limitTypes: string[] = res.limits.map((item: MonetaryLimitType) => item.type);
162
+ limitTypeList = Array.from(new Set(limitTypes)).filter( (type) => allowedLimitTypes.includes(type));
163
+ if (!selectedLimitType) {
164
+ selectedLimitType = limitTypeList[0];
165
+ }
166
+
167
+ const filteredLimits = res.limits.filter((item: MonetaryLimitType) => item.type === selectedLimitType);
168
+ return filteredLimits.length > 0 ? filteredLimits : [];
169
+ } catch (err) {
170
+ throw err;
171
+ }
172
+ };
173
+
174
+ /**
175
+ * Handles the change in the selected period.
176
+ */
138
177
  const handlePeriodChange = ():void => {
139
- let selectedLimit = limitDefinitionList.find((limit) => `${limit.period} - ${limit.products[0]}` === selectedLimitPeriod)
178
+ let selectedLimit = limitDefinitionList.find((limit) => `${limit.period} - ${limit.products[0]}` === selectedLimitPeriod);
140
179
  getLimitBalanceById(selectedLimit);
141
180
  }
142
181
 
143
- const getLimitBalance = ():void => {
144
- getLimitDefinitions().then((depositLimitsDefinitionsData) => {
145
- limitDefinitionList = depositLimitsDefinitionsData;
182
+ /**
183
+ * Retrieves and sets the limit balance based on available limit definitions.
184
+ * If no limit definitions are found, sets appropriate flags.
185
+ */
186
+ const getLimitBalance = async (): Promise<void> => {
187
+ try {
188
+ limitDefinitionList = await getLimitDefinitions();
189
+
146
190
  if (limitDefinitionList.length === 0) {
147
191
  isLoading = false;
148
192
  noLimitToDisplay = true;
149
193
  return;
150
194
  }
151
195
 
152
- if (limitDefinitionList.length > 1) {
153
- const timeMap = {
154
- 'Daily': 0,
155
- 'Weekly': 1,
156
- 'Monthly': 2
157
- }
158
-
159
- limitDefinitionList.forEach((limit) => {
160
- if (limitPeriodList.indexOf(`${limit.period} - ${limit.products[0]}`) === -1) {
161
- limitPeriodList.push(`${limit.period} - ${limit.products[0]}`)
162
- }
163
- })
196
+ limitPeriodList = [];
164
197
 
165
- multipleDepositLimits = true;
166
- limitPeriodList = limitPeriodList.sort((a, b) => {
167
- return timeMap[a.split(' ')[0]] - timeMap[b.split(' ')[0]];
168
- });
198
+ const timeMap = {
199
+ 'Daily': 0,
200
+ 'Weekly': 1,
201
+ 'Monthly': 2
169
202
  }
170
203
 
204
+ limitDefinitionList.forEach((limit) => {
205
+ if (limitPeriodList.indexOf(`${limit.period} - ${limit.products[0]}`) === -1) {
206
+ limitPeriodList.push(`${limit.period} - ${limit.products[0]}`)
207
+ }
208
+ })
209
+
210
+ limitPeriodList = limitPeriodList.sort((a, b) => {
211
+ return timeMap[a.split(' ')[0]] - timeMap[b.split(' ')[0]];
212
+ });
213
+
171
214
  getLimitBalanceById(limitDefinitionList[0]);
172
- }, (err) => {
215
+ } catch ( err ) {
173
216
  isLoading = false;
174
- handleError(err)
175
- })
217
+ handleError(err.message);
218
+ return;
219
+ }
176
220
  }
177
221
 
222
+ /**
223
+ * Converts the date to a human-readable string
224
+ * @param date - Date to convert.
225
+ */
178
226
  const dateToReadableString = (date:string):string => {
179
227
  return moment(date).utc(true).format(datetimeformat || `DD/MM/YYYY HH:mm:ss`);
180
228
  }
181
229
 
182
- const setCurrentLimit = (limitBalance:any, limit:any):void => {
230
+ /**
231
+ * Sets the current limit based on the provided limit balance and financial transaction.
232
+ * @param limitBalance - The limit balance information.
233
+ * @param limit - The financial transaction representing the limit.
234
+ */
235
+ const setCurrentLimit = (limitBalance: LimitDefinition, limit: MonetaryLimitType): void => {
236
+
183
237
  selectedLimitPeriod = `${limitBalance.limitPeriod} - ${limitBalance.limitProducts[0]}`;
238
+
184
239
  const limitSchedule = formatScheduleData(limit.id, limit.schedules);
185
240
 
186
- displayedLimit = Object.assign({
187
- totalAmount: limitBalance.limitAmount,
188
- spentAmount: parseFloat(limitBalance.spentBalance.amount).toFixed(2),
189
- limitCurrency: limitBalance.limitCurrency,
190
- remainingAmount: parseFloat(limitBalance.limitAmount - limitBalance.spentBalance.amount).toFixed(2),
191
- limitPeriod: limitBalance.limitPeriod,
192
- limitProducts: limitBalance.limitProducts[0],
193
- product: limitBalance.limitWalletTypes
194
- }, limitSchedule)
241
+ displayedLimit = {
242
+ totalAmount: limitBalance.limitAmount,
243
+ spentAmount: limitBalance.spentBalance.amount.toFixed(2),
244
+ limitCurrency: limitBalance.limitCurrency,
245
+ remainingAmount: (limitBalance.limitAmount - limitBalance.spentBalance.amount).toFixed(2),
246
+ limitPeriod: limitBalance.limitPeriod,
247
+ limitProducts: limitBalance.limitProducts[0],
248
+ };
249
+
250
+ if ( limitSchedule ) {
251
+ displayedLimit.formattedSchedule = limitSchedule;
252
+ }
195
253
 
196
254
  setGauge(displayedLimit);
197
255
  }
198
256
 
199
- const setGauge = (limit:any):void => {
200
- let maxValueGauge = 0.50;
201
- let maxValueNeedle = 1.250;
202
- totalSpentPerc = 0.00;
203
- totalSpentPerc = (limit.spentAmount * 100) / limit.totalAmount;
204
- totalSpentPerc = parseFloat(totalSpentPerc).toFixed(2);
205
- let spentAmountPerc = (limit.spentAmount * maxValueGauge) / limit.totalAmount;
206
- gaugeValue = Number(parseFloat(spentAmountPerc).toFixed(3));
207
- gaugeValue = gaugeValue > maxValueGauge ? maxValueGauge : gaugeValue;
208
- gaugeNeedleValue = 0.750;
209
- gaugeNeedleValue = Number(parseFloat(spentAmountPerc + gaugeNeedleValue).toFixed(3));
210
- gaugeNeedleValue = gaugeNeedleValue > maxValueNeedle ? maxValueNeedle : gaugeNeedleValue;
257
+ /**
258
+ * Sets the gauge value based on the provided limit information.
259
+ * @param limit - The limit information to calculate the gauge value.
260
+ */
261
+ const setGauge = (limit:DisplayedLimit):void => {
262
+ const newGaugeValue = (180 / limit.totalAmount) * parseFloat(limit.spentAmount);
263
+ const step = 1.5; // Step size for increasing/decreasing gaugeValue
264
+ const interval = setInterval(() => {
265
+ if (Math.abs(gaugeValue - newGaugeValue) < step) {
266
+ gaugeValue = newGaugeValue;
267
+ clearInterval(interval);
268
+ } else {
269
+ gaugeValue += (gaugeValue < newGaugeValue) ? step : -step;
270
+ }
271
+
272
+ // Boundary checks to ensure gaugeValue stays within [0, 180]
273
+ gaugeValue = Math.min(180, Math.max(0, gaugeValue));
274
+
275
+ if (gaugeValue === newGaugeValue) {
276
+ clearInterval(interval);
277
+ }
278
+ });
279
+
211
280
  isGaugeLoading = false;
212
281
  }
213
282
 
214
- onMount(() => {
215
- setTimeout(() => {
216
- isMounted = true;
217
- }, 50)
218
- })
219
-
283
+ /**
284
+ * Formats schedule data for a specific limit definition.
285
+ * @param limitDefinitionId - The ID of the limit definition.
286
+ * @param scheduleData - The schedule data to be formatted.
287
+ * @returns The formatted limit schedule.
288
+ */
289
+ const formatScheduleData = (
290
+ limitDefinitionId: string,
291
+ scheduleData: ScheduleType[]
292
+ ):FormattedSchedule => {
293
+ const limitSchedule = scheduleData.find(schedule => schedule.playerLimitId === limitDefinitionId);
294
+ return limitSchedule
295
+ ? {
296
+ updateAmount: limitSchedule.updateAmount,
297
+ expires: limitSchedule.applyAt,
298
+ expiresString: dateToReadableString(limitSchedule.applyAt),
299
+ id: limitSchedule.id,
300
+ isRemoved: limitSchedule.updateAmount < 1,
301
+ isUpdated: limitSchedule.updateAmount > 0
302
+ }
303
+ : {
304
+ updateAmount: '',
305
+ expires: '',
306
+ expiresString: '',
307
+ id: '',
308
+ isRemoved: false,
309
+ isUpdated: false
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Sets client styling by appending a style element to the custom styling container.
315
+ */
220
316
  const setClientStyling = ():void => {
221
317
  let sheet = document.createElement('style');
222
318
  sheet.innerHTML = clientstyling;
223
319
  customStylingContainer.appendChild(sheet);
224
320
  }
225
321
 
322
+ /**
323
+ * Sets client styling URL by fetching the CSS file and appending a style element to the custom styling container.
324
+ */
226
325
  const setClientStylingURL = ():void => {
227
326
  displayNone = true;
228
327
 
@@ -239,10 +338,16 @@
239
338
  });
240
339
  }
241
340
 
341
+ /**
342
+ * Sets the active language by updating the locale.
343
+ */
242
344
  const setActiveLanguage = ():void => {
243
345
  setLocale(lang);
244
346
  }
245
347
 
348
+ /**
349
+ * Sets translation URL by fetching translation data and updating messages.
350
+ */
246
351
  const setTranslationUrl = ():void => {
247
352
  let url:URL = new URL(translationurl);
248
353
 
@@ -264,96 +369,116 @@
264
369
  $: translationurl && setTranslationUrl();
265
370
  </script>
266
371
 
267
-
268
- <div class="LimitsContainer" bind:this={customStylingContainer}>
269
- {#if noLimitToDisplay}
270
- <p>{$_('noLimitToDisplay')}</p>
271
- {:else if errorMessage}
272
- <strong class="ErrorMessage">{errorMessage}</strong>
273
- {:else}
274
- <div class="ContentLeft">
275
- <h2 class="LimitTypeHeader"><img class="HeaderIcon" alt="user" src="{faDollarCircleSvg}" /> Deposit limit</h2>
276
- {#if isLoading}
277
- <general-animation-loading clientstyling={clientstyling} clientstylingurl={clientstylingurl} />
278
- {:else}
279
- <div class="DetailsContainer Entries">
280
- <span>{$_('spentAmount')}: </span>
281
- <span>{$_('remainingAmount')}: </span>
282
- {#if displayedLimit.isUpdated === true}
283
- <span>{$_('futureAmount')}: </span>
284
- {/if}
285
- <span>{$_('limitPeriod')}: </span>
286
- <span>{$_('displayedProduct')}: </span>
287
- </div>
288
- <div class="TextContainer">
289
- <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.spentAmount} {displayedLimit.limitCurrency}</p>
290
- <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.remainingAmount} {displayedLimit.limitCurrency}</p>
291
- {#if displayedLimit.isUpdated === true}
292
- <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.updateAmount} {displayedLimit.limitCurrency} </p>
293
- {/if}
294
- <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.limitPeriod}</p>
295
- <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.limitProducts}</p>
296
- </div>
297
- {#if displayedLimit.isRemoved === true || displayedLimit.isUpdated === true }
298
- <div class="ExtraInfoContainer">
299
- {#if displayedLimit.isRemoved === true}
300
- <span>{$_('limitRemoved')}: {displayedLimit.expiresString}</span>
301
- {/if}
302
- {#if displayedLimit.isUpdated === true}
303
- <span>{$_('limitUpdated')}: {displayedLimit.expiresString}</span>
372
+ <div class={displayNone ? 'DisplayNone' : ''}>
373
+ <div class="LimitsContainer" bind:this={customStylingContainer}>
374
+ {#if noLimitToDisplay}
375
+ <div class="ContainerCenter">
376
+ <p>{$_('noLimitToDisplay')}</p>
377
+ </div>
378
+ {:else if errorMessage}
379
+ <div class="ContainerCenter">
380
+ <strong class="ErrorMessage">{errorMessage}</strong>
381
+ </div>
382
+ {:else}
383
+ <div class="ContentLeft">
384
+ <h2 class="LimitTypeHeader"><img class="HeaderIcon" alt="user" src="{faDollarCircleSvg}" />{getWidgetTitle(selectedLimitType)}</h2>
385
+ {#if isLoading}
386
+ <general-animation-loading clientstyling={clientstyling} clientstylingurl={clientstylingurl} />
387
+ {:else}
388
+ <div class="DetailsContainer Entries">
389
+ <span>{$_('spentAmount')}: </span>
390
+ <span>{$_('remainingAmount')}: </span>
391
+ {#if displayedLimit.formattedSchedule.isUpdated === true}
392
+ <span>{$_('futureAmount')}: </span>
304
393
  {/if}
394
+ <span>{$_('limitPeriod')}: </span>
395
+ <span>{$_('displayedProduct')}: </span>
305
396
  </div>
306
- {/if}
307
- {#if sessiontype === 'admin'}
308
- <div class="UsefulLinksSection">
309
- {#if transdetailsurl}
310
- <a href="{transdetailsurl}" target="_blank">{$_('additionalLink1')}</a>
311
- {:else if transactionspageurl}
312
- <a href="{transactionspageurl}" target="_blank">{$_('additionalLink2')}</a>
397
+ <div class="TextContainer">
398
+ <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.spentAmount} {displayedLimit.limitCurrency}</p>
399
+ <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.remainingAmount} {displayedLimit.limitCurrency}</p>
400
+ {#if displayedLimit.formattedSchedule.isUpdated === true}
401
+ <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.formattedSchedule.updateAmount} {displayedLimit.limitCurrency} </p>
313
402
  {/if}
403
+ <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.limitPeriod}</p>
404
+ <p><img class="TextIcon" alt="user" src="{faCaretRightSvg}" /> {displayedLimit.limitProducts}</p>
314
405
  </div>
406
+ {#if displayedLimit.formattedSchedule.isRemoved === true || displayedLimit.formattedSchedule.isUpdated === true }
407
+ <div class="ExtraInfoContainer">
408
+ {#if displayedLimit.formattedSchedule.isRemoved === true}
409
+ <span>{$_('limitRemoved')}: {displayedLimit.formattedSchedule.expiresString}</span>
410
+ {/if}
411
+ {#if displayedLimit.formattedSchedule.isUpdated === true}
412
+ <span>{$_('limitUpdated')}: {displayedLimit.formattedSchedule.expiresString}</span>
413
+ {/if}
414
+ </div>
415
+ {/if}
416
+ {#if sessiontype === 'admin'}
417
+ <div class="UsefulLinksSection">
418
+ {#if transdetailsurl}
419
+ <a href="{transdetailsurl}" target="_blank">{$_('additionalLink1')}</a>
420
+ {:else if transactionspageurl}
421
+ <a href="{transactionspageurl}" target="_blank">{$_('additionalLink2')}</a>
422
+ {/if}
423
+ </div>
424
+ {/if}
315
425
  {/if}
316
- {/if}
317
- {#if multipleDepositLimits}
318
- <div class="MultipleSelectContainer">
319
- <label for="LimitPeriod">{$_('changeLimitLabel')}: </label>
320
- <select bind:value={selectedLimitPeriod} id="LimitPeriod" on:change={handlePeriodChange}>
321
- {#each limitPeriodList as value}
322
- <option {value}>
323
- {value}
324
- </option>
325
- {/each}
326
- </select>
426
+ <div class="WidgetFooterControls">
427
+ {#if limitPeriodList.length > 0}
428
+ <div class="ControlContainer">
429
+ <label for="LimitPeriod">{$_('changeLimitLabel')}: </label>
430
+ <select bind:value={selectedLimitPeriod} id="LimitPeriod" on:change={handlePeriodChange}>
431
+ {#each limitPeriodList as value}
432
+ <option {value}>
433
+ {value}
434
+ </option>
435
+ {/each}
436
+ </select>
437
+ </div>
438
+ {/if}
439
+ {#if limitTypeList.length > 0}
440
+ <div class="ControlContainer">
441
+ <label for="LimitType">{$_('limitTypeLabel')}: </label>
442
+ <select bind:value={selectedLimitType} id="LimitType" on:change={getLimitBalance}>
443
+ {#each limitTypeList as value}
444
+ <option {value}>
445
+ {value}
446
+ </option>
447
+ {/each}
448
+ </select>
449
+ </div>
450
+ {/if}
327
451
  </div>
328
- {/if}
329
- </div>
330
- <div class="ContentRight">
331
- {#if isGaugeLoading}
332
- <general-animation-loading clientstyling={clientstyling} clientstylingurl={clientstylingurl} />
333
- {:else}
334
- <div class="AmountContainer">{displayedLimit.totalAmount} {displayedLimit.limitCurrency}</div>
335
- <div class="Gauge">
336
- <div class="GaugeBody">
337
- <div class="GaugeFill" style="--transform-value: rotate({gaugeValue}turn)"></div>
338
- <div class="GaugeCover">{displayedLimit.spentAmount} {displayedLimit.limitCurrency}</div>
339
- <div class="GaugeCoverMin"></div>
340
- <div class="GaugeNeedleCover"></div>
341
- <div class="GaugeNeedle" style="--transform-needle-value: rotate({gaugeNeedleValue}turn)"></div>
452
+ </div>
453
+ <div class="ContentRight">
454
+ {#if isGaugeLoading}
455
+ <general-animation-loading clientstyling={clientstyling} clientstylingurl={clientstylingurl} />
456
+ {:else}
457
+ <div class="AmountContainer">{displayedLimit.totalAmount} {displayedLimit.limitCurrency}</div>
458
+ <div class="Gauge">
459
+ <div class="GaugeBody">
460
+ <img class="Archbg" src="{archBgSvg}" alt="arch"/>
461
+ <div class="GaugeFill" style="--p:{gaugeValue}deg"></div>
462
+ <div class="GaugeCover">{displayedLimit.spentAmount} {displayedLimit.limitCurrency}</div>
463
+ <div class="GaugeNeedleCover"></div>
464
+ <div class="GaugeNeedle" style="--transform-needle-value: rotate({gaugeValue - 90}deg)"></div>
465
+ </div>
342
466
  </div>
343
- </div>
344
- <div class="MinMaxContainer">
345
- <div class="Min"> <strong class="MinContent">0</strong> <span class="MinContentCurrency">{displayedLimit.limitCurrency}</span></div>
346
- <div class="Max"> <strong class="MaxContent">{displayedLimit.totalAmount}</strong> <span class="MaxContentCurrency">{displayedLimit.limitCurrency}</span></div>
347
- </div>
348
- {/if}
349
- </div>
350
- {/if}
467
+ <div class="MinMaxContainer">
468
+ <div class="Min"> <strong class="MinContent">0</strong> <span class="MinContentCurrency">{displayedLimit.limitCurrency}</span></div>
469
+ <div class="Max"> <strong class="MaxContent">{displayedLimit.totalAmount}</strong> <span class="MaxContentCurrency">{displayedLimit.limitCurrency}</span></div>
470
+ </div>
471
+ {/if}
472
+ </div>
473
+ {/if}
474
+ </div>
351
475
  </div>
352
476
 
353
477
 
354
478
  <style lang="scss">
355
479
  $primary-text-color: var(--emw--color-gray-150, #a9b6ce);
356
- $primary-gauge-fill-color: var(--emw--color-primary, #D0046C);
480
+ $primary-color: var(--emw--color-primary, #D0046C);
481
+ $primary-gauge-fill-color: var(--emw--gauge-fill-bg, $primary-color);
357
482
  $secondary-gauge-background-color: var(--emw--color-gray-100, #E6E6E6);
358
483
  $primary-contrast-color: var(--emw--color-contrast, #07072A);
359
484
 
@@ -368,6 +493,18 @@
368
493
  box-sizing: border-box;
369
494
  }
370
495
 
496
+ .DisplayNone {
497
+ display: none;
498
+ }
499
+
500
+ .ContainerCenter {
501
+ width: 100%;
502
+ display: flex;
503
+ justify-content: center;
504
+ align-items: center;
505
+ min-height: 219px;
506
+ }
507
+
371
508
  .ErrorMessage {
372
509
  margin: 0 15px;
373
510
  font-size: 12px;
@@ -383,18 +520,47 @@
383
520
  border-radius: 20px;
384
521
  overflow: hidden;
385
522
  box-shadow: 0.6em 0.6em 0.4em $primary-text-color;
523
+ }
386
524
 
387
- @container (max-width: 720px) {
388
- max-width: 355px;
525
+ .ContentLeft {
526
+ padding: 1rem;
527
+ width: 318px;
528
+ line-height: 20px;
529
+ }
530
+
531
+ .LimitTypeHeader {
532
+ display: flex;
533
+ align-items: center;
534
+ color: $primary-text-color;
535
+ margin-bottom: 20px;
536
+ gap: 5px;
537
+ }
538
+
539
+ .HeaderIcon {
540
+ height: 20px;
541
+ width: 20px;
542
+ color: $primary-text-color;
543
+ }
544
+
545
+ .DetailsContainer {
546
+ margin-bottom: 15px;
547
+ display: inline;
548
+ span {
549
+ float: left;
550
+ clear: both;
389
551
  }
390
552
  }
391
- .ContentLeft { padding: 1rem; width: 318px; line-height: 20px; }
392
- .LimitTypeHeader { color: $primary-text-color; margin-bottom: 20px; max-width: 225px; }
393
- .HeaderIcon { height: 20px; width: 20px; color: $primary-text-color; }
394
- .DetailsContainer { margin-bottom: 15px; display: inline;
395
- span { float: left; clear: both; }
553
+
554
+ .ContentRight {
555
+ min-width: 300px;
556
+ padding: 1rem;
557
+ display: flex;
558
+ justify-content:
559
+ center;
560
+ flex-direction:
561
+ column;
396
562
  }
397
- .ContentRight { min-width: 300px; padding: 1rem; display: flex; justify-content: center; flex-direction: column; }
563
+
398
564
  .AmountContainer {
399
565
  padding: 0.3rem;
400
566
  font-size: xx-large;
@@ -404,91 +570,91 @@
404
570
  box-shadow: 0.16em 0.16em 0.1em var(--emw--color-gray-150, #a9b6ce);
405
571
  background-color: #F5F5F5;
406
572
  }
407
- .TextContainer { display: inline-block; padding-left: 5px;
408
- p { width: 100%; display: block; clear: both; font-weight: bold; }
573
+
574
+ .TextContainer {
575
+ display: inline-block;
576
+ padding-left: 5px;
577
+ p {
578
+ width: 100%;
579
+ display: block;
580
+ clear: both;
581
+ font-weight: bold;
582
+ }
583
+ }
584
+
585
+ .ExtraInfoContainer {
586
+ padding: 1rem 0;
587
+ color: var(--emfe-w-color-red, #ed0909);
588
+ }
589
+
590
+ .UsefulLinksSection {
591
+ padding-top: 5px;
592
+ a {
593
+ float: left;
594
+ clear: both;
595
+ color: $primary-contrast-color;
596
+ }
409
597
  }
410
- .ExtraInfoContainer { padding: 1rem 0; color: var(--emfe-w-color-red, #ed0909); }
411
- .UsefulLinksSection { padding-top: 5px;
412
- a { float: left; clear: both; color: $primary-contrast-color; }
598
+
599
+ .WidgetFooterControls {
600
+ display: flex;
413
601
  }
414
- .MultipleSelectContainer { float: left; padding-top: 1rem; clear: both;
415
- label { float: left; padding-right: 5px; }
416
- select { float: right; background: $primary-text-color; border-radius: 5px; margin: 2px }
602
+
603
+ .ControlContainer {
604
+ label {
605
+ display: inline-block;
606
+ width: 100%;
607
+ font-size: 12px;
608
+ font-weight: bold;
609
+ }
417
610
  }
418
- .TextIcon { color: $primary-gauge-fill-color; }
611
+ .TextIcon { color: $primary-color; }
419
612
 
420
613
  .Gauge {
421
614
  width: 100%;
422
615
  font-family: 'Roboto', sans-serif;
423
616
  font-size: 32px;
424
617
  color: black;
425
- margin-top: 1rem;
618
+ margin: 1rem 0;
426
619
  }
427
620
 
428
621
  .GaugeBody {
429
622
  width: 100%;
430
- height: 0;
431
- padding-bottom: 50%;
432
- background: $secondary-gauge-background-color;
433
623
  position: relative;
434
- border-top-left-radius: 100% 200%;
435
- border-top-right-radius: 100% 200%;
436
- overflow: hidden;
437
- }
438
-
439
- .GaugeFill {
440
- position: absolute;
441
- top: 100%;
442
- left: 0;
443
- width: inherit;
444
- height: 100%;
445
- background: $primary-gauge-fill-color;
446
- transform-origin: center top;
447
- transform: var(--transform-value);
448
- transition: transform 0.2s ease-out;
624
+ text-align: center;
625
+ padding: 23px;
626
+ padding-bottom: 0;
449
627
  }
450
628
 
451
- .GaugeCover {
452
- width: 75%;
453
- height: 150%;
454
- background: var(--emw--color-white, #FFFFFF);
629
+ .Archbg {
455
630
  position: absolute;
456
- border-radius: 50%;
457
- top: 25%;
458
- left: 50%;
459
- transform: translateX(-50%);
460
-
461
- //Text
462
- display: flex;
463
- align-items: center;
464
- justify-content: center;
465
- padding-bottom: 47%;
466
- box-sizing: border-box;
467
- font-size: 20px;
631
+ right: 0;
632
+ bottom: 0;
468
633
  }
469
634
 
470
- .GaugeCoverMin {
471
- width: 50px;
472
- height: 50px;
635
+ .GaugeFill {
636
+ --p:0deg;
637
+ --b:25px;
638
+ border-radius:500px 500px 0 0;
473
639
  background: $primary-gauge-fill-color;
474
- position: relative;
475
- top: 6.7rem;
476
- left: 50%;
477
- border-radius: 50%;
478
- transform: translateX(-50%);
640
+ 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%;
641
+ -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%;
642
+ mask-composite:intersect;
643
+ -webkit-mask-composite:destination-in;
644
+ }
645
+
646
+ .GaugeFill::before {
647
+ content:"";
648
+ display:block;
649
+ padding-top:50%;
479
650
  }
480
651
 
481
- .GaugeNeedleCover {
482
- width: 8%;
483
- height: 16%;
484
- background: var(--emw--color-black, #000000);
652
+ .GaugeCover {
485
653
  position: absolute;
486
- top: 90%;
487
- left: 50%;
488
- border: 4px solid var(--emw--color-white, #FFFFFF);
489
- border-radius: 50%;
490
- transform: translateX(-50%);
491
- transition: transform 0.2s ease-out;
654
+ left: 0;
655
+ right: 0;
656
+ top: 50%;
657
+ font-size: 20px;
492
658
  }
493
659
 
494
660
  .GaugeNeedle {
@@ -498,13 +664,27 @@
498
664
  display: inline-block;
499
665
  left: 49.5%;
500
666
  position: absolute;
501
- bottom: 1%;
667
+ bottom: 0.1rem;
502
668
  transform: var(--transform-needle-value);
503
669
  transform-origin: bottom;
504
- transition: transform 0.2s ease-out;
505
670
  }
506
671
 
507
- .MinMaxContainer { }
508
- .Min { width: 50%; float: left; }
509
- .Max { width: 49%; float: right; text-align: right; }
672
+ .GaugeNeedleCover {
673
+ width: 60px;
674
+ height: 30px;
675
+ border-radius: 150px 150px 0 0;
676
+ background: var(--emw--color-black, #000000);
677
+ 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%);
678
+ position: absolute;
679
+ bottom: 0;
680
+ left: 50%;
681
+ border: 4px solid var(--emw--color-white, #FFFFFF);
682
+ border-bottom: 0;
683
+ transform: translateX(-50%);
684
+ }
685
+
686
+ .MinMaxContainer {
687
+ display: flex;
688
+ justify-content: space-between;
689
+ }
510
690
  </style>