· 3 years ago · Jul 18, 2022, 11:40 AM
1import { MONTH_NAME, MIN_SHOWN_DISCOUNT_PERCENT, MONTH_SHORT_NAME } from 'Helpers/constants';
2import { osName, OsTypes, osVersion, browserVersion, browserName, BrowserTypes, isChrome } from 'react-device-detect';
3import copy from 'copy-to-clipboard';
4import { config } from 'Config/config';
5import { ITranslation, translateText, REGION_CODE } from './translation-helper';
6import { IRegionData } from 'Redux/reducers/region';
7import { IForeignExchangeRate } from './foreign-exchange-helper';
8
9export function getStatic(fileName: string) {
10 return '/static/' + fileName;
11}
12
13export function getAbsoluteUrl(path?: string) {
14 const slashedPath = path ? (path.startsWith('/') ? path : '/' + path) : '';
15 return window.location.origin + slashedPath;
16}
17
18export function getClientSideValue<T>(value: T, deffaultFalue: T): T {
19 return typeof window !== 'undefined' ? value : deffaultFalue;
20}
21
22export function isNullOrUndefinedHelper(object: any): object is null | undefined {
23 return object === undefined || object === null;
24}
25export const isset = (input: any): boolean => {
26 return input !== undefined && input !== null;
27};
28
29export const getDefault = <T>(input: any, def: T): T => {
30 return isset(input) ? input : def;
31};
32
33export const safeCall = <T extends (...args: any[]) => any>(
34 fn: T | null | undefined,
35 ...args: Parameters<T>
36): ReturnType<T> | undefined => {
37 return fn && fn(...args);
38};
39
40export function isObjectEmptyHelper(obj: object) {
41 if (!obj || typeof obj !== 'object') return true;
42 return Object.keys(obj).length === 0;
43}
44
45export const isJson = (value: any) => {
46 if (
47 value !== undefined &&
48 value !== '' &&
49 value !== 0 &&
50 /^[\],:{}\s]*$/.test(
51 value
52 .replace(/\\["\\/bfnrtu]/g, '@')
53 .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?/g, ']')
54 .replace(/(?:^|:|,)(?:\s*\[)+/g, ''),
55 )
56 ) {
57 return true;
58 } else {
59 return false;
60 }
61};
62
63export const getJson = (value: any) => {
64 if (isJson(value)) {
65 return JSON.parse(value);
66 } else {
67 return value;
68 }
69};
70
71export function titleCase(str: string) {
72 if (!str || typeof str !== 'string') return str;
73 const splitStr = str.replace(/_/g, ' ').toLowerCase().split(' ');
74 for (let i = 0; i < splitStr.length; i++) {
75 splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1);
76 }
77
78 return splitStr.join(' ');
79}
80export function titleCaseUnder(str: string) {
81 if (!str || typeof str !== 'string') return str;
82 const splitStr = str.replace(/-/g, ' ').toLowerCase().split(' ');
83 for (let i = 0; i < splitStr.length; i++) {
84 splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1);
85 }
86
87 return splitStr.join(' ');
88}
89
90export function titleCaseFromCamelCase(str: string) {
91 if (!str || typeof str !== 'string') return str;
92 let result = str.replace(/([A-Z]+)/g, ' $1').replace(/([A-Z][a-z])/g, ' $1');
93 result = result.charAt(0).toUpperCase() + result.substring(1);
94
95 return result;
96}
97
98export function arrayMessage(inputData: { [rule: string]: string[] }) {
99 const properties = Object.keys(inputData);
100 let message = '';
101 for (const i of properties) {
102 message = i + ' ' + inputData[i];
103 }
104
105 return message;
106}
107
108export function errorMessage(errData: { [rule: string]: any }, fieldName = 'errorMessage') {
109 const properties = Object.keys(errData);
110 let message = '';
111 for (const i of properties) {
112 if (typeof errData[i][fieldName] !== 'undefined') {
113 message = message + errData[i][fieldName] + ' ';
114 }
115 }
116
117 return message;
118}
119
120export function numberLimit(nmb: number, nmbMax: number, nmbMin = 1) {
121 if (nmb > nmbMax) {
122 return nmbMax;
123 } else if (nmb < nmbMin) {
124 return nmbMin;
125 }
126
127 return nmb;
128}
129
130export function getValue(value: any, defaultVal: any = null) {
131 return typeof value === 'undefined' ? defaultVal : value;
132}
133
134export function getValueBoolean(value: any) {
135 return typeof value === 'undefined' ? false : value;
136}
137
138export function issetValue(value: any) {
139 return value === undefined || value === null ? false : value;
140}
141
142export function getObjRefVal(
143 objVal: any[], // Haystack
144 ref: string,
145 idx: number,
146 key = 'id',
147): any {
148 for (const val of objVal) {
149 if (val[key] !== undefined && val[key] === idx) {
150 return val[ref];
151 }
152 }
153
154 return null;
155}
156
157export function getObjectValue(
158 val: any, // Haystack
159 key: string | string[], // Needle
160 defVal: any = null, // Default Value
161): any {
162 const datChk = typeof key === 'string' ? [key] : key;
163 const isValid = val !== undefined && val[datChk[0]] !== undefined;
164 const result = isValid ? val[datChk[0]] : defVal;
165
166 if (isValid && datChk.length > 1) {
167 return getObjectValue(result, datChk.slice(1), defVal);
168 }
169
170 return result;
171}
172
173export function removeDuplicate(arrVal: string[]): any {
174 return [...new Set(arrVal)];
175}
176
177export function getJsonStorageData(name: string, defaultVal: any): any {
178 const storageData = localStorage.getItem(name);
179 return storageData !== null && isJson(storageData) ? JSON.parse(storageData) : defaultVal;
180}
181
182export function setJsonStorageData(name: string, val: any): any {
183 return localStorage.setItem(name, JSON.stringify(val));
184}
185
186export const moneyDot = (num: any | undefined | null) => {
187 if (num === null || num === undefined) {
188 return '';
189 } else if (num === 0) {
190 return 0;
191 }
192
193 const str = num.toString().split('.');
194
195 if (str[0].length >= 4) {
196 str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1.');
197 }
198 if (str[1] && str[1].length >= 4) {
199 str[1] = str[1].replace(/(\d{3})/g, '$1 ');
200 }
201 return str.join(',');
202};
203
204export const dynamicSort = (property: any) => {
205 let sortOrder = 1;
206 if (property[0] === '-') {
207 sortOrder = -1;
208 property = property.substr(1);
209 }
210 return (a: any, b: any) => {
211 const result = a[property] < b[property] ? -1 : a[property] > b[property] ? 1 : 0;
212 return result * sortOrder;
213 };
214};
215
216export const dateHelper = (date: string, hideTime?: boolean, shortMonth?: boolean) => {
217 if (!date) {
218 return '-';
219 }
220
221 const d = customDateConstructor(date);
222
223 const newDate =
224 d.getDate() + ' ' + (shortMonth ? MONTH_SHORT_NAME[d.getMonth()] : MONTH_NAME[d.getMonth()]) + ' ' + d.getFullYear();
225 const newTime = zeroFormat(d.getHours()) + ':' + zeroFormat(d.getMinutes());
226
227 return newDate + (hideTime ? '' : ' pukul ' + newTime);
228};
229
230// use this function if need to convert timezone by region in FE
231export const timezoneDateHelper = (date: string, region: IRegionData | any, hideTime?: boolean, shortMonth?: boolean, showDay?: boolean) => {
232 if (!date) {
233 return '-';
234 }
235
236 const d = customDateConstructor(date);
237 const timeZone = region ? region.country_timezone : 'Asia/Jakarta';
238 const locale = region ? region.default_locale_id : 'id-ID';
239 const dayOptions: any = {
240 timeZone: timeZone,
241 weekday: 'long',
242 };
243 const dateOptions: any = {
244 timeZone: timeZone,
245 year: 'numeric',
246 month: shortMonth == undefined ? 'long' : (shortMonth ? 'short' : 'long'),
247 day: 'numeric',
248 };
249 const timeOptions: any = {
250 timeZone: timeZone,
251 hour: 'numeric',
252 minute: 'numeric',
253 };
254
255 if (!region || region.country_code == REGION_CODE.INDONESIA) {
256 return dateHelper(date, hideTime, shortMonth);
257 } else {
258 let options = { ...dateOptions };
259 if (!hideTime) {
260 options = { ...options, ...timeOptions };
261 }
262 if (showDay) {
263 options = { ...options, ...dayOptions };
264 }
265 return d.toLocaleString(locale, options);
266 }
267};
268
269export const customDateConstructor = (date: string) => {
270 return isIos() || isSafari() ? convertDateForIos(date) : new Date(date);
271};
272
273export const detailedDateHelper = (date: string, hideTime?: boolean) => {
274 const d = customDateConstructor(date);
275 const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'];
276
277 const newDate = d.getDate() + ' ' + monthNames[d.getMonth()] + ' ' + d.getFullYear();
278 const newTime =
279 ' - ' + zeroFormat(d.getHours()) + ':' + zeroFormat(d.getMinutes()) + ':' + zeroFormat(d.getSeconds());
280
281 return newDate + (hideTime ? '' : ' ' + newTime);
282};
283
284export function convertDateForIos(date: string) {
285 const normalDate = new Date(date);
286 if (isNaN(normalDate.getDate())) {
287 const arr = date.split(/[- :]/);
288 const intArray = arr.map((element) => parseInt(element));
289 const parsedDate = new Date(intArray[0], intArray[1] - 1, intArray[2], intArray[3], intArray[4], intArray[5]);
290 return parsedDate;
291 } else {
292 return normalDate;
293 }
294}
295
296export const zeroFormat = (num: any) => {
297 return num < 10 ? '0' + num : num;
298};
299
300export const objectSearch = (value: any, nameKey: any, myArray: any) => {
301 for (let i = 0; i < myArray.length; i++) {
302 if (myArray[i][nameKey] === value) {
303 return i;
304 }
305 }
306 return false;
307};
308
309export const inArray = (needle: number | string, haystack: any[]): boolean => {
310 const length = haystack.length;
311 for (let i = 0; i < length; i++) {
312 if (haystack[i] === needle) {
313 return true;
314 }
315 }
316 return false;
317};
318
319export const isFile = (value: any, checkType?: string) => {
320 return (
321 value !== undefined &&
322 value.name !== undefined &&
323 value.size !== undefined &&
324 (checkType === undefined || value.name.substring(value.name.lastIndexOf('.') + 1) === checkType)
325 );
326};
327
328export const processCSV = (fileStream: string | ArrayBuffer | null) => {
329 let csvObj: any = [];
330 if (typeof fileStream === 'string') {
331 csvObj = fileStream.split('\n');
332 if (csvObj.length) {
333 csvObj.forEach((el: string, idx: number) => {
334 csvObj[idx] = el.split(',');
335 });
336 }
337 }
338
339 return csvObj;
340};
341
342export function isSupportWebp() {
343 const isMac = osName === OsTypes.MAC_OS;
344 const isApple = osName === OsTypes.IOS;
345 if (isApple) {
346 const isNewSafari = isSafari() && parseFloat(browserVersion) >= 14;
347 return isNewSafari || isChrome;
348 }
349 if (isMac) {
350 const isMacBigSur = parseFloat(osVersion) >= 11;
351 const isNewSafari = browserName === BrowserTypes.Safari && parseFloat(browserVersion) >= 14;
352 return isMacBigSur && isNewSafari;
353 } else {
354 return true;
355 }
356}
357
358export function isNaNOrUndefinedHelper<T = number>(
359 userIdNumber: number | undefined,
360 defaultValue: T | null = null,
361): T | number | null {
362 return userIdNumber ? (isNaN(userIdNumber) ? defaultValue : userIdNumber) : defaultValue;
363}
364
365export const isImage = (extension?: string) => {
366 const imageExtension = ['.webp', '.png', '.jpg', '.jpeg'];
367 return extension && imageExtension.includes(extension);
368};
369
370export function newGenImage(url: string) {
371 const isHttp = url?.slice(0, 4) === 'http';
372
373 if (isHttp && url) {
374 const urlExtension = url.split('.').pop();
375 // because some image return like this
376 const newUrl = isImage(`.${urlExtension}`) ? url?.replace(`.${urlExtension}`, '.webp') : url + '.webp';
377 return newUrl;
378 } else {
379 return url;
380 }
381}
382
383export const phoneFormatHelper = (phoneNumber: string, reverse?: boolean) => {
384 if (phoneNumber) {
385 if (reverse) {
386 if (phoneNumber.substring(0, 3) === '628') {
387 return '08' + phoneNumber.substring(3);
388 }
389 } else if (phoneNumber.substring(0, 2) === '08') {
390 return '628' + phoneNumber.substring(2);
391 }
392 return phoneNumber;
393 }
394 return '';
395};
396
397export const phoneVisualFormatHelper = (phonenumber: string) => {
398 phonenumber = phoneFormatHelper(phonenumber, true);
399 return `${phonenumber.slice(0, 4)}-${phonenumber.slice(4, 8)}-${phonenumber.slice(8)}`;
400};
401
402export function isMobile() {
403 return typeof window !== 'undefined' ? /(android|iphone|ipad|mobile)/i.test(navigator.userAgent) : false;
404}
405
406export function isIos() {
407 return typeof window !== 'undefined'
408 ? /iPad|iPhone|iPod|Mac/.test(navigator.platform) ||
409 (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
410 : false;
411}
412
413export function isSafari() {
414 return typeof window !== 'undefined' ? /^((?!chrome|android).)*safari/i.test(navigator.userAgent) : false;
415}
416
417export function getBlobUrlFromFile(file: File) {
418 return URL.createObjectURL(file);
419}
420
421export async function blobUrlToFile(url: string, filename: string, mimeType: string) {
422 const options = {
423 type: mimeType,
424 };
425 return window
426 .fetch(url)
427 .then((res: any) => {
428 return res.blob();
429 })
430 .then((blob: any) => {
431 return new window.File([blob], filename, options);
432 });
433}
434
435export function secondsToDayHourMinuteFormat(seconds: number, translation: ITranslation) {
436 const days = Math.floor(seconds / (3600 * 24));
437 const hour = Math.floor((seconds % (3600 * 24)) / 3600);
438 const minute = Math.floor((seconds % 3600) / 60);
439
440 const dayText = translateText('{{count}}_hari', translation.languageCode, translation.languageList, { count: days });
441 const hourText = translateText('{{count}}_jam', translation.languageCode, translation.languageList, { count: hour });
442 const minuteText = translateText('{{count}}_menit', translation.languageCode, translation.languageList, { count: minute });
443
444 const dDisplay = days > 0 ? dayText + ' ' : '';
445 const hDisplay = hour > 0 ? hourText + ' ' : '';
446 const mDisplay = minute > 0 ? minuteText : '';
447
448 return dDisplay + hDisplay + mDisplay;
449}
450
451export const localNumberUnitFormat = (inNumber: number, translation: ITranslation) => {
452 const millionText = translateText('juta', translation.languageCode, translation.languageList);
453 const billionText = translateText('milyar', translation.languageCode, translation.languageList);
454 const trillionText = translateText('trilliun', translation.languageCode, translation.languageList);
455
456 const unit = ['', millionText, billionText, trillionText];
457 const unitAmount = [1, 1000000, 1000000000, 1000000000000];
458 let biggestUnitIndex = -1;
459 let numberByUnit = 0.0;
460 for (let i = unit.length - 1; i >= 0; i--) {
461 if (inNumber >= unitAmount[i]) {
462 biggestUnitIndex = i;
463 numberByUnit = inNumber / unitAmount[i];
464 break;
465 }
466 }
467 let unitString = '';
468 if (biggestUnitIndex >= 0) {
469 unitString = unit[biggestUnitIndex];
470 if (unitString !== '') {
471 unitString = ' ' + unitString;
472 }
473 }
474 return numberByUnit + unitString;
475};
476// try to take from laravel https://github.com/laravel/framework/blob/9.x/src/Illuminate/Support/Str.php
477export function convertToSlug(str: string) {
478 if (typeof str !== 'string') return str;
479 return str
480 .toLowerCase()
481 // Convert all underscores into -
482 .replace(/_/g, ' ')
483 // Remove all characters that are not -, letters, numbers, or whitespace.
484 .replace(/[^\w\s-]+/g, '')
485 // Convert all white space into singgle space
486 .replace(/\s+/g, ' ')
487 .trim()
488 // Convert all white space to -
489 .replace(/ /g, '-');
490}
491
492export function stringToBoolean(str: string) {
493 let bool;
494 if (str === 'true') {
495 bool = true;
496 } else if (str === '1') {
497 bool = true;
498 } else {
499 bool = false;
500 }
501 return bool;
502}
503
504export function arrayToSentence(arr: string[]) {
505 if (arr.length === 0) return '';
506 if (arr.length === 1) return arr[0];
507 const strArr = [...arr];
508 const last = strArr.pop();
509 return strArr.join(', ') + ' dan ' + last;
510}
511
512export function isSlug(params: string) {
513 return /^[\w\s-]+$/.test(params);
514}
515
516export function isAlphaNumericSpace(str: string) {
517 return /^[\w\s]+$/.test(str);
518}
519
520export function isProductKeywordSafe(str: string) {
521 return /^[\w\s-()|'.+,!\\/$]+$/.test(str);
522}
523
524export const stringToSlug = (input: string) => {
525 input = input.replace(/^\s+|\s+$/g, ''); // trim
526 input = input.toLowerCase();
527
528 input = input
529 .replace(/[^a-z0-9 -]/g, '')
530 .replace(/\s+/g, '-')
531 .replace(/-+/g, '-');
532
533 return input;
534};
535export const getRandomNumber = (min: number, max: number) => {
536 return Math.floor(Math.random() * (max - min) + min);
537};
538export function arrayObjectToArray(obj: Array<object>, index: string) {
539 const temp: any = [];
540 const objTemp: any = {};
541
542 try {
543 obj.forEach((data: any) => {
544 temp.push(data[index]);
545 objTemp[data[index]] = data;
546 });
547 return { temp, objTemp };
548 } catch (e) {
549 return { temp, objTemp };
550 }
551}
552
553export function filterObjectUndefinedProp(data: any) {
554 return Object.keys(data).reduce((acc: any, key: string) => {
555 const value = data[key];
556 return value === undefined || value === '' ? acc : { ...acc, [key]: data[key] };
557 }, {});
558}
559export function addSpaceOn4thChar(value: any) {
560 if (value) {
561 value = value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ');
562 value = value.trimEnd();
563 return value;
564 } else {
565 return '';
566 }
567}
568
569export function toCommaDelimiter(number: number) {
570 return number.toString().split('.').join(',');
571}
572
573export function formatMilisecondsToTimeRemaining(duration: number) {
574 if (duration < 0) return '00:00:00';
575 const seconds = Math.floor((duration / 1000) % 60);
576 const minutes = Math.floor((duration / (1000 * 60)) % 60);
577 const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
578
579 const strHours = hours < 10 ? '0' + hours : hours;
580 const strMinutes = minutes < 10 ? '0' + minutes : minutes;
581 const strSeconds = seconds < 10 ? '0' + seconds : seconds;
582
583 return strHours + ':' + strMinutes + ':' + strSeconds;
584}
585
586export function numberToDayOfWeek(number: number) {
587 if (number > 6 || number < 0) return '';
588 const day = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
589 return day[number];
590}
591
592export function unixTimeToDayInNumber(date: string) {
593 const dateTest = Date.parse(date);
594 const today = new Date().getTime();
595 const expireDay = Math.round((dateTest - today) / 86400000);
596 return expireDay;
597}
598
599export function formatDayDateTimeWIB(date: Date) {
600 const day = numberToDayOfWeek(date.getDay());
601 const formattedDate = dateHelper(date.toString());
602 return `${day}, ${formattedDate} WIB`;
603}
604
605// use this function if need to convert timezone by region in FE
606export function timezoneFormatDayDateTime(date: Date, region: IRegionData) {
607 if (!region || region.country_code == REGION_CODE.INDONESIA) {
608 const formattedDate = dateHelper(date.toString());
609 const day = numberToDayOfWeek(date.getDay());
610 return `${day}, ${formattedDate} WIB`;
611 } else {
612 return timezoneDateHelper(date.toString(), region, false, false, true);
613 }
614}
615
616export function msToMinutes(ms: number) {
617 return Math.floor(ms / 60000);
618}
619
620export function timeIsBetweenHours(time: Date, startHour: number, endHour: number, options?: { gmt?: number }) {
621 let currMinutes = time.getHours() * 60 + time.getMinutes();
622 if (options && options.gmt !== null && options.gmt !== undefined) {
623 const offsetMins = time.getTimezoneOffset();
624 const utcMinutes = currMinutes + offsetMins;
625 currMinutes = utcMinutes + options.gmt * 60;
626 }
627 if (currMinutes < 0) currMinutes += 1440;
628 if (currMinutes >= 1440) currMinutes -= 1440;
629 const startMinutes = startHour * 60;
630 const endMinutes = endHour * 60;
631 return currMinutes > startMinutes && currMinutes < endMinutes;
632}
633
634export function isGmail(data: string) {
635 const mailFormat = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
636 if (data.match(mailFormat)) {
637 const mailSplit = data.split('@');
638 if (mailSplit[1].search(/gmail/) == 0) {
639 return true;
640 } else {
641 return false;
642 }
643 } else {
644 return false;
645 }
646}
647
648export function arrayIntersects(array1: any[], array2: any[]) {
649 if (array1) {
650 return array1.some((item: any) => array2.includes(item));
651 }
652}
653
654export function discountCalc(price: number, competitorPrice: number) {
655 if (competitorPrice) {
656 const value = Math.round(((competitorPrice - price) / competitorPrice) * 100);
657 return value >= MIN_SHOWN_DISCOUNT_PERCENT ? value : 0;
658 }
659 return 0;
660}
661
662export function getWithdrawAmount(balance: number, topupLimit: number, options = { multiplier: 1000, minimum: 20000 }) {
663 if (topupLimit >= balance) return 0;
664 const difference = balance - topupLimit;
665 if (difference < options.minimum) return options.minimum;
666 const withdraw = Math.ceil(difference / options.multiplier) * options.multiplier;
667 return withdraw;
668}
669
670export function getTopUpAmount(balance: number, totalPayment: number, options = { multiplier: 1000, minimum: 10000 }) {
671 if (totalPayment <= balance) return 0;
672 const difference = totalPayment - balance;
673 if (difference < options.minimum) return options.minimum;
674 const topup = Math.ceil(difference / options.multiplier) * options.multiplier;
675 return topup;
676}
677
678export function getDayCountBetween(startDate: Date, endDate: Date) {
679 const millisecondsPerDay = 24 * 60 * 60 * 1000;
680 return (endDate.getTime() - startDate.getTime()) / millisecondsPerDay;
681}
682export const doCopy = (text: string) => {
683 navigator.clipboard.writeText(text);
684};
685export const toPascalCase = (text: string | undefined) => {
686 if (!text || typeof text !== 'string') return text;
687 return text
688 .replace(/(\w)(\w*)/g, (_g0, g1, g2) => {
689 return g1.toUpperCase() + g2.toLowerCase();
690 })
691 .replace(/\s/g, '');
692};
693export const arrayObjectToArrayValue = (array: any[], valueName: string) => {
694 return array.map((val: any) => {
695 return val[valueName];
696 });
697};
698
699export function durationHandler(startTime: string, endTime: string) {
700 const startDate = customDateConstructor(startTime);
701 const endData = customDateConstructor(endTime);
702 const isSameMonth = startDate.getMonth() === endData.getMonth();
703 const isSameYear = startDate.getFullYear() === endData.getFullYear();
704 let startMounth = '';
705 const startYear = isSameYear ? '' : `${startDate.getFullYear()} `;
706 if (isSameYear) {
707 startMounth = isSameMonth ? '' : MONTH_NAME[startDate.getMonth()] + ' ';
708 } else {
709 startMounth = MONTH_NAME[startDate.getMonth()] + ' ';
710 }
711
712 const initalString = `${startDate.getDate()} ${startMounth}${startYear}`;
713 return `${initalString} - ${dateHelper(endTime, true)}`;
714}
715
716export function addDurationDate(date: string, duration: number, region: IRegionData) {
717 date = date.replace(/ /g, 'T');
718 const endDate = new Date(date);
719 endDate.setDate(endDate.getDate() + duration);
720
721 const customEndDate = customDateConstructor(endDate.toString());
722 return timezoneDateHelper(customEndDate.toString(), region, false, false, false);
723}
724
725export async function copyText(text: string) {
726 if (typeof text !== 'string') return;
727 const fallbackCopy = () => {
728 try {
729 copy(text);
730 } catch (error) {
731 // Give up trying to copy
732 throw Error(error as any);
733 }
734 };
735
736 // New clipboard API for modern browser
737 if (navigator?.clipboard?.writeText) {
738 try {
739 await navigator.clipboard.writeText(text);
740 } catch (error) {
741 fallbackCopy();
742 }
743 } else {
744 // Fallback for old browser, update if document.execCommand('copy') is not longer supported.
745 fallbackCopy();
746 }
747}
748
749export const stringBuilder = (text: string, params: Record<string, string>) => {
750 let tempText = text;
751 Object.keys(params).forEach((key) => {
752 tempText = tempText.replace(`{${key}}`, params[key]);
753 });
754 return tempText;
755};
756
757export const getMsDifferenceBetweenDates = (
758 startDate: Date,
759 endDate: Date,
760 options?: { startDateGmt?: number; endDateGmt?: number },
761) => {
762 const minutesToMs = 60000;
763
764 let startTime = startDate.getTime();
765 if (options && options.startDateGmt !== null && options.startDateGmt !== undefined) {
766 const offsetTime = startDate.getTimezoneOffset() * minutesToMs;
767 const utcTime = startTime + offsetTime;
768 startTime = utcTime + options.startDateGmt * 60 * minutesToMs;
769 }
770
771 let endTime = endDate.getTime();
772 if (options && options.endDateGmt !== null && options.endDateGmt !== undefined) {
773 const offsetTime = endDate.getTimezoneOffset() * minutesToMs;
774 const utcTime = endTime + offsetTime;
775 endTime = utcTime + options.endDateGmt * 60 * minutesToMs;
776 }
777
778 return endTime - startTime;
779};
780
781export const stringToArrayNumber = (string: string) => {
782 if (string) {
783 return string.slice(1, -1).split(',').map(Number);
784 }
785 return [];
786};
787
788export const sleep = (ms: number) => {
789 return new Promise((resolve) => setTimeout(resolve, ms));
790};
791
792export const removeUndefinedValue = (value: { [key: string]: any }) => {
793 const newObjectValue = { ...value };
794 Object.keys(newObjectValue).forEach((key: any) => {
795 if (newObjectValue[key] === null || newObjectValue[key] === undefined) {
796 delete newObjectValue[key];
797 }
798 });
799 return newObjectValue;
800};
801
802export const replaceUndefinedPropsToNull = <T>(object: { [key: string]: any }) => {
803 if (!object || typeof object !== 'object') return object;
804 for (const key in object) {
805 if (object[key] === undefined) object[key] = null;
806 }
807 return object as T;
808};
809
810export const parseStringArray = <T>(str: string) => {
811 try {
812 const result = JSON.parse(str);
813 if (!Array.isArray(result)) return [];
814 return result as T[];
815 } catch {
816 return [] as T[];
817 }
818};
819
820export const isProduction = () => {
821 return config.environment === 'production';
822};
823
824export const isDevelopment = () => {
825 return config.environment === 'development' || config.environment === 'local';
826};
827
828export const capitalizeFirstLetter = (str: string) => {
829 if (!str || typeof str !== 'string') return str;
830 return str.charAt(0).toUpperCase() + str.slice(1);
831};
832
833export const isNotificationAPISupported = (): boolean => (typeof window !== 'undefined') && ('Notification' in window);
834
835export const replaceAll = (text: string, key: string, replace: string) => {
836 return text.replace(new RegExp(key, 'g'), replace);
837
838};
839
840export const exchangeRateConversionHelper = (foreignExchangeRate: IForeignExchangeRate | any, region: IRegionData | any, amount: number | null): number => {
841 if (!amount || !foreignExchangeRate) { return 0; }
842 if ((region.country_code == REGION_CODE.INDONESIA) || foreignExchangeRate.source_currency == 'IDR' && foreignExchangeRate.target_currency == 'IDR') {
843 return amount;
844 }
845 const locale = region ? region.default_locale_id : 'id-ID';
846 // eslint-disable-next-line @babel/new-cap
847 const formater = Intl.NumberFormat(locale, {
848 maximumFractionDigits: 2,
849 });
850 return parseFloat(formater.format((amount / foreignExchangeRate.exchange_rate)));
851};
852
853export const currencyHelper = (foreignExchangeRate: IForeignExchangeRate | any, region: IRegionData | any, amount: number | null, isConverted: boolean = false): string => {
854 if (amount == null || amount == undefined || !foreignExchangeRate) { return ''; }
855 if (amount <= 0) { amount = 0; }
856 if ((region.country_code == REGION_CODE.INDONESIA) || foreignExchangeRate.source_currency == 'IDR' && foreignExchangeRate.target_currency == 'IDR') {
857 return `Rp${moneyDot(amount)}`;
858 }
859 const locale = region ? region.default_locale_id : 'id-ID';
860 let convertedAmounts = amount / foreignExchangeRate.exchange_rate;
861 let truncVal = Math.trunc(convertedAmounts * 1000) / 1000;
862 let roundedVal = Math.round((truncVal + Number.EPSILON) * 100) / 100;
863 let result = (isConverted ? amount : roundedVal).toLocaleString(locale, { style: 'currency', currency: foreignExchangeRate.source_currency, maximumFractionDigits: 2 });
864 return result;
865};
866